diff options
Diffstat (limited to 'src/core/hle')
94 files changed, 2420 insertions, 1207 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index d8ba9e6cf..721a600b5 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.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" 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 3f73b5538..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" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -41,7 +43,15 @@ void CallSVC(u32 opcode) { void Reschedule(const char *reason) { _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; } @@ -56,6 +66,8 @@ void RegisterAllModules() { void Init() { Service::Init(); + Service::FS::ArchiveInit(); + Service::CFG::CFGInit(); RegisterAllModules(); @@ -63,6 +75,8 @@ void Init() { } void Shutdown() { + Service::CFG::CFGShutdown(); + Service::FS::ArchiveShutdown(); Service::Shutdown(); g_module_db.clear(); 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 9a921108d..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,8 +20,8 @@ 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) }; @@ -62,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 ddc09e13b..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null @@ -1,435 +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: - { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - CloseArchive(backend->GetIdCode()); - break; - } - // Unknown command... - default: - { - LOG_ERROR(Service_FS, "Unknown command=0x%08X", cmd); - return UnimplementedFunction(ErrorModule::FS); - } - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } -}; - -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]; - LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address); - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - - // Write to file... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - - case FileCommand::GetSize: - { - LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); - u64 size = backend->GetSize(); - cmd_buff[2] = (u32)size; - cmd_buff[3] = size >> 32; - break; - } - - case FileCommand::SetSize: - { - u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - LOG_TRACE(Service_FS, "SetSize %s %s size=%llu", - GetTypeName().c_str(), GetName().c_str(), size); - backend->SetSize(size); - break; - } - - case FileCommand::Close: - { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<File>(GetHandle()); - break; - } - - // 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 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)); - LOG_TRACE(Service_FS, "Read %s %s: count=%d", - GetTypeName().c_str(), GetName().c_str(), count); - - // Number of entries actually read - cmd_buff[2] = backend->Read(count, entries); - break; - } - - case DirectoryCommand::Close: - { - LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<Directory>(GetHandle()); - break; - } - - // Unknown command... - default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); - ResultCode error = UnimplementedFunction(ErrorModule::FS); - cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return error; - } - cmd_buff[1] = 0; // No error - return MakeResult<bool>(false); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -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()) { - LOG_ERROR(Service_FS, "Cannot close archive %d, does not exist!", (int)id_code); - return InvalidHandle(ErrorModule::FS); - } - - LOG_TRACE(Service_FS, "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()) { - LOG_ERROR(Service_FS, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); - return archive_handle.Code(); - } - g_archive_map[id_code] = archive->GetHandle(); - LOG_TRACE(Service_FS, "Mounted archive %s", archive->GetName().c_str()); - return RESULT_SUCCESS; -} - -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); -} - -ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); - if (archive->backend->DeleteFile(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); -} - -ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path) { - Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); - Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); - 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); - } - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::NothingHappened, ErrorLevel::Status); -} - -ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); - if (archive->backend->DeleteDirectory(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); -} - -ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); - if (archive->backend->CreateDirectory(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); -} - -ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path) { - Archive* src_archive = Kernel::g_object_pool.GetFast<Archive>(src_archive_handle); - Archive* dest_archive = Kernel::g_object_pool.GetFast<Archive>(dest_archive_handle); - 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); - } - 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(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); - - if (!directory->backend) { - return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Permanent); - } - - 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 - LOG_ERROR(Service_FS, "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/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 b38be0a49..e59ed1b57 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,5 +1,5 @@ -// 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> @@ -9,95 +9,109 @@ #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; } - LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use."); - return 0; -} + next_free_slot = generations[slot]; -bool ObjectPool::IsValid(Handle handle) const { - int index = handle - HANDLE_OFFSET; - if (index < 0) - return false; - if (index >= MAX_COUNT) - return false; + u16 generation = next_generation++; - return occupied[index]; + // 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; + + 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]) { - LOG_DEBUG(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() const { - return std::count(occupied.begin(), occupied.end(), true); + if (!IsValid(handle)) { + return nullptr; + } + return objects[GetSlot(handle)]; } -Object* ObjectPool::CreateByIDType(int type) { - LOG_ERROR(Kernel, "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 } /** @@ -106,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 85e3264b9..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,15 +58,6 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Synchronize kernel object. - * @return True if the current thread should wait as a result of the sync - */ - virtual ResultVal<bool> SyncRequest() { - 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 */ @@ -69,100 +65,138 @@ public: LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); return UnimplementedFunction(ErrorModule::Kernel); } + +private: + friend void intrusive_ptr_add_ref(Object*); + friend void intrusive_ptr_release(Object*); + + unsigned int ref_count = 0; }; -class ObjectPool : NonCopyable { +// 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; +} + +inline void intrusive_ptr_release(Object* object) { + if (--object->ref_count == 0) { + delete object; + } +} + +/** + * 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: - ObjectPool(); - ~ObjectPool() {} + HandleTable(); - // 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); + /** + * 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); - static Object* CreateByIDType(int type); + /** + * 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> - void Destroy(Handle handle) { - if (Get<T>(handle)) { - occupied[handle - HANDLE_OFFSET] = false; - delete pool[handle - HANDLE_OFFSET]; - } - } + /** + * 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); + /// Checks if a handle is valid and points to an existing object. bool IsValid(Handle handle) const; - template <class T> - T* Get(Handle handle) { - if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { - if (handle != 0) { - LOG_ERROR(Kernel, "Bad object handle %08x", handle); - } - return nullptr; - } else { - Object* t = pool[handle - HANDLE_OFFSET]; - if (t->GetHandleType() != T::GetStaticHandleType()) { - LOG_ERROR(Kernel, "Wrong object type for %08x", handle); - return nullptr; - } - return static_cast<T*>(t); - } - } + /** + * Looks up a handle. + * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. + */ + Object* GetGeneric(Handle handle) const; - // ONLY use this when you know the handle is valid. + /** + * 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 *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]); - } - - 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; - } + T* Get(Handle handle) const { + Object* object = GetGeneric(handle); + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return static_cast<T*>(object); } + return nullptr; } - bool GetIDType(Handle handle, HandleType* type) const { - if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || - !occupied[handle - HANDLE_OFFSET]) { - LOG_ERROR(Kernel, "Bad object handle %08X", handle); - return false; - } - Object* t = pool[handle - HANDLE_OFFSET]; - *type = t->GetHandleType(); - return true; - } - - Object* &operator [](Handle handle); - void List(); + /// Closes all handles held in this table. void Clear(); - int GetCount() const; 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 5a173e129..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 @@ -87,7 +87,7 @@ void ReleaseThreadMutexes(Handle thread) { // Release every mutex that the thread holds, and resume execution on the waiting threads for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second); + Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); ResumeWaitingThread(mutex); } @@ -115,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)) { @@ -136,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; diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 7f4909a6e..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 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 3c8c502c6..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,8 +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; } + 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) @@ -32,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; } @@ -60,7 +61,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions 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; @@ -71,7 +72,7 @@ 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) 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 1c04701de..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> @@ -26,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; } @@ -83,8 +83,7 @@ static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup static u32 next_thread_id; ///< The next available thread id -/// Gets the current thread -inline Thread* GetCurrentThread() { +Thread* GetCurrentThread() { return current_thread; } @@ -148,21 +147,24 @@ void ChangeReadyState(Thread* t, bool ready) { } } -/// Verify that a thread has not been released from waiting -static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { - _dbg_assert_(Kernel, thread != nullptr); - return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); +/// 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()); } -/// Verify that a thread has not been released from waiting (with wait address) -static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { - _dbg_assert_(Kernel, thread != nullptr); - return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); +/// 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 @@ -171,10 +173,10 @@ ResultCode StopThread(Handle handle, const char* reason) { 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(); @@ -208,9 +210,9 @@ 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); - if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) + if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) continue; if (thread == nullptr) @@ -233,9 +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); - if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) + if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) ResumeThreadFromWait(handle); } } @@ -286,7 +288,7 @@ Thread* NextThread() { if (next == 0) { return nullptr; } - return Kernel::g_object_pool.Get<Thread>(next); + return Kernel::g_handle_table.Get<Thread>(next); } void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { @@ -303,9 +305,11 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_addres /// 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); } @@ -337,7 +341,8 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio 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); @@ -394,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); @@ -406,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); } @@ -469,23 +474,31 @@ void Reschedule() { Thread* prev = GetCurrentThread(); Thread* next = NextThread(); HLE::g_reschedule = false; - if (next > 0) { - LOG_TRACE(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_object_pool.Get<Thread>(handle); + Thread* thread = g_handle_table.Get<Thread>(handle); if (thread == nullptr) return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index be7adface..0e1397cd9 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.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 @@ -40,7 +40,6 @@ enum WaitType { WAITTYPE_SEMA, WAITTYPE_EVENT, WAITTYPE_THREADEND, - WAITTYPE_VBLANK, WAITTYPE_MUTEX, WAITTYPE_SYNCH, WAITTYPE_ARB, 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 4130feb9d..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" @@ -18,7 +18,7 @@ namespace AC_U { * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. */ void GetWifiStatus(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. @@ -56,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 b6d5d101f..d8b261ba7 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_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. @@ -40,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 @@ -57,7 +57,7 @@ void Initialize(Service::Interface* self) { } 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) { @@ -78,14 +78,14 @@ void GetLockHandle(Service::Interface* self) { } 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 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 @@ -112,7 +112,7 @@ void InquireNotification(Service::Interface* self) { * 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 @@ -143,7 +143,7 @@ void ReceiveParameter(Service::Interface* self) { * 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]; @@ -170,7 +170,7 @@ void GlanceParameter(Service::Interface* self) { * 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]; @@ -196,7 +196,7 @@ void AppletUtility(Service::Interface* self) { void GetSharedFont(Service::Interface* self) { LOG_TRACE(Kernel_SVC, "called"); - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); if (!shared_font.empty()) { // TODO(bunnei): This function shouldn't copy the shared font every time it's called. @@ -315,8 +315,8 @@ Interface::Interface() { if (file.IsOpen()) { // Read shared font data - shared_font.resize(file.GetSize()); - file.ReadBytes(shared_font.data(), file.GetSize()); + 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"); @@ -330,7 +330,4 @@ Interface::Interface() { Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } -Interface::~Interface() { -} - } // 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_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 972cc0534..03c01cf90 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -1,10 +1,14 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// 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_u.h" +#include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/cfg/cfg_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace CFG_U @@ -52,7 +56,7 @@ static const std::array<u16, 187> country_codes = { * 2 : Country's 2-char string */ static void GetCountryCodeString(Service::Interface* self) { - u32* cmd_buffer = Service::GetCommandBuffer(); + 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]) { @@ -74,7 +78,7 @@ static void GetCountryCodeString(Service::Interface* self) { * 2 : Country Code ID */ static void GetCountryCodeID(Service::Interface* self) { - u32* cmd_buffer = Service::GetCommandBuffer(); + u32* cmd_buffer = Kernel::GetCommandBuffer(); u16 country_code = cmd_buffer[1]; u16 country_code_id = 0; @@ -99,18 +103,85 @@ static void GetCountryCodeID(Service::Interface* self) { 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, nullptr, "GetConfigInfoBlk2"}, + {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, {0x00020000, nullptr, "SecureInfoGetRegion"}, {0x00030000, nullptr, "GenHashConsoleUnique"}, {0x00040000, nullptr, "GetRegionCanadaUSA"}, - {0x00050000, nullptr, "GetSystemModel"}, - {0x00060000, nullptr, "GetModelNintendo2DS"}, + {0x00050000, GetSystemModel, "GetSystemModel"}, + {0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"}, {0x00070040, nullptr, "unknown"}, {0x00080080, nullptr, "unknown"}, {0x00090040, GetCountryCodeString, "GetCountryCodeString"}, {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, }; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class @@ -118,7 +189,4 @@ Interface::Interface() { Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } -Interface::~Interface() { -} - } // 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/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 ce1c9938d..2cf4d118f 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.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,7 @@ static Handle interrupt_event; * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) */ void ConvertProcessAddressFromDspDram(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); u32 addr = cmd_buff[1]; @@ -48,7 +48,7 @@ void ConvertProcessAddressFromDspDram(Service::Interface* self) { * 2 : Component loaded, 0 on not loaded, 1 on loaded */ void LoadComponent(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; // No error cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware @@ -65,7 +65,7 @@ void LoadComponent(Service::Interface* self) { * 3 : Semaphore event handle */ void GetSemaphoreEventHandle(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; // No error cmd_buff[3] = semaphore_event; // Event handle @@ -83,7 +83,7 @@ void GetSemaphoreEventHandle(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ void RegisterInterruptEvents(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); interrupt_event = static_cast<Handle>(cmd_buff[4]); @@ -100,7 +100,7 @@ void RegisterInterruptEvents(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ void WriteReg0x10(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); Kernel::SignalEvent(interrupt_event); @@ -121,7 +121,7 @@ void WriteReg0x10(Service::Interface* self) { * 2 : Number of bytes read from pipe */ void ReadPipeIfPossible(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size VAddr addr = cmd_buff[0x41]; @@ -190,7 +190,4 @@ Interface::Interface() { Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } -Interface::~Interface() { -} - } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 9431b62f6..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,11 +14,7 @@ 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"; } 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/kernel/archive.h b/src/core/hle/service/fs/archive.h index b50833a2b..b39bc41b6 100644 --- a/src/core/hle/kernel/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -1,39 +1,50 @@ // Copyright 2014 Citra Emulator 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/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace +namespace Service { +namespace FS { -namespace Kernel { +/// Supported archive types +enum class ArchiveIdCode : u32 { + RomFS = 0x00000003, + SaveData = 0x00000004, + ExtSaveData = 0x00000006, + SharedExtSaveData = 0x00000007, + SystemSaveData = 0x00000008, + SDMC = 0x00000009, + SDMCWriteOnly = 0x0000000A, +}; + +typedef u64 ArchiveHandle; /** * Opens an archive * @param id_code IdCode of the archive to open * @return Handle to the opened archive */ -ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code); +ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code); /** * Closes an archive * @param id_code IdCode of the archive to open */ -ResultCode CloseArchive(FileSys::Archive::IdCode id_code); +ResultCode CloseArchive(ArchiveHandle handle); /** * Creates an Archive * @param backend File system backend interface to the archive - * @param name Name of Archive + * @param id_code Id code used to access this type of archive */ -ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); +ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code); /** * Open a File from an Archive @@ -42,7 +53,7 @@ ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name); * @param mode Mode under which to open the File * @return Handle to the opened File object */ -ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); +ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); /** * Delete a File from an Archive @@ -50,7 +61,7 @@ ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path * @param path Path to the File inside of the Archive * @return Whether deletion succeeded */ -ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /** * Rename a File between two Archives @@ -60,8 +71,8 @@ ResultCode DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& pat * @param dest_path Path to the File inside of the destination Archive * @return Whether rename succeeded */ -ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path); +ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); /** * Delete a Directory from an Archive @@ -69,7 +80,16 @@ ResultCode RenameFileBetweenArchives(Handle src_archive_handle, const FileSys::P * @param path Path to the Directory inside of the Archive * @return Whether deletion succeeded */ -ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); + +/** + * Create a 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 @@ -77,7 +97,7 @@ ResultCode DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path * @param path Path to the Directory inside of the Archive * @return Whether creation of directory succeeded */ -ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); /** * Rename a Directory between two Archives @@ -87,8 +107,8 @@ ResultCode CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path * @param dest_path Path to the Directory inside of the destination Archive * @return Whether rename succeeded */ -ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileSys::Path& src_path, - Handle dest_archive_handle, const FileSys::Path& dest_path); +ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path, + ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path); /** * Open a Directory from an Archive @@ -96,7 +116,13 @@ ResultCode RenameDirectoryBetweenArchives(Handle src_archive_handle, const FileS * @param path Path to the Directory inside of the Archive * @return Handle to the opened File object */ -ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path); +ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); + +/** + * Creates a blank SaveData archive. + * @return ResultCode 0 on success or the corresponding code on error + */ +ResultCode FormatSaveData(); /// Initialize archives void ArchiveInit(); @@ -104,4 +130,5 @@ void ArchiveInit(); /// Shutdown archives void ArchiveShutdown(); -} // namespace FileSys +} // namespace FS +} // namespace Service diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 9bda4fe8a..b1a465274 100644 --- a/src/core/hle/service/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -1,23 +1,28 @@ // 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 "common/scope_exit.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/hle/service/fs/archive.h" +#include "core/hle/service/fs/fs_user.h" #include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User -namespace FS_User { +namespace Service { +namespace FS { + +static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) { + return (u64)low_word | ((u64)high_word << 32); +} static void Initialize(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + 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 @@ -43,11 +48,9 @@ static void Initialize(Service::Interface* self) { * 3 : File handle */ static void OpenFile(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 filename_size = cmd_buff[5]; FileSys::Mode mode; mode.hex = cmd_buff[6]; @@ -57,11 +60,12 @@ static void OpenFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); - ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode); + ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; } else { + cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } @@ -86,9 +90,9 @@ static void OpenFile(Service::Interface* self) { * 3 : File handle */ static void OpenFileDirectly(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); - auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]); + auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]); auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); u32 archivename_size = cmd_buff[4]; auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]); @@ -106,25 +110,25 @@ static void OpenFileDirectly(Service::Interface* self) { if (archive_path.GetType() != FileSys::Empty) { LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; + cmd_buff[3] = 0; return; } - // TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it - // TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here? - ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id); - cmd_buff[1] = archive_handle.Code().raw; + ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id); if (archive_handle.Failed()) { LOG_ERROR(Service_FS, "failed to get a handle for archive"); + cmd_buff[1] = archive_handle.Code().raw; + cmd_buff[3] = 0; return; } - // cmd_buff[2] isn't used according to 3dmoo's implementation. - cmd_buff[3] = *archive_handle; + SCOPE_EXIT({ CloseArchive(*archive_handle); }); - ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode); + ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; } else { + cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } @@ -140,12 +144,10 @@ static void OpenFileDirectly(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void DeleteFile(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); +static void DeleteFile(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 filename_size = cmd_buff[5]; u32 filename_ptr = cmd_buff[7]; @@ -155,7 +157,7 @@ void DeleteFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::DeleteFileFromArchive(archive_handle, file_path).raw; + cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw; } /* @@ -174,15 +176,13 @@ void DeleteFile(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void RenameFile(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); +static void RenameFile(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 src_filename_size = cmd_buff[5]; - Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); + ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);; auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); u32 dest_filename_size = cmd_buff[9]; u32 src_filename_ptr = cmd_buff[11]; @@ -195,7 +195,7 @@ void RenameFile(Service::Interface* self) { src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(), dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; + cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw; } /* @@ -209,12 +209,10 @@ void RenameFile(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void DeleteDirectory(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); +static void DeleteDirectory(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 dirname_size = cmd_buff[5]; u32 dirname_ptr = cmd_buff[7]; @@ -224,7 +222,36 @@ void DeleteDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::DeleteDirectoryFromArchive(archive_handle, dir_path).raw; + cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw; +} + +/* + * 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; } /* @@ -239,11 +266,9 @@ void DeleteDirectory(Service::Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void CreateDirectory(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 dirname_size = cmd_buff[5]; u32 dirname_ptr = cmd_buff[8]; @@ -252,7 +277,7 @@ static void CreateDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_path).raw; + cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw; } /* @@ -271,15 +296,13 @@ static void CreateDirectory(Service::Interface* self) { * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -void RenameDirectory(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); +static void RenameDirectory(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2] and cmd_buff[6], aka archive handle lower word, aren't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle src_archive_handle = static_cast<Handle>(cmd_buff[3]); + ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); u32 src_dirname_size = cmd_buff[5]; - Handle dest_archive_handle = static_cast<Handle>(cmd_buff[7]); + ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]); auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]); u32 dest_dirname_size = cmd_buff[9]; u32 src_dirname_ptr = cmd_buff[11]; @@ -292,15 +315,26 @@ void RenameDirectory(Service::Interface* self) { src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(), dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str()); - cmd_buff[1] = Kernel::RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; + cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw; } +/** + * FS_User::OpenDirectory service function + * Inputs: + * 1 : Archive handle low word + * 2 : Archive handle high word + * 3 : Low path type + * 4 : Low path size + * 7 : (LowPathSize << 14) | 2 + * 8 : Low path data pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : Directory handle + */ static void OpenDirectory(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to - // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. - Handle archive_handle = static_cast<Handle>(cmd_buff[2]); + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]); u32 dirname_size = cmd_buff[4]; u32 dirname_ptr = cmd_buff[6]; @@ -309,7 +343,7 @@ static void OpenDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path); + ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[3] = *handle; @@ -332,9 +366,9 @@ static void OpenDirectory(Service::Interface* self) { * 3 : Archive handle upper word (same as file handle) */ static void OpenArchive(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); - auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]); + auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); u32 archivename_size = cmd_buff[3]; u32 archivename_ptr = cmd_buff[5]; @@ -348,16 +382,34 @@ static void OpenArchive(Service::Interface* self) { return; } - ResultVal<Handle> handle = Kernel::OpenArchive(archive_id); + ResultVal<ArchiveHandle> handle = OpenArchive(archive_id); cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { - // cmd_buff[2] isn't used according to 3dmoo's implementation. - cmd_buff[3] = *handle; + cmd_buff[2] = *handle & 0xFFFFFFFF; + cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF; } else { + cmd_buff[2] = cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for archive"); } } +/** + * FS_User::CloseArchive service function + * Inputs: + * 0 : 0x080E0080 + * 1 : Archive handle low word + * 2 : Archive handle high word + * Outputs: + * 0 : ??? TODO(yuriks): Verify return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CloseArchive(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); + cmd_buff[1] = CloseArchive(archive_handle).raw; +} + /* * FS_User::IsSdmcDetected service function * Outputs: @@ -365,7 +417,7 @@ static void OpenArchive(Service::Interface* self) { * 2 : Whether the Sdmc could be detected */ static void IsSdmcDetected(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; @@ -373,7 +425,83 @@ static void IsSdmcDetected(Service::Interface* self) { LOG_DEBUG(Service_FS, "called"); } -const Interface::FunctionInfo FunctionTable[] = { +/** + * 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"}, @@ -383,14 +511,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08050244, RenameFile, "RenameFile"}, {0x08060142, DeleteDirectory, "DeleteDirectory"}, {0x08070142, nullptr, "DeleteDirectoryRecursively"}, - {0x08080202, nullptr, "CreateFile"}, + {0x08080202, CreateFile, "CreateFile"}, {0x08090182, CreateDirectory, "CreateDirectory"}, {0x080A0244, RenameDirectory, "RenameDirectory"}, {0x080B0102, OpenDirectory, "OpenDirectory"}, {0x080C00C2, OpenArchive, "OpenArchive"}, {0x080D0144, nullptr, "ControlArchive"}, - {0x080E0080, nullptr, "CloseArchive"}, - {0x080F0180, nullptr, "FormatThisUserSaveData"}, + {0x080E0080, CloseArchive, "CloseArchive"}, + {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, {0x08100200, nullptr, "CreateSystemSaveData"}, {0x08110040, nullptr, "DeleteSystemSaveData"}, {0x08120080, nullptr, "GetFreeBytes"}, @@ -399,7 +527,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08150000, nullptr, "GetNandArchiveResource"}, {0x08160000, nullptr, "GetSdmcFatfsErro"}, {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, - {0x08180000, nullptr, "IsSdmcWritable"}, + {0x08180000, IsSdmcWriteable, "IsSdmcWritable"}, {0x08190042, nullptr, "GetSdmcCid"}, {0x081A0042, nullptr, "GetNandCid"}, {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, @@ -451,7 +579,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08490040, nullptr, "GetArchiveResource"}, {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, - {0x084C0242, nullptr, "FormatSaveData"}, + {0x084C0242, FormatSaveData, "FormatSaveData"}, {0x084D0102, nullptr, "GetLegacySubBannerData"}, {0x084E0342, nullptr, "UpdateSha256Context"}, {0x084F0102, nullptr, "ReadSpecialFile"}, @@ -465,11 +593,9 @@ const Interface::FunctionInfo FunctionTable[] = { //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class -Interface::Interface() { +FSUserInterface::FSUserInterface() { Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } -Interface::~Interface() { -} - -} // namespace +} // 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/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 223800560..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. @@ -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,7 +83,7 @@ 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]; @@ -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,7 +179,7 @@ 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"); @@ -323,7 +347,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { } } - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; // No error } @@ -335,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"}, @@ -372,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 5772199d4..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" @@ -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; @@ -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 8f53befdb..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 @@ -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 559f148dd..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" @@ -34,7 +34,7 @@ static bool battery_is_charging = true; * 2 : Output of function, 0 = not charging, 1 = charging. */ static void GetAdapterState(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. @@ -52,7 +52,7 @@ static void GetAdapterState(Service::Interface* self) { * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0) */ static void GetShellState(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; cmd_buff[2] = shell_open ? 1 : 0; @@ -68,7 +68,7 @@ static void GetShellState(Service::Interface* self) { * 3 = half full battery, 2 = low battery, 1 = critical battery. */ static void GetBatteryLevel(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. @@ -86,7 +86,7 @@ static void GetBatteryLevel(Service::Interface* self) { * 2 : Output of function, 0 = not charging, 1 = charging. */ static void GetBatteryChargeState(Service::Interface* self) { - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. @@ -122,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 e6973572b..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,22 +93,30 @@ 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); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index baae910a1..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 @@ -14,6 +14,7 @@ #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/session.h" #include "core/hle/svc.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -21,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*); @@ -64,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; } @@ -72,12 +63,12 @@ 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() || itr->second.func == nullptr) { 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 24a846533..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" @@ -16,7 +16,7 @@ static Handle g_event_handle = 0; static void Initialize(Service::Interface* self) { LOG_DEBUG(Service_SRV, "called"); - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; // No error } @@ -24,7 +24,7 @@ static void Initialize(Service::Interface* self) { static void GetProcSemaphore(Service::Interface* self) { LOG_TRACE(Service_SRV, "called"); - u32* cmd_buff = Service::GetCommandBuffer(); + u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(bunnei): Change to a semaphore once these have been implemented g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); @@ -36,7 +36,7 @@ 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); @@ -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 db0c42e74..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" @@ -63,6 +64,10 @@ 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)); @@ -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!"); - LOG_TRACE(Kernel_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? } @@ -117,11 +119,9 @@ 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); LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); @@ -148,10 +148,9 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, // 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]); LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), object->GetName().c_str()); @@ -288,6 +287,21 @@ static Result GetThreadId(u32* thread_id, Handle 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) { LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); @@ -304,19 +318,12 @@ static Result CreateEvent(Handle* evt, u32 reset_type) { /// Duplicates a kernel handle static Result DuplicateHandle(Handle* out, Handle handle) { - LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle); - - // Translate kernel handles -> real handles - if (handle == Kernel::CurrentThread) { - 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 @@ -335,7 +342,8 @@ static Result ClearEvent(Handle evt) { static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); - // Check for next thread to schedule + // Sleep current thread and check for next thread to schedule + Kernel::WaitCurrentThread(WAITTYPE_SLEEP); HLE::Reschedule(__func__); } @@ -366,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 |
