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