summaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorGravatar darkf2014-12-29 19:47:41 -0800
committerGravatar darkf2014-12-29 19:47:41 -0800
commit8ba9ac0f74abb0408a26207a76a0c1808bad8de0 (patch)
treef1c7c3393fa726435b5b90bf335567c93e528ef1 /src/core/hle
parentAdd comment regarding __WIN32__ in SkyEye code (diff)
parentMerge pull request #367 from bunnei/usat_ssat (diff)
downloadyuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.gz
yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.tar.xz
yuzu-8ba9ac0f74abb0408a26207a76a0c1808bad8de0.zip
Fix merge conflicts
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/config_mem.cpp5
-rw-r--r--src/core/hle/config_mem.h2
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h16
-rw-r--r--src/core/hle/hle.cpp28
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp19
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/archive.cpp429
-rw-r--r--src/core/hle/kernel/archive.h85
-rw-r--r--src/core/hle/kernel/event.cpp17
-rw-r--r--src/core/hle/kernel/event.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp139
-rw-r--r--src/core/hle/kernel/kernel.h217
-rw-r--r--src/core/hle/kernel/mutex.cpp108
-rw-r--r--src/core/hle/kernel/mutex.h8
-rw-r--r--src/core/hle/kernel/semaphore.cpp95
-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.cpp23
-rw-r--r--src/core/hle/kernel/shared_memory.h16
-rw-r--r--src/core/hle/kernel/thread.cpp136
-rw-r--r--src/core/hle/kernel/thread.h22
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/ac_u.cpp25
-rw-r--r--src/core/hle/service/ac_u.h8
-rw-r--r--src/core/hle/service/act_u.cpp24
-rw-r--r--src/core/hle/service/act_u.h23
-rw-r--r--src/core/hle/service/am_app.cpp24
-rw-r--r--src/core/hle/service/am_app.h23
-rw-r--r--src/core/hle/service/am_net.cpp5
-rw-r--r--src/core/hle/service/am_net.h8
-rw-r--r--src/core/hle/service/apt_a.cpp34
-rw-r--r--src/core/hle/service/apt_a.h23
-rw-r--r--src/core/hle/service/apt_u.cpp165
-rw-r--r--src/core/hle/service/apt_u.h9
-rw-r--r--src/core/hle/service/boss_u.cpp21
-rw-r--r--src/core/hle/service/boss_u.h22
-rw-r--r--src/core/hle/service/cecd_u.cpp24
-rw-r--r--src/core/hle/service/cecd_u.h23
-rw-r--r--src/core/hle/service/cfg/cfg.cpp202
-rw-r--r--src/core/hle/service/cfg/cfg.h144
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp110
-rw-r--r--src/core/hle/service/cfg/cfg_i.h (renamed from src/core/hle/service/cfg_i.h)8
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp192
-rw-r--r--src/core/hle/service/cfg/cfg_u.h (renamed from src/core/hle/service/cfg_u.h)8
-rw-r--r--src/core/hle/service/cfg_i.cpp59
-rw-r--r--src/core/hle/service/cfg_u.cpp36
-rw-r--r--src/core/hle/service/csnd_snd.cpp5
-rw-r--r--src/core/hle/service/csnd_snd.h8
-rw-r--r--src/core/hle/service/dsp_dsp.cpp199
-rw-r--r--src/core/hle/service/dsp_dsp.h10
-rw-r--r--src/core/hle/service/err_f.cpp20
-rw-r--r--src/core/hle/service/err_f.h22
-rw-r--r--src/core/hle/service/frd_u.cpp36
-rw-r--r--src/core/hle/service/frd_u.h22
-rw-r--r--src/core/hle/service/fs/archive.cpp442
-rw-r--r--src/core/hle/service/fs/archive.h134
-rw-r--r--src/core/hle/service/fs/fs_user.cpp601
-rw-r--r--src/core/hle/service/fs/fs_user.h (renamed from src/core/hle/service/fs_user.h)19
-rw-r--r--src/core/hle/service/fs_user.cpp409
-rw-r--r--src/core/hle/service/gsp_gpu.cpp78
-rw-r--r--src/core/hle/service/gsp_gpu.h10
-rw-r--r--src/core/hle/service/hid_user.cpp14
-rw-r--r--src/core/hle/service/hid_user.h14
-rw-r--r--src/core/hle/service/http_c.cpp64
-rw-r--r--src/core/hle/service/http_c.h23
-rw-r--r--src/core/hle/service/ir_rst.cpp5
-rw-r--r--src/core/hle/service/ir_rst.h10
-rw-r--r--src/core/hle/service/ir_u.cpp5
-rw-r--r--src/core/hle/service/ir_u.h8
-rw-r--r--src/core/hle/service/ldr_ro.cpp29
-rw-r--r--src/core/hle/service/ldr_ro.h23
-rw-r--r--src/core/hle/service/mic_u.cpp5
-rw-r--r--src/core/hle/service/mic_u.h8
-rw-r--r--src/core/hle/service/ndm_u.cpp5
-rw-r--r--src/core/hle/service/ndm_u.h10
-rw-r--r--src/core/hle/service/news_u.cpp25
-rw-r--r--src/core/hle/service/news_u.h23
-rw-r--r--src/core/hle/service/nim_aoc.cpp31
-rw-r--r--src/core/hle/service/nim_aoc.h23
-rw-r--r--src/core/hle/service/nwm_uds.cpp5
-rw-r--r--src/core/hle/service/nwm_uds.h8
-rw-r--r--src/core/hle/service/pm_app.cpp5
-rw-r--r--src/core/hle/service/pm_app.h8
-rw-r--r--src/core/hle/service/ptm_u.cpp99
-rw-r--r--src/core/hle/service/ptm_u.h8
-rw-r--r--src/core/hle/service/service.cpp35
-rw-r--r--src/core/hle/service/service.h63
-rw-r--r--src/core/hle/service/soc_u.cpp5
-rw-r--r--src/core/hle/service/soc_u.h8
-rw-r--r--src/core/hle/service/srv.cpp19
-rw-r--r--src/core/hle/service/srv.h11
-rw-r--r--src/core/hle/service/ssl_c.cpp5
-rw-r--r--src/core/hle/service/ssl_c.h10
-rw-r--r--src/core/hle/svc.cpp126
-rw-r--r--src/core/hle/svc.h2
97 files changed, 3610 insertions, 1834 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index c7cf5b1d3..721a600b5 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -1,8 +1,9 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/log.h"
6 7
7#include "core/hle/config_mem.h" 8#include "core/hle/config_mem.h"
8 9
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
54 break; 55 break;
55 56
56 default: 57 default:
57 ERROR_LOG(HLE, "unknown addr=0x%08X", addr); 58 LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
58 } 59 }
59} 60}
60 61
diff --git a/src/core/hle/config_mem.h b/src/core/hle/config_mem.h
index fa01b5cdb..3975af18f 100644
--- a/src/core/hle/config_mem.h
+++ b/src/core/hle/config_mem.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index e34229a57..425959be4 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/coprocessor.h" 5#include "core/hle/coprocessor.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 3dbe25037..3259ce9eb 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
114 FuncReturn(retval); 114 FuncReturn(retval);
115} 115}
116 116
117template<s32 func(u32*, s32, s32)> void Wrap() {
118 u32 param_1 = 0;
119 u32 retval = func(&param_1, PARAM(1), PARAM(2));
120 Core::g_app_core->SetReg(1, param_1);
121 FuncReturn(retval);
122}
123
124template<s32 func(s32*, u32, s32)> void Wrap() {
125 s32 param_1 = 0;
126 u32 retval = func(&param_1, PARAM(1), PARAM(2));
127 Core::g_app_core->SetReg(1, param_1);
128 FuncReturn(retval);
129}
130
117//////////////////////////////////////////////////////////////////////////////////////////////////// 131////////////////////////////////////////////////////////////////////////////////////////////////////
118// Function wrappers that return type u32 132// Function wrappers that return type u32
119 133
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b8ac186f6..33ac12507 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <vector> 5#include <vector>
@@ -8,6 +8,8 @@
8#include "core/hle/hle.h" 8#include "core/hle/hle.h"
9#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
10#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
11#include "core/hle/service/fs/archive.h"
12#include "core/hle/service/cfg/cfg.h"
11 13
12//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
13 15
@@ -20,7 +22,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
20const FunctionDef* GetSVCInfo(u32 opcode) { 22const FunctionDef* GetSVCInfo(u32 opcode) {
21 u32 func_num = opcode & 0xFFFFFF; // 8 bits 23 u32 func_num = opcode & 0xFFFFFF; // 8 bits
22 if (func_num > 0xFF) { 24 if (func_num > 0xFF) {
23 ERROR_LOG(HLE,"unknown svc=0x%02X", func_num); 25 LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
24 return nullptr; 26 return nullptr;
25 } 27 }
26 return &g_module_db[0].func_table[func_num]; 28 return &g_module_db[0].func_table[func_num];
@@ -35,15 +37,21 @@ void CallSVC(u32 opcode) {
35 if (info->func) { 37 if (info->func) {
36 info->func(); 38 info->func();
37 } else { 39 } else {
38 ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str()); 40 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
39 } 41 }
40} 42}
41 43
42void Reschedule(const char *reason) { 44void Reschedule(const char *reason) {
43#ifdef _DEBUG 45 _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
44 _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason."); 46
45#endif 47 // TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE
48 // routines. This simulates that time by artificially advancing the number of CPU "ticks".
49 // The value was chosen empirically, it seems to work well enough for everything tested, but
50 // is likely not ideal. We should find a more accurate way to simulate timing with HLE.
51 Core::g_app_core->AddTicks(4000);
52
46 Core::g_app_core->PrepareReschedule(); 53 Core::g_app_core->PrepareReschedule();
54
47 g_reschedule = true; 55 g_reschedule = true;
48} 56}
49 57
@@ -58,18 +66,22 @@ void RegisterAllModules() {
58 66
59void Init() { 67void Init() {
60 Service::Init(); 68 Service::Init();
69 Service::FS::ArchiveInit();
70 Service::CFG::CFGInit();
61 71
62 RegisterAllModules(); 72 RegisterAllModules();
63 73
64 NOTICE_LOG(HLE, "initialized OK"); 74 LOG_DEBUG(Kernel, "initialized OK");
65} 75}
66 76
67void Shutdown() { 77void Shutdown() {
78 Service::CFG::CFGShutdown();
79 Service::FS::ArchiveShutdown();
68 Service::Shutdown(); 80 Service::Shutdown();
69 81
70 g_module_db.clear(); 82 g_module_db.clear();
71 83
72 NOTICE_LOG(HLE, "shutdown OK"); 84 LOG_DEBUG(Kernel, "shutdown OK");
73} 85}
74 86
75} // namespace 87} // namespace
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 4ab258c69..59b770f02 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index db571b895..38705e3cd 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
@@ -20,16 +20,10 @@ public:
20 std::string GetTypeName() const override { return "Arbiter"; } 20 std::string GetTypeName() const override { return "Arbiter"; }
21 std::string GetName() const override { return name; } 21 std::string GetName() const override { return name; }
22 22
23 static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; } 23 static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
24 Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } 24 HandleType GetHandleType() const override { return HANDLE_TYPE; }
25 25
26 std::string name; ///< Name of address arbiter object (optional) 26 std::string name; ///< Name of address arbiter object (optional)
27
28 ResultVal<bool> WaitSynchronization() override {
29 // TODO(bunnei): ImplementMe
30 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
31 return UnimplementedFunction(ErrorModule::OS);
32 }
33}; 27};
34 28
35//////////////////////////////////////////////////////////////////////////////////////////////////// 29////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
53 // Wait current thread (acquire the arbiter)... 47 // Wait current thread (acquire the arbiter)...
54 case ArbitrationType::WaitIfLessThan: 48 case ArbitrationType::WaitIfLessThan:
55 if ((s32)Memory::Read32(address) <= value) { 49 if ((s32)Memory::Read32(address) <= value) {
56 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); 50 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
57 HLE::Reschedule(__func__); 51 HLE::Reschedule(__func__);
58 } 52 }
59 break; 53 break;
60 54
61 default: 55 default:
62 ERROR_LOG(KERNEL, "unknown type=%d", type); 56 LOG_ERROR(Kernel, "unknown type=%d", type);
63 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); 57 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
64 } 58 }
65 return RESULT_SUCCESS; 59 return RESULT_SUCCESS;
@@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
68/// Create an address arbiter 62/// Create an address arbiter
69AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { 63AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
70 AddressArbiter* address_arbiter = new AddressArbiter; 64 AddressArbiter* address_arbiter = new AddressArbiter;
71 handle = Kernel::g_object_pool.Create(address_arbiter); 65 // TOOD(yuriks): Fix error reporting
66 handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE);
72 address_arbiter->name = name; 67 address_arbiter->name = name;
73 return address_arbiter; 68 return address_arbiter;
74} 69}
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 8a5fb10b4..030e7ad7b 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index e273444c9..000000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/file_util.h"
7#include "common/math_util.h"
8
9#include "core/file_sys/archive.h"
10#include "core/file_sys/archive_sdmc.h"
11#include "core/file_sys/directory.h"
12#include "core/hle/kernel/archive.h"
13#include "core/hle/result.h"
14#include "core/hle/service/service.h"
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// Kernel namespace
18
19namespace Kernel {
20
21// Command to access archive file
22enum class FileCommand : u32 {
23 Dummy1 = 0x000100C6,
24 Control = 0x040100C4,
25 OpenSubFile = 0x08010100,
26 Read = 0x080200C2,
27 Write = 0x08030102,
28 GetSize = 0x08040000,
29 SetSize = 0x08050080,
30 GetAttributes = 0x08060000,
31 SetAttributes = 0x08070040,
32 Close = 0x08080000,
33 Flush = 0x08090000,
34};
35
36// Command to access directory
37enum class DirectoryCommand : u32 {
38 Dummy1 = 0x000100C6,
39 Control = 0x040100C4,
40 Read = 0x08010042,
41 Close = 0x08020000,
42};
43
44class Archive : public Object {
45public:
46 std::string GetTypeName() const override { return "Archive"; }
47 std::string GetName() const override { return name; }
48
49 static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
50 Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
51
52 std::string name; ///< Name of archive (optional)
53 FileSys::Archive* backend; ///< Archive backend interface
54
55 ResultVal<bool> SyncRequest() override {
56 u32* cmd_buff = Service::GetCommandBuffer();
57 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
58
59 switch (cmd) {
60 // Read from archive...
61 case FileCommand::Read:
62 {
63 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
64 u32 length = cmd_buff[3];
65 u32 address = cmd_buff[5];
66
67 // Number of bytes read
68 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
69 break;
70 }
71 // Write to archive...
72 case FileCommand::Write:
73 {
74 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
75 u32 length = cmd_buff[3];
76 u32 flush = cmd_buff[4];
77 u32 address = cmd_buff[6];
78
79 // Number of bytes written
80 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
81 break;
82 }
83 case FileCommand::GetSize:
84 {
85 u64 filesize = (u64) backend->GetSize();
86 cmd_buff[2] = (u32) filesize; // Lower word
87 cmd_buff[3] = (u32) (filesize >> 32); // Upper word
88 break;
89 }
90 case FileCommand::SetSize:
91 {
92 backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
93 break;
94 }
95 case FileCommand::Close:
96 {
97 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
98 CloseArchive(backend->GetIdCode());
99 break;
100 }
101 // Unknown command...
102 default:
103 {
104 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
105 return UnimplementedFunction(ErrorModule::FS);
106 }
107 }
108 cmd_buff[1] = 0; // No error
109 return MakeResult<bool>(false);
110 }
111
112 ResultVal<bool> WaitSynchronization() override {
113 // TODO(bunnei): ImplementMe
114 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
115 return UnimplementedFunction(ErrorModule::FS);
116 }
117};
118
119class File : public Object {
120public:
121 std::string GetTypeName() const override { return "File"; }
122 std::string GetName() const override { return path.DebugStr(); }
123
124 static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
125 Kernel::HandleType GetHandleType() const override { return HandleType::File; }
126
127 FileSys::Path path; ///< Path of the file
128 std::unique_ptr<FileSys::File> backend; ///< File backend interface
129
130 ResultVal<bool> SyncRequest() override {
131 u32* cmd_buff = Service::GetCommandBuffer();
132 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
133 switch (cmd) {
134
135 // Read from file...
136 case FileCommand::Read:
137 {
138 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
139 u32 length = cmd_buff[3];
140 u32 address = cmd_buff[5];
141 DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
142 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
143 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
144 break;
145 }
146
147 // Write to file...
148 case FileCommand::Write:
149 {
150 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
151 u32 length = cmd_buff[3];
152 u32 flush = cmd_buff[4];
153 u32 address = cmd_buff[6];
154 DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
155 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
156 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
157 break;
158 }
159
160 case FileCommand::GetSize:
161 {
162 DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
163 u64 size = backend->GetSize();
164 cmd_buff[2] = (u32)size;
165 cmd_buff[3] = size >> 32;
166 break;
167 }
168
169 case FileCommand::SetSize:
170 {
171 u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
172 DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu",
173 GetTypeName().c_str(), GetName().c_str(), size);
174 backend->SetSize(size);
175 break;
176 }
177
178 case FileCommand::Close:
179 {
180 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
181 Kernel::g_object_pool.Destroy<File>(GetHandle());
182 break;
183 }
184
185 // Unknown command...
186 default:
187 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
188 ResultCode error = UnimplementedFunction(ErrorModule::FS);
189 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
190 return error;
191 }
192 cmd_buff[1] = 0; // No error
193 return MakeResult<bool>(false);
194 }
195
196 ResultVal<bool> WaitSynchronization() override {
197 // TODO(bunnei): ImplementMe
198 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
199 return UnimplementedFunction(ErrorModule::FS);
200 }
201};
202
203class Directory : public Object {
204public:
205 std::string GetTypeName() const override { return "Directory"; }
206 std::string GetName() const override { return path.DebugStr(); }
207
208 static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
209 Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
210
211 FileSys::Path path; ///< Path of the directory
212 std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
213
214 ResultVal<bool> SyncRequest() override {
215 u32* cmd_buff = Service::GetCommandBuffer();
216 DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
217 switch (cmd) {
218
219 // Read from directory...
220 case DirectoryCommand::Read:
221 {
222 u32 count = cmd_buff[1];
223 u32 address = cmd_buff[3];
224 auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
225 DEBUG_LOG(KERNEL, "Read %s %s: count=%d",
226 GetTypeName().c_str(), GetName().c_str(), count);
227
228 // Number of entries actually read
229 cmd_buff[2] = backend->Read(count, entries);
230 break;
231 }
232
233 case DirectoryCommand::Close:
234 {
235 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
236 Kernel::g_object_pool.Destroy<Directory>(GetHandle());
237 break;
238 }
239
240 // Unknown command...
241 default:
242 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
243 ResultCode error = UnimplementedFunction(ErrorModule::FS);
244 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
245 return error;
246 }
247 cmd_buff[1] = 0; // No error
248 return MakeResult<bool>(false);
249 }
250
251 ResultVal<bool> WaitSynchronization() override {
252 // TODO(bunnei): ImplementMe
253 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
254 return UnimplementedFunction(ErrorModule::FS);
255 }
256};
257
258////////////////////////////////////////////////////////////////////////////////////////////////////
259
260std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
261
262ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) {
263 auto itr = g_archive_map.find(id_code);
264 if (itr == g_archive_map.end()) {
265 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
266 ErrorSummary::NotFound, ErrorLevel::Permanent);
267 }
268
269 return MakeResult<Handle>(itr->second);
270}
271
272ResultCode CloseArchive(FileSys::Archive::IdCode id_code) {
273 auto itr = g_archive_map.find(id_code);
274 if (itr == g_archive_map.end()) {
275 ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code);
276 return InvalidHandle(ErrorModule::FS);
277 }
278
279 INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
280 return RESULT_SUCCESS;
281}
282
283/**
284 * Mounts an archive
285 * @param archive Pointer to the archive to mount
286 */
287ResultCode MountArchive(Archive* archive) {
288 FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
289 ResultVal<Handle> archive_handle = OpenArchive(id_code);
290 if (archive_handle.Succeeded()) {
291 ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
292 return archive_handle.Code();
293 }
294 g_archive_map[id_code] = archive->GetHandle();
295 INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
296 return RESULT_SUCCESS;
297}
298
299ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) {
300 Archive* archive = new Archive;
301 Handle handle = Kernel::g_object_pool.Create(archive);
302 archive->name = name;
303 archive->backend = backend;
304
305 ResultCode result = MountArchive(archive);
306 if (result.IsError()) {
307 return result;
308 }
309
310 return RESULT_SUCCESS;
311}
312
313ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
314 // TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create
315 // the archive file handles at app loading, and then keep them persistent throughout execution.
316 // Archives file handles are just reused and not actually freed until emulation shut down.
317 // Verify if real hardware works this way, or if new handles are created each time
318 if (path.GetType() == FileSys::Binary)
319 // TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend
320 // design. While the functionally of this is OK, our implementation decision to separate
321 // normal files from archive file pointers is very likely wrong.
322 // See https://github.com/citra-emu/citra/issues/205
323 return MakeResult<Handle>(archive_handle);
324
325 File* file = new File;
326 Handle handle = Kernel::g_object_pool.Create(file);
327
328 Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
329 if (archive == nullptr) {
330 return InvalidHandle(ErrorModule::FS);
331 }
332 file->path = path;
333 file->backend = archive->backend->OpenFile(path, mode);
334
335 if (!file->backend) {
336 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
337 ErrorSummary::NotFound, ErrorLevel::Permanent);
338 }
339
340 return MakeResult<Handle>(handle);
341}
342
343/**
344 * Delete a File from an Archive
345 * @param archive_handle Handle to an open Archive object
346 * @param path Path to the File inside of the Archive
347 * @return Whether deletion succeeded
348 */
349Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) {
350 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
351 if (archive == nullptr)
352 return -1;
353 if (archive->backend->DeleteFile(path))
354 return 0;
355 return -1;
356}
357
358/**
359 * Delete a Directory from an Archive
360 * @param archive_handle Handle to an open Archive object
361 * @param path Path to the Directory inside of the Archive
362 * @return Whether deletion succeeded
363 */
364Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
365 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
366 if (archive == nullptr)
367 return -1;
368 if (archive->backend->DeleteDirectory(path))
369 return 0;
370 return -1;
371}
372
373/**
374 * Create a Directory from an Archive
375 * @param archive_handle Handle to an open Archive object
376 * @param path Path to the Directory inside of the Archive
377 * @return Whether creation succeeded
378 */
379Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
380 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
381 if (archive == nullptr)
382 return -1;
383 if (archive->backend->CreateDirectory(path))
384 return 0;
385 return -1;
386}
387
388/**
389 * Open a Directory from an Archive
390 * @param archive_handle Handle to an open Archive object
391 * @param path Path to the Directory inside of the Archive
392 * @return Opened Directory object
393 */
394ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
395 Directory* directory = new Directory;
396 Handle handle = Kernel::g_object_pool.Create(directory);
397
398 Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
399 if (archive == nullptr) {
400 return InvalidHandle(ErrorModule::FS);
401 }
402 directory->path = path;
403 directory->backend = archive->backend->OpenDirectory(path);
404
405 return MakeResult<Handle>(handle);
406}
407
408/// Initialize archives
409void ArchiveInit() {
410 g_archive_map.clear();
411
412 // TODO(Link Mauve): Add the other archive types (see here for the known types:
413 // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
414 // archive type is SDMC, so it is the only one getting exposed.
415
416 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
417 auto archive = new FileSys::Archive_SDMC(sdmc_directory);
418 if (archive->Initialize())
419 CreateArchive(archive, "SDMC");
420 else
421 ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
422}
423
424/// Shutdown archives
425void ArchiveShutdown() {
426 g_archive_map.clear();
427}
428
429} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 6fc4f0f25..000000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,85 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Kernel namespace
15
16namespace Kernel {
17
18/**
19 * Opens an archive
20 * @param id_code IdCode of the archive to open
21 * @return Handle to the opened archive
22 */
23ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code);
24
25/**
26 * Closes an archive
27 * @param id_code IdCode of the archive to open
28 */
29ResultCode CloseArchive(FileSys::Archive::IdCode id_code);
30
31/**
32 * Creates an Archive
33 * @param backend File system backend interface to the archive
34 * @param name Name of Archive
35 */
36ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name);
37
38/**
39 * Open a File from an Archive
40 * @param archive_handle Handle to an open Archive object
41 * @param path Path to the File inside of the Archive
42 * @param mode Mode under which to open the File
43 * @return Handle to the opened File object
44 */
45ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
46
47/**
48 * Delete a File from an Archive
49 * @param archive_handle Handle to an open Archive object
50 * @param path Path to the File inside of the Archive
51 * @return Whether deletion succeeded
52 */
53Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path);
54
55/**
56 * Delete a Directory from an Archive
57 * @param archive_handle Handle to an open Archive object
58 * @param path Path to the Directory inside of the Archive
59 * @return Whether deletion succeeded
60 */
61Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
62
63/**
64 * Create a Directory from an Archive
65 * @param archive_handle Handle to an open Archive object
66 * @param path Path to the Directory inside of the Archive
67 * @return Whether creation of directory succeeded
68 */
69Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
70
71/**
72 * Open a Directory from an Archive
73 * @param archive_handle Handle to an open Archive object
74 * @param path Path to the Directory inside of the Archive
75 * @return Handle to the opened File object
76 */
77ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
78
79/// Initialize archives
80void ArchiveInit();
81
82/// Shutdown archives
83void ArchiveShutdown();
84
85} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 288080209..e43c3ee4e 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
@@ -19,8 +19,8 @@ public:
19 std::string GetTypeName() const override { return "Event"; } 19 std::string GetTypeName() const override { return "Event"; }
20 std::string GetName() const override { return name; } 20 std::string GetName() const override { return name; }
21 21
22 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } 22 static const HandleType HANDLE_TYPE = HandleType::Event;
23 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; } 23 HandleType GetHandleType() const override { return HANDLE_TYPE; }
24 24
25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization 25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization
26 ResetType reset_type; ///< Current ResetType 26 ResetType reset_type; ///< Current ResetType
@@ -53,7 +53,7 @@ public:
53 * @return Result of operation, 0 on success, otherwise error code 53 * @return Result of operation, 0 on success, otherwise error code
54 */ 54 */
55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { 55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
56 Event* evt = g_object_pool.Get<Event>(handle); 56 Event* evt = g_handle_table.Get<Event>(handle);
57 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 57 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
58 58
59 evt->permanent_locked = permanent_locked; 59 evt->permanent_locked = permanent_locked;
@@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
67 * @return Result of operation, 0 on success, otherwise error code 67 * @return Result of operation, 0 on success, otherwise error code
68 */ 68 */
69ResultCode SetEventLocked(const Handle handle, const bool locked) { 69ResultCode SetEventLocked(const Handle handle, const bool locked) {
70 Event* evt = g_object_pool.Get<Event>(handle); 70 Event* evt = g_handle_table.Get<Event>(handle);
71 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 71 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
72 72
73 if (!evt->permanent_locked) { 73 if (!evt->permanent_locked) {
@@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) {
82 * @return Result of operation, 0 on success, otherwise error code 82 * @return Result of operation, 0 on success, otherwise error code
83 */ 83 */
84ResultCode SignalEvent(const Handle handle) { 84ResultCode SignalEvent(const Handle handle) {
85 Event* evt = g_object_pool.Get<Event>(handle); 85 Event* evt = g_handle_table.Get<Event>(handle);
86 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 86 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
87 87
88 // Resume threads waiting for event to signal 88 // Resume threads waiting for event to signal
@@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) {
110 * @return Result of operation, 0 on success, otherwise error code 110 * @return Result of operation, 0 on success, otherwise error code
111 */ 111 */
112ResultCode ClearEvent(Handle handle) { 112ResultCode ClearEvent(Handle handle) {
113 Event* evt = g_object_pool.Get<Event>(handle); 113 Event* evt = g_handle_table.Get<Event>(handle);
114 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); 114 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
115 115
116 if (!evt->permanent_locked) { 116 if (!evt->permanent_locked) {
@@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) {
129Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { 129Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
130 Event* evt = new Event; 130 Event* evt = new Event;
131 131
132 handle = Kernel::g_object_pool.Create(evt); 132 // TOOD(yuriks): Fix error reporting
133 handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE);
133 134
134 evt->locked = true; 135 evt->locked = true;
135 evt->permanent_locked = false; 136 evt->permanent_locked = false;
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 73aec4e79..da793df1a 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 018000abd..e59ed1b57 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,106 +1,117 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
5#include "common/common.h" 7#include "common/common.h"
6 8
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
10#include "core/hle/kernel/archive.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14Handle g_main_thread = 0; 15Handle g_main_thread = 0;
15ObjectPool g_object_pool; 16HandleTable g_handle_table;
17u64 g_program_id = 0;
16 18
17ObjectPool::ObjectPool() { 19HandleTable::HandleTable() {
18 next_id = INITIAL_NEXT_ID; 20 next_generation = 1;
21 Clear();
19} 22}
20 23
21Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { 24ResultVal<Handle> HandleTable::Create(Object* obj) {
22 if (range_top > MAX_COUNT) { 25 _dbg_assert_(Kernel, obj != nullptr);
23 range_top = MAX_COUNT; 26
24 } 27 u16 slot = next_free_slot;
25 if (next_id >= range_bottom && next_id < range_top) { 28 if (slot >= generations.size()) {
26 range_bottom = next_id++; 29 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
27 } 30 return ERR_OUT_OF_HANDLES;
28 for (int i = range_bottom; i < range_top; i++) {
29 if (!occupied[i]) {
30 occupied[i] = true;
31 pool[i] = obj;
32 pool[i]->handle = i + HANDLE_OFFSET;
33 return i + HANDLE_OFFSET;
34 }
35 } 31 }
36 ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); 32 next_free_slot = generations[slot];
37 return 0; 33
38} 34 u16 generation = next_generation++;
39 35
40bool ObjectPool::IsValid(Handle handle) { 36 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
41 int index = handle - HANDLE_OFFSET; 37 // CTR-OS doesn't use generation 0, so skip straight to 1.
42 if (index < 0) 38 if (next_generation >= (1 << 15)) next_generation = 1;
43 return false;
44 if (index >= MAX_COUNT)
45 return false;
46 39
47 return occupied[index]; 40 generations[slot] = generation;
41 intrusive_ptr_add_ref(obj);
42 objects[slot] = obj;
43
44 Handle handle = generation | (slot << 15);
45 obj->handle = handle;
46 return MakeResult<Handle>(handle);
48} 47}
49 48
50void ObjectPool::Clear() { 49ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
51 for (int i = 0; i < MAX_COUNT; i++) { 50 Object* object = GetGeneric(handle);
52 //brutally clear everything, no validation 51 if (object == nullptr) {
53 if (occupied[i]) 52 LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
54 delete pool[i]; 53 return ERR_INVALID_HANDLE;
55 occupied[i] = false;
56 } 54 }
57 pool.fill(nullptr); 55 return Create(object);
58 next_id = INITIAL_NEXT_ID; 56}
57
58ResultCode HandleTable::Close(Handle handle) {
59 if (!IsValid(handle))
60 return ERR_INVALID_HANDLE;
61
62 size_t slot = GetSlot(handle);
63 u16 generation = GetGeneration(handle);
64
65 intrusive_ptr_release(objects[slot]);
66 objects[slot] = nullptr;
67
68 generations[generation] = next_free_slot;
69 next_free_slot = slot;
70 return RESULT_SUCCESS;
59} 71}
60 72
61Object* &ObjectPool::operator [](Handle handle) 73bool HandleTable::IsValid(Handle handle) const {
62{ 74 size_t slot = GetSlot(handle);
63 _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); 75 u16 generation = GetGeneration(handle);
64 return pool[handle - HANDLE_OFFSET]; 76
77 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
65} 78}
66 79
67void ObjectPool::List() { 80Object* HandleTable::GetGeneric(Handle handle) const {
68 for (int i = 0; i < MAX_COUNT; i++) { 81 if (handle == CurrentThread) {
69 if (occupied[i]) { 82 // TODO(yuriks) Directly return the pointer once this is possible.
70 if (pool[i]) { 83 handle = GetCurrentThreadHandle();
71 INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), 84 } else if (handle == CurrentProcess) {
72 pool[i]->GetName().c_str()); 85 LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess);
73 } 86 return nullptr;
74 }
75 } 87 }
76}
77 88
78int ObjectPool::GetCount() { 89 if (!IsValid(handle)) {
79 int count = 0; 90 return nullptr;
80 for (int i = 0; i < MAX_COUNT; i++) {
81 if (occupied[i])
82 count++;
83 } 91 }
84 return count; 92 return objects[GetSlot(handle)];
85} 93}
86 94
87Object* ObjectPool::CreateByIDType(int type) { 95void HandleTable::Clear() {
88 ERROR_LOG(COMMON, "Unimplemented: %d.", type); 96 for (size_t i = 0; i < MAX_COUNT; ++i) {
89 return nullptr; 97 generations[i] = i + 1;
98 if (objects[i] != nullptr)
99 intrusive_ptr_release(objects[i]);
100 objects[i] = nullptr;
101 }
102 next_free_slot = 0;
90} 103}
91 104
92/// Initialize the kernel 105/// Initialize the kernel
93void Init() { 106void Init() {
94 Kernel::ThreadingInit(); 107 Kernel::ThreadingInit();
95 Kernel::ArchiveInit();
96} 108}
97 109
98/// Shutdown the kernel 110/// Shutdown the kernel
99void Shutdown() { 111void Shutdown() {
100 Kernel::ThreadingShutdown(); 112 Kernel::ThreadingShutdown();
101 Kernel::ArchiveShutdown();
102 113
103 g_object_pool.Clear(); // Free all kernel objects 114 g_handle_table.Clear(); // Free all kernel objects
104} 115}
105 116
106/** 117/**
@@ -109,8 +120,6 @@ void Shutdown() {
109 * @return True on success, otherwise false 120 * @return True on success, otherwise false
110 */ 121 */
111bool LoadExec(u32 entry_point) { 122bool LoadExec(u32 entry_point) {
112 Init();
113
114 Core::g_app_core->SetPC(entry_point); 123 Core::g_app_core->SetPC(entry_point);
115 124
116 // 0x30 is the typical main thread priority I've seen used so far 125 // 0x30 is the typical main thread priority I've seen used so far
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d3937ce8..7f86fd07d 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -12,9 +12,17 @@
12typedef u32 Handle; 12typedef u32 Handle;
13typedef s32 Result; 13typedef s32 Result;
14 14
15const Handle INVALID_HANDLE = 0;
16
15namespace Kernel { 17namespace Kernel {
16 18
17enum KernelHandle { 19// TODO: Verify code
20const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
21 ErrorSummary::OutOfResource, ErrorLevel::Temporary);
22// TOOD: Verify code
23const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
24
25enum KernelHandle : Handle {
18 CurrentThread = 0xFFFF8000, 26 CurrentThread = 0xFFFF8000,
19 CurrentProcess = 0xFFFF8001, 27 CurrentProcess = 0xFFFF8001,
20}; 28};
@@ -22,7 +30,7 @@ enum KernelHandle {
22enum class HandleType : u32 { 30enum class HandleType : u32 {
23 Unknown = 0, 31 Unknown = 0,
24 Port = 1, 32 Port = 1,
25 Service = 2, 33 Session = 2,
26 Event = 3, 34 Event = 3,
27 Mutex = 4, 35 Mutex = 4,
28 SharedMemory = 5, 36 SharedMemory = 5,
@@ -30,20 +38,17 @@ enum class HandleType : u32 {
30 Thread = 7, 38 Thread = 7,
31 Process = 8, 39 Process = 8,
32 AddressArbiter = 9, 40 AddressArbiter = 9,
33 File = 10, 41 Semaphore = 10,
34 Semaphore = 11,
35 Archive = 12,
36 Directory = 13,
37}; 42};
38 43
39enum { 44enum {
40 DEFAULT_STACK_SIZE = 0x4000, 45 DEFAULT_STACK_SIZE = 0x4000,
41}; 46};
42 47
43class ObjectPool; 48class HandleTable;
44 49
45class Object : NonCopyable { 50class Object : NonCopyable {
46 friend class ObjectPool; 51 friend class HandleTable;
47 u32 handle; 52 u32 handle;
48public: 53public:
49 virtual ~Object() {} 54 virtual ~Object() {}
@@ -53,113 +58,145 @@ public:
53 virtual Kernel::HandleType GetHandleType() const = 0; 58 virtual Kernel::HandleType GetHandleType() const = 0;
54 59
55 /** 60 /**
56 * Synchronize kernel object. 61 * Wait for kernel object to synchronize.
57 * @return True if the current thread should wait as a result of the sync 62 * @return True if the current thread should wait as a result of the wait
58 */ 63 */
59 virtual ResultVal<bool> SyncRequest() { 64 virtual ResultVal<bool> WaitSynchronization() {
60 ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); 65 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
61 return UnimplementedFunction(ErrorModule::Kernel); 66 return UnimplementedFunction(ErrorModule::Kernel);
62 } 67 }
63 68
64 /** 69private:
65 * Wait for kernel object to synchronize. 70 friend void intrusive_ptr_add_ref(Object*);
66 * @return True if the current thread should wait as a result of the wait 71 friend void intrusive_ptr_release(Object*);
67 */ 72
68 virtual ResultVal<bool> WaitSynchronization() = 0; 73 unsigned int ref_count = 0;
69}; 74};
70 75
71class ObjectPool : NonCopyable { 76// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
72public: 77inline void intrusive_ptr_add_ref(Object* object) {
73 ObjectPool(); 78 ++object->ref_count;
74 ~ObjectPool() {} 79}
75 80
76 // Allocates a handle within the range and inserts the object into the map. 81inline void intrusive_ptr_release(Object* object) {
77 Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); 82 if (--object->ref_count == 0) {
83 delete object;
84 }
85}
78 86
79 static Object* CreateByIDType(int type); 87/**
88 * This class allows the creation of Handles, which are references to objects that can be tested
89 * for validity and looked up. Here they are used to pass references to kernel objects to/from the
90 * emulated process. it has been designed so that it follows the same handle format and has
91 * approximately the same restrictions as the handle manager in the CTR-OS.
92 *
93 * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
94 * The slot index is used to index into the arrays in this class to access the data corresponding
95 * to the Handle.
96 *
97 * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
98 * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
99 * value of the counter is stored into the Handle as well as in the handle table (in the
100 * "generations" array). When looking up a handle, the Handle's generation must match with the
101 * value stored on the class, otherwise the Handle is considered invalid.
102 *
103 * To find free slots when allocating a Handle without needing to scan the entire object array, the
104 * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
105 * When a Handle is created, an index is popped off the list and used for the new Handle. When it
106 * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
107 * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
108 * verified and isn't likely to cause any problems.
109 */
110class HandleTable final : NonCopyable {
111public:
112 HandleTable();
80 113
81 template <class T> 114 /**
82 void Destroy(Handle handle) { 115 * Allocates a handle for the given object.
83 if (Get<T>(handle)) { 116 * @return The created Handle or one of the following errors:
84 occupied[handle - HANDLE_OFFSET] = false; 117 * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
85 delete pool[handle - HANDLE_OFFSET]; 118 */
86 } 119 ResultVal<Handle> Create(Object* obj);
87 }
88 120
89 bool IsValid(Handle handle); 121 /**
122 * Returns a new handle that points to the same object as the passed in handle.
123 * @return The duplicated Handle or one of the following errors:
124 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
125 * - Any errors returned by `Create()`.
126 */
127 ResultVal<Handle> Duplicate(Handle handle);
90 128
91 template <class T> 129 /**
92 T* Get(Handle handle) { 130 * Closes a handle, removing it from the table and decreasing the object's ref-count.
93 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { 131 * @return `RESULT_SUCCESS` or one of the following errors:
94 if (handle != 0) { 132 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
95 WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); 133 */
96 } 134 ResultCode Close(Handle handle);
97 return nullptr;
98 } else {
99 Object* t = pool[handle - HANDLE_OFFSET];
100 if (t->GetHandleType() != T::GetStaticHandleType()) {
101 WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
102 return nullptr;
103 }
104 return static_cast<T*>(t);
105 }
106 }
107 135
108 // ONLY use this when you know the handle is valid. 136 /// Checks if a handle is valid and points to an existing object.
109 template <class T> 137 bool IsValid(Handle handle) const;
110 T *GetFast(Handle handle) {
111 const Handle realHandle = handle - HANDLE_OFFSET;
112 _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
113 return static_cast<T*>(pool[realHandle]);
114 }
115 138
116 template <class T, typename ArgT> 139 /**
117 void Iterate(bool func(T*, ArgT), ArgT arg) { 140 * Looks up a handle.
118 int type = T::GetStaticIDType(); 141 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
119 for (int i = 0; i < MAX_COUNT; i++) 142 */
120 { 143 Object* GetGeneric(Handle handle) const;
121 if (!occupied[i])
122 continue;
123 T* t = static_cast<T*>(pool[i]);
124 if (t->GetIDType() == type) {
125 if (!func(t, arg))
126 break;
127 }
128 }
129 }
130 144
131 bool GetIDType(Handle handle, HandleType* type) const { 145 /**
132 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || 146 * Looks up a handle while verifying its type.
133 !occupied[handle - HANDLE_OFFSET]) { 147 * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
134 ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); 148 * type differs from the handle type `T::HANDLE_TYPE`.
135 return false; 149 */
150 template <class T>
151 T* Get(Handle handle) const {
152 Object* object = GetGeneric(handle);
153 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
154 return static_cast<T*>(object);
136 } 155 }
137 Object* t = pool[handle - HANDLE_OFFSET]; 156 return nullptr;
138 *type = t->GetHandleType();
139 return true;
140 } 157 }
141 158
142 Object* &operator [](Handle handle); 159 /// Closes all handles held in this table.
143 void List();
144 void Clear(); 160 void Clear();
145 int GetCount();
146 161
147private: 162private:
163 /**
164 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
165 * reduced by ExHeader values, but this is not emulated here.
166 */
167 static const size_t MAX_COUNT = 4096;
148 168
149 enum { 169 static size_t GetSlot(Handle handle) { return handle >> 15; }
150 MAX_COUNT = 0x1000, 170 static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
151 HANDLE_OFFSET = 0x100,
152 INITIAL_NEXT_ID = 0x10,
153 };
154 171
155 std::array<Object*, MAX_COUNT> pool; 172 /// Stores the Object referenced by the handle or null if the slot is empty.
156 std::array<bool, MAX_COUNT> occupied; 173 std::array<Object*, MAX_COUNT> objects;
157 int next_id; 174
175 /**
176 * The value of `next_generation` when the handle was created, used to check for validity. For
177 * empty slots, contains the index of the next free slot in the list.
178 */
179 std::array<u16, MAX_COUNT> generations;
180
181 /**
182 * Global counter of the number of created handles. Stored in `generations` when a handle is
183 * created, and wraps around to 1 when it hits 0x8000.
184 */
185 u16 next_generation;
186
187 /// Head of the free slots linked list.
188 u16 next_free_slot;
158}; 189};
159 190
160extern ObjectPool g_object_pool; 191extern HandleTable g_handle_table;
161extern Handle g_main_thread; 192extern Handle g_main_thread;
162 193
194/// The ID code of the currently running game
195/// TODO(Subv): This variable should not be here,
196/// we need a way to store information about the currently loaded application
197/// for later query during runtime, maybe using the LDR service?
198extern u64 g_program_id;
199
163/// Initialize the kernel 200/// Initialize the kernel
164void Init(); 201void Init();
165 202
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index b303ba128..558068c79 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
@@ -18,8 +18,8 @@ public:
18 std::string GetTypeName() const override { return "Mutex"; } 18 std::string GetTypeName() const override { return "Mutex"; }
19 std::string GetName() const override { return name; } 19 std::string GetName() const override { return name; }
20 20
21 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } 21 static const HandleType HANDLE_TYPE = HandleType::Mutex;
22 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; } 22 HandleType GetHandleType() const override { return HANDLE_TYPE; }
23 23
24 bool initial_locked; ///< Initial lock state when mutex was created 24 bool initial_locked; ///< Initial lock state when mutex was created
25 bool locked; ///< Current locked state 25 bool locked; ///< Current locked state
@@ -27,21 +27,7 @@ public:
27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex 27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
28 std::string name; ///< Name of mutex (optional) 28 std::string name; ///< Name of mutex (optional)
29 29
30 ResultVal<bool> SyncRequest() override { 30 ResultVal<bool> WaitSynchronization() override;
31 // TODO(bunnei): ImplementMe
32 locked = true;
33 return MakeResult<bool>(false);
34 }
35
36 ResultVal<bool> WaitSynchronization() override {
37 // TODO(bunnei): ImplementMe
38 bool wait = locked;
39 if (locked) {
40 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
41 }
42
43 return MakeResult<bool>(wait);
44 }
45}; 31};
46 32
47//////////////////////////////////////////////////////////////////////////////////////////////////// 33////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -49,21 +35,46 @@ public:
49typedef std::multimap<Handle, Handle> MutexMap; 35typedef std::multimap<Handle, Handle> MutexMap;
50static MutexMap g_mutex_held_locks; 36static MutexMap g_mutex_held_locks;
51 37
52void MutexAcquireLock(Mutex* mutex, Handle thread) { 38/**
39 * Acquires the specified mutex for the specified thread
40 * @param mutex Mutex that is to be acquired
41 * @param thread Thread that will acquired
42 */
43void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
53 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); 44 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
54 mutex->lock_thread = thread; 45 mutex->lock_thread = thread;
55} 46}
56 47
57void MutexAcquireLock(Mutex* mutex) { 48bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
58 Handle thread = GetCurrentThreadHandle();
59 MutexAcquireLock(mutex, thread); 49 MutexAcquireLock(mutex, thread);
50 Kernel::ResumeThreadFromWait(thread);
51 return true;
52}
53
54/**
55 * Resumes a thread waiting for the specified mutex
56 * @param mutex The mutex that some thread is waiting on
57 */
58void ResumeWaitingThread(Mutex* mutex) {
59 // Find the next waiting thread for the mutex...
60 if (mutex->waiting_threads.empty()) {
61 // Reset mutex lock thread handle, nothing is waiting
62 mutex->locked = false;
63 mutex->lock_thread = -1;
64 }
65 else {
66 // Resume the next waiting thread and re-lock the mutex
67 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
68 ReleaseMutexForThread(mutex, *iter);
69 mutex->waiting_threads.erase(iter);
70 }
60} 71}
61 72
62void MutexEraseLock(Mutex* mutex) { 73void MutexEraseLock(Mutex* mutex) {
63 Handle handle = mutex->GetHandle(); 74 Handle handle = mutex->GetHandle();
64 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); 75 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
65 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { 76 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
66 if ((*iter).second == handle) { 77 if (iter->second == handle) {
67 g_mutex_held_locks.erase(iter); 78 g_mutex_held_locks.erase(iter);
68 break; 79 break;
69 } 80 }
@@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
71 mutex->lock_thread = -1; 82 mutex->lock_thread = -1;
72} 83}
73 84
85void ReleaseThreadMutexes(Handle thread) {
86 auto locked = g_mutex_held_locks.equal_range(thread);
87
88 // Release every mutex that the thread holds, and resume execution on the waiting threads
89 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
90 Mutex* mutex = g_handle_table.Get<Mutex>(iter->second);
91 ResumeWaitingThread(mutex);
92 }
93
94 // Erase all the locks that this thread holds
95 g_mutex_held_locks.erase(thread);
96}
97
74bool LockMutex(Mutex* mutex) { 98bool LockMutex(Mutex* mutex) {
75 // Mutex alread locked? 99 // Mutex alread locked?
76 if (mutex->locked) { 100 if (mutex->locked) {
@@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) {
80 return true; 104 return true;
81} 105}
82 106
83bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
84 MutexAcquireLock(mutex, thread);
85 Kernel::ResumeThreadFromWait(thread);
86 return true;
87}
88
89bool ReleaseMutex(Mutex* mutex) { 107bool ReleaseMutex(Mutex* mutex) {
90 MutexEraseLock(mutex); 108 MutexEraseLock(mutex);
91 bool woke_threads = false; 109 ResumeWaitingThread(mutex);
92 110 return true;
93 // Find the next waiting thread for the mutex...
94 while (!woke_threads && !mutex->waiting_threads.empty()) {
95 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
96 woke_threads |= ReleaseMutexForThread(mutex, *iter);
97 mutex->waiting_threads.erase(iter);
98 }
99 // Reset mutex lock thread handle, nothing is waiting
100 if (!woke_threads) {
101 mutex->locked = false;
102 mutex->lock_thread = -1;
103 }
104 return woke_threads;
105} 111}
106 112
107/** 113/**
@@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) {
109 * @param handle Handle to mutex to release 115 * @param handle Handle to mutex to release
110 */ 116 */
111ResultCode ReleaseMutex(Handle handle) { 117ResultCode ReleaseMutex(Handle handle) {
112 Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); 118 Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
113 if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); 119 if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
114 120
115 if (!ReleaseMutex(mutex)) { 121 if (!ReleaseMutex(mutex)) {
@@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) {
130 */ 136 */
131Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { 137Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
132 Mutex* mutex = new Mutex; 138 Mutex* mutex = new Mutex;
133 handle = Kernel::g_object_pool.Create(mutex); 139 // TODO(yuriks): Fix error reporting
140 handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE);
134 141
135 mutex->locked = mutex->initial_locked = initial_locked; 142 mutex->locked = mutex->initial_locked = initial_locked;
136 mutex->name = name; 143 mutex->name = name;
@@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
158 return handle; 165 return handle;
159} 166}
160 167
168ResultVal<bool> Mutex::WaitSynchronization() {
169 bool wait = locked;
170 if (locked) {
171 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
172 }
173 else {
174 // Lock the mutex when the first thread accesses it
175 locked = true;
176 MutexAcquireLock(this);
177 }
178
179 return MakeResult<bool>(wait);
180}
161} // namespace 181} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 155449f95..a8ca97014 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle);
24 */ 24 */
25Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); 25Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
26 26
27/**
28 * Releases all the mutexes held by the specified thread
29 * @param thread Thread that is holding the mutexes
30 */
31void ReleaseThreadMutexes(Handle thread);
32
27} // namespace 33} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 000000000..6bc8066a6
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,95 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <queue>
6
7#include "common/common.h"
8
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/semaphore.h"
11#include "core/hle/kernel/thread.h"
12
13namespace Kernel {
14
15class Semaphore : public Object {
16public:
17 std::string GetTypeName() const override { return "Semaphore"; }
18 std::string GetName() const override { return name; }
19
20 static const HandleType HANDLE_TYPE = HandleType::Semaphore;
21 HandleType GetHandleType() const override { return HANDLE_TYPE; }
22
23 s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
24 s32 available_count; ///< Number of free slots left in the semaphore
25 std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
26 std::string name; ///< Name of semaphore (optional)
27
28 /**
29 * Tests whether a semaphore still has free slots
30 * @return Whether the semaphore is available
31 */
32 bool IsAvailable() const {
33 return available_count > 0;
34 }
35
36 ResultVal<bool> WaitSynchronization() override {
37 bool wait = !IsAvailable();
38
39 if (wait) {
40 Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
41 waiting_threads.push(GetCurrentThreadHandle());
42 } else {
43 --available_count;
44 }
45
46 return MakeResult<bool>(wait);
47 }
48};
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51
52ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
53 s32 max_count, const std::string& name) {
54
55 if (initial_count > max_count)
56 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
57 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
58
59 Semaphore* semaphore = new Semaphore;
60 // TOOD(yuriks): Fix error reporting
61 *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE);
62
63 // When the semaphore is created, some slots are reserved for other threads,
64 // and the rest is reserved for the caller thread
65 semaphore->max_count = max_count;
66 semaphore->available_count = initial_count;
67 semaphore->name = name;
68
69 return RESULT_SUCCESS;
70}
71
72ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
73 Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle);
74 if (semaphore == nullptr)
75 return InvalidHandle(ErrorModule::Kernel);
76
77 if (semaphore->max_count - semaphore->available_count < release_count)
78 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
79 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
80
81 *count = semaphore->available_count;
82 semaphore->available_count += release_count;
83
84 // Notify some of the threads that the semaphore has been released
85 // stop once the semaphore is full again or there are no more waiting threads
86 while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
87 Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
88 semaphore->waiting_threads.pop();
89 --semaphore->available_count;
90 }
91
92 return RESULT_SUCCESS;
93}
94
95} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 000000000..8644ecf0c
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/**
14 * Creates a semaphore.
15 * @param handle Pointer to the handle of the newly created object
16 * @param initial_count Number of slots reserved for other threads
17 * @param max_count Maximum number of slots the semaphore can have
18 * @param name Optional name of semaphore
19 * @return ResultCode of the error
20 */
21ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown");
22
23/**
24 * Releases a certain number of slots from a semaphore.
25 * @param count The number of free slots the semaphore had before this call
26 * @param handle The handle of the semaphore to release
27 * @param release_count The number of slots to release
28 * @return ResultCode of the error
29 */
30ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
31
32} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 000000000..91f3ffc2c
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/kernel/kernel.h"
8
9namespace Kernel {
10
11static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
12
13/**
14 * Returns a pointer to the command buffer in kernel memory
15 * @param offset Optional offset into command buffer
16 * @return Pointer to command buffer
17 */
18inline static u32* GetCommandBuffer(const int offset=0) {
19 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
20}
21
22/**
23 * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
24 * primitive for communication between different processes, and are used to implement service calls
25 * to the various system services.
26 *
27 * To make a service call, the client must write the command header and parameters to the buffer
28 * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
29 * SVC call with its Session handle. The kernel will read the command header, using it to marshall
30 * the parameters to the process at the server endpoint of the session. After the server replies to
31 * the request, the response is marshalled back to the caller's TLS buffer and control is
32 * transferred back to it.
33 *
34 * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
35 * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
36 * with the session handle, this class's SyncRequest method is called, which should read the TLS
37 * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
38 * no parameter marshalling is done.
39 *
40 * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
41 * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
42 * opposed to HLE simulations.
43 */
44class Session : public Object {
45public:
46 std::string GetTypeName() const override { return "Session"; }
47
48 static const HandleType HANDLE_TYPE = HandleType::Session;
49 HandleType GetHandleType() const override { return HANDLE_TYPE; }
50
51 /**
52 * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
53 * aren't supported yet.
54 */
55 virtual ResultVal<bool> SyncRequest() = 0;
56};
57
58}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index cfcc0e0b7..cea1f6fa1 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common.h" 5#include "common/common.h"
@@ -13,14 +13,8 @@ class SharedMemory : public Object {
13public: 13public:
14 std::string GetTypeName() const override { return "SharedMemory"; } 14 std::string GetTypeName() const override { return "SharedMemory"; }
15 15
16 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } 16 static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
17 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } 17 HandleType GetHandleType() const override { return HANDLE_TYPE; }
18
19 ResultVal<bool> WaitSynchronization() override {
20 // TODO(bunnei): ImplementMe
21 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
22 return UnimplementedFunction(ErrorModule::OS);
23 }
24 18
25 u32 base_address; ///< Address of shared memory block in RAM 19 u32 base_address; ///< Address of shared memory block in RAM
26 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) 20 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
@@ -38,7 +32,8 @@ public:
38 */ 32 */
39SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { 33SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
40 SharedMemory* shared_memory = new SharedMemory; 34 SharedMemory* shared_memory = new SharedMemory;
41 handle = Kernel::g_object_pool.Create(shared_memory); 35 // TOOD(yuriks): Fix error reporting
36 handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE);
42 shared_memory->name = name; 37 shared_memory->name = name;
43 return shared_memory; 38 return shared_memory;
44} 39}
@@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
61 MemoryPermission other_permissions) { 56 MemoryPermission other_permissions) {
62 57
63 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { 58 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
64 ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", 59 LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
65 handle, address); 60 handle, address);
66 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 61 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
67 ErrorSummary::InvalidArgument, ErrorLevel::Permanent); 62 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
68 } 63 }
69 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); 64 SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
70 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); 65 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
71 66
72 shared_memory->base_address = address; 67 shared_memory->base_address = address;
@@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
77} 72}
78 73
79ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { 74ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
80 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); 75 SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
81 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); 76 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
82 77
83 if (0 != shared_memory->base_address) 78 if (0 != shared_memory->base_address)
84 return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); 79 return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
85 80
86 ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); 81 LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
87 // TODO(yuriks): Verify error code. 82 // TODO(yuriks): Verify error code.
88 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, 83 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
89 ErrorSummary::InvalidState, ErrorLevel::Permanent); 84 ErrorSummary::InvalidState, ErrorLevel::Permanent);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 304cf5b67..bb65c7ccd 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -12,11 +12,15 @@ namespace Kernel {
12 12
13/// Permissions for mapped shared memory blocks 13/// Permissions for mapped shared memory blocks
14enum class MemoryPermission : u32 { 14enum class MemoryPermission : u32 {
15 None = 0, 15 None = 0,
16 Read = (1u << 0), 16 Read = (1u << 0),
17 Write = (1u << 1), 17 Write = (1u << 1),
18 ReadWrite = (Read | Write), 18 ReadWrite = (Read | Write),
19 DontCare = (1u << 28) 19 Execute = (1u << 2),
20 ReadExecute = (Read | Execute),
21 WriteExecute = (Write | Execute),
22 ReadWriteExecute = (Read | Write | Execute),
23 DontCare = (1u << 28)
20}; 24};
21 25
22/** 26/**
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3f54a4e9..872df2d14 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
@@ -14,6 +14,7 @@
14#include "core/hle/hle.h" 14#include "core/hle/hle.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
17#include "core/hle/kernel/mutex.h"
17#include "core/hle/result.h" 18#include "core/hle/result.h"
18#include "core/mem_map.h" 19#include "core/mem_map.h"
19 20
@@ -25,8 +26,8 @@ public:
25 std::string GetName() const override { return name; } 26 std::string GetName() const override { return name; }
26 std::string GetTypeName() const override { return "Thread"; } 27 std::string GetTypeName() const override { return "Thread"; }
27 28
28 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } 29 static const HandleType HANDLE_TYPE = HandleType::Thread;
29 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } 30 HandleType GetHandleType() const override { return HANDLE_TYPE; }
30 31
31 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } 32 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
32 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } 33 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
@@ -49,6 +50,8 @@ public:
49 50
50 ThreadContext context; 51 ThreadContext context;
51 52
53 u32 thread_id;
54
52 u32 status; 55 u32 status;
53 u32 entry_point; 56 u32 entry_point;
54 u32 stack_top; 57 u32 stack_top;
@@ -61,6 +64,7 @@ public:
61 64
62 WaitType wait_type; 65 WaitType wait_type;
63 Handle wait_handle; 66 Handle wait_handle;
67 VAddr wait_address;
64 68
65 std::vector<Handle> waiting_threads; 69 std::vector<Handle> waiting_threads;
66 70
@@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue;
76static Handle current_thread_handle; 80static Handle current_thread_handle;
77static Thread* current_thread; 81static Thread* current_thread;
78 82
79/// Gets the current thread 83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
80inline Thread* GetCurrentThread() { 84static u32 next_thread_id; ///< The next available thread id
85
86Thread* GetCurrentThread() {
81 return current_thread; 87 return current_thread;
82} 88}
83 89
@@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
121 } 127 }
122 t->wait_type = WAITTYPE_NONE; 128 t->wait_type = WAITTYPE_NONE;
123 t->wait_handle = 0; 129 t->wait_handle = 0;
130 t->wait_address = 0;
124} 131}
125 132
126/// Change a thread to "ready" state 133/// Change a thread to "ready" state
@@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) {
140 } 147 }
141} 148}
142 149
143/// Verify that a thread has not been released from waiting 150/// Check if a thread is blocking on a specified wait type
144inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { 151static bool CheckWaitType(const Thread* thread, WaitType type) {
145 _dbg_assert_(KERNEL, thread != nullptr); 152 return (type == thread->wait_type) && (thread->IsWaiting());
146 return type == thread->wait_type && wait_handle == thread->wait_handle; 153}
154
155/// Check if a thread is blocking on a specified wait type with a specified handle
156static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
157 return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
158}
159
160/// Check if a thread is blocking on a specified wait type with a specified handle and address
161static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
162 return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
147} 163}
148 164
149/// Stops the current thread 165/// Stops the current thread
150ResultCode StopThread(Handle handle, const char* reason) { 166ResultCode StopThread(Handle handle, const char* reason) {
151 Thread* thread = g_object_pool.Get<Thread>(handle); 167 Thread* thread = g_handle_table.Get<Thread>(handle);
152 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 168 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
153 169
170 // Release all the mutexes that this thread holds
171 ReleaseThreadMutexes(handle);
172
154 ChangeReadyState(thread, false); 173 ChangeReadyState(thread, false);
155 thread->status = THREADSTATUS_DORMANT; 174 thread->status = THREADSTATUS_DORMANT;
156 for (Handle waiting_handle : thread->waiting_threads) { 175 for (Handle waiting_handle : thread->waiting_threads) {
157 Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); 176 Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
158 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { 177
178 if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
159 ResumeThreadFromWait(waiting_handle); 179 ResumeThreadFromWait(waiting_handle);
160 }
161 } 180 }
162 thread->waiting_threads.clear(); 181 thread->waiting_threads.clear();
163 182
164 // Stopped threads are never waiting. 183 // Stopped threads are never waiting.
165 thread->wait_type = WAITTYPE_NONE; 184 thread->wait_type = WAITTYPE_NONE;
166 thread->wait_handle = 0; 185 thread->wait_handle = 0;
186 thread->wait_address = 0;
167 187
168 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
169} 189}
@@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
178 198
179 if (new_status == THREADSTATUS_WAIT) { 199 if (new_status == THREADSTATUS_WAIT) {
180 if (t->wait_type == WAITTYPE_NONE) { 200 if (t->wait_type == WAITTYPE_NONE) {
181 ERROR_LOG(KERNEL, "Waittype none not allowed"); 201 LOG_ERROR(Kernel, "Waittype none not allowed");
182 } 202 }
183 } 203 }
184} 204}
@@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
190 210
191 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 211 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
192 for (Handle handle : thread_queue) { 212 for (Handle handle : thread_queue) {
193 Thread* thread = g_object_pool.Get<Thread>(handle); 213 Thread* thread = g_handle_table.Get<Thread>(handle);
194 214
195 // TODO(bunnei): Verify arbiter address... 215 if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
196 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
197 continue; 216 continue;
198 217
199 if (thread == nullptr) 218 if (thread == nullptr)
200 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. 219 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
220
201 if(thread->current_priority <= priority) { 221 if(thread->current_priority <= priority) {
202 highest_priority_thread = handle; 222 highest_priority_thread = handle;
203 priority = thread->current_priority; 223 priority = thread->current_priority;
@@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
215 235
216 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 236 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
217 for (Handle handle : thread_queue) { 237 for (Handle handle : thread_queue) {
218 Thread* thread = g_object_pool.Get<Thread>(handle); 238 Thread* thread = g_handle_table.Get<Thread>(handle);
219 239
220 // TODO(bunnei): Verify arbiter address... 240 if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
221 if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
222 ResumeThreadFromWait(handle); 241 ResumeThreadFromWait(handle);
223 } 242 }
224} 243}
@@ -269,14 +288,9 @@ Thread* NextThread() {
269 if (next == 0) { 288 if (next == 0) {
270 return nullptr; 289 return nullptr;
271 } 290 }
272 return Kernel::g_object_pool.Get<Thread>(next); 291 return Kernel::g_handle_table.Get<Thread>(next);
273} 292}
274 293
275/**
276 * Puts the current thread in the wait state for the given type
277 * @param wait_type Type of wait
278 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
279 */
280void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 294void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
281 Thread* thread = GetCurrentThread(); 295 Thread* thread = GetCurrentThread();
282 thread->wait_type = wait_type; 296 thread->wait_type = wait_type;
@@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
284 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 298 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
285} 299}
286 300
301void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
302 WaitCurrentThread(wait_type, wait_handle);
303 GetCurrentThread()->wait_address = wait_address;
304}
305
287/// Resumes a thread from waiting by marking it as "ready" 306/// Resumes a thread from waiting by marking it as "ready"
288void ResumeThreadFromWait(Handle handle) { 307void ResumeThreadFromWait(Handle handle) {
289 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); 308 Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
290 if (thread) { 309 if (thread) {
291 thread->status &= ~THREADSTATUS_WAIT; 310 thread->status &= ~THREADSTATUS_WAIT;
311 thread->wait_handle = 0;
312 thread->wait_type = WAITTYPE_NONE;
292 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 313 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
293 ChangeReadyState(thread, true); 314 ChangeReadyState(thread, true);
294 } 315 }
@@ -301,12 +322,12 @@ void DebugThreadQueue() {
301 if (!thread) { 322 if (!thread) {
302 return; 323 return;
303 } 324 }
304 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); 325 LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
305 for (u32 i = 0; i < thread_queue.size(); i++) { 326 for (u32 i = 0; i < thread_queue.size(); i++) {
306 Handle handle = thread_queue[i]; 327 Handle handle = thread_queue[i];
307 s32 priority = thread_ready_queue.contains(handle); 328 s32 priority = thread_ready_queue.contains(handle);
308 if (priority != -1) { 329 if (priority != -1) {
309 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); 330 LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
310 } 331 }
311 } 332 }
312} 333}
@@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
316 s32 processor_id, u32 stack_top, int stack_size) { 337 s32 processor_id, u32 stack_top, int stack_size) {
317 338
318 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 339 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
319 "CreateThread priority=%d, outside of allowable range!", priority) 340 "priority=%d, outside of allowable range!", priority)
320 341
321 Thread* thread = new Thread; 342 Thread* thread = new Thread;
322 343
323 handle = Kernel::g_object_pool.Create(thread); 344 // TOOD(yuriks): Fix error reporting
345 handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
324 346
325 thread_queue.push_back(handle); 347 thread_queue.push_back(handle);
326 thread_ready_queue.prepare(priority); 348 thread_ready_queue.prepare(priority);
327 349
350 thread->thread_id = next_thread_id++;
328 thread->status = THREADSTATUS_DORMANT; 351 thread->status = THREADSTATUS_DORMANT;
329 thread->entry_point = entry_point; 352 thread->entry_point = entry_point;
330 thread->stack_top = stack_top; 353 thread->stack_top = stack_top;
@@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
333 thread->processor_id = processor_id; 356 thread->processor_id = processor_id;
334 thread->wait_type = WAITTYPE_NONE; 357 thread->wait_type = WAITTYPE_NONE;
335 thread->wait_handle = 0; 358 thread->wait_handle = 0;
359 thread->wait_address = 0;
336 thread->name = name; 360 thread->name = name;
337 361
338 return thread; 362 return thread;
@@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
343 u32 stack_top, int stack_size) { 367 u32 stack_top, int stack_size) {
344 368
345 if (name == nullptr) { 369 if (name == nullptr) {
346 ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); 370 LOG_ERROR(Kernel_SVC, "nullptr name");
347 return -1; 371 return -1;
348 } 372 }
349 if ((u32)stack_size < 0x200) { 373 if ((u32)stack_size < 0x200) {
350 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, 374 LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
351 stack_size); 375 stack_size);
352 return -1; 376 return -1;
353 } 377 }
354 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 378 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
355 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 379 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
356 WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", 380 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
357 name, priority, new_priority); 381 name, priority, new_priority);
358 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 382 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
359 // validity of this 383 // validity of this
360 priority = new_priority; 384 priority = new_priority;
361 } 385 }
362 if (!Memory::GetPointer(entry_point)) { 386 if (!Memory::GetPointer(entry_point)) {
363 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); 387 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
364 return -1; 388 return -1;
365 } 389 }
366 Handle handle; 390 Handle handle;
@@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
375 399
376/// Get the priority of the thread specified by handle 400/// Get the priority of the thread specified by handle
377ResultVal<u32> GetThreadPriority(const Handle handle) { 401ResultVal<u32> GetThreadPriority(const Handle handle) {
378 Thread* thread = g_object_pool.Get<Thread>(handle); 402 Thread* thread = g_handle_table.Get<Thread>(handle);
379 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 403 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
380 404
381 return MakeResult<u32>(thread->current_priority); 405 return MakeResult<u32>(thread->current_priority);
@@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
387 if (!handle) { 411 if (!handle) {
388 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? 412 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
389 } else { 413 } else {
390 thread = g_object_pool.Get<Thread>(handle); 414 thread = g_handle_table.Get<Thread>(handle);
391 if (thread == nullptr) { 415 if (thread == nullptr) {
392 return InvalidHandle(ErrorModule::Kernel); 416 return InvalidHandle(ErrorModule::Kernel);
393 } 417 }
@@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
397 // If priority is invalid, clamp to valid range 421 // If priority is invalid, clamp to valid range
398 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 422 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
399 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 423 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
400 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); 424 LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
401 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 425 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
402 // validity of this 426 // validity of this
403 priority = new_priority; 427 priority = new_priority;
@@ -450,24 +474,44 @@ void Reschedule() {
450 Thread* prev = GetCurrentThread(); 474 Thread* prev = GetCurrentThread();
451 Thread* next = NextThread(); 475 Thread* next = NextThread();
452 HLE::g_reschedule = false; 476 HLE::g_reschedule = false;
453 if (next > 0) {
454 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
455 477
478 if (next != nullptr) {
479 LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
456 SwitchContext(next); 480 SwitchContext(next);
481 } else {
482 LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
457 483
458 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep 484 for (Handle handle : thread_queue) {
459 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. 485 Thread* thread = g_handle_table.Get<Thread>(handle);
460 // This results in the current thread yielding on a VBLANK once, and then it will be 486 LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
461 // immediately placed back in the queue for execution. 487 thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
462 if (prev->wait_type == WAITTYPE_VBLANK) {
463 ResumeThreadFromWait(prev->GetHandle());
464 } 488 }
465 } 489 }
490
491 // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
492 // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
493 // switch has occurred. This results in the current thread yielding on a sleep once, and then it
494 // will immediately be placed back in the queue for execution.
495
496 if (CheckWaitType(prev, WAITTYPE_SLEEP))
497 ResumeThreadFromWait(prev->GetHandle());
498}
499
500ResultCode GetThreadId(u32* thread_id, Handle handle) {
501 Thread* thread = g_handle_table.Get<Thread>(handle);
502 if (thread == nullptr)
503 return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
504 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
505
506 *thread_id = thread->thread_id;
507
508 return RESULT_SUCCESS;
466} 509}
467 510
468//////////////////////////////////////////////////////////////////////////////////////////////////// 511////////////////////////////////////////////////////////////////////////////////////////////////////
469 512
470void ThreadingInit() { 513void ThreadingInit() {
514 next_thread_id = INITIAL_THREAD_ID;
471} 515}
472 516
473void ThreadingShutdown() { 517void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index ce63a70d3..0e1397cd9 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,10 +1,13 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8
9#include "core/mem_map.h"
10
8#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
9#include "core/hle/result.h" 12#include "core/hle/result.h"
10 13
@@ -37,7 +40,6 @@ enum WaitType {
37 WAITTYPE_SEMA, 40 WAITTYPE_SEMA,
38 WAITTYPE_EVENT, 41 WAITTYPE_EVENT,
39 WAITTYPE_THREADEND, 42 WAITTYPE_THREADEND,
40 WAITTYPE_VBLANK,
41 WAITTYPE_MUTEX, 43 WAITTYPE_MUTEX,
42 WAITTYPE_SYNCH, 44 WAITTYPE_SYNCH,
43 WAITTYPE_ARB, 45 WAITTYPE_ARB,
@@ -58,6 +60,14 @@ void Reschedule();
58/// Stops the current thread 60/// Stops the current thread
59ResultCode StopThread(Handle thread, const char* reason); 61ResultCode StopThread(Handle thread, const char* reason);
60 62
63/**
64 * Retrieves the ID of the specified thread handle
65 * @param thread_id Will contain the output thread id
66 * @param handle Handle to the thread we want
67 * @return Whether the function was successful or not
68 */
69ResultCode GetThreadId(u32* thread_id, Handle handle);
70
61/// Resumes a thread from waiting by marking it as "ready" 71/// Resumes a thread from waiting by marking it as "ready"
62void ResumeThreadFromWait(Handle handle); 72void ResumeThreadFromWait(Handle handle);
63 73
@@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle();
77 */ 87 */
78void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); 88void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
79 89
90/**
91 * Puts the current thread in the wait state for the given type
92 * @param wait_type Type of wait
93 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
94 * @param wait_address Arbitration address used to resume from wait
95 */
96void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
97
80/// Put current thread in a wait state - on WaitSynchronization 98/// Put current thread in a wait state - on WaitSynchronization
81void WaitThread_Synchronization(); 99void WaitThread_Synchronization();
82 100
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 15c4a2677..0e9c213e0 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -17,6 +17,8 @@
17/// Detailed description of the error. This listing is likely incomplete. 17/// Detailed description of the error. This listing is likely incomplete.
18enum class ErrorDescription : u32 { 18enum class ErrorDescription : u32 {
19 Success = 0, 19 Success = 0,
20 FS_NotFound = 100,
21 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
20 InvalidSection = 1000, 22 InvalidSection = 1000,
21 TooLarge = 1001, 23 TooLarge = 1001,
22 NotAuthorized = 1002, 24 NotAuthorized = 1002,
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index 9af96f6b8..20a3fa2e5 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -11,6 +11,24 @@
11 11
12namespace AC_U { 12namespace AC_U {
13 13
14/**
15 * AC_U::GetWifiStatus service function
16 * Outputs:
17 * 1 : Result of function, 0 on success, otherwise error code
18 * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
19 */
20void GetWifiStatus(Service::Interface* self) {
21 u32* cmd_buff = Kernel::GetCommandBuffer();
22
23 // TODO(purpasmart96): This function is only a stub,
24 // it returns a valid result without implementing full functionality.
25
26 cmd_buff[1] = 0; // No error
27 cmd_buff[2] = 0; // Connection type set to none
28
29 LOG_WARNING(Service_AC, "(STUBBED) called");
30}
31
14const Interface::FunctionInfo FunctionTable[] = { 32const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010000, nullptr, "CreateDefaultConfig"}, 33 {0x00010000, nullptr, "CreateDefaultConfig"},
16 {0x00040006, nullptr, "ConnectAsync"}, 34 {0x00040006, nullptr, "ConnectAsync"},
@@ -18,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = {
18 {0x00080004, nullptr, "CloseAsync"}, 36 {0x00080004, nullptr, "CloseAsync"},
19 {0x00090002, nullptr, "GetCloseResult"}, 37 {0x00090002, nullptr, "GetCloseResult"},
20 {0x000A0000, nullptr, "GetLastErrorCode"}, 38 {0x000A0000, nullptr, "GetLastErrorCode"},
21 {0x000D0000, nullptr, "GetWifiStatus"}, 39 {0x000D0000, GetWifiStatus, "GetWifiStatus"},
22 {0x000E0042, nullptr, "GetCurrentAPInfo"}, 40 {0x000E0042, nullptr, "GetCurrentAPInfo"},
23 {0x00100042, nullptr, "GetCurrentNZoneInfo"}, 41 {0x00100042, nullptr, "GetCurrentNZoneInfo"},
24 {0x00110042, nullptr, "GetNZoneApNumService"}, 42 {0x00110042, nullptr, "GetNZoneApNumService"},
@@ -38,7 +56,4 @@ Interface::Interface() {
38 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 56 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
39} 57}
40 58
41Interface::~Interface() {
42}
43
44} // namespace 59} // namespace
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h
index c91b28353..f1d26ebe8 100644
--- a/src/core/hle/service/ac_u.h
+++ b/src/core/hle/service/ac_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -16,11 +16,7 @@ namespace AC_U {
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18 Interface(); 18 Interface();
19 ~Interface(); 19
20 /**
21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service
23 */
24 std::string GetPortName() const override { 20 std::string GetPortName() const override {
25 return "ac:u"; 21 return "ac:u";
26 } 22 }
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
new file mode 100644
index 000000000..10870f14b
--- /dev/null
+++ b/src/core/hle/service/act_u.cpp
@@ -0,0 +1,24 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/act_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace ACT_U
11
12namespace ACT_U {
13
14// Empty arrays are illegal -- commented out until an entry is added.
15//const Interface::FunctionInfo FunctionTable[] = { };
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// Interface class
19
20Interface::Interface() {
21 //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22}
23
24} // namespace
diff --git a/src/core/hle/service/act_u.h b/src/core/hle/service/act_u.h
new file mode 100644
index 000000000..be41454a4
--- /dev/null
+++ b/src/core/hle/service/act_u.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace ACT_U
11
12namespace ACT_U {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "act:u";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
new file mode 100644
index 000000000..0b396b6d3
--- /dev/null
+++ b/src/core/hle/service/am_app.cpp
@@ -0,0 +1,24 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/am_app.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AM_APP
11
12namespace AM_APP {
13
14// Empty arrays are illegal -- commented out until an entry is added.
15//const Interface::FunctionInfo FunctionTable[] = { };
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// Interface class
19
20Interface::Interface() {
21 //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22}
23
24} // namespace
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h
new file mode 100644
index 000000000..50dc2f5a2
--- /dev/null
+++ b/src/core/hle/service/am_app.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AM_APP
11
12namespace AM_APP {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "am:app";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
index 403cac353..112844e5b 100644
--- a/src/core/hle/service/am_net.cpp
+++ b/src/core/hle/service/am_net.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -41,7 +41,4 @@ Interface::Interface() {
41 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 41 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
42} 42}
43 43
44Interface::~Interface() {
45}
46
47} // namespace 44} // namespace
diff --git a/src/core/hle/service/am_net.h b/src/core/hle/service/am_net.h
index 4816e1697..616c33ee8 100644
--- a/src/core/hle/service/am_net.h
+++ b/src/core/hle/service/am_net.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace AM_NET {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "am:net"; 19 return "am:net";
24 } 20 }
diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp
new file mode 100644
index 000000000..dcf5ec4fe
--- /dev/null
+++ b/src/core/hle/service/apt_a.cpp
@@ -0,0 +1,34 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/apt_a.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace APT_A
11
12namespace APT_A {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010040, nullptr, "GetLockHandle?"},
16 {0x00020080, nullptr, "Initialize?"},
17 {0x00030040, nullptr, "Enable?"},
18 {0x00040040, nullptr, "Finalize?"},
19 {0x00050040, nullptr, "GetAppletManInfo?"},
20 {0x00060040, nullptr, "GetAppletInfo?"},
21 {0x003B0040, nullptr, "CancelLibraryApplet?"},
22 {0x00430040, nullptr, "NotifyToWait?"},
23 {0x004B00C2, nullptr, "AppletUtility?"},
24 {0x00550040, nullptr, "WriteInputToNsState?"},
25};
26
27////////////////////////////////////////////////////////////////////////////////////////////////////
28// Interface class
29
30Interface::Interface() {
31 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
32}
33
34} // namespace
diff --git a/src/core/hle/service/apt_a.h b/src/core/hle/service/apt_a.h
new file mode 100644
index 000000000..6cbf1288f
--- /dev/null
+++ b/src/core/hle/service/apt_a.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace APT_A
11
12namespace APT_A {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "APT:A";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index 4bb05ce40..d8b261ba7 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -1,13 +1,15 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5 5
6#include "common/common.h" 6#include "common/common.h"
7#include "common/file_util.h"
7 8
8#include "core/hle/hle.h" 9#include "core/hle/hle.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
12#include "core/hle/kernel/shared_memory.h"
11#include "apt_u.h" 13#include "apt_u.h"
12 14
13//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,7 +17,19 @@
15 17
16namespace APT_U { 18namespace APT_U {
17 19
20// Address used for shared font (as observed on HW)
21// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
22// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
23// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
24// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
25// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
26static const VAddr SHARED_FONT_VADDR = 0x18000000;
27
28// Handle to shared memory region designated to for shared system font
29static Handle shared_font_mem = 0;
30
18static Handle lock_handle = 0; 31static Handle lock_handle = 0;
32static std::vector<u8> shared_font;
19 33
20/// Signals used by APT functions 34/// Signals used by APT functions
21enum class SignalType : u32 { 35enum class SignalType : u32 {
@@ -26,7 +40,7 @@ enum class SignalType : u32 {
26}; 40};
27 41
28void Initialize(Service::Interface* self) { 42void Initialize(Service::Interface* self) {
29 u32* cmd_buff = Service::GetCommandBuffer(); 43 u32* cmd_buff = Kernel::GetCommandBuffer();
30 44
31 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle 45 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
32 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle 46 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
@@ -39,11 +53,11 @@ void Initialize(Service::Interface* self) {
39 53
40 cmd_buff[1] = 0; // No error 54 cmd_buff[1] = 0; // No error
41 55
42 DEBUG_LOG(KERNEL, "called"); 56 LOG_DEBUG(Service_APT, "called");
43} 57}
44 58
45void GetLockHandle(Service::Interface* self) { 59void GetLockHandle(Service::Interface* self) {
46 u32* cmd_buff = Service::GetCommandBuffer(); 60 u32* cmd_buff = Kernel::GetCommandBuffer();
47 u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field 61 u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
48 62
49 if (0 == lock_handle) { 63 if (0 == lock_handle) {
@@ -60,22 +74,22 @@ void GetLockHandle(Service::Interface* self) {
60 cmd_buff[4] = 0; 74 cmd_buff[4] = 0;
61 75
62 cmd_buff[5] = lock_handle; 76 cmd_buff[5] = lock_handle;
63 DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]); 77 LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
64} 78}
65 79
66void Enable(Service::Interface* self) { 80void Enable(Service::Interface* self) {
67 u32* cmd_buff = Service::GetCommandBuffer(); 81 u32* cmd_buff = Kernel::GetCommandBuffer();
68 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? 82 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
69 cmd_buff[1] = 0; // No error 83 cmd_buff[1] = 0; // No error
70 WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk); 84 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
71} 85}
72 86
73void InquireNotification(Service::Interface* self) { 87void InquireNotification(Service::Interface* self) {
74 u32* cmd_buff = Service::GetCommandBuffer(); 88 u32* cmd_buff = Kernel::GetCommandBuffer();
75 u32 app_id = cmd_buff[2]; 89 u32 app_id = cmd_buff[2];
76 cmd_buff[1] = 0; // No error 90 cmd_buff[1] = 0; // No error
77 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type 91 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
78 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id); 92 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
79} 93}
80 94
81/** 95/**
@@ -84,21 +98,21 @@ void InquireNotification(Service::Interface* self) {
84 * state so that this command will return an error if this command is used again if parameters were 98 * state so that this command will return an error if this command is used again if parameters were
85 * not set again. This is called when the second Initialize event is triggered. It returns a signal 99 * not set again. This is called when the second Initialize event is triggered. It returns a signal
86 * type indicating why it was triggered. 100 * type indicating why it was triggered.
87 * Inputs: 101 * Inputs:
88 * 1 : AppID 102 * 1 : AppID
89 * 2 : Parameter buffer size, max size is 0x1000 103 * 2 : Parameter buffer size, max size is 0x1000
90 * Outputs: 104 * Outputs:
91 * 1 : Result of function, 0 on success, otherwise error code 105 * 1 : Result of function, 0 on success, otherwise error code
92 * 2 : Unknown, for now assume AppID of the process which sent these parameters 106 * 2 : Unknown, for now assume AppID of the process which sent these parameters
93 * 3 : Unknown, for now assume Signal type 107 * 3 : Unknown, for now assume Signal type
94 * 4 : Actual parameter buffer size, this is <= to the the input size 108 * 4 : Actual parameter buffer size, this is <= to the the input size
95 * 5 : Value 109 * 5 : Value
96 * 6 : Handle from the source process which set the parameters, likely used for shared memory 110 * 6 : Handle from the source process which set the parameters, likely used for shared memory
97 * 7 : Size 111 * 7 : Size
98 * 8 : Output parameter buffer ptr 112 * 8 : Output parameter buffer ptr
99 */ 113 */
100void ReceiveParameter(Service::Interface* self) { 114void ReceiveParameter(Service::Interface* self) {
101 u32* cmd_buff = Service::GetCommandBuffer(); 115 u32* cmd_buff = Kernel::GetCommandBuffer();
102 u32 app_id = cmd_buff[1]; 116 u32 app_id = cmd_buff[1];
103 u32 buffer_size = cmd_buff[2]; 117 u32 buffer_size = cmd_buff[2];
104 cmd_buff[1] = 0; // No error 118 cmd_buff[1] = 0; // No error
@@ -108,28 +122,28 @@ void ReceiveParameter(Service::Interface* self) {
108 cmd_buff[5] = 0; 122 cmd_buff[5] = 0;
109 cmd_buff[6] = 0; 123 cmd_buff[6] = 0;
110 cmd_buff[7] = 0; 124 cmd_buff[7] = 0;
111 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 125 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
112} 126}
113 127
114/** 128/**
115 * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter 129 * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
116 * (except for the word value prior to the output handle), except this will not clear the flag 130 * (except for the word value prior to the output handle), except this will not clear the flag
117 * (except when responseword[3]==8 || responseword[3]==9) in NS state. 131 * (except when responseword[3]==8 || responseword[3]==9) in NS state.
118 * Inputs: 132 * Inputs:
119 * 1 : AppID 133 * 1 : AppID
120 * 2 : Parameter buffer size, max size is 0x1000 134 * 2 : Parameter buffer size, max size is 0x1000
121 * Outputs: 135 * Outputs:
122 * 1 : Result of function, 0 on success, otherwise error code 136 * 1 : Result of function, 0 on success, otherwise error code
123 * 2 : Unknown, for now assume AppID of the process which sent these parameters 137 * 2 : Unknown, for now assume AppID of the process which sent these parameters
124 * 3 : Unknown, for now assume Signal type 138 * 3 : Unknown, for now assume Signal type
125 * 4 : Actual parameter buffer size, this is <= to the the input size 139 * 4 : Actual parameter buffer size, this is <= to the the input size
126 * 5 : Value 140 * 5 : Value
127 * 6 : Handle from the source process which set the parameters, likely used for shared memory 141 * 6 : Handle from the source process which set the parameters, likely used for shared memory
128 * 7 : Size 142 * 7 : Size
129 * 8 : Output parameter buffer ptr 143 * 8 : Output parameter buffer ptr
130 */ 144 */
131void GlanceParameter(Service::Interface* self) { 145void GlanceParameter(Service::Interface* self) {
132 u32* cmd_buff = Service::GetCommandBuffer(); 146 u32* cmd_buff = Kernel::GetCommandBuffer();
133 u32 app_id = cmd_buff[1]; 147 u32 app_id = cmd_buff[1];
134 u32 buffer_size = cmd_buff[2]; 148 u32 buffer_size = cmd_buff[2];
135 149
@@ -141,22 +155,22 @@ void GlanceParameter(Service::Interface* self) {
141 cmd_buff[6] = 0; 155 cmd_buff[6] = 0;
142 cmd_buff[7] = 0; 156 cmd_buff[7] = 0;
143 157
144 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 158 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
145} 159}
146 160
147/** 161/**
148 * APT_U::AppletUtility service function 162 * APT_U::AppletUtility service function
149 * Inputs: 163 * Inputs:
150 * 1 : Unknown, but clearly used for something 164 * 1 : Unknown, but clearly used for something
151 * 2 : Buffer 1 size (purpose is unknown) 165 * 2 : Buffer 1 size (purpose is unknown)
152 * 3 : Buffer 2 size (purpose is unknown) 166 * 3 : Buffer 2 size (purpose is unknown)
153 * 5 : Buffer 1 address (purpose is unknown) 167 * 5 : Buffer 1 address (purpose is unknown)
154 * 65 : Buffer 2 address (purpose is unknown) 168 * 65 : Buffer 2 address (purpose is unknown)
155 * Outputs: 169 * Outputs:
156 * 1 : Result of function, 0 on success, otherwise error code 170 * 1 : Result of function, 0 on success, otherwise error code
157 */ 171 */
158void AppletUtility(Service::Interface* self) { 172void AppletUtility(Service::Interface* self) {
159 u32* cmd_buff = Service::GetCommandBuffer(); 173 u32* cmd_buff = Kernel::GetCommandBuffer();
160 174
161 // These are from 3dbrew - I'm not really sure what they're used for. 175 // These are from 3dbrew - I'm not really sure what they're used for.
162 u32 unk = cmd_buff[1]; 176 u32 unk = cmd_buff[1];
@@ -167,11 +181,39 @@ void AppletUtility(Service::Interface* self) {
167 181
168 cmd_buff[1] = 0; // No error 182 cmd_buff[1] = 0; // No error
169 183
170 WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " 184 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
171 "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, 185 "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
172 buffer1_addr, buffer2_addr); 186 buffer1_addr, buffer2_addr);
173} 187}
174 188
189/**
190 * APT_U::GetSharedFont service function
191 * Outputs:
192 * 1 : Result of function, 0 on success, otherwise error code
193 * 2 : Virtual address of where shared font will be loaded in memory
194 * 4 : Handle to shared font memory
195 */
196void GetSharedFont(Service::Interface* self) {
197 LOG_TRACE(Kernel_SVC, "called");
198
199 u32* cmd_buff = Kernel::GetCommandBuffer();
200
201 if (!shared_font.empty()) {
202 // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
203 // Instead, it should probably map the shared font as RO memory. We don't currently have
204 // an easy way to do this, but the copy should be sufficient for now.
205 memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
206
207 cmd_buff[0] = 0x00440082;
208 cmd_buff[1] = 0; // No error
209 cmd_buff[2] = SHARED_FONT_VADDR;
210 cmd_buff[4] = shared_font_mem;
211 } else {
212 cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
213 LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
214 }
215}
216
175const Interface::FunctionInfo FunctionTable[] = { 217const Interface::FunctionInfo FunctionTable[] = {
176 {0x00010040, GetLockHandle, "GetLockHandle"}, 218 {0x00010040, GetLockHandle, "GetLockHandle"},
177 {0x00020080, Initialize, "Initialize"}, 219 {0x00020080, Initialize, "Initialize"},
@@ -240,7 +282,7 @@ const Interface::FunctionInfo FunctionTable[] = {
240 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, 282 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
241 {0x00420080, nullptr, "SleepSystem"}, 283 {0x00420080, nullptr, "SleepSystem"},
242 {0x00430040, nullptr, "NotifyToWait"}, 284 {0x00430040, nullptr, "NotifyToWait"},
243 {0x00440000, nullptr, "GetSharedFont"}, 285 {0x00440000, GetSharedFont, "GetSharedFont"},
244 {0x00450040, nullptr, "GetWirelessRebootInfo"}, 286 {0x00450040, nullptr, "GetWirelessRebootInfo"},
245 {0x00460104, nullptr, "Wrap"}, 287 {0x00460104, nullptr, "Wrap"},
246 {0x00470104, nullptr, "Unwrap"}, 288 {0x00470104, nullptr, "Unwrap"},
@@ -259,12 +301,33 @@ const Interface::FunctionInfo FunctionTable[] = {
259// Interface class 301// Interface class
260 302
261Interface::Interface() { 303Interface::Interface() {
262 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 304 // Load the shared system font (if available).
305 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
306 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
307 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
308 // "shared_font.bin" in the Citra "sysdata" directory.
309
310 shared_font.clear();
311 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
312
313 FileUtil::CreateFullPath(filepath); // Create path if not already created
314 FileUtil::IOFile file(filepath, "rb");
315
316 if (file.IsOpen()) {
317 // Read shared font data
318 shared_font.resize((size_t)file.GetSize());
319 file.ReadBytes(shared_font.data(), (size_t)file.GetSize());
320
321 // Create shared font memory object
322 shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
323 } else {
324 LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
325 shared_font_mem = 0;
326 }
263 327
264 lock_handle = 0; 328 lock_handle = 0;
265}
266 329
267Interface::~Interface() { 330 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
268} 331}
269 332
270} // namespace 333} // namespace
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h
index 306730400..aad918cfc 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -20,15 +20,8 @@ namespace APT_U {
20/// Interface to "APT:U" service 20/// Interface to "APT:U" service
21class Interface : public Service::Interface { 21class Interface : public Service::Interface {
22public: 22public:
23
24 Interface(); 23 Interface();
25 24
26 ~Interface();
27
28 /**
29 * Gets the string port name used by CTROS for the service
30 * @return Port name of service
31 */
32 std::string GetPortName() const override { 25 std::string GetPortName() const override {
33 return "APT:U"; 26 return "APT:U";
34 } 27 }
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp
index b2ff4a756..1820ea7ad 100644
--- a/src/core/hle/service/boss_u.cpp
+++ b/src/core/hle/service/boss_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -11,18 +11,15 @@
11 11
12namespace BOSS_U { 12namespace BOSS_U {
13 13
14 const Interface::FunctionInfo FunctionTable[] = { 14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00020100, nullptr, "GetStorageInfo"}, 15 {0x00020100, nullptr, "GetStorageInfo"},
16 }; 16};
17 17
18 //////////////////////////////////////////////////////////////////////////////////////////////////// 18////////////////////////////////////////////////////////////////////////////////////////////////////
19 // Interface class 19// Interface class
20
21 Interface::Interface() {
22 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
23 }
24 20
25 Interface::~Interface() { 21Interface::Interface() {
26 } 22 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
23}
27 24
28} // namespace 25} // namespace
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h
index af39b8e65..2668f2dfd 100644
--- a/src/core/hle/service/boss_u.h
+++ b/src/core/hle/service/boss_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -11,17 +11,13 @@
11 11
12namespace BOSS_U { 12namespace BOSS_U {
13 13
14 class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15 public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /** 18 std::string GetPortName() const override {
19 * Gets the string port name used by CTROS for the service 19 return "boss:U";
20 * @return Port name of service 20 }
21 */ 21};
22 std::string GetPortName() const {
23 return "boss:U";
24 }
25 };
26 22
27} // namespace 23} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
new file mode 100644
index 000000000..b7655ef0b
--- /dev/null
+++ b/src/core/hle/service/cecd_u.cpp
@@ -0,0 +1,24 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cecd_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CECD_U
11
12namespace CECD_U {
13
14// Empty arrays are illegal -- commented out until an entry is added.
15//const Interface::FunctionInfo FunctionTable[] = { };
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// Interface class
19
20Interface::Interface() {
21 //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22}
23
24} // namespace
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h
new file mode 100644
index 000000000..e67564135
--- /dev/null
+++ b/src/core/hle/service/cecd_u.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CECD_U
11
12namespace CECD_U {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "cecd:u";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
new file mode 100644
index 000000000..161aa8531
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -0,0 +1,202 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/log.h"
7#include "common/make_unique.h"
8#include "core/file_sys/archive_systemsavedata.h"
9#include "core/hle/service/cfg/cfg.h"
10
11namespace Service {
12namespace CFG {
13
14const u64 CFG_SAVE_ID = 0x00010017;
15const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
16const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
17const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
18const char CONSOLE_USERNAME[0x14] = "CITRA";
19/// This will be initialized in CFGInit, and will be used when creating the block
20UsernameBlock CONSOLE_USERNAME_BLOCK;
21/// TODO(Subv): Find out what this actually is
22const u8 SOUND_OUTPUT_MODE = 2;
23const u8 UNITED_STATES_COUNTRY_ID = 49;
24/// TODO(Subv): Find what the other bytes are
25const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
26
27/**
28 * TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
29 * for example Nintendo Zone
30 * Thanks Normmatt for providing this information
31 */
32const std::array<float, 8> STEREO_CAMERA_SETTINGS = {
33 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f,
34 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f
35};
36
37static const u32 CONFIG_SAVEFILE_SIZE = 0x8000;
38static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
39
40static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data;
41
42ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
43 // Read the header
44 SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
45
46 auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
47 [&](const SaveConfigBlockEntry& entry) {
48 return entry.block_id == block_id && entry.size == size && (entry.flags & flag);
49 });
50
51 if (itr == std::end(config->block_entries)) {
52 LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag);
53 return ResultCode(-1); // TODO(Subv): Find the correct error code
54 }
55
56 // The data is located in the block header itself if the size is less than 4 bytes
57 if (itr->size <= 4)
58 memcpy(output, &itr->offset_or_data, itr->size);
59 else
60 memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size);
61
62 return RESULT_SUCCESS;
63}
64
65ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data) {
66 SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
67 if (config->total_entries >= CONFIG_FILE_MAX_BLOCK_ENTRIES)
68 return ResultCode(-1); // TODO(Subv): Find the right error code
69
70 // Insert the block header with offset 0 for now
71 config->block_entries[config->total_entries] = { block_id, 0, size, flags };
72 if (size > 4) {
73 u32 offset = config->data_entries_offset;
74 // Perform a search to locate the next offset for the new data
75 // use the offset and size of the previous block to determine the new position
76 for (int i = config->total_entries - 1; i >= 0; --i) {
77 // Ignore the blocks that don't have a separate data offset
78 if (config->block_entries[i].size > 4) {
79 offset = config->block_entries[i].offset_or_data +
80 config->block_entries[i].size;
81 break;
82 }
83 }
84
85 config->block_entries[config->total_entries].offset_or_data = offset;
86
87 // Write the data at the new offset
88 memcpy(&cfg_config_file_buffer[offset], data, size);
89 }
90 else {
91 // The offset_or_data field in the header contains the data itself if it's 4 bytes or less
92 memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size);
93 }
94
95 ++config->total_entries;
96 return RESULT_SUCCESS;
97}
98
99ResultCode DeleteConfigNANDSaveFile() {
100 FileSys::Path path("config");
101 if (cfg_system_save_data->DeleteFile(path))
102 return RESULT_SUCCESS;
103 return ResultCode(-1); // TODO(Subv): Find the right error code
104}
105
106ResultCode UpdateConfigNANDSavegame() {
107 FileSys::Mode mode = {};
108 mode.write_flag = 1;
109 mode.create_flag = 1;
110 FileSys::Path path("config");
111 auto file = cfg_system_save_data->OpenFile(path, mode);
112 _assert_msg_(Service_CFG, file != nullptr, "could not open file");
113 file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
114 return RESULT_SUCCESS;
115}
116
117ResultCode FormatConfig() {
118 ResultCode res = DeleteConfigNANDSaveFile();
119 if (!res.IsSuccess())
120 return res;
121 // Delete the old data
122 cfg_config_file_buffer.fill(0);
123 // Create the header
124 SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
125 // This value is hardcoded, taken from 3dbrew, verified by hardware, it's always the same value
126 config->data_entries_offset = 0x455C;
127 // Insert the default blocks
128 res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE,
129 reinterpret_cast<const u8*>(STEREO_CAMERA_SETTINGS.data()));
130 if (!res.IsSuccess())
131 return res;
132 res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE,
133 reinterpret_cast<const u8*>(&CONSOLE_UNIQUE_ID));
134 if (!res.IsSuccess())
135 return res;
136 res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0x8,
137 reinterpret_cast<const u8*>(&CONSOLE_MODEL));
138 if (!res.IsSuccess())
139 return res;
140 res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xA, &CONSOLE_LANGUAGE);
141 if (!res.IsSuccess())
142 return res;
143 res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
144 if (!res.IsSuccess())
145 return res;
146 res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE,
147 reinterpret_cast<const u8*>(&COUNTRY_INFO));
148 if (!res.IsSuccess())
149 return res;
150 res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE,
151 reinterpret_cast<const u8*>(&CONSOLE_USERNAME_BLOCK));
152 if (!res.IsSuccess())
153 return res;
154 // Save the buffer to the file
155 res = UpdateConfigNANDSavegame();
156 if (!res.IsSuccess())
157 return res;
158 return RESULT_SUCCESS;
159}
160
161void CFGInit() {
162 // TODO(Subv): In the future we should use the FS service to query this archive,
163 // currently it is not possible because you can only have one open archive of the same type at any time
164 std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX);
165 cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>(
166 syssavedata_directory, CFG_SAVE_ID);
167 if (!cfg_system_save_data->Initialize()) {
168 LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service");
169 return;
170 }
171
172 // TODO(Subv): All this code should be moved to cfg:i,
173 // it's only here because we do not currently emulate the lower level code that uses that service
174 // Try to open the file in read-only mode to check its existence
175 FileSys::Mode mode = {};
176 mode.read_flag = 1;
177 FileSys::Path path("config");
178 auto file = cfg_system_save_data->OpenFile(path, mode);
179
180 // Load the config if it already exists
181 if (file != nullptr) {
182 file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
183 return;
184 }
185
186 // Initialize the Username block
187 // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
188 CONSOLE_USERNAME_BLOCK.ng_word = 0;
189 CONSOLE_USERNAME_BLOCK.zero = 0;
190 // Copy string to buffer and pad with zeros at the end
191 auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
192 std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
193 std::end(CONSOLE_USERNAME_BLOCK.username), 0);
194 FormatConfig();
195}
196
197void CFGShutdown() {
198
199}
200
201} // namespace CFG
202} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
new file mode 100644
index 000000000..c74527ca4
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg.h
@@ -0,0 +1,144 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "core/hle/result.h"
9
10namespace Service {
11namespace CFG {
12
13enum SystemModel {
14 NINTENDO_3DS = 0,
15 NINTENDO_3DS_XL = 1,
16 NEW_NINTENDO_3DS = 2,
17 NINTENDO_2DS = 3,
18 NEW_NINTENDO_3DS_XL = 4
19};
20
21enum SystemLanguage {
22 LANGUAGE_JP = 0,
23 LANGUAGE_EN = 1,
24 LANGUAGE_FR = 2,
25 LANGUAGE_DE = 3,
26 LANGUAGE_IT = 4,
27 LANGUAGE_ES = 5,
28 LANGUAGE_ZH = 6,
29 LANGUAGE_KO = 7,
30 LANGUAGE_NL = 8,
31 LANGUAGE_PT = 9,
32 LANGUAGE_RU = 10
33};
34
35/// Block header in the config savedata file
36struct SaveConfigBlockEntry {
37 u32 block_id; ///< The id of the current block
38 u32 offset_or_data; ///< This is the absolute offset to the block data if the size is greater than 4 bytes, otherwise it contains the data itself
39 u16 size; ///< The size of the block
40 u16 flags; ///< The flags of the block, possibly used for access control
41};
42
43/// The maximum number of block entries that can exist in the config file
44static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479;
45
46/**
47* The header of the config savedata file,
48* contains information about the blocks in the file
49*/
50struct SaveFileConfig {
51 u16 total_entries; ///< The total number of set entries in the config file
52 u16 data_entries_offset; ///< The offset where the data for the blocks start, this is hardcoded to 0x455C as per hardware
53 SaveConfigBlockEntry block_entries[CONFIG_FILE_MAX_BLOCK_ENTRIES]; ///< The block headers, the maximum possible value is 1479 as per hardware
54 u32 unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware
55};
56static_assert(sizeof(SaveFileConfig) == 0x455C, "The SaveFileConfig header must be exactly 0x455C bytes");
57
58struct UsernameBlock {
59 char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
60 u32 zero;
61 u32 ng_word;
62};
63static_assert(sizeof(UsernameBlock) == 0x1C, "Size of UsernameBlock must be 0x1C");
64
65struct ConsoleModelInfo {
66 u8 model; ///< The console model (3DS, 2DS, etc)
67 u8 unknown[3]; ///< Unknown data
68};
69static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes");
70
71struct ConsoleCountryInfo {
72 u8 unknown[3]; ///< Unknown data
73 u8 country_code; ///< The country code of the console
74};
75static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
76
77extern const u64 CFG_SAVE_ID;
78extern const u64 CONSOLE_UNIQUE_ID;
79extern const ConsoleModelInfo CONSOLE_MODEL;
80extern const u8 CONSOLE_LANGUAGE;
81extern const char CONSOLE_USERNAME[0x14];
82/// This will be initialized in the Interface constructor, and will be used when creating the block
83extern UsernameBlock CONSOLE_USERNAME_BLOCK;
84/// TODO(Subv): Find out what this actually is
85extern const u8 SOUND_OUTPUT_MODE;
86extern const u8 UNITED_STATES_COUNTRY_ID;
87/// TODO(Subv): Find what the other bytes are
88extern const ConsoleCountryInfo COUNTRY_INFO;
89extern const std::array<float, 8> STEREO_CAMERA_SETTINGS;
90
91static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20, "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes");
92static_assert(sizeof(CONSOLE_UNIQUE_ID) == 8, "CONSOLE_UNIQUE_ID must be exactly 8 bytes");
93static_assert(sizeof(CONSOLE_LANGUAGE) == 1, "CONSOLE_LANGUAGE must be exactly 1 byte");
94static_assert(sizeof(SOUND_OUTPUT_MODE) == 1, "SOUND_OUTPUT_MODE must be exactly 1 byte");
95
96/**
97 * Reads a block with the specified id and flag from the Config savegame buffer
98 * and writes the output to output.
99 * The input size must match exactly the size of the requested block
100 * @param block_id The id of the block we want to read
101 * @param size The size of the block we want to read
102 * @param flag The requested block must have this flag set
103 * @param output A pointer where we will write the read data
104 * @returns ResultCode indicating the result of the operation, 0 on success
105 */
106ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
107
108/**
109 * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory.
110 * The config savegame file in the filesystem is not updated.
111 * @param block_id The id of the block we want to create
112 * @param size The size of the block we want to create
113 * @param flag The flags of the new block
114 * @param data A pointer containing the data we will write to the new block
115 * @returns ResultCode indicating the result of the operation, 0 on success
116 */
117ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data);
118
119/**
120 * Deletes the config savegame file from the filesystem, the buffer in memory is not affected
121 * @returns ResultCode indicating the result of the operation, 0 on success
122 */
123ResultCode DeleteConfigNANDSaveFile();
124
125/**
126 * Writes the config savegame memory buffer to the config savegame file in the filesystem
127 * @returns ResultCode indicating the result of the operation, 0 on success
128 */
129ResultCode UpdateConfigNANDSavegame();
130
131/**
132 * Re-creates the config savegame file in memory and the filesystem with the default blocks
133 * @returns ResultCode indicating the result of the operation, 0 on success
134 */
135ResultCode FormatConfig();
136
137/// Initialize the config service
138void CFGInit();
139
140/// Shutdown the config service
141void CFGShutdown();
142
143} // namespace CFG
144} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
new file mode 100644
index 000000000..7c1ee8ac3
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -0,0 +1,110 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cfg/cfg.h"
8#include "core/hle/service/cfg/cfg_i.h"
9
10////////////////////////////////////////////////////////////////////////////////////////////////////
11// Namespace CFG_I
12
13namespace CFG_I {
14
15/**
16 * CFG_I::GetConfigInfoBlk8 service function
17 * This function is called by two command headers,
18 * there appears to be no difference between them according to 3dbrew
19 * Inputs:
20 * 0 : 0x04010082 / 0x08010082
21 * 1 : Size
22 * 2 : Block ID
23 * 3 : Descriptor for the output buffer
24 * 4 : Output buffer pointer
25 * Outputs:
26 * 1 : Result of function, 0 on success, otherwise error code
27 */
28static void GetConfigInfoBlk8(Service::Interface* self) {
29 u32* cmd_buffer = Kernel::GetCommandBuffer();
30 u32 size = cmd_buffer[1];
31 u32 block_id = cmd_buffer[2];
32 u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
33
34 if (data_pointer == nullptr) {
35 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
36 return;
37 }
38
39 cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
40}
41
42/**
43 * CFG_I::UpdateConfigNANDSavegame service function
44 * This function is called by two command headers,
45 * there appears to be no difference between them according to 3dbrew
46 * Inputs:
47 * 0 : 0x04030000 / 0x08030000
48 * Outputs:
49 * 1 : Result of function, 0 on success, otherwise error code
50 */
51static void UpdateConfigNANDSavegame(Service::Interface* self) {
52 u32* cmd_buffer = Kernel::GetCommandBuffer();
53 cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
54}
55
56/**
57 * CFG_I::FormatConfig service function
58 * Inputs:
59 * 0 : 0x08060000
60 * Outputs:
61 * 1 : Result of function, 0 on success, otherwise error code
62 */
63static void FormatConfig(Service::Interface* self) {
64 u32* cmd_buffer = Kernel::GetCommandBuffer();
65 cmd_buffer[1] = Service::CFG::FormatConfig().raw;
66}
67
68const Interface::FunctionInfo FunctionTable[] = {
69 {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
70 {0x04020082, nullptr, "SetConfigInfoBlk4"},
71 {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
72 {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
73 {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
74 {0x04060000, nullptr, "SecureInfoGetRegion"},
75 {0x04070000, nullptr, "SecureInfoGetByte101"},
76 {0x04080042, nullptr, "SecureInfoGetSerialNo"},
77 {0x04090000, nullptr, "UpdateConfigBlk00040003"},
78 {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
79 {0x08020082, nullptr, "SetConfigInfoBlk4"},
80 {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
81 {0x080400C2, nullptr, "CreateConfigInfoBlk"},
82 {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
83 {0x08060000, FormatConfig, "FormatConfig"},
84 {0x08080000, nullptr, "UpdateConfigBlk1"},
85 {0x08090000, nullptr, "UpdateConfigBlk2"},
86 {0x080A0000, nullptr, "UpdateConfigBlk3"},
87 {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
88 {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
89 {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
90 {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
91 {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
92 {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
93 {0x08110084, nullptr, "SetSecureInfo"},
94 {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
95 {0x08130000, nullptr, "VerifySigSecureInfo"},
96 {0x08140042, nullptr, "SecureInfoGetData"},
97 {0x08150042, nullptr, "SecureInfoGetSignature"},
98 {0x08160000, nullptr, "SecureInfoGetRegion"},
99 {0x08170000, nullptr, "SecureInfoGetByte101"},
100 {0x08180042, nullptr, "SecureInfoGetSerialNo"},
101};
102
103////////////////////////////////////////////////////////////////////////////////////////////////////
104// Interface class
105
106Interface::Interface() {
107 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
108}
109
110} // namespace
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h
index fe343c968..a498dd589 100644
--- a/src/core/hle/service/cfg_i.h
+++ b/src/core/hle/service/cfg/cfg_i.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace CFG_I {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "cfg:i"; 19 return "cfg:i";
24 } 20 }
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
new file mode 100644
index 000000000..03c01cf90
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -0,0 +1,192 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/file_util.h"
6#include "common/log.h"
7#include "common/string_util.h"
8#include "core/file_sys/archive_systemsavedata.h"
9#include "core/hle/hle.h"
10#include "core/hle/service/cfg/cfg.h"
11#include "core/hle/service/cfg/cfg_u.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Namespace CFG_U
15
16namespace CFG_U {
17
18// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
19#define C(code) ((code)[0] | ((code)[1] << 8))
20
21static const std::array<u16, 187> country_codes = {
22 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
23 C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
24 C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
25 C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
26 C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
27 C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
28 C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
29 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
30 C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
31 C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
32 C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
33 C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
34 C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
35 C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
36 C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
37 C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
38 C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
39 C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
40 C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
41 C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
42 C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
43 C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
44 C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
45 C("SM"), C("VA"), C("BM") // 184-186
46};
47
48#undef C
49
50/**
51 * CFG_User::GetCountryCodeString service function
52 * Inputs:
53 * 1 : Country Code ID
54 * Outputs:
55 * 1 : Result of function, 0 on success, otherwise error code
56 * 2 : Country's 2-char string
57 */
58static void GetCountryCodeString(Service::Interface* self) {
59 u32* cmd_buffer = Kernel::GetCommandBuffer();
60 u32 country_code_id = cmd_buffer[1];
61
62 if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
63 LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
64 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
65 return;
66 }
67
68 cmd_buffer[1] = 0;
69 cmd_buffer[2] = country_codes[country_code_id];
70}
71
72/**
73 * CFG_User::GetCountryCodeID service function
74 * Inputs:
75 * 1 : Country Code 2-char string
76 * Outputs:
77 * 1 : Result of function, 0 on success, otherwise error code
78 * 2 : Country Code ID
79 */
80static void GetCountryCodeID(Service::Interface* self) {
81 u32* cmd_buffer = Kernel::GetCommandBuffer();
82 u16 country_code = cmd_buffer[1];
83 u16 country_code_id = 0;
84
85 // The following algorithm will fail if the first country code isn't 0.
86 _dbg_assert_(Service_CFG, country_codes[0] == 0);
87
88 for (size_t id = 0; id < country_codes.size(); ++id) {
89 if (country_codes[id] == country_code) {
90 country_code_id = id;
91 break;
92 }
93 }
94
95 if (0 == country_code_id) {
96 LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
97 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
98 cmd_buffer[2] = 0xFFFF;
99 return;
100 }
101
102 cmd_buffer[1] = 0;
103 cmd_buffer[2] = country_code_id;
104}
105
106/**
107 * CFG_User::GetConfigInfoBlk2 service function
108 * Inputs:
109 * 0 : 0x00010082
110 * 1 : Size
111 * 2 : Block ID
112 * 3 : Descriptor for the output buffer
113 * 4 : Output buffer pointer
114 * Outputs:
115 * 1 : Result of function, 0 on success, otherwise error code
116 */
117static void GetConfigInfoBlk2(Service::Interface* self) {
118 u32* cmd_buffer = Kernel::GetCommandBuffer();
119 u32 size = cmd_buffer[1];
120 u32 block_id = cmd_buffer[2];
121 u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
122
123 if (data_pointer == nullptr) {
124 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
125 return;
126 }
127
128 cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
129}
130
131/**
132 * CFG_User::GetSystemModel service function
133 * Inputs:
134 * 0 : 0x00050000
135 * Outputs:
136 * 1 : Result of function, 0 on success, otherwise error code
137 * 2 : Model of the console
138 */
139static void GetSystemModel(Service::Interface* self) {
140 u32* cmd_buffer = Kernel::GetCommandBuffer();
141 u32 data;
142
143 // TODO(Subv): Find out the correct error codes
144 cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8,
145 reinterpret_cast<u8*>(&data)).raw;
146 cmd_buffer[2] = data & 0xFF;
147}
148
149/**
150 * CFG_User::GetModelNintendo2DS service function
151 * Inputs:
152 * 0 : 0x00060000
153 * Outputs:
154 * 1 : Result of function, 0 on success, otherwise error code
155 * 2 : 0 if the system is a Nintendo 2DS, 1 otherwise
156 */
157static void GetModelNintendo2DS(Service::Interface* self) {
158 u32* cmd_buffer = Kernel::GetCommandBuffer();
159 u32 data;
160
161 // TODO(Subv): Find out the correct error codes
162 cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8,
163 reinterpret_cast<u8*>(&data)).raw;
164
165 u8 model = data & 0xFF;
166 if (model == Service::CFG::NINTENDO_2DS)
167 cmd_buffer[2] = 0;
168 else
169 cmd_buffer[2] = 1;
170}
171
172const Interface::FunctionInfo FunctionTable[] = {
173 {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
174 {0x00020000, nullptr, "SecureInfoGetRegion"},
175 {0x00030000, nullptr, "GenHashConsoleUnique"},
176 {0x00040000, nullptr, "GetRegionCanadaUSA"},
177 {0x00050000, GetSystemModel, "GetSystemModel"},
178 {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
179 {0x00070040, nullptr, "unknown"},
180 {0x00080080, nullptr, "unknown"},
181 {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
182 {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
183};
184
185////////////////////////////////////////////////////////////////////////////////////////////////////
186// Interface class
187
188Interface::Interface() {
189 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
190}
191
192} // namespace
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h
index 8075d19a8..9ad73f355 100644
--- a/src/core/hle/service/cfg_u.h
+++ b/src/core/hle/service/cfg/cfg_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace CFG_U {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "cfg:u"; 19 return "cfg:u";
24 } 20 }
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp
deleted file mode 100644
index 88d13d459..000000000
--- a/src/core/hle/service/cfg_i.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cfg_i.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CFG_I
11
12namespace CFG_I {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x04010082, nullptr, "GetConfigInfoBlk8"},
16 {0x04020082, nullptr, "GetConfigInfoBlk4"},
17 {0x04030000, nullptr, "UpdateConfigNANDSavegame"},
18 {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
19 {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
20 {0x04060000, nullptr, "SecureInfoGetRegion"},
21 {0x04070000, nullptr, "SecureInfoGetByte101"},
22 {0x04080042, nullptr, "SecureInfoGetSerialNo"},
23 {0x04090000, nullptr, "UpdateConfigBlk00040003"},
24 {0x08010082, nullptr, "GetConfigInfoBlk8"},
25 {0x08020082, nullptr, "GetConfigInfoBlk4"},
26 {0x08030000, nullptr, "UpdateConfigNANDSavegame"},
27 {0x080400C2, nullptr, "CreateConfigInfoBlk"},
28 {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
29 {0x08060000, nullptr, "FormatConfig"},
30 {0x08070000, nullptr, "Unknown"},
31 {0x08080000, nullptr, "UpdateConfigBlk1"},
32 {0x08090000, nullptr, "UpdateConfigBlk2"},
33 {0x080A0000, nullptr, "UpdateConfigBlk3"},
34 {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
35 {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
36 {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
37 {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
38 {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
39 {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
40 {0x08110084, nullptr, "SetSecureInfo"},
41 {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
42 {0x08130000, nullptr, "VerifySigSecureInfo"},
43 {0x08140042, nullptr, "SecureInfoGetData"},
44 {0x08150042, nullptr, "SecureInfoGetSignature"},
45 {0x08160000, nullptr, "SecureInfoGetRegion"},
46 {0x08170000, nullptr, "SecureInfoGetByte101"},
47 {0x08180042, nullptr, "SecureInfoGetSerialNo"},
48};
49////////////////////////////////////////////////////////////////////////////////////////////////////
50// Interface class
51
52Interface::Interface() {
53 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
54}
55
56Interface::~Interface() {
57}
58
59} // namespace
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
deleted file mode 100644
index 822b0e2b8..000000000
--- a/src/core/hle/service/cfg_u.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cfg_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CFG_U
11
12namespace CFG_U {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010082, nullptr, "GetConfigInfoBlk2"},
16 {0x00020000, nullptr, "SecureInfoGetRegion"},
17 {0x00030000, nullptr, "GenHashConsoleUnique"},
18 {0x00040000, nullptr, "GetRegionCanadaUSA"},
19 {0x00050000, nullptr, "GetSystemModel"},
20 {0x00060000, nullptr, "GetModelNintendo2DS"},
21 {0x00070040, nullptr, "unknown"},
22 {0x00080080, nullptr, "unknown"},
23 {0x00090080, nullptr, "GetCountryCodeString"},
24 {0x000A0040, nullptr, "GetCountryCodeID"},
25};
26////////////////////////////////////////////////////////////////////////////////////////////////////
27// Interface class
28
29Interface::Interface() {
30 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
31}
32
33Interface::~Interface() {
34}
35
36} // namespace
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6e59a9bf3..aef8cfbca 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -33,7 +33,4 @@ Interface::Interface() {
33 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 33 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
34} 34}
35 35
36Interface::~Interface() {
37}
38
39} // namespace 36} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
index 31cc85b07..a84752473 100644
--- a/src/core/hle/service/csnd_snd.h
+++ b/src/core/hle/service/csnd_snd.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace CSND_SND {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "csnd:SND"; 19 return "csnd:SND";
24 } 20 }
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index bbcf26f61..2cf4d118f 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -1,9 +1,10 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
6#include "core/hle/hle.h" 6#include "core/hle/hle.h"
7#include "core/hle/kernel/event.h"
7#include "core/hle/service/dsp_dsp.h" 8#include "core/hle/service/dsp_dsp.h"
8 9
9//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,42 +12,182 @@
11 12
12namespace DSP_DSP { 13namespace DSP_DSP {
13 14
15static u32 read_pipe_count;
16static Handle semaphore_event;
17static Handle interrupt_event;
18
19/**
20 * DSP_DSP::ConvertProcessAddressFromDspDram service function
21 * Inputs:
22 * 1 : Address
23 * Outputs:
24 * 1 : Result of function, 0 on success, otherwise error code
25 * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
26 */
27void ConvertProcessAddressFromDspDram(Service::Interface* self) {
28 u32* cmd_buff = Kernel::GetCommandBuffer();
29
30 u32 addr = cmd_buff[1];
31
32 cmd_buff[1] = 0; // No error
33 cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
34
35 LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
36}
37
38/**
39 * DSP_DSP::LoadComponent service function
40 * Inputs:
41 * 1 : Size
42 * 2 : Unknown (observed only half word used)
43 * 3 : Unknown (observed only half word used)
44 * 4 : (size << 4) | 0xA
45 * 5 : Buffer address
46 * Outputs:
47 * 1 : Result of function, 0 on success, otherwise error code
48 * 2 : Component loaded, 0 on not loaded, 1 on loaded
49 */
50void LoadComponent(Service::Interface* self) {
51 u32* cmd_buff = Kernel::GetCommandBuffer();
52
53 cmd_buff[1] = 0; // No error
54 cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
55
56 // TODO(bunnei): Implement real DSP firmware loading
57
58 LOG_WARNING(Service_DSP, "(STUBBED) called");
59}
60
61/**
62 * DSP_DSP::GetSemaphoreEventHandle service function
63 * Outputs:
64 * 1 : Result of function, 0 on success, otherwise error code
65 * 3 : Semaphore event handle
66 */
67void GetSemaphoreEventHandle(Service::Interface* self) {
68 u32* cmd_buff = Kernel::GetCommandBuffer();
69
70 cmd_buff[1] = 0; // No error
71 cmd_buff[3] = semaphore_event; // Event handle
72
73 LOG_WARNING(Service_DSP, "(STUBBED) called");
74}
75
76/**
77 * DSP_DSP::RegisterInterruptEvents service function
78 * Inputs:
79 * 1 : Parameter 0 (purpose unknown)
80 * 2 : Parameter 1 (purpose unknown)
81 * 4 : Interrupt event handle
82 * Outputs:
83 * 1 : Result of function, 0 on success, otherwise error code
84 */
85void RegisterInterruptEvents(Service::Interface* self) {
86 u32* cmd_buff = Kernel::GetCommandBuffer();
87
88 interrupt_event = static_cast<Handle>(cmd_buff[4]);
89
90 cmd_buff[1] = 0; // No error
91
92 LOG_WARNING(Service_DSP, "(STUBBED) called");
93}
94
95/**
96 * DSP_DSP::WriteReg0x10 service function
97 * Inputs:
98 * 1 : Unknown (observed only half word used)
99 * Outputs:
100 * 1 : Result of function, 0 on success, otherwise error code
101 */
102void WriteReg0x10(Service::Interface* self) {
103 u32* cmd_buff = Kernel::GetCommandBuffer();
104
105 Kernel::SignalEvent(interrupt_event);
106
107 cmd_buff[1] = 0; // No error
108
109 LOG_WARNING(Service_DSP, "(STUBBED) called");
110}
111
112/**
113 * DSP_DSP::ReadPipeIfPossible service function
114 * Inputs:
115 * 1 : Unknown
116 * 2 : Unknown
117 * 3 : Size in bytes of read (observed only lower half word used)
118 * 0x41 : Virtual address to read from DSP pipe to in memory
119 * Outputs:
120 * 1 : Result of function, 0 on success, otherwise error code
121 * 2 : Number of bytes read from pipe
122 */
123void ReadPipeIfPossible(Service::Interface* self) {
124 u32* cmd_buff = Kernel::GetCommandBuffer();
125
126 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
127 VAddr addr = cmd_buff[0x41];
128
129 // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
130 // TODO: Remove this hack :)
131 static const std::array<u16, 16> canned_read_pipe = {
132 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
133 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
134 };
135
136 u32 initial_size = read_pipe_count;
137
138 for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
139 if (read_pipe_count < canned_read_pipe.size()) {
140 Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
141 read_pipe_count++;
142 } else {
143 LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
144 break;
145 }
146 }
147
148 cmd_buff[1] = 0; // No error
149 cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
150
151 LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
152}
153
14const Interface::FunctionInfo FunctionTable[] = { 154const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010040, nullptr, "RecvData"}, 155 {0x00010040, nullptr, "RecvData"},
16 {0x00020040, nullptr, "RecvDataIsReady"}, 156 {0x00020040, nullptr, "RecvDataIsReady"},
17 {0x00030080, nullptr, "SendData"}, 157 {0x00030080, nullptr, "SendData"},
18 {0x00040040, nullptr, "SendDataIsEmpty"}, 158 {0x00040040, nullptr, "SendDataIsEmpty"},
19 {0x00070040, nullptr, "WriteReg0x10"}, 159 {0x00070040, WriteReg0x10, "WriteReg0x10"},
20 {0x00080000, nullptr, "GetSemaphore"}, 160 {0x00080000, nullptr, "GetSemaphore"},
21 {0x00090040, nullptr, "ClearSemaphore"}, 161 {0x00090040, nullptr, "ClearSemaphore"},
22 {0x000B0000, nullptr, "CheckSemaphoreRequest"}, 162 {0x000B0000, nullptr, "CheckSemaphoreRequest"},
23 {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"}, 163 {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
24 {0x000D0082, nullptr, "WriteProcessPipe"}, 164 {0x000D0082, nullptr, "WriteProcessPipe"},
25 {0x001000C0, nullptr, "ReadPipeIfPossible"}, 165 {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
26 {0x001100C2, nullptr, "LoadComponent"}, 166 {0x001100C2, LoadComponent, "LoadComponent"},
27 {0x00120000, nullptr, "UnloadComponent"}, 167 {0x00120000, nullptr, "UnloadComponent"},
28 {0x00130082, nullptr, "FlushDataCache"}, 168 {0x00130082, nullptr, "FlushDataCache"},
29 {0x00140082, nullptr, "InvalidateDCache"}, 169 {0x00140082, nullptr, "InvalidateDCache"},
30 {0x00150082, nullptr, "RegisterInterruptEvents"}, 170 {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
31 {0x00160000, nullptr, "GetSemaphoreEventHandle"}, 171 {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
32 {0x00170040, nullptr, "SetSemaphoreMask"}, 172 {0x00170040, nullptr, "SetSemaphoreMask"},
33 {0x00180040, nullptr, "GetPhysicalAddress"}, 173 {0x00180040, nullptr, "GetPhysicalAddress"},
34 {0x00190040, nullptr, "GetVirtualAddress"}, 174 {0x00190040, nullptr, "GetVirtualAddress"},
35 {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, 175 {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
36 {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, 176 {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
37 {0x001C0082, nullptr, "SetIirFilterEQ"}, 177 {0x001C0082, nullptr, "SetIirFilterEQ"},
38 {0x001F0000, nullptr, "GetHeadphoneStatus"}, 178 {0x001F0000, nullptr, "GetHeadphoneStatus"},
39 {0x00210000, nullptr, "GetIsDspOccupied"}, 179 {0x00210000, nullptr, "GetIsDspOccupied"},
40}; 180};
41 181
42//////////////////////////////////////////////////////////////////////////////////////////////////// 182////////////////////////////////////////////////////////////////////////////////////////////////////
43// Interface class 183// Interface class
44 184
45Interface::Interface() { 185Interface::Interface() {
46 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 186 semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
47} 187 interrupt_event = 0;
188 read_pipe_count = 0;
48 189
49Interface::~Interface() { 190 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
50} 191}
51 192
52} // namespace 193} // namespace
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index c4ce44245..0b8b64600 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,13 +14,9 @@ namespace DSP_DSP {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "dsp:DSP"; 19 return "dsp::DSP";
24 } 20 }
25}; 21};
26 22
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 785c351e9..8c900eabc 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -11,17 +11,15 @@
11 11
12namespace ERR_F { 12namespace ERR_F {
13 13
14 const Interface::FunctionInfo FunctionTable[] = { 14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010800, nullptr, "ThrowFatalError"} 15 {0x00010800, nullptr, "ThrowFatalError"}
16 }; 16};
17 ////////////////////////////////////////////////////////////////////////////////////////////////////
18 // Interface class
19 17
20 Interface::Interface() { 18////////////////////////////////////////////////////////////////////////////////////////////////////
21 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 19// Interface class
22 }
23 20
24 Interface::~Interface() { 21Interface::Interface() {
25 } 22 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
23}
26 24
27} // namespace 25} // namespace
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
index 6d7141c1b..892d8af9b 100644
--- a/src/core/hle/service/err_f.h
+++ b/src/core/hle/service/err_f.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -11,17 +11,13 @@
11 11
12namespace ERR_F { 12namespace ERR_F {
13 13
14 class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15 public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /** 18 std::string GetPortName() const override {
19 * Gets the string port name used by CTROS for the service 19 return "err:f";
20 * @return Port name of service 20 }
21 */ 21};
22 std::string GetPortName() const override {
23 return "err:f";
24 }
25 };
26 22
27} // namespace 23} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
index 58023e536..021186e57 100644
--- a/src/core/hle/service/frd_u.cpp
+++ b/src/core/hle/service/frd_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -11,25 +11,23 @@
11 11
12namespace FRD_U { 12namespace FRD_U {
13 13
14 const Interface::FunctionInfo FunctionTable[] = { 14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00050000, nullptr, "GetFriendKey"}, 15 {0x00050000, nullptr, "GetFriendKey"},
16 {0x00080000, nullptr, "GetMyPresence"}, 16 {0x00080000, nullptr, "GetMyPresence"},
17 {0x00100040, nullptr, "GetPassword"}, 17 {0x00100040, nullptr, "GetPassword"},
18 {0x00190042, nullptr, "GetFriendFavoriteGame"}, 18 {0x00190042, nullptr, "GetFriendFavoriteGame"},
19 {0x001A00C4, nullptr, "GetFriendInfo"}, 19 {0x001A00C4, nullptr, "GetFriendInfo"},
20 {0x001B0080, nullptr, "IsOnFriendList"}, 20 {0x001B0080, nullptr, "IsOnFriendList"},
21 {0x001C0042, nullptr, "DecodeLocalFriendCode"}, 21 {0x001C0042, nullptr, "DecodeLocalFriendCode"},
22 {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, 22 {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
23 {0x00320042, nullptr, "SetClientSdkVersion"} 23 {0x00320042, nullptr, "SetClientSdkVersion"}
24 }; 24};
25 ////////////////////////////////////////////////////////////////////////////////////////////////////
26 // Interface class
27 25
28 Interface::Interface() { 26////////////////////////////////////////////////////////////////////////////////////////////////////
29 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 27// Interface class
30 }
31 28
32 Interface::~Interface() { 29Interface::Interface() {
33 } 30 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
31}
34 32
35} // namespace 33} // namespace
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h
index 4020c6664..ab8897d5b 100644
--- a/src/core/hle/service/frd_u.h
+++ b/src/core/hle/service/frd_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -11,17 +11,13 @@
11 11
12namespace FRD_U { 12namespace FRD_U {
13 13
14 class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15 public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /** 18 std::string GetPortName() const override {
19 * Gets the string port name used by CTROS for the service 19 return "frd:u";
20 * @return Port name of service 20 }
21 */ 21};
22 std::string GetPortName() const override {
23 return "frd:u";
24 }
25 };
26 22
27} // namespace 23} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
new file mode 100644
index 000000000..487bf3aa7
--- /dev/null
+++ b/src/core/hle/service/fs/archive.cpp
@@ -0,0 +1,442 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <unordered_map>
7
8#include "common/common_types.h"
9#include "common/file_util.h"
10#include "common/make_unique.h"
11#include "common/math_util.h"
12
13#include "core/file_sys/archive_savedata.h"
14#include "core/file_sys/archive_backend.h"
15#include "core/file_sys/archive_sdmc.h"
16#include "core/file_sys/directory_backend.h"
17#include "core/hle/service/fs/archive.h"
18#include "core/hle/kernel/session.h"
19#include "core/hle/result.h"
20
21// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
22// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
23namespace std {
24 template <>
25 struct hash<Service::FS::ArchiveIdCode> {
26 typedef Service::FS::ArchiveIdCode argument_type;
27 typedef std::size_t result_type;
28
29 result_type operator()(const argument_type& id_code) const {
30 typedef std::underlying_type<argument_type>::type Type;
31 return std::hash<Type>()(static_cast<Type>(id_code));
32 }
33 };
34}
35
36namespace Service {
37namespace FS {
38
39// Command to access archive file
40enum class FileCommand : u32 {
41 Dummy1 = 0x000100C6,
42 Control = 0x040100C4,
43 OpenSubFile = 0x08010100,
44 Read = 0x080200C2,
45 Write = 0x08030102,
46 GetSize = 0x08040000,
47 SetSize = 0x08050080,
48 GetAttributes = 0x08060000,
49 SetAttributes = 0x08070040,
50 Close = 0x08080000,
51 Flush = 0x08090000,
52};
53
54// Command to access directory
55enum class DirectoryCommand : u32 {
56 Dummy1 = 0x000100C6,
57 Control = 0x040100C4,
58 Read = 0x08010042,
59 Close = 0x08020000,
60};
61
62class Archive {
63public:
64 Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
65 : backend(std::move(backend)), id_code(id_code) {
66 }
67
68 std::string GetName() const { return "Archive: " + backend->GetName(); }
69
70 ArchiveIdCode id_code; ///< Id code of the archive
71 std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
72};
73
74class File : public Kernel::Session {
75public:
76 File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
77 : backend(std::move(backend)), path(path) {
78 }
79
80 std::string GetName() const override { return "Path: " + path.DebugStr(); }
81
82 FileSys::Path path; ///< Path of the file
83 std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
84
85 ResultVal<bool> SyncRequest() override {
86 u32* cmd_buff = Kernel::GetCommandBuffer();
87 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
88 switch (cmd) {
89
90 // Read from file...
91 case FileCommand::Read:
92 {
93 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
94 u32 length = cmd_buff[3];
95 u32 address = cmd_buff[5];
96 LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
97 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
98 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
99 break;
100 }
101
102 // Write to file...
103 case FileCommand::Write:
104 {
105 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
106 u32 length = cmd_buff[3];
107 u32 flush = cmd_buff[4];
108 u32 address = cmd_buff[6];
109 LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
110 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
111 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
112 break;
113 }
114
115 case FileCommand::GetSize:
116 {
117 LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
118 u64 size = backend->GetSize();
119 cmd_buff[2] = (u32)size;
120 cmd_buff[3] = size >> 32;
121 break;
122 }
123
124 case FileCommand::SetSize:
125 {
126 u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
127 LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
128 GetTypeName().c_str(), GetName().c_str(), size);
129 backend->SetSize(size);
130 break;
131 }
132
133 case FileCommand::Close:
134 {
135 LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
136 backend->Close();
137 break;
138 }
139
140 case FileCommand::Flush:
141 {
142 LOG_TRACE(Service_FS, "Flush");
143 backend->Flush();
144 break;
145 }
146
147 // Unknown command...
148 default:
149 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
150 ResultCode error = UnimplementedFunction(ErrorModule::FS);
151 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
152 return error;
153 }
154 cmd_buff[1] = 0; // No error
155 return MakeResult<bool>(false);
156 }
157};
158
159class Directory : public Kernel::Session {
160public:
161 Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
162 : backend(std::move(backend)), path(path) {
163 }
164
165 std::string GetName() const override { return "Directory: " + path.DebugStr(); }
166
167 FileSys::Path path; ///< Path of the directory
168 std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
169
170 ResultVal<bool> SyncRequest() override {
171 u32* cmd_buff = Kernel::GetCommandBuffer();
172 DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
173 switch (cmd) {
174
175 // Read from directory...
176 case DirectoryCommand::Read:
177 {
178 u32 count = cmd_buff[1];
179 u32 address = cmd_buff[3];
180 auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
181 LOG_TRACE(Service_FS, "Read %s %s: count=%d",
182 GetTypeName().c_str(), GetName().c_str(), count);
183
184 // Number of entries actually read
185 cmd_buff[2] = backend->Read(count, entries);
186 break;
187 }
188
189 case DirectoryCommand::Close:
190 {
191 LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
192 backend->Close();
193 break;
194 }
195
196 // Unknown command...
197 default:
198 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
199 ResultCode error = UnimplementedFunction(ErrorModule::FS);
200 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
201 return MakeResult<bool>(false);
202 }
203 cmd_buff[1] = 0; // No error
204 return MakeResult<bool>(false);
205 }
206};
207
208////////////////////////////////////////////////////////////////////////////////////////////////////
209
210/**
211 * Map of registered archives, identified by id code. Once an archive is registered here, it is
212 * never removed until the FS service is shut down.
213 */
214static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
215
216/**
217 * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
218 */
219static std::unordered_map<ArchiveHandle, Archive*> handle_map;
220static ArchiveHandle next_handle;
221
222static Archive* GetArchive(ArchiveHandle handle) {
223 auto itr = handle_map.find(handle);
224 return (itr == handle_map.end()) ? nullptr : itr->second;
225}
226
227ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
228 LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
229
230 auto itr = id_code_map.find(id_code);
231 if (itr == id_code_map.end()) {
232 if (id_code == ArchiveIdCode::SaveData) {
233 // When a SaveData archive is created for the first time, it is not yet formatted
234 // and the save file/directory structure expected by the game has not yet been initialized.
235 // Returning the NotFormatted error code will signal the game to provision the SaveData archive
236 // with the files and folders that it expects.
237 // The FormatSaveData service call will create the SaveData archive when it is called.
238 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
239 ErrorSummary::InvalidState, ErrorLevel::Status);
240 }
241 // TODO: Verify error against hardware
242 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
243 ErrorSummary::NotFound, ErrorLevel::Permanent);
244 }
245
246 // This should never even happen in the first place with 64-bit handles,
247 while (handle_map.count(next_handle) != 0) {
248 ++next_handle;
249 }
250 handle_map.emplace(next_handle, itr->second.get());
251 return MakeResult<ArchiveHandle>(next_handle++);
252}
253
254ResultCode CloseArchive(ArchiveHandle handle) {
255 if (handle_map.erase(handle) == 0)
256 return InvalidHandle(ErrorModule::FS);
257 else
258 return RESULT_SUCCESS;
259}
260
261// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
262// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
263ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
264 auto result = id_code_map.emplace(id_code, Common::make_unique<Archive>(std::move(backend), id_code));
265
266 bool inserted = result.second;
267 _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
268
269 auto& archive = result.first->second;
270 LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
271 return RESULT_SUCCESS;
272}
273
274ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
275 Archive* archive = GetArchive(archive_handle);
276 if (archive == nullptr)
277 return InvalidHandle(ErrorModule::FS);
278
279 std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
280 if (backend == nullptr) {
281 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
282 ErrorSummary::NotFound, ErrorLevel::Status);
283 }
284
285 auto file = Common::make_unique<File>(std::move(backend), path);
286 // TOOD(yuriks): Fix error reporting
287 Handle handle = Kernel::g_handle_table.Create(file.release()).ValueOr(INVALID_HANDLE);
288 return MakeResult<Handle>(handle);
289}
290
291ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
292 Archive* archive = GetArchive(archive_handle);
293 if (archive == nullptr)
294 return InvalidHandle(ErrorModule::FS);
295
296 if (archive->backend->DeleteFile(path))
297 return RESULT_SUCCESS;
298 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
299 ErrorSummary::Canceled, ErrorLevel::Status);
300}
301
302ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
303 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
304 Archive* src_archive = GetArchive(src_archive_handle);
305 Archive* dest_archive = GetArchive(dest_archive_handle);
306 if (src_archive == nullptr || dest_archive == nullptr)
307 return InvalidHandle(ErrorModule::FS);
308
309 if (src_archive == dest_archive) {
310 if (src_archive->backend->RenameFile(src_path, dest_path))
311 return RESULT_SUCCESS;
312 } else {
313 // TODO: Implement renaming across archives
314 return UnimplementedFunction(ErrorModule::FS);
315 }
316
317 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
318 // exist or similar. Verify.
319 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
320 ErrorSummary::NothingHappened, ErrorLevel::Status);
321}
322
323ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
324 Archive* archive = GetArchive(archive_handle);
325 if (archive == nullptr)
326 return InvalidHandle(ErrorModule::FS);
327
328 if (archive->backend->DeleteDirectory(path))
329 return RESULT_SUCCESS;
330 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
331 ErrorSummary::Canceled, ErrorLevel::Status);
332}
333
334ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size) {
335 Archive* archive = GetArchive(archive_handle);
336 if (archive == nullptr)
337 return InvalidHandle(ErrorModule::FS);
338
339 return archive->backend->CreateFile(path, file_size);
340}
341
342ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
343 Archive* archive = GetArchive(archive_handle);
344 if (archive == nullptr)
345 return InvalidHandle(ErrorModule::FS);
346
347 if (archive->backend->CreateDirectory(path))
348 return RESULT_SUCCESS;
349 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
350 ErrorSummary::Canceled, ErrorLevel::Status);
351}
352
353ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
354 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
355 Archive* src_archive = GetArchive(src_archive_handle);
356 Archive* dest_archive = GetArchive(dest_archive_handle);
357 if (src_archive == nullptr || dest_archive == nullptr)
358 return InvalidHandle(ErrorModule::FS);
359
360 if (src_archive == dest_archive) {
361 if (src_archive->backend->RenameDirectory(src_path, dest_path))
362 return RESULT_SUCCESS;
363 } else {
364 // TODO: Implement renaming across archives
365 return UnimplementedFunction(ErrorModule::FS);
366 }
367
368 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
369 // exist or similar. Verify.
370 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
371 ErrorSummary::NothingHappened, ErrorLevel::Status);
372}
373
374/**
375 * Open a Directory from an Archive
376 * @param archive_handle Handle to an open Archive object
377 * @param path Path to the Directory inside of the Archive
378 * @return Opened Directory object
379 */
380ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
381 Archive* archive = GetArchive(archive_handle);
382 if (archive == nullptr)
383 return InvalidHandle(ErrorModule::FS);
384
385 std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
386 if (backend == nullptr) {
387 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
388 ErrorSummary::NotFound, ErrorLevel::Permanent);
389 }
390
391 auto directory = Common::make_unique<Directory>(std::move(backend), path);
392 // TOOD(yuriks): Fix error reporting
393 Handle handle = Kernel::g_handle_table.Create(directory.release()).ValueOr(INVALID_HANDLE);
394 return MakeResult<Handle>(handle);
395}
396
397ResultCode FormatSaveData() {
398 // TODO(Subv): Actually wipe the savedata folder after creating or opening it
399
400 // Do not create the archive again if it already exists
401 if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
402 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
403
404 // Create the SaveData archive
405 std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
406 auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory,
407 Kernel::g_program_id);
408
409 if (savedata_archive->Initialize()) {
410 CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
411 return RESULT_SUCCESS;
412 } else {
413 LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
414 savedata_archive->GetMountPoint().c_str());
415 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
416 }
417}
418
419/// Initialize archives
420void ArchiveInit() {
421 next_handle = 1;
422
423 // TODO(Link Mauve): Add the other archive types (see here for the known types:
424 // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
425 // archive type is SDMC, so it is the only one getting exposed.
426
427 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
428 auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
429 if (sdmc_archive->Initialize())
430 CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
431 else
432 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
433}
434
435/// Shutdown archives
436void ArchiveShutdown() {
437 handle_map.clear();
438 id_code_map.clear();
439}
440
441} // namespace FS
442} // namespace Service
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
new file mode 100644
index 000000000..b39bc41b6
--- /dev/null
+++ b/src/core/hle/service/fs/archive.h
@@ -0,0 +1,134 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive_backend.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
12
13namespace Service {
14namespace FS {
15
16/// Supported archive types
17enum class ArchiveIdCode : u32 {
18 RomFS = 0x00000003,
19 SaveData = 0x00000004,
20 ExtSaveData = 0x00000006,
21 SharedExtSaveData = 0x00000007,
22 SystemSaveData = 0x00000008,
23 SDMC = 0x00000009,
24 SDMCWriteOnly = 0x0000000A,
25};
26
27typedef u64 ArchiveHandle;
28
29/**
30 * Opens an archive
31 * @param id_code IdCode of the archive to open
32 * @return Handle to the opened archive
33 */
34ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code);
35
36/**
37 * Closes an archive
38 * @param id_code IdCode of the archive to open
39 */
40ResultCode CloseArchive(ArchiveHandle handle);
41
42/**
43 * Creates an Archive
44 * @param backend File system backend interface to the archive
45 * @param id_code Id code used to access this type of archive
46 */
47ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code);
48
49/**
50 * Open a File from an Archive
51 * @param archive_handle Handle to an open Archive object
52 * @param path Path to the File inside of the Archive
53 * @param mode Mode under which to open the File
54 * @return Handle to the opened File object
55 */
56ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
57
58/**
59 * Delete a File from an Archive
60 * @param archive_handle Handle to an open Archive object
61 * @param path Path to the File inside of the Archive
62 * @return Whether deletion succeeded
63 */
64ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
65
66/**
67 * Rename a File between two Archives
68 * @param src_archive_handle Handle to the source Archive object
69 * @param src_path Path to the File inside of the source Archive
70 * @param dest_archive_handle Handle to the destination Archive object
71 * @param dest_path Path to the File inside of the destination Archive
72 * @return Whether rename succeeded
73 */
74ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
75 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
76
77/**
78 * Delete a Directory from an Archive
79 * @param archive_handle Handle to an open Archive object
80 * @param path Path to the Directory inside of the Archive
81 * @return Whether deletion succeeded
82 */
83ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
84
85/**
86 * Create a File in an Archive
87 * @param archive_handle Handle to an open Archive object
88 * @param path Path to the File inside of the Archive
89 * @param file_size The size of the new file, filled with zeroes
90 * @return File creation result code
91 */
92ResultCode CreateFileInArchive(Handle archive_handle, const FileSys::Path& path, u32 file_size);
93
94/**
95 * Create a Directory from an Archive
96 * @param archive_handle Handle to an open Archive object
97 * @param path Path to the Directory inside of the Archive
98 * @return Whether creation of directory succeeded
99 */
100ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
101
102/**
103 * Rename a Directory between two Archives
104 * @param src_archive_handle Handle to the source Archive object
105 * @param src_path Path to the Directory inside of the source Archive
106 * @param dest_archive_handle Handle to the destination Archive object
107 * @param dest_path Path to the Directory inside of the destination Archive
108 * @return Whether rename succeeded
109 */
110ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
111 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
112
113/**
114 * Open a Directory from an Archive
115 * @param archive_handle Handle to an open Archive object
116 * @param path Path to the Directory inside of the Archive
117 * @return Handle to the opened File object
118 */
119ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
120
121/**
122 * Creates a blank SaveData archive.
123 * @return ResultCode 0 on success or the corresponding code on error
124 */
125ResultCode FormatSaveData();
126
127/// Initialize archives
128void ArchiveInit();
129
130/// Shutdown archives
131void ArchiveShutdown();
132
133} // namespace FS
134} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
new file mode 100644
index 000000000..b1a465274
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -0,0 +1,601 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6#include "common/file_util.h"
7#include "common/scope_exit.h"
8#include "common/string_util.h"
9#include "core/hle/result.h"
10#include "core/hle/service/fs/archive.h"
11#include "core/hle/service/fs/fs_user.h"
12#include "core/settings.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// Namespace FS_User
16
17namespace Service {
18namespace FS {
19
20static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
21 return (u64)low_word | ((u64)high_word << 32);
22}
23
24static void Initialize(Service::Interface* self) {
25 u32* cmd_buff = Kernel::GetCommandBuffer();
26
27 // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
28 // http://3dbrew.org/wiki/FS:Initialize#Request
29 cmd_buff[1] = RESULT_SUCCESS.raw;
30
31 LOG_DEBUG(Service_FS, "called");
32}
33
34/**
35 * FS_User::OpenFile service function
36 * Inputs:
37 * 1 : Transaction
38 * 2 : Archive handle lower word
39 * 3 : Archive handle upper word
40 * 4 : Low path type
41 * 5 : Low path size
42 * 6 : Open flags
43 * 7 : Attributes
44 * 8 : (LowPathSize << 14) | 2
45 * 9 : Low path data pointer
46 * Outputs:
47 * 1 : Result of function, 0 on success, otherwise error code
48 * 3 : File handle
49 */
50static void OpenFile(Service::Interface* self) {
51 u32* cmd_buff = Kernel::GetCommandBuffer();
52
53 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
54 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
55 u32 filename_size = cmd_buff[5];
56 FileSys::Mode mode; mode.hex = cmd_buff[6];
57 u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
58 u32 filename_ptr = cmd_buff[9];
59 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
60
61 LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
62
63 ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
64 cmd_buff[1] = handle.Code().raw;
65 if (handle.Succeeded()) {
66 cmd_buff[3] = *handle;
67 } else {
68 cmd_buff[3] = 0;
69 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
70 }
71}
72
73/**
74 * FS_User::OpenFileDirectly service function
75 * Inputs:
76 * 1 : Transaction
77 * 2 : Archive ID
78 * 3 : Archive low path type
79 * 4 : Archive low path size
80 * 5 : File low path type
81 * 6 : File low path size
82 * 7 : Flags
83 * 8 : Attributes
84 * 9 : (ArchiveLowPathSize << 14) | 0x802
85 * 10 : Archive low path
86 * 11 : (FileLowPathSize << 14) | 2
87 * 12 : File low path
88 * Outputs:
89 * 1 : Result of function, 0 on success, otherwise error code
90 * 3 : File handle
91 */
92static void OpenFileDirectly(Service::Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94
95 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]);
96 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
97 u32 archivename_size = cmd_buff[4];
98 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
99 u32 filename_size = cmd_buff[6];
100 FileSys::Mode mode; mode.hex = cmd_buff[7];
101 u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
102 u32 archivename_ptr = cmd_buff[10];
103 u32 filename_ptr = cmd_buff[12];
104 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
105 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
106
107 LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
108 archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
109
110 if (archive_path.GetType() != FileSys::Empty) {
111 LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
112 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
113 cmd_buff[3] = 0;
114 return;
115 }
116
117 ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id);
118 if (archive_handle.Failed()) {
119 LOG_ERROR(Service_FS, "failed to get a handle for archive");
120 cmd_buff[1] = archive_handle.Code().raw;
121 cmd_buff[3] = 0;
122 return;
123 }
124 SCOPE_EXIT({ CloseArchive(*archive_handle); });
125
126 ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
127 cmd_buff[1] = handle.Code().raw;
128 if (handle.Succeeded()) {
129 cmd_buff[3] = *handle;
130 } else {
131 cmd_buff[3] = 0;
132 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
133 }
134}
135
136/*
137 * FS_User::DeleteFile service function
138 * Inputs:
139 * 2 : Archive handle lower word
140 * 3 : Archive handle upper word
141 * 4 : File path string type
142 * 5 : File path string size
143 * 7 : File path string data
144 * Outputs:
145 * 1 : Result of function, 0 on success, otherwise error code
146 */
147static void DeleteFile(Service::Interface* self) {
148 u32* cmd_buff = Kernel::GetCommandBuffer();
149
150 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
151 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
152 u32 filename_size = cmd_buff[5];
153 u32 filename_ptr = cmd_buff[7];
154
155 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
156
157 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
158 filename_type, filename_size, file_path.DebugStr().c_str());
159
160 cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw;
161}
162
163/*
164 * FS_User::RenameFile service function
165 * Inputs:
166 * 2 : Source archive handle lower word
167 * 3 : Source archive handle upper word
168 * 4 : Source file path type
169 * 5 : Source file path size
170 * 6 : Dest archive handle lower word
171 * 7 : Dest archive handle upper word
172 * 8 : Dest file path type
173 * 9 : Dest file path size
174 * 11: Source file path string data
175 * 13: Dest file path string
176 * Outputs:
177 * 1 : Result of function, 0 on success, otherwise error code
178 */
179static void RenameFile(Service::Interface* self) {
180 u32* cmd_buff = Kernel::GetCommandBuffer();
181
182 ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
183 auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
184 u32 src_filename_size = cmd_buff[5];
185 ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);;
186 auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
187 u32 dest_filename_size = cmd_buff[9];
188 u32 src_filename_ptr = cmd_buff[11];
189 u32 dest_filename_ptr = cmd_buff[13];
190
191 FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr);
192 FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr);
193
194 LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
195 src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(),
196 dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str());
197
198 cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw;
199}
200
201/*
202 * FS_User::DeleteDirectory service function
203 * Inputs:
204 * 2 : Archive handle lower word
205 * 3 : Archive handle upper word
206 * 4 : Directory path string type
207 * 5 : Directory path string size
208 * 7 : Directory path string data
209 * Outputs:
210 * 1 : Result of function, 0 on success, otherwise error code
211 */
212static void DeleteDirectory(Service::Interface* self) {
213 u32* cmd_buff = Kernel::GetCommandBuffer();
214
215 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
216 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
217 u32 dirname_size = cmd_buff[5];
218 u32 dirname_ptr = cmd_buff[7];
219
220 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
221
222 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
223 dirname_type, dirname_size, dir_path.DebugStr().c_str());
224
225 cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw;
226}
227
228/*
229 * FS_User::CreateFile service function
230 * Inputs:
231 * 0 : Command header 0x08080202
232 * 2 : Archive handle lower word
233 * 3 : Archive handle upper word
234 * 4 : File path string type
235 * 5 : File path string size
236 * 7 : File size (filled with zeroes)
237 * 10: File path string data
238 * Outputs:
239 * 1 : Result of function, 0 on success, otherwise error code
240 */
241static void CreateFile(Service::Interface* self) {
242 u32* cmd_buff = Kernel::GetCommandBuffer();
243
244 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
245 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
246 u32 filename_size = cmd_buff[5];
247 u32 file_size = cmd_buff[7];
248 u32 filename_ptr = cmd_buff[10];
249
250 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
251
252 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
253
254 cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
255}
256
257/*
258 * FS_User::CreateDirectory service function
259 * Inputs:
260 * 2 : Archive handle lower word
261 * 3 : Archive handle upper word
262 * 4 : Directory path string type
263 * 5 : Directory path string size
264 * 8 : Directory path string data
265 * Outputs:
266 * 1 : Result of function, 0 on success, otherwise error code
267 */
268static void CreateDirectory(Service::Interface* self) {
269 u32* cmd_buff = Kernel::GetCommandBuffer();
270
271 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
272 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
273 u32 dirname_size = cmd_buff[5];
274 u32 dirname_ptr = cmd_buff[8];
275
276 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
277
278 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
279
280 cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw;
281}
282
283/*
284 * FS_User::RenameDirectory service function
285 * Inputs:
286 * 2 : Source archive handle lower word
287 * 3 : Source archive handle upper word
288 * 4 : Source dir path type
289 * 5 : Source dir path size
290 * 6 : Dest archive handle lower word
291 * 7 : Dest archive handle upper word
292 * 8 : Dest dir path type
293 * 9 : Dest dir path size
294 * 11: Source dir path string data
295 * 13: Dest dir path string
296 * Outputs:
297 * 1 : Result of function, 0 on success, otherwise error code
298 */
299static void RenameDirectory(Service::Interface* self) {
300 u32* cmd_buff = Kernel::GetCommandBuffer();
301
302 ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
303 auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
304 u32 src_dirname_size = cmd_buff[5];
305 ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);
306 auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
307 u32 dest_dirname_size = cmd_buff[9];
308 u32 src_dirname_ptr = cmd_buff[11];
309 u32 dest_dirname_ptr = cmd_buff[13];
310
311 FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr);
312 FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr);
313
314 LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
315 src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(),
316 dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str());
317
318 cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw;
319}
320
321/**
322 * FS_User::OpenDirectory service function
323 * Inputs:
324 * 1 : Archive handle low word
325 * 2 : Archive handle high word
326 * 3 : Low path type
327 * 4 : Low path size
328 * 7 : (LowPathSize << 14) | 2
329 * 8 : Low path data pointer
330 * Outputs:
331 * 1 : Result of function, 0 on success, otherwise error code
332 * 3 : Directory handle
333 */
334static void OpenDirectory(Service::Interface* self) {
335 u32* cmd_buff = Kernel::GetCommandBuffer();
336
337 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
338 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
339 u32 dirname_size = cmd_buff[4];
340 u32 dirname_ptr = cmd_buff[6];
341
342 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
343
344 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
345
346 ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
347 cmd_buff[1] = handle.Code().raw;
348 if (handle.Succeeded()) {
349 cmd_buff[3] = *handle;
350 } else {
351 LOG_ERROR(Service_FS, "failed to get a handle for directory");
352 }
353}
354
355/**
356 * FS_User::OpenArchive service function
357 * Inputs:
358 * 1 : Archive ID
359 * 2 : Archive low path type
360 * 3 : Archive low path size
361 * 4 : (LowPathSize << 14) | 2
362 * 5 : Archive low path
363 * Outputs:
364 * 1 : Result of function, 0 on success, otherwise error code
365 * 2 : Archive handle lower word (unused)
366 * 3 : Archive handle upper word (same as file handle)
367 */
368static void OpenArchive(Service::Interface* self) {
369 u32* cmd_buff = Kernel::GetCommandBuffer();
370
371 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
372 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
373 u32 archivename_size = cmd_buff[3];
374 u32 archivename_ptr = cmd_buff[5];
375 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
376
377 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
378
379 if (archive_path.GetType() != FileSys::Empty) {
380 LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
381 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
382 return;
383 }
384
385 ResultVal<ArchiveHandle> handle = OpenArchive(archive_id);
386 cmd_buff[1] = handle.Code().raw;
387 if (handle.Succeeded()) {
388 cmd_buff[2] = *handle & 0xFFFFFFFF;
389 cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF;
390 } else {
391 cmd_buff[2] = cmd_buff[3] = 0;
392 LOG_ERROR(Service_FS, "failed to get a handle for archive");
393 }
394}
395
396/**
397 * FS_User::CloseArchive service function
398 * Inputs:
399 * 0 : 0x080E0080
400 * 1 : Archive handle low word
401 * 2 : Archive handle high word
402 * Outputs:
403 * 0 : ??? TODO(yuriks): Verify return header
404 * 1 : Result of function, 0 on success, otherwise error code
405 */
406static void CloseArchive(Service::Interface* self) {
407 u32* cmd_buff = Kernel::GetCommandBuffer();
408
409 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
410 cmd_buff[1] = CloseArchive(archive_handle).raw;
411}
412
413/*
414* FS_User::IsSdmcDetected service function
415* Outputs:
416* 1 : Result of function, 0 on success, otherwise error code
417* 2 : Whether the Sdmc could be detected
418*/
419static void IsSdmcDetected(Service::Interface* self) {
420 u32* cmd_buff = Kernel::GetCommandBuffer();
421
422 cmd_buff[1] = 0;
423 cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
424
425 LOG_DEBUG(Service_FS, "called");
426}
427
428/**
429 * FS_User::IsSdmcWriteable service function
430 * Outputs:
431 * 0 : Command header 0x08180000
432 * 1 : Result of function, 0 on success, otherwise error code
433 * 2 : Whether the Sdmc is currently writeable
434 */
435static void IsSdmcWriteable(Service::Interface* self) {
436 u32* cmd_buff = Kernel::GetCommandBuffer();
437
438 cmd_buff[1] = RESULT_SUCCESS.raw;
439 // If the SD isn't enabled, it can't be writeable...else, stubbed true
440 cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
441
442 LOG_DEBUG(Service_FS, " (STUBBED)");
443}
444
445/**
446 * FS_User::FormatSaveData service function,
447 * formats the SaveData specified by the input path.
448 * Inputs:
449 * 0 : 0x084C0242
450 * 1 : Archive ID
451 * 2 : Archive low path type
452 * 3 : Archive low path size
453 * 10 : (LowPathSize << 14) | 2
454 * 11 : Archive low path
455 * Outputs:
456 * 1 : Result of function, 0 on success, otherwise error code
457 */
458static void FormatSaveData(Service::Interface* self) {
459 // TODO(Subv): Find out what the other inputs and outputs of this function are
460 u32* cmd_buff = Kernel::GetCommandBuffer();
461 LOG_DEBUG(Service_FS, "(STUBBED)");
462
463 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
464 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
465 u32 archivename_size = cmd_buff[3];
466 u32 archivename_ptr = cmd_buff[11];
467 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
468
469 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
470
471 if (archive_id != FS::ArchiveIdCode::SaveData) {
472 // TODO(Subv): What should happen if somebody attempts to format a different archive?
473 LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]);
474 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
475 return;
476 }
477
478 if (archive_path.GetType() != FileSys::LowPathType::Empty) {
479 // TODO(Subv): Implement formatting the SaveData of other games
480 LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
481 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
482 return;
483 }
484
485 cmd_buff[1] = FormatSaveData().raw;
486}
487
488/**
489 * FS_User::FormatThisUserSaveData service function
490 * Inputs:
491 * 0: 0x080F0180
492 * Outputs:
493 * 1 : Result of function, 0 on success, otherwise error code
494 */
495static void FormatThisUserSaveData(Service::Interface* self) {
496 u32* cmd_buff = Kernel::GetCommandBuffer();
497 LOG_DEBUG(Service_FS, "(STUBBED)");
498
499 // TODO(Subv): Find out what the inputs and outputs of this function are
500
501 cmd_buff[1] = FormatSaveData().raw;
502}
503
504const FSUserInterface::FunctionInfo FunctionTable[] = {
505 {0x000100C6, nullptr, "Dummy1"},
506 {0x040100C4, nullptr, "Control"},
507 {0x08010002, Initialize, "Initialize"},
508 {0x080201C2, OpenFile, "OpenFile"},
509 {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
510 {0x08040142, DeleteFile, "DeleteFile"},
511 {0x08050244, RenameFile, "RenameFile"},
512 {0x08060142, DeleteDirectory, "DeleteDirectory"},
513 {0x08070142, nullptr, "DeleteDirectoryRecursively"},
514 {0x08080202, CreateFile, "CreateFile"},
515 {0x08090182, CreateDirectory, "CreateDirectory"},
516 {0x080A0244, RenameDirectory, "RenameDirectory"},
517 {0x080B0102, OpenDirectory, "OpenDirectory"},
518 {0x080C00C2, OpenArchive, "OpenArchive"},
519 {0x080D0144, nullptr, "ControlArchive"},
520 {0x080E0080, CloseArchive, "CloseArchive"},
521 {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
522 {0x08100200, nullptr, "CreateSystemSaveData"},
523 {0x08110040, nullptr, "DeleteSystemSaveData"},
524 {0x08120080, nullptr, "GetFreeBytes"},
525 {0x08130000, nullptr, "GetCardType"},
526 {0x08140000, nullptr, "GetSdmcArchiveResource"},
527 {0x08150000, nullptr, "GetNandArchiveResource"},
528 {0x08160000, nullptr, "GetSdmcFatfsErro"},
529 {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
530 {0x08180000, IsSdmcWriteable, "IsSdmcWritable"},
531 {0x08190042, nullptr, "GetSdmcCid"},
532 {0x081A0042, nullptr, "GetNandCid"},
533 {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
534 {0x081C0000, nullptr, "GetNandSpeedInfo"},
535 {0x081D0042, nullptr, "GetSdmcLog"},
536 {0x081E0042, nullptr, "GetNandLog"},
537 {0x081F0000, nullptr, "ClearSdmcLog"},
538 {0x08200000, nullptr, "ClearNandLog"},
539 {0x08210000, nullptr, "CardSlotIsInserted"},
540 {0x08220000, nullptr, "CardSlotPowerOn"},
541 {0x08230000, nullptr, "CardSlotPowerOff"},
542 {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
543 {0x08250040, nullptr, "CardNorDirectCommand"},
544 {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
545 {0x08270082, nullptr, "CardNorDirectRead"},
546 {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
547 {0x08290082, nullptr, "CardNorDirectWrite"},
548 {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
549 {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
550 {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
551 {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
552 {0x082E0040, nullptr, "GetProductInfo"},
553 {0x082F0040, nullptr, "GetProgramLaunchInfo"},
554 {0x08300182, nullptr, "CreateExtSaveData"},
555 {0x08310180, nullptr, "CreateSharedExtSaveData"},
556 {0x08320102, nullptr, "ReadExtSaveDataIcon"},
557 {0x08330082, nullptr, "EnumerateExtSaveData"},
558 {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
559 {0x08350080, nullptr, "DeleteExtSaveData"},
560 {0x08360080, nullptr, "DeleteSharedExtSaveData"},
561 {0x08370040, nullptr, "SetCardSpiBaudRate"},
562 {0x08380040, nullptr, "SetCardSpiBusMode"},
563 {0x08390000, nullptr, "SendInitializeInfoTo9"},
564 {0x083A0100, nullptr, "GetSpecialContentIndex"},
565 {0x083B00C2, nullptr, "GetLegacyRomHeader"},
566 {0x083C00C2, nullptr, "GetLegacyBannerData"},
567 {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
568 {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
569 {0x083F00C0, nullptr, "GetExtDataBlockSize"},
570 {0x08400040, nullptr, "AbnegateAccessRight"},
571 {0x08410000, nullptr, "DeleteSdmcRoot"},
572 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
573 {0x08430000, nullptr, "InitializeCtrFileSystem"},
574 {0x08440000, nullptr, "CreateSeed"},
575 {0x084500C2, nullptr, "GetFormatInfo"},
576 {0x08460102, nullptr, "GetLegacyRomHeader2"},
577 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
578 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
579 {0x08490040, nullptr, "GetArchiveResource"},
580 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
581 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
582 {0x084C0242, FormatSaveData, "FormatSaveData"},
583 {0x084D0102, nullptr, "GetLegacySubBannerData"},
584 {0x084E0342, nullptr, "UpdateSha256Context"},
585 {0x084F0102, nullptr, "ReadSpecialFile"},
586 {0x08500040, nullptr, "GetSpecialFileSize"},
587 {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
588 {0x08610042, nullptr, "InitializeWithSdkVersion"},
589 {0x08620040, nullptr, "SetPriority"},
590 {0x08630000, nullptr, "GetPriority"},
591};
592
593////////////////////////////////////////////////////////////////////////////////////////////////////
594// Interface class
595
596FSUserInterface::FSUserInterface() {
597 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
598}
599
600} // namespace FS
601} // namespace Service
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/fs/fs_user.h
index 005382540..2d896dd5f 100644
--- a/src/core/hle/service/fs_user.h
+++ b/src/core/hle/service/fs/fs_user.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -9,23 +9,18 @@
9//////////////////////////////////////////////////////////////////////////////////////////////////// 9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FS_User 10// Namespace FS_User
11 11
12namespace FS_User { 12namespace Service {
13namespace FS {
13 14
14/// Interface to "fs:USER" service 15/// Interface to "fs:USER" service
15class Interface : public Service::Interface { 16class FSUserInterface : public Service::Interface {
16public: 17public:
18 FSUserInterface();
17 19
18 Interface();
19
20 ~Interface();
21
22 /**
23 * Gets the string port name used by CTROS for the service
24 * @return Port name of service
25 */
26 std::string GetPortName() const override { 20 std::string GetPortName() const override {
27 return "fs:USER"; 21 return "fs:USER";
28 } 22 }
29}; 23};
30 24
31} // namespace 25} // namespace FS
26} // namespace Service
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs_user.cpp
deleted file mode 100644
index 435be5b5d..000000000
--- a/src/core/hle/service/fs_user.cpp
+++ /dev/null
@@ -1,409 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6
7#include "common/string_util.h"
8#include "core/hle/kernel/archive.h"
9#include "core/hle/kernel/archive.h"
10#include "core/hle/result.h"
11#include "core/hle/service/fs_user.h"
12#include "core/settings.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// Namespace FS_User
16
17namespace FS_User {
18
19static void Initialize(Service::Interface* self) {
20 u32* cmd_buff = Service::GetCommandBuffer();
21
22 // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
23 // http://3dbrew.org/wiki/FS:Initialize#Request
24 cmd_buff[1] = RESULT_SUCCESS.raw;
25
26 DEBUG_LOG(KERNEL, "called");
27}
28
29/**
30 * FS_User::OpenFile service function
31 * Inputs:
32 * 1 : Transaction
33 * 2 : Archive handle lower word
34 * 3 : Archive handle upper word
35 * 4 : Low path type
36 * 5 : Low path size
37 * 6 : Open flags
38 * 7 : Attributes
39 * 8 : (LowPathSize << 14) | 2
40 * 9 : Low path data pointer
41 * Outputs:
42 * 1 : Result of function, 0 on success, otherwise error code
43 * 3 : File handle
44 */
45static void OpenFile(Service::Interface* self) {
46 u32* cmd_buff = Service::GetCommandBuffer();
47
48 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
49 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
50 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
51 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
52 u32 filename_size = cmd_buff[5];
53 FileSys::Mode mode; mode.hex = cmd_buff[6];
54 u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
55 u32 filename_ptr = cmd_buff[9];
56 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
57
58 DEBUG_LOG(KERNEL, "path=%s, mode=%d attrs=%d", file_path.DebugStr().c_str(), mode, attributes);
59
60 ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode);
61 cmd_buff[1] = handle.Code().raw;
62 if (handle.Succeeded()) {
63 cmd_buff[3] = *handle;
64 } else {
65 ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
66 }
67
68 DEBUG_LOG(KERNEL, "called");
69}
70
71/**
72 * FS_User::OpenFileDirectly service function
73 * Inputs:
74 * 1 : Transaction
75 * 2 : Archive ID
76 * 3 : Archive low path type
77 * 4 : Archive low path size
78 * 5 : File low path type
79 * 6 : File low path size
80 * 7 : Flags
81 * 8 : Attributes
82 * 9 : (ArchiveLowPathSize << 14) | 0x802
83 * 10 : Archive low path
84 * 11 : (FileLowPathSize << 14) | 2
85 * 12 : File low path
86 * Outputs:
87 * 1 : Result of function, 0 on success, otherwise error code
88 * 3 : File handle
89 */
90static void OpenFileDirectly(Service::Interface* self) {
91 u32* cmd_buff = Service::GetCommandBuffer();
92
93 auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
94 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
95 u32 archivename_size = cmd_buff[4];
96 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
97 u32 filename_size = cmd_buff[6];
98 FileSys::Mode mode; mode.hex = cmd_buff[7];
99 u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
100 u32 archivename_ptr = cmd_buff[10];
101 u32 filename_ptr = cmd_buff[12];
102 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
103 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
104
105 DEBUG_LOG(KERNEL, "archive_path=%s file_path=%s, mode=%d attributes=%d",
106 archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode, attributes);
107
108 if (archive_path.GetType() != FileSys::Empty) {
109 ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
110 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
111 return;
112 }
113
114 // TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it
115 // TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here?
116 ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id);
117 cmd_buff[1] = archive_handle.Code().raw;
118 if (archive_handle.Failed()) {
119 ERROR_LOG(KERNEL, "failed to get a handle for archive");
120 return;
121 }
122 // cmd_buff[2] isn't used according to 3dmoo's implementation.
123 cmd_buff[3] = *archive_handle;
124
125 ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode);
126 cmd_buff[1] = handle.Code().raw;
127 if (handle.Succeeded()) {
128 cmd_buff[3] = *handle;
129 } else {
130 ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
131 }
132
133 DEBUG_LOG(KERNEL, "called");
134}
135
136/*
137 * FS_User::DeleteFile service function
138 * Inputs:
139 * 2 : Archive handle lower word
140 * 3 : Archive handle upper word
141 * 4 : File path string type
142 * 5 : File path string size
143 * 7 : File path string data
144 * Outputs:
145 * 1 : Result of function, 0 on success, otherwise error code
146 */
147void DeleteFile(Service::Interface* self) {
148 u32* cmd_buff = Service::GetCommandBuffer();
149
150 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
151 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
152 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
153 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
154 u32 filename_size = cmd_buff[5];
155 u32 filename_ptr = cmd_buff[7];
156
157 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
158
159 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s",
160 filename_type, filename_size, file_path.DebugStr().c_str());
161
162 cmd_buff[1] = Kernel::DeleteFileFromArchive(archive_handle, file_path);
163
164 DEBUG_LOG(KERNEL, "called");
165}
166
167/*
168 * FS_User::DeleteDirectory service function
169 * Inputs:
170 * 2 : Archive handle lower word
171 * 3 : Archive handle upper word
172 * 4 : Directory path string type
173 * 5 : Directory path string size
174 * 7 : Directory path string data
175 * Outputs:
176 * 1 : Result of function, 0 on success, otherwise error code
177 */
178void DeleteDirectory(Service::Interface* self) {
179 u32* cmd_buff = Service::GetCommandBuffer();
180
181 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
182 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
183 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
184 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
185 u32 dirname_size = cmd_buff[5];
186 u32 dirname_ptr = cmd_buff[7];
187
188 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
189
190 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s",
191 dirname_type, dirname_size, dir_path.DebugStr().c_str());
192
193 cmd_buff[1] = Kernel::DeleteDirectoryFromArchive(archive_handle, dir_path);
194
195 DEBUG_LOG(KERNEL, "called");
196}
197
198/*
199 * FS_User::CreateDirectory service function
200 * Inputs:
201 * 2 : Archive handle lower word
202 * 3 : Archive handle upper word
203 * 4 : Directory path string type
204 * 5 : Directory path string size
205 * 8 : Directory path string data
206 * Outputs:
207 * 1 : Result of function, 0 on success, otherwise error code
208 */
209static void CreateDirectory(Service::Interface* self) {
210 u32* cmd_buff = Service::GetCommandBuffer();
211
212 // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to
213 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
214 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
215 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
216 u32 dirname_size = cmd_buff[5];
217 u32 dirname_ptr = cmd_buff[8];
218
219 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
220
221 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
222
223 cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_path);
224
225 DEBUG_LOG(KERNEL, "called");
226}
227
228static void OpenDirectory(Service::Interface* self) {
229 u32* cmd_buff = Service::GetCommandBuffer();
230
231 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
232 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
233 Handle archive_handle = static_cast<Handle>(cmd_buff[2]);
234 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
235 u32 dirname_size = cmd_buff[4];
236 u32 dirname_ptr = cmd_buff[6];
237
238 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
239
240 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
241
242 ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path);
243 cmd_buff[1] = handle.Code().raw;
244 if (handle.Succeeded()) {
245 cmd_buff[3] = *handle;
246 } else {
247 ERROR_LOG(KERNEL, "failed to get a handle for directory");
248 }
249
250 DEBUG_LOG(KERNEL, "called");
251}
252
253/**
254 * FS_User::OpenArchive service function
255 * Inputs:
256 * 1 : Archive ID
257 * 2 : Archive low path type
258 * 3 : Archive low path size
259 * 4 : (LowPathSize << 14) | 2
260 * 5 : Archive low path
261 * Outputs:
262 * 1 : Result of function, 0 on success, otherwise error code
263 * 2 : Archive handle lower word (unused)
264 * 3 : Archive handle upper word (same as file handle)
265 */
266static void OpenArchive(Service::Interface* self) {
267 u32* cmd_buff = Service::GetCommandBuffer();
268
269 auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]);
270 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
271 u32 archivename_size = cmd_buff[3];
272 u32 archivename_ptr = cmd_buff[5];
273 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
274
275 DEBUG_LOG(KERNEL, "archive_path=%s", archive_path.DebugStr().c_str());
276
277 if (archive_path.GetType() != FileSys::Empty) {
278 ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
279 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
280 return;
281 }
282
283 ResultVal<Handle> handle = Kernel::OpenArchive(archive_id);
284 cmd_buff[1] = handle.Code().raw;
285 if (handle.Succeeded()) {
286 // cmd_buff[2] isn't used according to 3dmoo's implementation.
287 cmd_buff[3] = *handle;
288 } else {
289 ERROR_LOG(KERNEL, "failed to get a handle for archive");
290 }
291
292 DEBUG_LOG(KERNEL, "called");
293}
294
295/*
296* FS_User::IsSdmcDetected service function
297* Outputs:
298* 1 : Result of function, 0 on success, otherwise error code
299* 2 : Whether the Sdmc could be detected
300*/
301static void IsSdmcDetected(Service::Interface* self) {
302 u32* cmd_buff = Service::GetCommandBuffer();
303
304 cmd_buff[1] = 0;
305 cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
306
307 DEBUG_LOG(KERNEL, "called");
308}
309
310const Interface::FunctionInfo FunctionTable[] = {
311 {0x000100C6, nullptr, "Dummy1"},
312 {0x040100C4, nullptr, "Control"},
313 {0x08010002, Initialize, "Initialize"},
314 {0x080201C2, OpenFile, "OpenFile"},
315 {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
316 {0x08040142, DeleteFile, "DeleteFile"},
317 {0x08050244, nullptr, "RenameFile"},
318 {0x08060142, DeleteDirectory, "DeleteDirectory"},
319 {0x08070142, nullptr, "DeleteDirectoryRecursively"},
320 {0x08080202, nullptr, "CreateFile"},
321 {0x08090182, CreateDirectory, "CreateDirectory"},
322 {0x080A0244, nullptr, "RenameDirectory"},
323 {0x080B0102, OpenDirectory, "OpenDirectory"},
324 {0x080C00C2, OpenArchive, "OpenArchive"},
325 {0x080D0144, nullptr, "ControlArchive"},
326 {0x080E0080, nullptr, "CloseArchive"},
327 {0x080F0180, nullptr, "FormatThisUserSaveData"},
328 {0x08100200, nullptr, "CreateSystemSaveData"},
329 {0x08110040, nullptr, "DeleteSystemSaveData"},
330 {0x08120080, nullptr, "GetFreeBytes"},
331 {0x08130000, nullptr, "GetCardType"},
332 {0x08140000, nullptr, "GetSdmcArchiveResource"},
333 {0x08150000, nullptr, "GetNandArchiveResource"},
334 {0x08160000, nullptr, "GetSdmcFatfsErro"},
335 {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
336 {0x08180000, nullptr, "IsSdmcWritable"},
337 {0x08190042, nullptr, "GetSdmcCid"},
338 {0x081A0042, nullptr, "GetNandCid"},
339 {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
340 {0x081C0000, nullptr, "GetNandSpeedInfo"},
341 {0x081D0042, nullptr, "GetSdmcLog"},
342 {0x081E0042, nullptr, "GetNandLog"},
343 {0x081F0000, nullptr, "ClearSdmcLog"},
344 {0x08200000, nullptr, "ClearNandLog"},
345 {0x08210000, nullptr, "CardSlotIsInserted"},
346 {0x08220000, nullptr, "CardSlotPowerOn"},
347 {0x08230000, nullptr, "CardSlotPowerOff"},
348 {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
349 {0x08250040, nullptr, "CardNorDirectCommand"},
350 {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
351 {0x08270082, nullptr, "CardNorDirectRead"},
352 {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
353 {0x08290082, nullptr, "CardNorDirectWrite"},
354 {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
355 {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
356 {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
357 {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
358 {0x082E0040, nullptr, "GetProductInfo"},
359 {0x082F0040, nullptr, "GetProgramLaunchInfo"},
360 {0x08300182, nullptr, "CreateExtSaveData"},
361 {0x08310180, nullptr, "CreateSharedExtSaveData"},
362 {0x08320102, nullptr, "ReadExtSaveDataIcon"},
363 {0x08330082, nullptr, "EnumerateExtSaveData"},
364 {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
365 {0x08350080, nullptr, "DeleteExtSaveData"},
366 {0x08360080, nullptr, "DeleteSharedExtSaveData"},
367 {0x08370040, nullptr, "SetCardSpiBaudRate"},
368 {0x08380040, nullptr, "SetCardSpiBusMode"},
369 {0x08390000, nullptr, "SendInitializeInfoTo9"},
370 {0x083A0100, nullptr, "GetSpecialContentIndex"},
371 {0x083B00C2, nullptr, "GetLegacyRomHeader"},
372 {0x083C00C2, nullptr, "GetLegacyBannerData"},
373 {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
374 {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
375 {0x083F00C0, nullptr, "GetExtDataBlockSize"},
376 {0x08400040, nullptr, "AbnegateAccessRight"},
377 {0x08410000, nullptr, "DeleteSdmcRoot"},
378 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
379 {0x08430000, nullptr, "InitializeCtrFileSystem"},
380 {0x08440000, nullptr, "CreateSeed"},
381 {0x084500C2, nullptr, "GetFormatInfo"},
382 {0x08460102, nullptr, "GetLegacyRomHeader2"},
383 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
384 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
385 {0x08490040, nullptr, "GetArchiveResource"},
386 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
387 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
388 {0x084C0242, nullptr, "FormatSaveData"},
389 {0x084D0102, nullptr, "GetLegacySubBannerData"},
390 {0x084E0342, nullptr, "UpdateSha256Context"},
391 {0x084F0102, nullptr, "ReadSpecialFile"},
392 {0x08500040, nullptr, "GetSpecialFileSize"},
393 {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
394 {0x08610042, nullptr, "InitializeWithSdkVersion"},
395 {0x08620040, nullptr, "SetPriority"},
396 {0x08630000, nullptr, "GetPriority"},
397};
398
399////////////////////////////////////////////////////////////////////////////////////////////////////
400// Interface class
401
402Interface::Interface() {
403 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
404}
405
406Interface::~Interface() {
407}
408
409} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index de1bd3f61..0127d4ee5 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5 5
@@ -33,7 +33,7 @@ static inline u8* GetCommandBuffer(u32 thread_id) {
33} 33}
34 34
35static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { 35static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
36 _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index"); 36 _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index");
37 37
38 // For each thread there are two FrameBufferUpdate fields 38 // For each thread there are two FrameBufferUpdate fields
39 u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); 39 u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
@@ -50,14 +50,14 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
50static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { 50static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
51 // TODO: Return proper error codes 51 // TODO: Return proper error codes
52 if (base_address + size_in_bytes >= 0x420000) { 52 if (base_address + size_in_bytes >= 0x420000) {
53 ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", 53 LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
54 base_address, size_in_bytes); 54 base_address, size_in_bytes);
55 return; 55 return;
56 } 56 }
57 57
58 // size should be word-aligned 58 // size should be word-aligned
59 if ((size_in_bytes % 4) != 0) { 59 if ((size_in_bytes % 4) != 0) {
60 ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes); 60 LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
61 return; 61 return;
62 } 62 }
63 63
@@ -72,7 +72,7 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
72 72
73/// Write a GSP GPU hardware register 73/// Write a GSP GPU hardware register
74static void WriteHWRegs(Service::Interface* self) { 74static void WriteHWRegs(Service::Interface* self) {
75 u32* cmd_buff = Service::GetCommandBuffer(); 75 u32* cmd_buff = Kernel::GetCommandBuffer();
76 u32 reg_addr = cmd_buff[1]; 76 u32 reg_addr = cmd_buff[1];
77 u32 size = cmd_buff[2]; 77 u32 size = cmd_buff[2];
78 78
@@ -83,19 +83,19 @@ static void WriteHWRegs(Service::Interface* self) {
83 83
84/// Read a GSP GPU hardware register 84/// Read a GSP GPU hardware register
85static void ReadHWRegs(Service::Interface* self) { 85static void ReadHWRegs(Service::Interface* self) {
86 u32* cmd_buff = Service::GetCommandBuffer(); 86 u32* cmd_buff = Kernel::GetCommandBuffer();
87 u32 reg_addr = cmd_buff[1]; 87 u32 reg_addr = cmd_buff[1];
88 u32 size = cmd_buff[2]; 88 u32 size = cmd_buff[2];
89 89
90 // TODO: Return proper error codes 90 // TODO: Return proper error codes
91 if (reg_addr + size >= 0x420000) { 91 if (reg_addr + size >= 0x420000) {
92 ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); 92 LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
93 return; 93 return;
94 } 94 }
95 95
96 // size should be word-aligned 96 // size should be word-aligned
97 if ((size % 4) != 0) { 97 if ((size % 4) != 0) {
98 ERROR_LOG(GPU, "Invalid size 0x%08x", size); 98 LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size);
99 return; 99 return;
100 } 100 }
101 101
@@ -136,7 +136,7 @@ static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
136 * 1: Result code 136 * 1: Result code
137 */ 137 */
138static void SetBufferSwap(Service::Interface* self) { 138static void SetBufferSwap(Service::Interface* self) {
139 u32* cmd_buff = Service::GetCommandBuffer(); 139 u32* cmd_buff = Kernel::GetCommandBuffer();
140 u32 screen_id = cmd_buff[1]; 140 u32 screen_id = cmd_buff[1];
141 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; 141 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
142 SetBufferSwap(screen_id, *fb_info); 142 SetBufferSwap(screen_id, *fb_info);
@@ -145,6 +145,30 @@ static void SetBufferSwap(Service::Interface* self) {
145} 145}
146 146
147/** 147/**
148 * GSP_GPU::FlushDataCache service function
149 *
150 * This Function is a no-op, We aren't emulating the CPU cache any time soon.
151 *
152 * Inputs:
153 * 1 : Address
154 * 2 : Size
155 * 3 : Value 0, some descriptor for the KProcess Handle
156 * 4 : KProcess handle
157 * Outputs:
158 * 1 : Result of function, 0 on success, otherwise error code
159 */
160static void FlushDataCache(Service::Interface* self) {
161 u32* cmd_buff = Kernel::GetCommandBuffer();
162 u32 address = cmd_buff[1];
163 u32 size = cmd_buff[2];
164 u32 process = cmd_buff[4];
165
166 // TODO(purpasmart96): Verify return header on HW
167
168 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
169}
170
171/**
148 * GSP_GPU::RegisterInterruptRelayQueue service function 172 * GSP_GPU::RegisterInterruptRelayQueue service function
149 * Inputs: 173 * Inputs:
150 * 1 : "Flags" field, purpose is unknown 174 * 1 : "Flags" field, purpose is unknown
@@ -155,14 +179,15 @@ static void SetBufferSwap(Service::Interface* self) {
155 * 4 : Handle to GSP shared memory 179 * 4 : Handle to GSP shared memory
156 */ 180 */
157static void RegisterInterruptRelayQueue(Service::Interface* self) { 181static void RegisterInterruptRelayQueue(Service::Interface* self) {
158 u32* cmd_buff = Service::GetCommandBuffer(); 182 u32* cmd_buff = Kernel::GetCommandBuffer();
159 u32 flags = cmd_buff[1]; 183 u32 flags = cmd_buff[1];
160 g_interrupt_event = cmd_buff[3]; 184 g_interrupt_event = cmd_buff[3];
161 g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); 185 g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
162 186
163 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); 187 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
164 188
165 cmd_buff[2] = g_thread_id++; // ThreadID 189 cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
190 cmd_buff[2] = g_thread_id++; // Thread ID
166 cmd_buff[4] = g_shared_memory; // GSP shared memory 191 cmd_buff[4] = g_shared_memory; // GSP shared memory
167 192
168 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? 193 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -172,14 +197,15 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
172 * Signals that the specified interrupt type has occurred to userland code 197 * Signals that the specified interrupt type has occurred to userland code
173 * @param interrupt_id ID of interrupt that is being signalled 198 * @param interrupt_id ID of interrupt that is being signalled
174 * @todo This should probably take a thread_id parameter and only signal this thread? 199 * @todo This should probably take a thread_id parameter and only signal this thread?
200 * @todo This probably does not belong in the GSP module, instead move to video_core
175 */ 201 */
176void SignalInterrupt(InterruptId interrupt_id) { 202void SignalInterrupt(InterruptId interrupt_id) {
177 if (0 == g_interrupt_event) { 203 if (0 == g_interrupt_event) {
178 WARN_LOG(GSP, "cannot synchronize until GSP event has been created!"); 204 LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
179 return; 205 return;
180 } 206 }
181 if (0 == g_shared_memory) { 207 if (0 == g_shared_memory) {
182 WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!"); 208 LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
183 return; 209 return;
184 } 210 }
185 for (int thread_id = 0; thread_id < 0x4; ++thread_id) { 211 for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -210,6 +236,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
210 memcpy(Memory::GetPointer(command.dma_request.dest_address), 236 memcpy(Memory::GetPointer(command.dma_request.dest_address),
211 Memory::GetPointer(command.dma_request.source_address), 237 Memory::GetPointer(command.dma_request.source_address),
212 command.dma_request.size); 238 command.dma_request.size);
239 SignalInterrupt(InterruptId::DMA);
213 break; 240 break;
214 241
215 // ctrulib homebrew sends all relevant command list data with this command, 242 // ctrulib homebrew sends all relevant command list data with this command,
@@ -218,13 +245,13 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
218 case CommandId::SET_COMMAND_LIST_LAST: 245 case CommandId::SET_COMMAND_LIST_LAST:
219 { 246 {
220 auto& params = command.set_command_list_last; 247 auto& params = command.set_command_list_last;
248
221 WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); 249 WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3);
222 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); 250 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
223 251
224 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though 252 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
225 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); 253 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
226 254
227 SignalInterrupt(InterruptId::P3D);
228 break; 255 break;
229 } 256 }
230 257
@@ -242,6 +269,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
242 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); 269 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
243 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); 270 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
244 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); 271 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
272
273 SignalInterrupt(InterruptId::PSC0);
245 break; 274 break;
246 } 275 }
247 276
@@ -255,14 +284,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
255 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); 284 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
256 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); 285 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
257 286
258 // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to 287 // TODO(bunnei): Determine if these interrupts should be signalled here.
259 // work well enough for running demos. Need to figure out how these all work and trigger
260 // them correctly.
261 SignalInterrupt(InterruptId::PSC0);
262 SignalInterrupt(InterruptId::PSC1); 288 SignalInterrupt(InterruptId::PSC1);
263 SignalInterrupt(InterruptId::PPF); 289 SignalInterrupt(InterruptId::PPF);
264 SignalInterrupt(InterruptId::P3D);
265 SignalInterrupt(InterruptId::DMA);
266 290
267 // Update framebuffer information if requested 291 // Update framebuffer information if requested
268 for (int screen_id = 0; screen_id < 2; ++screen_id) { 292 for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -298,13 +322,15 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
298 } 322 }
299 323
300 default: 324 default:
301 ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value()); 325 LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value());
302 } 326 }
303} 327}
304 328
305/// This triggers handling of the GX command written to the command buffer in shared memory. 329/// This triggers handling of the GX command written to the command buffer in shared memory.
306static void TriggerCmdReqQueue(Service::Interface* self) { 330static void TriggerCmdReqQueue(Service::Interface* self) {
307 331
332 LOG_TRACE(Service_GSP, "called");
333
308 // Iterate through each thread's command queue... 334 // Iterate through each thread's command queue...
309 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { 335 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
310 CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); 336 CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id);
@@ -320,6 +346,9 @@ static void TriggerCmdReqQueue(Service::Interface* self) {
320 command_buffer->number_commands = command_buffer->number_commands - 1; 346 command_buffer->number_commands = command_buffer->number_commands - 1;
321 } 347 }
322 } 348 }
349
350 u32* cmd_buff = Kernel::GetCommandBuffer();
351 cmd_buff[1] = 0; // No error
323} 352}
324 353
325const Interface::FunctionInfo FunctionTable[] = { 354const Interface::FunctionInfo FunctionTable[] = {
@@ -330,7 +359,7 @@ const Interface::FunctionInfo FunctionTable[] = {
330 {0x00050200, SetBufferSwap, "SetBufferSwap"}, 359 {0x00050200, SetBufferSwap, "SetBufferSwap"},
331 {0x00060082, nullptr, "SetCommandList"}, 360 {0x00060082, nullptr, "SetCommandList"},
332 {0x000700C2, nullptr, "RequestDma"}, 361 {0x000700C2, nullptr, "RequestDma"},
333 {0x00080082, nullptr, "FlushDataCache"}, 362 {0x00080082, FlushDataCache, "FlushDataCache"},
334 {0x00090082, nullptr, "InvalidateDataCache"}, 363 {0x00090082, nullptr, "InvalidateDataCache"},
335 {0x000A0044, nullptr, "RegisterInterruptEvents"}, 364 {0x000A0044, nullptr, "RegisterInterruptEvents"},
336 {0x000B0040, nullptr, "SetLcdForceBlack"}, 365 {0x000B0040, nullptr, "SetLcdForceBlack"},
@@ -367,7 +396,4 @@ Interface::Interface() {
367 g_thread_id = 1; 396 g_thread_id = 1;
368} 397}
369 398
370Interface::~Interface() {
371}
372
373} // namespace 399} // namespace
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 177ce8da6..932b6170f 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -158,19 +158,11 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec
158/// Interface to "srv:" service 158/// Interface to "srv:" service
159class Interface : public Service::Interface { 159class Interface : public Service::Interface {
160public: 160public:
161
162 Interface(); 161 Interface();
163 162
164 ~Interface();
165
166 /**
167 * Gets the string port name used by CTROS for the service
168 * @return Port name of service
169 */
170 std::string GetPortName() const override { 163 std::string GetPortName() const override {
171 return "gsp::Gpu"; 164 return "gsp::Gpu";
172 } 165 }
173
174}; 166};
175 167
176/** 168/**
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index d29de1a52..99b0ea5a0 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -55,7 +55,7 @@ static void UpdateNextCirclePadState() {
55/** 55/**
56 * Sets a Pad state (button or button combo) as pressed 56 * Sets a Pad state (button or button combo) as pressed
57 */ 57 */
58void PadButtonPress(PadState pad_state) { 58void PadButtonPress(const PadState& pad_state) {
59 next_state.hex |= pad_state.hex; 59 next_state.hex |= pad_state.hex;
60 UpdateNextCirclePadState(); 60 UpdateNextCirclePadState();
61} 61}
@@ -63,7 +63,7 @@ void PadButtonPress(PadState pad_state) {
63/** 63/**
64 * Sets a Pad state (button or button combo) as released 64 * Sets a Pad state (button or button combo) as released
65 */ 65 */
66void PadButtonRelease(PadState pad_state) { 66void PadButtonRelease(const PadState& pad_state) {
67 next_state.hex &= ~pad_state.hex; 67 next_state.hex &= ~pad_state.hex;
68 UpdateNextCirclePadState(); 68 UpdateNextCirclePadState();
69} 69}
@@ -153,7 +153,7 @@ void PadUpdateComplete() {
153 * 8 : Event signaled by HID_User 153 * 8 : Event signaled by HID_User
154 */ 154 */
155static void GetIPCHandles(Service::Interface* self) { 155static void GetIPCHandles(Service::Interface* self) {
156 u32* cmd_buff = Service::GetCommandBuffer(); 156 u32* cmd_buff = Kernel::GetCommandBuffer();
157 157
158 cmd_buff[1] = 0; // No error 158 cmd_buff[1] = 0; // No error
159 cmd_buff[3] = shared_mem; 159 cmd_buff[3] = shared_mem;
@@ -163,7 +163,7 @@ static void GetIPCHandles(Service::Interface* self) {
163 cmd_buff[7] = event_gyroscope; 163 cmd_buff[7] = event_gyroscope;
164 cmd_buff[8] = event_debug_pad; 164 cmd_buff[8] = event_debug_pad;
165 165
166 DEBUG_LOG(KERNEL, "called"); 166 LOG_TRACE(Service_HID, "called");
167} 167}
168 168
169const Interface::FunctionInfo FunctionTable[] = { 169const Interface::FunctionInfo FunctionTable[] = {
@@ -179,7 +179,6 @@ const Interface::FunctionInfo FunctionTable[] = {
179 {0x00170000, nullptr, "GetSoundVolume"}, 179 {0x00170000, nullptr, "GetSoundVolume"},
180}; 180};
181 181
182
183//////////////////////////////////////////////////////////////////////////////////////////////////// 182////////////////////////////////////////////////////////////////////////////////////////////////////
184// Interface class 183// Interface class
185 184
@@ -196,7 +195,4 @@ Interface::Interface() {
196 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 195 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
197} 196}
198 197
199Interface::~Interface() {
200}
201
202} // namespace 198} // namespace
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h
index 5ed97085d..5b96dda60 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid_user.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}};
93const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; 93const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
94 94
95// Methods for updating the HID module's state 95// Methods for updating the HID module's state
96void PadButtonPress(PadState pad_state); 96void PadButtonPress(const PadState& pad_state);
97void PadButtonRelease(PadState pad_state); 97void PadButtonRelease(const PadState& pad_state);
98void PadUpdateComplete(); 98void PadUpdateComplete();
99 99
100/** 100/**
@@ -102,19 +102,11 @@ void PadUpdateComplete();
102 */ 102 */
103class Interface : public Service::Interface { 103class Interface : public Service::Interface {
104public: 104public:
105
106 Interface(); 105 Interface();
107 106
108 ~Interface();
109
110 /**
111 * Gets the string port name used by CTROS for the service
112 * @return Port name of service
113 */
114 std::string GetPortName() const override { 107 std::string GetPortName() const override {
115 return "hid:USER"; 108 return "hid:USER";
116 } 109 }
117
118}; 110};
119 111
120} // namespace 112} // namespace
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
new file mode 100644
index 000000000..d0bff552f
--- /dev/null
+++ b/src/core/hle/service/http_c.cpp
@@ -0,0 +1,64 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/http_c.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace HTTP_C
11
12namespace HTTP_C {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010044, nullptr, "Initialize"},
16 {0x00020082, nullptr, "CreateContext"},
17 {0x00030040, nullptr, "CloseContext"},
18 {0x00040040, nullptr, "CancelConnection"},
19 {0x00050040, nullptr, "GetRequestState"},
20 {0x00060040, nullptr, "GetDownloadSizeState"},
21 {0x00070040, nullptr, "GetRequestError"},
22 {0x00080042, nullptr, "InitializeConnectionSession"},
23 {0x00090040, nullptr, "BeginRequest"},
24 {0x000A0040, nullptr, "BeginRequestAsync"},
25 {0x000B0082, nullptr, "ReceiveData"},
26 {0x000C0102, nullptr, "ReceiveDataTimeout"},
27 {0x000D0146, nullptr, "SetProxy"},
28 {0x000E0040, nullptr, "SetProxyDefault"},
29 {0x000F00C4, nullptr, "SetBasicAuthorization"},
30 {0x00100080, nullptr, "SetSocketBufferSize"},
31 {0x001100C4, nullptr, "AddRequestHeader"},
32 {0x001200C4, nullptr, "AddPostDataAscii"},
33 {0x001300C4, nullptr, "AddPostDataBinary"},
34 {0x00140082, nullptr, "AddPostDataRaw"},
35 {0x00150080, nullptr, "SetPostDataType"},
36 {0x001600C4, nullptr, "SendPostDataAscii"},
37 {0x00170144, nullptr, "SendPostDataAsciiTimeout"},
38 {0x001800C4, nullptr, "SendPostDataBinary"},
39 {0x00190144, nullptr, "SendPostDataBinaryTimeout"},
40 {0x001A0082, nullptr, "SendPostDataRaw"},
41 {0x001B0102, nullptr, "SendPOSTDataRawTimeout"},
42 {0x001C0080, nullptr, "SetPostDataEncoding"},
43 {0x001D0040, nullptr, "NotifyFinishSendPostData"},
44 {0x001E00C4, nullptr, "GetResponseHeader"},
45 {0x001F0144, nullptr, "GetResponseHeaderTimeout"},
46 {0x00200082, nullptr, "GetResponseData"},
47 {0x00210102, nullptr, "GetResponseDataTimeout"},
48 {0x00220040, nullptr, "GetResponseStatusCode"},
49 {0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
50 {0x00240082, nullptr, "AddTrustedRootCA"},
51 {0x00350186, nullptr, "SetDefaultProxy"},
52 {0x00360000, nullptr, "ClearDNSCache"},
53 {0x00370080, nullptr, "SetKeepAlive"},
54 {0x003800C0, nullptr, "Finalize"},
55};
56
57////////////////////////////////////////////////////////////////////////////////////////////////////
58// Interface class
59
60Interface::Interface() {
61 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
62}
63
64} // namespace
diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h
new file mode 100644
index 000000000..5ea3d1df3
--- /dev/null
+++ b/src/core/hle/service/http_c.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace HTTP_C
11
12namespace HTTP_C {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "http:C";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
index be15db231..b388afb15 100644
--- a/src/core/hle/service/ir_rst.cpp
+++ b/src/core/hle/service/ir_rst.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -30,7 +30,4 @@ Interface::Interface() {
30 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 30 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
31} 31}
32 32
33Interface::~Interface() {
34}
35
36} // namespace 33} // namespace
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h
index 73effd7e3..deef701c5 100644
--- a/src/core/hle/service/ir_rst.h
+++ b/src/core/hle/service/ir_rst.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included..
4 4
5#pragma once 5#pragma once
6 6
@@ -14,11 +14,7 @@ namespace IR_RST {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "ir:rst"; 19 return "ir:rst";
24 } 20 }
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
index aa9db6f6d..da6f38e41 100644
--- a/src/core/hle/service/ir_u.cpp
+++ b/src/core/hle/service/ir_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -39,7 +39,4 @@ Interface::Interface() {
39 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 39 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
40} 40}
41 41
42Interface::~Interface() {
43}
44
45} // namespace 42} // namespace
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h
index 86d98d079..ec47a1524 100644
--- a/src/core/hle/service/ir_u.h
+++ b/src/core/hle/service/ir_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace IR_U {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "ir:u"; 19 return "ir:u";
24 } 20 }
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
new file mode 100644
index 000000000..9c9e90a40
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -0,0 +1,29 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/ldr_ro.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace LDR_RO
11
12namespace LDR_RO {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x000100C2, nullptr, "Initialize"},
16 {0x00020082, nullptr, "CRR_Load"},
17 {0x00030042, nullptr, "CRR_Unload"},
18 {0x000402C2, nullptr, "CRO_LoadAndFix"},
19 {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"}
20};
21
22////////////////////////////////////////////////////////////////////////////////////////////////////
23// Interface class
24
25Interface::Interface() {
26 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
27}
28
29} // namespace
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h
new file mode 100644
index 000000000..331637cde
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace LDR_RO
11
12namespace LDR_RO {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "ldr:ro";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index d6f30e9ae..82bce9180 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -37,7 +37,4 @@ Interface::Interface() {
37 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 37 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
38} 38}
39 39
40Interface::~Interface() {
41}
42
43} // namespace 40} // namespace
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index 2a495f3a9..dc795d14c 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -16,11 +16,7 @@ namespace MIC_U {
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18 Interface(); 18 Interface();
19 ~Interface(); 19
20 /**
21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service
23 */
24 std::string GetPortName() const override { 20 std::string GetPortName() const override {
25 return "mic:u"; 21 return "mic:u";
26 } 22 }
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp
index 37c0661bf..233b14f6d 100644
--- a/src/core/hle/service/ndm_u.cpp
+++ b/src/core/hle/service/ndm_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/hle.h" 5#include "core/hle/hle.h"
@@ -24,7 +24,4 @@ Interface::Interface() {
24 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 24 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
25} 25}
26 26
27Interface::~Interface() {
28}
29
30} // namespace 27} // namespace
diff --git a/src/core/hle/service/ndm_u.h b/src/core/hle/service/ndm_u.h
index 2ca9fcf22..51c4b3902 100644
--- a/src/core/hle/service/ndm_u.h
+++ b/src/core/hle/service/ndm_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -15,19 +15,11 @@ namespace NDM_U {
15 15
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18
19 Interface(); 18 Interface();
20 19
21 ~Interface();
22
23 /**
24 * Gets the string port name used by CTROS for the service
25 * @return Port name of service
26 */
27 std::string GetPortName() const override { 20 std::string GetPortName() const override {
28 return "ndm:u"; 21 return "ndm:u";
29 } 22 }
30
31}; 23};
32 24
33} // namespace 25} // namespace
diff --git a/src/core/hle/service/news_u.cpp b/src/core/hle/service/news_u.cpp
new file mode 100644
index 000000000..b5adad4c6
--- /dev/null
+++ b/src/core/hle/service/news_u.cpp
@@ -0,0 +1,25 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/news_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NEWS_U
11
12namespace NEWS_U {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x000100C8, nullptr, "AddNotification"},
16};
17
18////////////////////////////////////////////////////////////////////////////////////////////////////
19// Interface class
20
21Interface::Interface() {
22 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
23}
24
25} // namespace
diff --git a/src/core/hle/service/news_u.h b/src/core/hle/service/news_u.h
new file mode 100644
index 000000000..0473cd19c
--- /dev/null
+++ b/src/core/hle/service/news_u.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NEWS_U
11
12namespace NEWS_U {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "news:u";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
new file mode 100644
index 000000000..17d1c4ff5
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -0,0 +1,31 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/nim_aoc.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NIM_AOC
11
12namespace NIM_AOC {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00030042, nullptr, "SetApplicationId"},
16 {0x00040042, nullptr, "SetTin"},
17 {0x000902D0, nullptr, "ListContentSetsEx"},
18 {0x00180000, nullptr, "GetBalance"},
19 {0x001D0000, nullptr, "GetCustomerSupportCode"},
20 {0x00210000, nullptr, "Initialize"},
21 {0x00240282, nullptr, "CalculateContentsRequiredSize"},
22 {0x00250000, nullptr, "RefreshServerTime"},
23};
24////////////////////////////////////////////////////////////////////////////////////////////////////
25// Interface class
26
27Interface::Interface() {
28 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
29}
30
31} // namespace
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h
new file mode 100644
index 000000000..aeb71eed2
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.h
@@ -0,0 +1,23 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NIM_AOC
11
12namespace NIM_AOC {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 std::string GetPortName() const override {
19 return "nim:aoc";
20 }
21};
22
23} // namespace
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 14df86d85..ce456a966 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -29,7 +29,4 @@ Interface::Interface() {
29 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 29 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
30} 30}
31 31
32Interface::~Interface() {
33}
34
35} // namespace 32} // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index 69d2c2002..9043f5aa7 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -16,11 +16,7 @@ namespace NWM_UDS {
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18 Interface(); 18 Interface();
19 ~Interface(); 19
20 /**
21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service
23 */
24 std::string GetPortName() const override { 20 std::string GetPortName() const override {
25 return "nwm:UDS"; 21 return "nwm:UDS";
26 } 22 }
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
index 90e9b1bfa..529dccafb 100644
--- a/src/core/hle/service/pm_app.cpp
+++ b/src/core/hle/service/pm_app.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -29,7 +29,4 @@ Interface::Interface() {
29 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 29 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
30} 30}
31 31
32Interface::~Interface() {
33}
34
35} // namespace 32} // namespace
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
index 28c38f582..c1fb1f9da 100644
--- a/src/core/hle/service/pm_app.h
+++ b/src/core/hle/service/pm_app.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace PM_APP {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "pm:app"; 19 return "pm:app";
24 } 20 }
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index d9122dbbc..d1498f05c 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -11,15 +11,101 @@
11 11
12namespace PTM_U { 12namespace PTM_U {
13 13
14/// Charge levels used by PTM functions
15enum class ChargeLevels : u32 {
16 CriticalBattery = 1,
17 LowBattery = 2,
18 HalfFull = 3,
19 MostlyFull = 4,
20 CompletelyFull = 5,
21};
22
23static bool shell_open = true;
24
25static bool battery_is_charging = true;
26
27/**
28 * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
29 * it is likely to just be a duplicate function of GetBatteryChargeState
30 * that controls another part of the HW.
31 * PTM_U::GetAdapterState service function
32 * Outputs:
33 * 1 : Result of function, 0 on success, otherwise error code
34 * 2 : Output of function, 0 = not charging, 1 = charging.
35 */
36static void GetAdapterState(Service::Interface* self) {
37 u32* cmd_buff = Kernel::GetCommandBuffer();
38
39 // TODO(purpasmart96): This function is only a stub,
40 // it returns a valid result without implementing full functionality.
41
42 cmd_buff[1] = 0; // No error
43 cmd_buff[2] = battery_is_charging ? 1 : 0;
44
45 LOG_WARNING(Service_PTM, "(STUBBED) called");
46}
47
48/*
49 * PTM_User::GetShellState service function.
50 * Outputs:
51 * 1 : Result of function, 0 on success, otherwise error code
52 * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
53 */
54static void GetShellState(Service::Interface* self) {
55 u32* cmd_buff = Kernel::GetCommandBuffer();
56
57 cmd_buff[1] = 0;
58 cmd_buff[2] = shell_open ? 1 : 0;
59
60 LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
61}
62
63/**
64 * PTM_U::GetBatteryLevel service function
65 * Outputs:
66 * 1 : Result of function, 0 on success, otherwise error code
67 * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
68 * 3 = half full battery, 2 = low battery, 1 = critical battery.
69 */
70static void GetBatteryLevel(Service::Interface* self) {
71 u32* cmd_buff = Kernel::GetCommandBuffer();
72
73 // TODO(purpasmart96): This function is only a stub,
74 // it returns a valid result without implementing full functionality.
75
76 cmd_buff[1] = 0; // No error
77 cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery
78
79 LOG_WARNING(Service_PTM, "(STUBBED) called");
80}
81
82/**
83 * PTM_U::GetBatteryChargeState service function
84 * Outputs:
85 * 1 : Result of function, 0 on success, otherwise error code
86 * 2 : Output of function, 0 = not charging, 1 = charging.
87 */
88static void GetBatteryChargeState(Service::Interface* self) {
89 u32* cmd_buff = Kernel::GetCommandBuffer();
90
91 // TODO(purpasmart96): This function is only a stub,
92 // it returns a valid result without implementing full functionality.
93
94 cmd_buff[1] = 0; // No error
95 cmd_buff[2] = battery_is_charging ? 1 : 0;
96
97 LOG_WARNING(Service_PTM, "(STUBBED) called");
98}
99
14const Interface::FunctionInfo FunctionTable[] = { 100const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010002, nullptr, "RegisterAlarmClient"}, 101 {0x00010002, nullptr, "RegisterAlarmClient"},
16 {0x00020080, nullptr, "SetRtcAlarm"}, 102 {0x00020080, nullptr, "SetRtcAlarm"},
17 {0x00030000, nullptr, "GetRtcAlarm"}, 103 {0x00030000, nullptr, "GetRtcAlarm"},
18 {0x00040000, nullptr, "CancelRtcAlarm"}, 104 {0x00040000, nullptr, "CancelRtcAlarm"},
19 {0x00050000, nullptr, "GetAdapterState"}, 105 {0x00050000, GetAdapterState, "GetAdapterState"},
20 {0x00060000, nullptr, "GetShellState"}, 106 {0x00060000, GetShellState, "GetShellState"},
21 {0x00070000, nullptr, "GetBatteryLevel"}, 107 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
22 {0x00080000, nullptr, "GetBatteryChargeState"}, 108 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
23 {0x00090000, nullptr, "GetPedometerState"}, 109 {0x00090000, nullptr, "GetPedometerState"},
24 {0x000A0042, nullptr, "GetStepHistoryEntry"}, 110 {0x000A0042, nullptr, "GetStepHistoryEntry"},
25 {0x000B00C2, nullptr, "GetStepHistory"}, 111 {0x000B00C2, nullptr, "GetStepHistory"},
@@ -36,7 +122,4 @@ Interface::Interface() {
36 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 122 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
37} 123}
38 124
39Interface::~Interface() {
40}
41
42} // namespace 125} // namespace
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h
index f8d9f57be..a44624fd5 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -16,11 +16,7 @@ namespace PTM_U {
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18 Interface(); 18 Interface();
19 ~Interface(); 19
20 /**
21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service
23 */
24 std::string GetPortName() const override { 20 std::string GetPortName() const override {
25 return "ptm:u"; 21 return "ptm:u";
26 } 22 }
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fed2268a0..0f3cc2aa8 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common.h" 5#include "common/common.h"
@@ -7,22 +7,30 @@
7 7
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9#include "core/hle/service/ac_u.h" 9#include "core/hle/service/ac_u.h"
10#include "core/hle/service/act_u.h"
11#include "core/hle/service/am_app.h"
10#include "core/hle/service/am_net.h" 12#include "core/hle/service/am_net.h"
13#include "core/hle/service/apt_a.h"
11#include "core/hle/service/apt_u.h" 14#include "core/hle/service/apt_u.h"
12#include "core/hle/service/boss_u.h" 15#include "core/hle/service/boss_u.h"
13#include "core/hle/service/cfg_i.h" 16#include "core/hle/service/cecd_u.h"
14#include "core/hle/service/cfg_u.h" 17#include "core/hle/service/cfg/cfg_i.h"
18#include "core/hle/service/cfg/cfg_u.h"
15#include "core/hle/service/csnd_snd.h" 19#include "core/hle/service/csnd_snd.h"
16#include "core/hle/service/dsp_dsp.h" 20#include "core/hle/service/dsp_dsp.h"
17#include "core/hle/service/err_f.h" 21#include "core/hle/service/err_f.h"
18#include "core/hle/service/fs_user.h" 22#include "core/hle/service/fs/fs_user.h"
19#include "core/hle/service/frd_u.h" 23#include "core/hle/service/frd_u.h"
20#include "core/hle/service/gsp_gpu.h" 24#include "core/hle/service/gsp_gpu.h"
21#include "core/hle/service/hid_user.h" 25#include "core/hle/service/hid_user.h"
26#include "core/hle/service/http_c.h"
22#include "core/hle/service/ir_rst.h" 27#include "core/hle/service/ir_rst.h"
23#include "core/hle/service/ir_u.h" 28#include "core/hle/service/ir_u.h"
29#include "core/hle/service/ldr_ro.h"
24#include "core/hle/service/mic_u.h" 30#include "core/hle/service/mic_u.h"
25#include "core/hle/service/ndm_u.h" 31#include "core/hle/service/ndm_u.h"
32#include "core/hle/service/news_u.h"
33#include "core/hle/service/nim_aoc.h"
26#include "core/hle/service/nwm_uds.h" 34#include "core/hle/service/nwm_uds.h"
27#include "core/hle/service/pm_app.h" 35#include "core/hle/service/pm_app.h"
28#include "core/hle/service/ptm_u.h" 36#include "core/hle/service/ptm_u.h"
@@ -48,7 +56,8 @@ Manager::~Manager() {
48 56
49/// Add a service to the manager (does not create it though) 57/// Add a service to the manager (does not create it though)
50void Manager::AddService(Interface* service) { 58void Manager::AddService(Interface* service) {
51 m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); 59 // TOOD(yuriks): Fix error reporting
60 m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
52 m_services.push_back(service); 61 m_services.push_back(service);
53} 62}
54 63
@@ -62,7 +71,7 @@ void Manager::DeleteService(const std::string& port_name) {
62 71
63/// Get a Service Interface from its Handle 72/// Get a Service Interface from its Handle
64Interface* Manager::FetchFromHandle(Handle handle) { 73Interface* Manager::FetchFromHandle(Handle handle) {
65 return Kernel::g_object_pool.Get<Interface>(handle); 74 return Kernel::g_handle_table.Get<Interface>(handle);
66} 75}
67 76
68/// Get a Service Interface from its port 77/// Get a Service Interface from its port
@@ -84,35 +93,43 @@ void Init() {
84 93
85 g_manager->AddService(new SRV::Interface); 94 g_manager->AddService(new SRV::Interface);
86 g_manager->AddService(new AC_U::Interface); 95 g_manager->AddService(new AC_U::Interface);
96 g_manager->AddService(new ACT_U::Interface);
97 g_manager->AddService(new AM_APP::Interface);
87 g_manager->AddService(new AM_NET::Interface); 98 g_manager->AddService(new AM_NET::Interface);
99 g_manager->AddService(new APT_A::Interface);
88 g_manager->AddService(new APT_U::Interface); 100 g_manager->AddService(new APT_U::Interface);
89 g_manager->AddService(new BOSS_U::Interface); 101 g_manager->AddService(new BOSS_U::Interface);
102 g_manager->AddService(new CECD_U::Interface);
90 g_manager->AddService(new CFG_I::Interface); 103 g_manager->AddService(new CFG_I::Interface);
91 g_manager->AddService(new CFG_U::Interface); 104 g_manager->AddService(new CFG_U::Interface);
92 g_manager->AddService(new CSND_SND::Interface); 105 g_manager->AddService(new CSND_SND::Interface);
93 g_manager->AddService(new DSP_DSP::Interface); 106 g_manager->AddService(new DSP_DSP::Interface);
94 g_manager->AddService(new ERR_F::Interface); 107 g_manager->AddService(new ERR_F::Interface);
95 g_manager->AddService(new FRD_U::Interface); 108 g_manager->AddService(new FRD_U::Interface);
96 g_manager->AddService(new FS_User::Interface); 109 g_manager->AddService(new FS::FSUserInterface);
97 g_manager->AddService(new GSP_GPU::Interface); 110 g_manager->AddService(new GSP_GPU::Interface);
98 g_manager->AddService(new HID_User::Interface); 111 g_manager->AddService(new HID_User::Interface);
112 g_manager->AddService(new HTTP_C::Interface);
99 g_manager->AddService(new IR_RST::Interface); 113 g_manager->AddService(new IR_RST::Interface);
100 g_manager->AddService(new IR_U::Interface); 114 g_manager->AddService(new IR_U::Interface);
115 g_manager->AddService(new LDR_RO::Interface);
101 g_manager->AddService(new MIC_U::Interface); 116 g_manager->AddService(new MIC_U::Interface);
102 g_manager->AddService(new NDM_U::Interface); 117 g_manager->AddService(new NDM_U::Interface);
118 g_manager->AddService(new NEWS_U::Interface);
119 g_manager->AddService(new NIM_AOC::Interface);
103 g_manager->AddService(new NWM_UDS::Interface); 120 g_manager->AddService(new NWM_UDS::Interface);
104 g_manager->AddService(new PM_APP::Interface); 121 g_manager->AddService(new PM_APP::Interface);
105 g_manager->AddService(new PTM_U::Interface); 122 g_manager->AddService(new PTM_U::Interface);
106 g_manager->AddService(new SOC_U::Interface); 123 g_manager->AddService(new SOC_U::Interface);
107 g_manager->AddService(new SSL_C::Interface); 124 g_manager->AddService(new SSL_C::Interface);
108 125
109 NOTICE_LOG(HLE, "initialized OK"); 126 LOG_DEBUG(Service, "initialized OK");
110} 127}
111 128
112/// Shutdown ServiceManager 129/// Shutdown ServiceManager
113void Shutdown() { 130void Shutdown() {
114 delete g_manager; 131 delete g_manager;
115 NOTICE_LOG(HLE, "shutdown OK"); 132 LOG_DEBUG(Service, "shutdown OK");
116} 133}
117 134
118 135
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 20e7fb4d3..28b4ccd17 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -10,9 +10,11 @@
10#include <string> 10#include <string>
11 11
12#include "common/common.h" 12#include "common/common.h"
13#include "common/string_util.h"
13#include "core/mem_map.h" 14#include "core/mem_map.h"
14 15
15#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/session.h"
16#include "core/hle/svc.h" 18#include "core/hle/svc.h"
17 19
18//////////////////////////////////////////////////////////////////////////////////////////////////// 20////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,30 +22,19 @@
20 22
21namespace Service { 23namespace Service {
22 24
23static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) 25static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
24static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
25
26/**
27 * Returns a pointer to the command buffer in kernel memory
28 * @param offset Optional offset into command buffer
29 * @return Pointer to command buffer
30 */
31inline static u32* GetCommandBuffer(const int offset=0) {
32 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
33}
34 26
35class Manager; 27class Manager;
36 28
37/// Interface to a CTROS service 29/// Interface to a CTROS service
38class Interface : public Kernel::Object { 30class Interface : public Kernel::Session {
31 // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
32 // just something that encapsulates a session and acts as a helper to implement service
33 // processes.
34
39 friend class Manager; 35 friend class Manager;
40public: 36public:
41
42 std::string GetName() const override { return GetPortName(); } 37 std::string GetName() const override { return GetPortName(); }
43 std::string GetTypeName() const override { return GetPortName(); }
44
45 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
46 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; }
47 38
48 typedef void (*Function)(Interface*); 39 typedef void (*Function)(Interface*);
49 40
@@ -63,7 +54,8 @@ public:
63 54
64 /// Allocates a new handle for the service 55 /// Allocates a new handle for the service
65 Handle CreateHandle(Kernel::Object *obj) { 56 Handle CreateHandle(Kernel::Object *obj) {
66 Handle handle = Kernel::g_object_pool.Create(obj); 57 // TODO(yuriks): Fix error reporting
58 Handle handle = Kernel::g_handle_table.Create(obj).ValueOr(INVALID_HANDLE);
67 m_handles.push_back(handle); 59 m_handles.push_back(handle);
68 return handle; 60 return handle;
69 } 61 }
@@ -71,29 +63,28 @@ public:
71 /// Frees a handle from the service 63 /// Frees a handle from the service
72 template <class T> 64 template <class T>
73 void DeleteHandle(const Handle handle) { 65 void DeleteHandle(const Handle handle) {
74 Kernel::g_object_pool.Destroy<T>(handle); 66 Kernel::g_handle_table.Close(handle);
75 m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); 67 m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
76 } 68 }
77 69
78 ResultVal<bool> SyncRequest() override { 70 ResultVal<bool> SyncRequest() override {
79 u32* cmd_buff = GetCommandBuffer(); 71 u32* cmd_buff = Kernel::GetCommandBuffer();
80 auto itr = m_functions.find(cmd_buff[0]); 72 auto itr = m_functions.find(cmd_buff[0]);
81 73
82 if (itr == m_functions.end()) { 74 if (itr == m_functions.end() || itr->second.func == nullptr) {
83 ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", 75 // Number of params == bits 0-5 + bits 6-11
84 GetPortName().c_str(), cmd_buff[0]); 76 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
85 77
86 // TODO(bunnei): Hack - ignore error 78 std::string error = "unknown/unimplemented function '%s': port=%s";
87 u32* cmd_buff = Service::GetCommandBuffer(); 79 for (int i = 1; i <= num_params; ++i) {
88 cmd_buff[1] = 0; 80 error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
89 return MakeResult<bool>(false); 81 }
90 } 82
91 if (itr->second.func == nullptr) { 83 std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
92 ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", 84
93 GetPortName().c_str(), itr->second.name.c_str()); 85 LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
94 86
95 // TODO(bunnei): Hack - ignore error 87 // TODO(bunnei): Hack - ignore error
96 u32* cmd_buff = Service::GetCommandBuffer();
97 cmd_buff[1] = 0; 88 cmd_buff[1] = 0;
98 return MakeResult<bool>(false); 89 return MakeResult<bool>(false);
99 } 90 }
@@ -103,12 +94,6 @@ public:
103 return MakeResult<bool>(false); // TODO: Implement return from actual function 94 return MakeResult<bool>(false); // TODO: Implement return from actual function
104 } 95 }
105 96
106 ResultVal<bool> WaitSynchronization() override {
107 // TODO(bunnei): ImplementMe
108 ERROR_LOG(OSHLE, "unimplemented function");
109 return UnimplementedFunction(ErrorModule::OS);
110 }
111
112protected: 97protected:
113 98
114 /** 99 /**
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 2f8910468..ef4f9829d 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -52,7 +52,4 @@ Interface::Interface() {
52 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 52 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
53} 53}
54 54
55Interface::~Interface() {
56}
57
58} // namespace 55} // namespace
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index d5590a683..2edf3b482 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,11 +14,7 @@ namespace SOC_U {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override { 18 std::string GetPortName() const override {
23 return "soc:U"; 19 return "soc:U";
24 } 20 }
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 0e7fa9e3b..25fab1a4f 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/hle.h" 5#include "core/hle/hle.h"
@@ -14,17 +14,17 @@ namespace SRV {
14static Handle g_event_handle = 0; 14static Handle g_event_handle = 0;
15 15
16static void Initialize(Service::Interface* self) { 16static void Initialize(Service::Interface* self) {
17 DEBUG_LOG(OSHLE, "called"); 17 LOG_DEBUG(Service_SRV, "called");
18 18
19 u32* cmd_buff = Service::GetCommandBuffer(); 19 u32* cmd_buff = Kernel::GetCommandBuffer();
20 20
21 cmd_buff[1] = 0; // No error 21 cmd_buff[1] = 0; // No error
22} 22}
23 23
24static void GetProcSemaphore(Service::Interface* self) { 24static void GetProcSemaphore(Service::Interface* self) {
25 DEBUG_LOG(OSHLE, "called"); 25 LOG_TRACE(Service_SRV, "called");
26 26
27 u32* cmd_buff = Service::GetCommandBuffer(); 27 u32* cmd_buff = Kernel::GetCommandBuffer();
28 28
29 // TODO(bunnei): Change to a semaphore once these have been implemented 29 // TODO(bunnei): Change to a semaphore once these have been implemented
30 g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); 30 g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
@@ -36,16 +36,16 @@ static void GetProcSemaphore(Service::Interface* self) {
36 36
37static void GetServiceHandle(Service::Interface* self) { 37static void GetServiceHandle(Service::Interface* self) {
38 ResultCode res = RESULT_SUCCESS; 38 ResultCode res = RESULT_SUCCESS;
39 u32* cmd_buff = Service::GetCommandBuffer(); 39 u32* cmd_buff = Kernel::GetCommandBuffer();
40 40
41 std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); 41 std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
42 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); 42 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
43 43
44 if (nullptr != service) { 44 if (nullptr != service) {
45 cmd_buff[3] = service->GetHandle(); 45 cmd_buff[3] = service->GetHandle();
46 DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); 46 LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
47 } else { 47 } else {
48 ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); 48 LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
49 res = UnimplementedFunction(ErrorModule::SRV); 49 res = UnimplementedFunction(ErrorModule::SRV);
50 } 50 }
51 cmd_buff[1] = res.raw; 51 cmd_buff[1] = res.raw;
@@ -68,7 +68,4 @@ Interface::Interface() {
68 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 68 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
69} 69}
70 70
71Interface::~Interface() {
72}
73
74} // namespace 71} // namespace
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h
index 6d5fe5048..653aba5cb 100644
--- a/src/core/hle/service/srv.h
+++ b/src/core/hle/service/srv.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/service/service.h" 5#include "core/hle/service/service.h"
@@ -11,21 +11,12 @@ namespace SRV {
11 11
12/// Interface to "srv:" service 12/// Interface to "srv:" service
13class Interface : public Service::Interface { 13class Interface : public Service::Interface {
14
15public: 14public:
16
17 Interface(); 15 Interface();
18 16
19 ~Interface();
20
21 /**
22 * Gets the string name used by CTROS for the service
23 * @return Port name of service
24 */
25 std::string GetPortName() const override { 17 std::string GetPortName() const override {
26 return "srv:"; 18 return "srv:";
27 } 19 }
28
29}; 20};
30 21
31} // namespace 22} // namespace
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index 4aa660ecc..360516cdf 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/log.h" 5#include "common/log.h"
@@ -25,7 +25,4 @@ Interface::Interface() {
25 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 25 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
26} 26}
27 27
28Interface::~Interface() {
29}
30
31} // namespace 28} // namespace
diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h
index 7b4e7fd8a..58e87c1cb 100644
--- a/src/core/hle/service/ssl_c.h
+++ b/src/core/hle/service/ssl_c.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
@@ -14,12 +14,8 @@ namespace SSL_C {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface(); 17
18 /** 18 std::string GetPortName() const override {
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const {
23 return "ssl:C"; 19 return "ssl:C";
24 } 20 }
25}; 21};
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 87d768856..25944fc68 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
@@ -12,6 +12,7 @@
12#include "core/hle/kernel/address_arbiter.h" 12#include "core/hle/kernel/address_arbiter.h"
13#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/mutex.h" 14#include "core/hle/kernel/mutex.h"
15#include "core/hle/kernel/semaphore.h"
15#include "core/hle/kernel/shared_memory.h" 16#include "core/hle/kernel/shared_memory.h"
16#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
17 18
@@ -31,7 +32,7 @@ enum ControlMemoryOperation {
31 32
32/// Map application or GSP heap memory 33/// Map application or GSP heap memory
33static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { 34static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
34 DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", 35 LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
35 operation, addr0, addr1, size, permissions); 36 operation, addr0, addr1, size, permissions);
36 37
37 switch (operation) { 38 switch (operation) {
@@ -43,19 +44,19 @@ static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1,
43 44
44 // Map GSP heap memory 45 // Map GSP heap memory
45 case MEMORY_OPERATION_GSP_HEAP: 46 case MEMORY_OPERATION_GSP_HEAP:
46 *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions); 47 *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions);
47 break; 48 break;
48 49
49 // Unknown ControlMemory operation 50 // Unknown ControlMemory operation
50 default: 51 default:
51 ERROR_LOG(SVC, "unknown operation=0x%08X", operation); 52 LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
52 } 53 }
53 return 0; 54 return 0;
54} 55}
55 56
56/// Maps a memory block to specified address 57/// Maps a memory block to specified address
57static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { 58static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
58 DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", 59 LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
59 handle, addr, permissions, other_permissions); 60 handle, addr, permissions, other_permissions);
60 61
61 Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); 62 Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
@@ -63,12 +64,16 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other
63 case Kernel::MemoryPermission::Read: 64 case Kernel::MemoryPermission::Read:
64 case Kernel::MemoryPermission::Write: 65 case Kernel::MemoryPermission::Write:
65 case Kernel::MemoryPermission::ReadWrite: 66 case Kernel::MemoryPermission::ReadWrite:
67 case Kernel::MemoryPermission::Execute:
68 case Kernel::MemoryPermission::ReadExecute:
69 case Kernel::MemoryPermission::WriteExecute:
70 case Kernel::MemoryPermission::ReadWriteExecute:
66 case Kernel::MemoryPermission::DontCare: 71 case Kernel::MemoryPermission::DontCare:
67 Kernel::MapSharedMemory(handle, addr, permissions_type, 72 Kernel::MapSharedMemory(handle, addr, permissions_type,
68 static_cast<Kernel::MemoryPermission>(other_permissions)); 73 static_cast<Kernel::MemoryPermission>(other_permissions));
69 break; 74 break;
70 default: 75 default:
71 ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions); 76 LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
72 } 77 }
73 return 0; 78 return 0;
74} 79}
@@ -77,7 +82,7 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other
77static Result ConnectToPort(Handle* out, const char* port_name) { 82static Result ConnectToPort(Handle* out, const char* port_name) {
78 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); 83 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
79 84
80 DEBUG_LOG(SVC, "called port_name=%s", port_name); 85 LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
81 _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); 86 _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
82 87
83 *out = service->GetHandle(); 88 *out = service->GetHandle();
@@ -87,17 +92,14 @@ static Result ConnectToPort(Handle* out, const char* port_name) {
87 92
88/// Synchronize to an OS service 93/// Synchronize to an OS service
89static Result SendSyncRequest(Handle handle) { 94static Result SendSyncRequest(Handle handle) {
90 // TODO(yuriks): ObjectPool::Get tries to check the Object type, which fails since this is a generic base Object, 95 Kernel::Session* session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
91 // so we are forced to use GetFast and manually verify the handle. 96 if (session == nullptr) {
92 if (!Kernel::g_object_pool.IsValid(handle)) {
93 return InvalidHandle(ErrorModule::Kernel).raw; 97 return InvalidHandle(ErrorModule::Kernel).raw;
94 } 98 }
95 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
96 99
97 _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!"); 100 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
98 DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
99 101
100 ResultVal<bool> wait = object->SyncRequest(); 102 ResultVal<bool> wait = session->SyncRequest();
101 if (wait.Succeeded() && *wait) { 103 if (wait.Succeeded() && *wait) {
102 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? 104 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
103 } 105 }
@@ -108,7 +110,7 @@ static Result SendSyncRequest(Handle handle) {
108/// Close a handle 110/// Close a handle
109static Result CloseHandle(Handle handle) { 111static Result CloseHandle(Handle handle) {
110 // ImplementMe 112 // ImplementMe
111 ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); 113 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
112 return 0; 114 return 0;
113} 115}
114 116
@@ -117,13 +119,11 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
117 // TODO(bunnei): Do something with nano_seconds, currently ignoring this 119 // TODO(bunnei): Do something with nano_seconds, currently ignoring this
118 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated 120 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
119 121
120 if (!Kernel::g_object_pool.IsValid(handle)) { 122 Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle);
123 if (object == nullptr)
121 return InvalidHandle(ErrorModule::Kernel).raw; 124 return InvalidHandle(ErrorModule::Kernel).raw;
122 }
123 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
124 _dbg_assert_(KERNEL, object != nullptr);
125 125
126 DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), 126 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
127 object->GetName().c_str(), nano_seconds); 127 object->GetName().c_str(), nano_seconds);
128 128
129 ResultVal<bool> wait = object->WaitSynchronization(); 129 ResultVal<bool> wait = object->WaitSynchronization();
@@ -143,17 +143,16 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
143 bool unlock_all = true; 143 bool unlock_all = true;
144 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated 144 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
145 145
146 DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", 146 LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
147 handle_count, (wait_all ? "true" : "false"), nano_seconds); 147 handle_count, (wait_all ? "true" : "false"), nano_seconds);
148 148
149 // Iterate through each handle, synchronize kernel object 149 // Iterate through each handle, synchronize kernel object
150 for (s32 i = 0; i < handle_count; i++) { 150 for (s32 i = 0; i < handle_count; i++) {
151 if (!Kernel::g_object_pool.IsValid(handles[i])) { 151 Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]);
152 if (object == nullptr)
152 return InvalidHandle(ErrorModule::Kernel).raw; 153 return InvalidHandle(ErrorModule::Kernel).raw;
153 }
154 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
155 154
156 DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), 155 LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
157 object->GetName().c_str()); 156 object->GetName().c_str());
158 157
159 // TODO(yuriks): Verify how the real function behaves when an error happens here 158 // TODO(yuriks): Verify how the real function behaves when an error happens here
@@ -181,7 +180,7 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
181 180
182/// Create an address arbiter (to allocate access to shared resources) 181/// Create an address arbiter (to allocate access to shared resources)
183static Result CreateAddressArbiter(u32* arbiter) { 182static Result CreateAddressArbiter(u32* arbiter) {
184 DEBUG_LOG(SVC, "called"); 183 LOG_TRACE(Kernel_SVC, "called");
185 Handle handle = Kernel::CreateAddressArbiter(); 184 Handle handle = Kernel::CreateAddressArbiter();
186 *arbiter = handle; 185 *arbiter = handle;
187 return 0; 186 return 0;
@@ -189,13 +188,15 @@ static Result CreateAddressArbiter(u32* arbiter) {
189 188
190/// Arbitrate address 189/// Arbitrate address
191static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { 190static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
191 LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
192 address, type, value);
192 return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), 193 return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
193 address, value).raw; 194 address, value).raw;
194} 195}
195 196
196/// Used to output a message on a debug hardware unit - does nothing on a retail unit 197/// Used to output a message on a debug hardware unit - does nothing on a retail unit
197static void OutputDebugString(const char* string) { 198static void OutputDebugString(const char* string) {
198 OS_LOG(SVC, "%s", string); 199 LOG_DEBUG(Debug_Emulated, "%s", string);
199} 200}
200 201
201/// Get resource limit 202/// Get resource limit
@@ -204,14 +205,14 @@ static Result GetResourceLimit(Handle* resource_limit, Handle process) {
204 // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for 205 // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
205 // the current KThread. 206 // the current KThread.
206 *resource_limit = 0xDEADBEEF; 207 *resource_limit = 0xDEADBEEF;
207 ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process); 208 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
208 return 0; 209 return 0;
209} 210}
210 211
211/// Get resource limit current values 212/// Get resource limit current values
212static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, 213static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
213 s32 name_count) { 214 s32 name_count) {
214 ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", 215 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
215 resource_limit, names, name_count); 216 resource_limit, names, name_count);
216 Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now 217 Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
217 return 0; 218 return 0;
@@ -232,7 +233,7 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top
232 233
233 Core::g_app_core->SetReg(1, thread); 234 Core::g_app_core->SetReg(1, thread);
234 235
235 DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " 236 LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
236 "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, 237 "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
237 name.c_str(), arg, stack_top, priority, processor_id, thread); 238 name.c_str(), arg, stack_top, priority, processor_id, thread);
238 239
@@ -243,7 +244,7 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top
243static u32 ExitThread() { 244static u32 ExitThread() {
244 Handle thread = Kernel::GetCurrentThreadHandle(); 245 Handle thread = Kernel::GetCurrentThreadHandle();
245 246
246 DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C 247 LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
247 248
248 Kernel::StopThread(thread, __func__); 249 Kernel::StopThread(thread, __func__);
249 HLE::Reschedule(__func__); 250 HLE::Reschedule(__func__);
@@ -267,70 +268,83 @@ static Result SetThreadPriority(Handle handle, s32 priority) {
267/// Create a mutex 268/// Create a mutex
268static Result CreateMutex(Handle* mutex, u32 initial_locked) { 269static Result CreateMutex(Handle* mutex, u32 initial_locked) {
269 *mutex = Kernel::CreateMutex((initial_locked != 0)); 270 *mutex = Kernel::CreateMutex((initial_locked != 0));
270 DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X", 271 LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
271 initial_locked ? "true" : "false", *mutex); 272 initial_locked ? "true" : "false", *mutex);
272 return 0; 273 return 0;
273} 274}
274 275
275/// Release a mutex 276/// Release a mutex
276static Result ReleaseMutex(Handle handle) { 277static Result ReleaseMutex(Handle handle) {
277 DEBUG_LOG(SVC, "called handle=0x%08X", handle); 278 LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
278 ResultCode res = Kernel::ReleaseMutex(handle); 279 ResultCode res = Kernel::ReleaseMutex(handle);
279 return res.raw; 280 return res.raw;
280} 281}
281 282
282/// Get current thread ID 283/// Get the ID for the specified thread.
283static Result GetThreadId(u32* thread_id, Handle thread) { 284static Result GetThreadId(u32* thread_id, Handle handle) {
284 ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread); 285 LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
285 return 0; 286 ResultCode result = Kernel::GetThreadId(thread_id, handle);
287 return result.raw;
288}
289
290/// Creates a semaphore
291static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
292 ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
293 LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
294 initial_count, max_count, *semaphore);
295 return res.raw;
296}
297
298/// Releases a certain number of slots in a semaphore
299static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
300 LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
301 ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
302 return res.raw;
286} 303}
287 304
288/// Query memory 305/// Query memory
289static Result QueryMemory(void* info, void* out, u32 addr) { 306static Result QueryMemory(void* info, void* out, u32 addr) {
290 ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); 307 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
291 return 0; 308 return 0;
292} 309}
293 310
294/// Create an event 311/// Create an event
295static Result CreateEvent(Handle* evt, u32 reset_type) { 312static Result CreateEvent(Handle* evt, u32 reset_type) {
296 *evt = Kernel::CreateEvent((ResetType)reset_type); 313 *evt = Kernel::CreateEvent((ResetType)reset_type);
297 DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X", 314 LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
298 reset_type, *evt); 315 reset_type, *evt);
299 return 0; 316 return 0;
300} 317}
301 318
302/// Duplicates a kernel handle 319/// Duplicates a kernel handle
303static Result DuplicateHandle(Handle* out, Handle handle) { 320static Result DuplicateHandle(Handle* out, Handle handle) {
304 DEBUG_LOG(SVC, "called handle=0x%08X", handle); 321 ResultVal<Handle> out_h = Kernel::g_handle_table.Duplicate(handle);
305 322 if (out_h.Succeeded()) {
306 // Translate kernel handles -> real handles 323 *out = *out_h;
307 if (handle == Kernel::CurrentThread) { 324 LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out);
308 handle = Kernel::GetCurrentThreadHandle();
309 } 325 }
310 _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess), 326 return out_h.Code().raw;
311 "(UNIMPLEMENTED) process handle duplication!");
312
313 // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
314 *out = handle;
315
316 return 0;
317} 327}
318 328
319/// Signals an event 329/// Signals an event
320static Result SignalEvent(Handle evt) { 330static Result SignalEvent(Handle evt) {
321 DEBUG_LOG(SVC, "called event=0x%08X", evt); 331 LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
322 return Kernel::SignalEvent(evt).raw; 332 return Kernel::SignalEvent(evt).raw;
323} 333}
324 334
325/// Clears an event 335/// Clears an event
326static Result ClearEvent(Handle evt) { 336static Result ClearEvent(Handle evt) {
327 DEBUG_LOG(SVC, "called event=0x%08X", evt); 337 LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
328 return Kernel::ClearEvent(evt).raw; 338 return Kernel::ClearEvent(evt).raw;
329} 339}
330 340
331/// Sleep the current thread 341/// Sleep the current thread
332static void SleepThread(s64 nanoseconds) { 342static void SleepThread(s64 nanoseconds) {
333 DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds); 343 LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
344
345 // Sleep current thread and check for next thread to schedule
346 Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
347 HLE::Reschedule(__func__);
334} 348}
335 349
336/// This returns the total CPU ticks elapsed since the CPU was powered-on 350/// This returns the total CPU ticks elapsed since the CPU was powered-on
@@ -360,8 +374,8 @@ const HLE::FunctionDef SVC_Table[] = {
360 {0x12, nullptr, "Run"}, 374 {0x12, nullptr, "Run"},
361 {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, 375 {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
362 {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, 376 {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
363 {0x15, nullptr, "CreateSemaphore"}, 377 {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
364 {0x16, nullptr, "ReleaseSemaphore"}, 378 {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
365 {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, 379 {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
366 {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, 380 {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
367 {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, 381 {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 6be393d0b..ad780818e 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once