mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-27 01:22:37 -06:00
commit
bb730855e5
24 changed files with 697 additions and 428 deletions
|
@ -133,6 +133,7 @@ set(HEADERS
|
||||||
hle/service/srv.h
|
hle/service/srv.h
|
||||||
hle/service/ssl_c.h
|
hle/service/ssl_c.h
|
||||||
hle/config_mem.h
|
hle/config_mem.h
|
||||||
|
hle/result.h
|
||||||
hle/function_wrappers.h
|
hle/function_wrappers.h
|
||||||
hle/hle.h
|
hle/hle.h
|
||||||
hle/svc.h
|
hle/svc.h
|
||||||
|
|
|
@ -799,22 +799,24 @@ pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "tb.h"
|
#include "tb.h"
|
||||||
*/
|
*/
|
||||||
#define EQ 0
|
enum ConditionCode {
|
||||||
#define NE 1
|
EQ = 0,
|
||||||
#define CS 2
|
NE = 1,
|
||||||
#define CC 3
|
CS = 2,
|
||||||
#define MI 4
|
CC = 3,
|
||||||
#define PL 5
|
MI = 4,
|
||||||
#define VS 6
|
PL = 5,
|
||||||
#define VC 7
|
VS = 6,
|
||||||
#define HI 8
|
VC = 7,
|
||||||
#define LS 9
|
HI = 8,
|
||||||
#define GE 10
|
LS = 9,
|
||||||
#define LT 11
|
GE = 10,
|
||||||
#define GT 12
|
LT = 11,
|
||||||
#define LE 13
|
GT = 12,
|
||||||
#define AL 14
|
LE = 13,
|
||||||
#define NV 15
|
AL = 14,
|
||||||
|
NV = 15,
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef NFLAG
|
#ifndef NFLAG
|
||||||
#define NFLAG state->NFlag
|
#define NFLAG state->NFlag
|
||||||
|
|
|
@ -25,24 +25,6 @@
|
||||||
|
|
||||||
#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
|
#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
|
||||||
|
|
||||||
/* Condition code values. */
|
|
||||||
#define EQ 0
|
|
||||||
#define NE 1
|
|
||||||
#define CS 2
|
|
||||||
#define CC 3
|
|
||||||
#define MI 4
|
|
||||||
#define PL 5
|
|
||||||
#define VS 6
|
|
||||||
#define VC 7
|
|
||||||
#define HI 8
|
|
||||||
#define LS 9
|
|
||||||
#define GE 10
|
|
||||||
#define LT 11
|
|
||||||
#define GT 12
|
|
||||||
#define LE 13
|
|
||||||
#define AL 14
|
|
||||||
#define NV 15
|
|
||||||
|
|
||||||
/* Shift Opcodes. */
|
/* Shift Opcodes. */
|
||||||
#define LSL 0
|
#define LSL 0
|
||||||
#define LSR 1
|
#define LSR 1
|
||||||
|
|
|
@ -25,22 +25,17 @@ public:
|
||||||
|
|
||||||
std::string name; ///< Name of address arbiter object (optional)
|
std::string name; ///< Name of address arbiter object (optional)
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::OS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/// Arbitrate an address
|
/// Arbitrate an address
|
||||||
Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
|
ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
// Signal thread(s) waiting for arbitrate address...
|
// Signal thread(s) waiting for arbitrate address...
|
||||||
|
@ -65,9 +60,9 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(KERNEL, "unknown type=%d", type);
|
ERROR_LOG(KERNEL, "unknown type=%d", type);
|
||||||
return -1;
|
return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
|
||||||
}
|
}
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an address arbiter
|
/// Create an address arbiter
|
||||||
|
|
|
@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Arbitrate an address
|
/// Arbitrate an address
|
||||||
Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
|
ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
|
||||||
|
|
||||||
/// Create an address arbiter
|
/// Create an address arbiter
|
||||||
Handle CreateAddressArbiter(const std::string& name = "Unknown");
|
Handle CreateAddressArbiter(const std::string& name = "Unknown");
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
#include "core/file_sys/archive.h"
|
#include "core/file_sys/archive.h"
|
||||||
#include "core/file_sys/archive_sdmc.h"
|
#include "core/file_sys/archive_sdmc.h"
|
||||||
#include "core/file_sys/directory.h"
|
#include "core/file_sys/directory.h"
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
#include "core/hle/kernel/archive.h"
|
#include "core/hle/kernel/archive.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Kernel namespace
|
// Kernel namespace
|
||||||
|
@ -51,12 +52,7 @@ public:
|
||||||
std::string name; ///< Name of archive (optional)
|
std::string name; ///< Name of archive (optional)
|
||||||
FileSys::Archive* backend; ///< Archive backend interface
|
FileSys::Archive* backend; ///< Archive backend interface
|
||||||
|
|
||||||
/**
|
ResultVal<bool> SyncRequest() override {
|
||||||
* Synchronize kernel object
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result SyncRequest(bool* wait) override {
|
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
|
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
|
||||||
|
|
||||||
|
@ -106,22 +102,17 @@ public:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
||||||
return -1;
|
return UnimplementedFunction(ErrorModule::FS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd_buff[1] = 0; // No error
|
cmd_buff[1] = 0; // No error
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::FS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,12 +127,7 @@ public:
|
||||||
FileSys::Path path; ///< Path of the file
|
FileSys::Path path; ///< Path of the file
|
||||||
std::unique_ptr<FileSys::File> backend; ///< File backend interface
|
std::unique_ptr<FileSys::File> backend; ///< File backend interface
|
||||||
|
|
||||||
/**
|
ResultVal<bool> SyncRequest() override {
|
||||||
* Synchronize kernel object
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result SyncRequest(bool* wait) override {
|
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
|
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
@ -183,7 +169,8 @@ public:
|
||||||
case FileCommand::SetSize:
|
case FileCommand::SetSize:
|
||||||
{
|
{
|
||||||
u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
|
u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
|
||||||
DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size);
|
DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu",
|
||||||
|
GetTypeName().c_str(), GetName().c_str(), size);
|
||||||
backend->SetSize(size);
|
backend->SetSize(size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -198,22 +185,18 @@ public:
|
||||||
// Unknown command...
|
// Unknown command...
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
||||||
cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
|
ResultCode error = UnimplementedFunction(ErrorModule::FS);
|
||||||
return -1;
|
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
cmd_buff[1] = 0; // No error
|
cmd_buff[1] = 0; // No error
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::FS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -228,12 +211,7 @@ public:
|
||||||
FileSys::Path path; ///< Path of the directory
|
FileSys::Path path; ///< Path of the directory
|
||||||
std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
|
std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
|
||||||
|
|
||||||
/**
|
ResultVal<bool> SyncRequest() override {
|
||||||
* Synchronize kernel object
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result SyncRequest(bool* wait) override {
|
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
|
DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
@ -243,8 +221,9 @@ public:
|
||||||
{
|
{
|
||||||
u32 count = cmd_buff[1];
|
u32 count = cmd_buff[1];
|
||||||
u32 address = cmd_buff[3];
|
u32 address = cmd_buff[3];
|
||||||
FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
|
auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
|
||||||
DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
|
DEBUG_LOG(KERNEL, "Read %s %s: count=%d",
|
||||||
|
GetTypeName().c_str(), GetName().c_str(), count);
|
||||||
|
|
||||||
// Number of entries actually read
|
// Number of entries actually read
|
||||||
cmd_buff[2] = backend->Read(count, entries);
|
cmd_buff[2] = backend->Read(count, entries);
|
||||||
|
@ -261,22 +240,18 @@ public:
|
||||||
// Unknown command...
|
// Unknown command...
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
|
||||||
cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
|
ResultCode error = UnimplementedFunction(ErrorModule::FS);
|
||||||
return -1;
|
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
cmd_buff[1] = 0; // No error
|
cmd_buff[1] = 0; // No error
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::FS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -284,89 +259,58 @@ public:
|
||||||
|
|
||||||
std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
|
std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
|
||||||
|
|
||||||
/**
|
ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) {
|
||||||
* Opens an archive
|
|
||||||
* @param id_code IdCode of the archive to open
|
|
||||||
* @return Handle to archive if it exists, otherwise a null handle (0)
|
|
||||||
*/
|
|
||||||
Handle OpenArchive(FileSys::Archive::IdCode id_code) {
|
|
||||||
auto itr = g_archive_map.find(id_code);
|
auto itr = g_archive_map.find(id_code);
|
||||||
if (itr == g_archive_map.end()) {
|
if (itr == g_archive_map.end()) {
|
||||||
return 0;
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
||||||
|
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
return itr->second;
|
|
||||||
|
return MakeResult<Handle>(itr->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultCode CloseArchive(FileSys::Archive::IdCode id_code) {
|
||||||
* Closes an archive
|
|
||||||
* @param id_code IdCode of the archive to open
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result CloseArchive(FileSys::Archive::IdCode id_code) {
|
|
||||||
auto itr = g_archive_map.find(id_code);
|
auto itr = g_archive_map.find(id_code);
|
||||||
if (itr == g_archive_map.end()) {
|
if (itr == g_archive_map.end()) {
|
||||||
ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code);
|
ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code);
|
||||||
return -1;
|
return InvalidHandle(ErrorModule::FS);
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
|
INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mounts an archive
|
* Mounts an archive
|
||||||
* @param archive Pointer to the archive to mount
|
* @param archive Pointer to the archive to mount
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result MountArchive(Archive* archive) {
|
ResultCode MountArchive(Archive* archive) {
|
||||||
FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
|
FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
|
||||||
if (0 != OpenArchive(id_code)) {
|
ResultVal<Handle> archive_handle = OpenArchive(id_code);
|
||||||
|
if (archive_handle.Succeeded()) {
|
||||||
ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
|
ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
|
||||||
return -1;
|
return archive_handle.Code();
|
||||||
}
|
}
|
||||||
g_archive_map[id_code] = archive->GetHandle();
|
g_archive_map[id_code] = archive->GetHandle();
|
||||||
INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
|
INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) {
|
||||||
* Creates an Archive
|
|
||||||
* @param handle Handle to newly created archive object
|
|
||||||
* @param backend File system backend interface to the archive
|
|
||||||
* @param name Optional name of Archive
|
|
||||||
* @return Newly created Archive object
|
|
||||||
*/
|
|
||||||
Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
|
|
||||||
Archive* archive = new Archive;
|
Archive* archive = new Archive;
|
||||||
handle = Kernel::g_object_pool.Create(archive);
|
Handle handle = Kernel::g_object_pool.Create(archive);
|
||||||
archive->name = name;
|
archive->name = name;
|
||||||
archive->backend = backend;
|
archive->backend = backend;
|
||||||
|
|
||||||
MountArchive(archive);
|
ResultCode result = MountArchive(archive);
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return archive;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
|
||||||
* Creates an Archive
|
|
||||||
* @param backend File system backend interface to the archive
|
|
||||||
* @param name Optional name of Archive
|
|
||||||
* @return Handle to newly created Archive object
|
|
||||||
*/
|
|
||||||
Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
|
|
||||||
Handle handle;
|
|
||||||
CreateArchive(handle, backend, name);
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a File from an Archive
|
|
||||||
* @param archive_handle Handle to an open Archive object
|
|
||||||
* @param path Path to the File inside of the Archive
|
|
||||||
* @param mode Mode under which to open the File
|
|
||||||
* @return Opened File object
|
|
||||||
*/
|
|
||||||
Handle OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
|
|
||||||
// TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create
|
// 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.
|
// 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.
|
// Archives file handles are just reused and not actually freed until emulation shut down.
|
||||||
|
@ -376,19 +320,24 @@ Handle OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, con
|
||||||
// design. While the functionally of this is OK, our implementation decision to separate
|
// design. While the functionally of this is OK, our implementation decision to separate
|
||||||
// normal files from archive file pointers is very likely wrong.
|
// normal files from archive file pointers is very likely wrong.
|
||||||
// See https://github.com/citra-emu/citra/issues/205
|
// See https://github.com/citra-emu/citra/issues/205
|
||||||
return archive_handle;
|
return MakeResult<Handle>(archive_handle);
|
||||||
|
|
||||||
File* file = new File;
|
File* file = new File;
|
||||||
Handle handle = Kernel::g_object_pool.Create(file);
|
Handle handle = Kernel::g_object_pool.Create(file);
|
||||||
|
|
||||||
Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
|
Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
|
||||||
|
if (archive == nullptr) {
|
||||||
|
return InvalidHandle(ErrorModule::FS);
|
||||||
|
}
|
||||||
file->path = path;
|
file->path = path;
|
||||||
file->backend = archive->backend->OpenFile(path, mode);
|
file->backend = archive->backend->OpenFile(path, mode);
|
||||||
|
|
||||||
if (!file->backend)
|
if (!file->backend) {
|
||||||
return 0;
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
||||||
|
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||||
|
}
|
||||||
|
|
||||||
return handle;
|
return MakeResult<Handle>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -442,15 +391,18 @@ Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& pa
|
||||||
* @param path Path to the Directory inside of the Archive
|
* @param path Path to the Directory inside of the Archive
|
||||||
* @return Opened Directory object
|
* @return Opened Directory object
|
||||||
*/
|
*/
|
||||||
Handle OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
|
ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
|
||||||
Directory* directory = new Directory;
|
Directory* directory = new Directory;
|
||||||
Handle handle = Kernel::g_object_pool.Create(directory);
|
Handle handle = Kernel::g_object_pool.Create(directory);
|
||||||
|
|
||||||
Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
|
Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
|
||||||
|
if (archive == nullptr) {
|
||||||
|
return InvalidHandle(ErrorModule::FS);
|
||||||
|
}
|
||||||
directory->path = path;
|
directory->path = path;
|
||||||
directory->backend = archive->backend->OpenDirectory(path);
|
directory->backend = archive->backend->OpenDirectory(path);
|
||||||
|
|
||||||
return handle;
|
return MakeResult<Handle>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize archives
|
/// Initialize archives
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/file_sys/archive.h"
|
#include "core/file_sys/archive.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Kernel namespace
|
// Kernel namespace
|
||||||
|
@ -17,33 +18,31 @@ namespace Kernel {
|
||||||
/**
|
/**
|
||||||
* Opens an archive
|
* Opens an archive
|
||||||
* @param id_code IdCode of the archive to open
|
* @param id_code IdCode of the archive to open
|
||||||
* @return Handle to archive if it exists, otherwise a null handle (0)
|
* @return Handle to the opened archive
|
||||||
*/
|
*/
|
||||||
Handle OpenArchive(FileSys::Archive::IdCode id_code);
|
ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes an archive
|
* Closes an archive
|
||||||
* @param id_code IdCode of the archive to open
|
* @param id_code IdCode of the archive to open
|
||||||
* @return true if it worked fine
|
|
||||||
*/
|
*/
|
||||||
Result CloseArchive(FileSys::Archive::IdCode id_code);
|
ResultCode CloseArchive(FileSys::Archive::IdCode id_code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an Archive
|
* Creates an Archive
|
||||||
* @param backend File system backend interface to the archive
|
* @param backend File system backend interface to the archive
|
||||||
* @param name Optional name of Archive
|
* @param name Name of Archive
|
||||||
* @return Handle to newly created Archive object
|
|
||||||
*/
|
*/
|
||||||
Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
|
ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a File from an Archive
|
* Open a File from an Archive
|
||||||
* @param archive_handle Handle to an open Archive object
|
* @param archive_handle Handle to an open Archive object
|
||||||
* @param path Path to the File inside of the Archive
|
* @param path Path to the File inside of the Archive
|
||||||
* @param mode Mode under which to open the File
|
* @param mode Mode under which to open the File
|
||||||
* @return Opened File object
|
* @return Handle to the opened File object
|
||||||
*/
|
*/
|
||||||
Handle OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
|
ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a File from an Archive
|
* Delete a File from an Archive
|
||||||
|
@ -73,9 +72,9 @@ Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& pa
|
||||||
* Open a Directory from an Archive
|
* Open a Directory from an Archive
|
||||||
* @param archive_handle Handle to an open Archive object
|
* @param archive_handle Handle to an open Archive object
|
||||||
* @param path Path to the Directory inside of the Archive
|
* @param path Path to the Directory inside of the Archive
|
||||||
* @return Opened Directory object
|
* @return Handle to the opened File object
|
||||||
*/
|
*/
|
||||||
Handle OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
|
ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
|
||||||
|
|
||||||
/// Initialize archives
|
/// Initialize archives
|
||||||
void ArchiveInit();
|
void ArchiveInit();
|
||||||
|
|
|
@ -30,13 +30,8 @@ public:
|
||||||
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
|
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
|
||||||
std::string name; ///< Name of event (optional)
|
std::string name; ///< Name of event (optional)
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
bool wait = locked;
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
*wait = locked;
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
Handle thread = GetCurrentThreadHandle();
|
Handle thread = GetCurrentThreadHandle();
|
||||||
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
||||||
|
@ -47,7 +42,7 @@ public:
|
||||||
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
|
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
return 0;
|
return MakeResult<bool>(wait);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,12 +52,12 @@ public:
|
||||||
* @param permanent_locked Boolean permanent locked value to set event
|
* @param permanent_locked Boolean permanent locked value to set event
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result SetPermanentLock(Handle handle, const bool permanent_locked) {
|
ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
|
||||||
Event* evt = g_object_pool.GetFast<Event>(handle);
|
Event* evt = g_object_pool.Get<Event>(handle);
|
||||||
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
evt->permanent_locked = permanent_locked;
|
evt->permanent_locked = permanent_locked;
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
|
||||||
* @param locked Boolean locked value to set event
|
* @param locked Boolean locked value to set event
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result SetEventLocked(const Handle handle, const bool locked) {
|
ResultCode SetEventLocked(const Handle handle, const bool locked) {
|
||||||
Event* evt = g_object_pool.GetFast<Event>(handle);
|
Event* evt = g_object_pool.Get<Event>(handle);
|
||||||
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
if (!evt->permanent_locked) {
|
if (!evt->permanent_locked) {
|
||||||
evt->locked = locked;
|
evt->locked = locked;
|
||||||
}
|
}
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,9 +81,9 @@ Result SetEventLocked(const Handle handle, const bool locked) {
|
||||||
* @param handle Handle to event to signal
|
* @param handle Handle to event to signal
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result SignalEvent(const Handle handle) {
|
ResultCode SignalEvent(const Handle handle) {
|
||||||
Event* evt = g_object_pool.GetFast<Event>(handle);
|
Event* evt = g_object_pool.Get<Event>(handle);
|
||||||
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
// Resume threads waiting for event to signal
|
// Resume threads waiting for event to signal
|
||||||
bool event_caught = false;
|
bool event_caught = false;
|
||||||
|
@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
|
||||||
if (!evt->permanent_locked) {
|
if (!evt->permanent_locked) {
|
||||||
evt->locked = event_caught;
|
evt->locked = event_caught;
|
||||||
}
|
}
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
|
||||||
* @param handle Handle to event to clear
|
* @param handle Handle to event to clear
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result ClearEvent(Handle handle) {
|
ResultCode ClearEvent(Handle handle) {
|
||||||
Event* evt = g_object_pool.GetFast<Event>(handle);
|
Event* evt = g_object_pool.Get<Event>(handle);
|
||||||
_assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
|
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
if (!evt->permanent_locked) {
|
if (!evt->permanent_locked) {
|
||||||
evt->locked = true;
|
evt->locked = true;
|
||||||
}
|
}
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,31 +15,27 @@ namespace Kernel {
|
||||||
* Changes whether an event is locked or not
|
* Changes whether an event is locked or not
|
||||||
* @param handle Handle to event to change
|
* @param handle Handle to event to change
|
||||||
* @param locked Boolean locked value to set event
|
* @param locked Boolean locked value to set event
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result SetEventLocked(const Handle handle, const bool locked);
|
ResultCode SetEventLocked(const Handle handle, const bool locked);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hackish function to set an events permanent lock state, used to pass through synch blocks
|
* Hackish function to set an events permanent lock state, used to pass through synch blocks
|
||||||
* @param handle Handle to event to change
|
* @param handle Handle to event to change
|
||||||
* @param permanent_locked Boolean permanent locked value to set event
|
* @param permanent_locked Boolean permanent locked value to set event
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result SetPermanentLock(Handle handle, const bool permanent_locked);
|
ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals an event
|
* Signals an event
|
||||||
* @param handle Handle to event to signal
|
* @param handle Handle to event to signal
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result SignalEvent(const Handle handle);
|
ResultCode SignalEvent(const Handle handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears an event
|
* Clears an event
|
||||||
* @param handle Handle to event to clear
|
* @param handle Handle to event to clear
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result ClearEvent(Handle handle);
|
ResultCode ClearEvent(Handle handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event
|
* Creates an event
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
typedef u32 Handle;
|
typedef u32 Handle;
|
||||||
typedef s32 Result;
|
typedef s32 Result;
|
||||||
|
@ -52,21 +53,19 @@ public:
|
||||||
virtual Kernel::HandleType GetHandleType() const = 0;
|
virtual Kernel::HandleType GetHandleType() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize kernel object
|
* Synchronize kernel object.
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
* @return True if the current thread should wait as a result of the sync
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
virtual Result SyncRequest(bool* wait) {
|
virtual ResultVal<bool> SyncRequest() {
|
||||||
ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
|
ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
|
||||||
return -1;
|
return UnimplementedFunction(ErrorModule::Kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for kernel object to synchronize
|
* Wait for kernel object to synchronize.
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
* @return True if the current thread should wait as a result of the wait
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
virtual Result WaitSynchronization(bool* wait) = 0;
|
virtual ResultVal<bool> WaitSynchronization() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectPool : NonCopyable {
|
class ObjectPool : NonCopyable {
|
||||||
|
@ -80,38 +79,29 @@ public:
|
||||||
static Object* CreateByIDType(int type);
|
static Object* CreateByIDType(int type);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
u32 Destroy(Handle handle) {
|
void Destroy(Handle handle) {
|
||||||
u32 error;
|
if (Get<T>(handle)) {
|
||||||
if (Get<T>(handle, error)) {
|
|
||||||
occupied[handle - HANDLE_OFFSET] = false;
|
occupied[handle - HANDLE_OFFSET] = false;
|
||||||
delete pool[handle - HANDLE_OFFSET];
|
delete pool[handle - HANDLE_OFFSET];
|
||||||
}
|
}
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValid(Handle handle);
|
bool IsValid(Handle handle);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T* Get(Handle handle, u32& outError) {
|
T* Get(Handle handle) {
|
||||||
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
|
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
|
||||||
// Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
|
if (handle != 0) {
|
||||||
if (handle != 0 && (u32)handle != 0x80020001) {
|
|
||||||
WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
|
WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
|
||||||
}
|
}
|
||||||
outError = 0;//T::GetMissingErrorCode();
|
return nullptr;
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
// Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
|
Object* t = pool[handle - HANDLE_OFFSET];
|
||||||
// it just acted as a static case and everything worked. This means that we will never
|
if (t->GetHandleType() != T::GetStaticHandleType()) {
|
||||||
// see the Wrong type object error below, but we'll just have to live with that danger.
|
|
||||||
T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
|
|
||||||
if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
|
|
||||||
WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
|
WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
|
||||||
outError = 0;//T::GetMissingErrorCode();
|
return nullptr;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
outError = 0;//SCE_KERNEL_ERROR_OK;
|
return static_cast<T*>(t);
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,31 +27,20 @@ public:
|
||||||
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
|
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
|
||||||
std::string name; ///< Name of mutex (optional)
|
std::string name; ///< Name of mutex (optional)
|
||||||
|
|
||||||
/**
|
ResultVal<bool> SyncRequest() override {
|
||||||
* Synchronize kernel object
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result SyncRequest(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
locked = true;
|
locked = true;
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
*wait = locked;
|
bool wait = locked;
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
|
Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return MakeResult<bool>(wait);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,15 +108,17 @@ bool ReleaseMutex(Mutex* mutex) {
|
||||||
* Releases a mutex
|
* Releases a mutex
|
||||||
* @param handle Handle to mutex to release
|
* @param handle Handle to mutex to release
|
||||||
*/
|
*/
|
||||||
Result ReleaseMutex(Handle handle) {
|
ResultCode ReleaseMutex(Handle handle) {
|
||||||
Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
|
Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
|
||||||
|
if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
_assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
|
|
||||||
|
|
||||||
if (!ReleaseMutex(mutex)) {
|
if (!ReleaseMutex(mutex)) {
|
||||||
return -1;
|
// TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
|
||||||
|
// what error condition this is supposed to be signaling.
|
||||||
|
return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
|
||||||
|
ErrorSummary::NothingHappened, ErrorLevel::Temporary);
|
||||||
}
|
}
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,9 +13,8 @@ namespace Kernel {
|
||||||
/**
|
/**
|
||||||
* Releases a mutex
|
* Releases a mutex
|
||||||
* @param handle Handle to mutex to release
|
* @param handle Handle to mutex to release
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result ReleaseMutex(Handle handle);
|
ResultCode ReleaseMutex(Handle handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a mutex
|
* Creates a mutex
|
||||||
|
|
|
@ -16,15 +16,10 @@ public:
|
||||||
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
|
||||||
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
|
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::OS);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 base_address; ///< Address of shared memory block in RAM
|
u32 base_address; ///< Address of shared memory block in RAM
|
||||||
|
@ -48,11 +43,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
|
||||||
return shared_memory;
|
return shared_memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a shared memory object
|
|
||||||
* @param name Optional name of shared memory object
|
|
||||||
* @return Handle of newly created shared memory object
|
|
||||||
*/
|
|
||||||
Handle CreateSharedMemory(const std::string& name) {
|
Handle CreateSharedMemory(const std::string& name) {
|
||||||
Handle handle;
|
Handle handle;
|
||||||
CreateSharedMemory(handle, name);
|
CreateSharedMemory(handle, name);
|
||||||
|
@ -67,39 +57,36 @@ Handle CreateSharedMemory(const std::string& name) {
|
||||||
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
|
ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
|
||||||
MemoryPermission other_permissions) {
|
MemoryPermission other_permissions) {
|
||||||
|
|
||||||
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
|
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
|
||||||
ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
|
ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
|
||||||
handle, address);
|
handle, address);
|
||||||
return -1;
|
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||||
|
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
|
SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
|
||||||
_assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
|
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
shared_memory->base_address = address;
|
shared_memory->base_address = address;
|
||||||
shared_memory->permissions = permissions;
|
shared_memory->permissions = permissions;
|
||||||
shared_memory->other_permissions = other_permissions;
|
shared_memory->other_permissions = other_permissions;
|
||||||
|
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
|
||||||
* Gets a pointer to the shared memory block
|
SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
|
||||||
* @param handle Shared memory block handle
|
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
* @param offset Offset from the start of the shared memory block to get pointer
|
|
||||||
* @return Pointer to the shared memory block from the specified offset
|
|
||||||
*/
|
|
||||||
u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
|
|
||||||
SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
|
|
||||||
_assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
|
|
||||||
|
|
||||||
if (0 != shared_memory->base_address)
|
if (0 != shared_memory->base_address)
|
||||||
return Memory::GetPointer(shared_memory->base_address + offset);
|
return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
|
||||||
|
|
||||||
ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
|
ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
|
||||||
return nullptr;
|
// TODO(yuriks): Verify error code.
|
||||||
|
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -32,9 +32,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
|
||||||
* @param address Address in system memory to map shared memory block to
|
* @param address Address in system memory to map shared memory block to
|
||||||
* @param permissions Memory block map permissions (specified by SVC field)
|
* @param permissions Memory block map permissions (specified by SVC field)
|
||||||
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
*/
|
||||||
Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
|
ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
|
||||||
MemoryPermission other_permissions);
|
MemoryPermission other_permissions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +42,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
|
||||||
* @param offset Offset from the start of the shared memory block to get pointer
|
* @param offset Offset from the start of the shared memory block to get pointer
|
||||||
* @return Pointer to the shared memory block from the specified offset
|
* @return Pointer to the shared memory block from the specified offset
|
||||||
*/
|
*/
|
||||||
u8* GetSharedMemoryPointer(Handle handle, u32 offset);
|
ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -11,10 +11,11 @@
|
||||||
#include "common/thread_queue_list.h"
|
#include "common/thread_queue_list.h"
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/mem_map.h"
|
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/mem_map.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -33,21 +34,17 @@ public:
|
||||||
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
|
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
|
||||||
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
|
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
const bool wait = status != THREADSTATUS_DORMANT;
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
if (wait) {
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
if (status != THREADSTATUS_DORMANT) {
|
|
||||||
Handle thread = GetCurrentThreadHandle();
|
Handle thread = GetCurrentThreadHandle();
|
||||||
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
|
||||||
waiting_threads.push_back(thread);
|
waiting_threads.push_back(thread);
|
||||||
}
|
}
|
||||||
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
|
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
|
||||||
*wait = true;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return MakeResult<bool>(wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadContext context;
|
ThreadContext context;
|
||||||
|
@ -144,27 +141,22 @@ void ChangeReadyState(Thread* t, bool ready) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that a thread has not been released from waiting
|
/// Verify that a thread has not been released from waiting
|
||||||
inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
|
inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
|
||||||
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
_dbg_assert_(KERNEL, thread != nullptr);
|
||||||
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
return type == thread->wait_type && wait_handle == thread->wait_handle;
|
||||||
|
|
||||||
if (type != thread->wait_type || wait_handle != thread->wait_handle)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the current thread
|
/// Stops the current thread
|
||||||
void StopThread(Handle handle, const char* reason) {
|
ResultCode StopThread(Handle handle, const char* reason) {
|
||||||
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
Thread* thread = g_object_pool.Get<Thread>(handle);
|
||||||
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
|
||||||
ChangeReadyState(thread, false);
|
ChangeReadyState(thread, false);
|
||||||
thread->status = THREADSTATUS_DORMANT;
|
thread->status = THREADSTATUS_DORMANT;
|
||||||
for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
|
for (Handle waiting_handle : thread->waiting_threads) {
|
||||||
const Handle waiting_thread = thread->waiting_threads[i];
|
Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
|
||||||
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
|
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
|
||||||
ResumeThreadFromWait(waiting_thread);
|
ResumeThreadFromWait(waiting_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thread->waiting_threads.clear();
|
thread->waiting_threads.clear();
|
||||||
|
@ -172,6 +164,8 @@ void StopThread(Handle handle, const char* reason) {
|
||||||
// Stopped threads are never waiting.
|
// Stopped threads are never waiting.
|
||||||
thread->wait_type = WAITTYPE_NONE;
|
thread->wait_type = WAITTYPE_NONE;
|
||||||
thread->wait_handle = 0;
|
thread->wait_handle = 0;
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes a threads state
|
/// Changes a threads state
|
||||||
|
@ -195,13 +189,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
|
||||||
s32 priority = THREADPRIO_LOWEST;
|
s32 priority = THREADPRIO_LOWEST;
|
||||||
|
|
||||||
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
|
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
|
||||||
for (const auto& handle : thread_queue) {
|
for (Handle handle : thread_queue) {
|
||||||
|
Thread* thread = g_object_pool.Get<Thread>(handle);
|
||||||
|
|
||||||
// TODO(bunnei): Verify arbiter address...
|
// TODO(bunnei): Verify arbiter address...
|
||||||
if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
|
if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
if (thread == nullptr)
|
||||||
|
continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
|
||||||
if(thread->current_priority <= priority) {
|
if(thread->current_priority <= priority) {
|
||||||
highest_priority_thread = handle;
|
highest_priority_thread = handle;
|
||||||
priority = thread->current_priority;
|
priority = thread->current_priority;
|
||||||
|
@ -218,10 +214,11 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
|
||||||
void ArbitrateAllThreads(u32 arbiter, u32 address) {
|
void ArbitrateAllThreads(u32 arbiter, u32 address) {
|
||||||
|
|
||||||
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
|
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
|
||||||
for (const auto& handle : thread_queue) {
|
for (Handle handle : thread_queue) {
|
||||||
|
Thread* thread = g_object_pool.Get<Thread>(handle);
|
||||||
|
|
||||||
// TODO(bunnei): Verify arbiter address...
|
// TODO(bunnei): Verify arbiter address...
|
||||||
if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
|
if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
|
||||||
ResumeThreadFromWait(handle);
|
ResumeThreadFromWait(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,7 +269,7 @@ Thread* NextThread() {
|
||||||
if (next == 0) {
|
if (next == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return Kernel::g_object_pool.GetFast<Thread>(next);
|
return Kernel::g_object_pool.Get<Thread>(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -289,8 +286,7 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
|
||||||
|
|
||||||
/// Resumes a thread from waiting by marking it as "ready"
|
/// Resumes a thread from waiting by marking it as "ready"
|
||||||
void ResumeThreadFromWait(Handle handle) {
|
void ResumeThreadFromWait(Handle handle) {
|
||||||
u32 error;
|
Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
|
||||||
Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
|
|
||||||
if (thread) {
|
if (thread) {
|
||||||
thread->status &= ~THREADSTATUS_WAIT;
|
thread->status &= ~THREADSTATUS_WAIT;
|
||||||
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
|
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
|
||||||
|
@ -378,19 +374,23 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the priority of the thread specified by handle
|
/// Get the priority of the thread specified by handle
|
||||||
u32 GetThreadPriority(const Handle handle) {
|
ResultVal<u32> GetThreadPriority(const Handle handle) {
|
||||||
Thread* thread = g_object_pool.GetFast<Thread>(handle);
|
Thread* thread = g_object_pool.Get<Thread>(handle);
|
||||||
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
|
||||||
return thread->current_priority;
|
|
||||||
|
return MakeResult<u32>(thread->current_priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the priority of the thread specified by handle
|
/// Set the priority of the thread specified by handle
|
||||||
Result SetThreadPriority(Handle handle, s32 priority) {
|
ResultCode SetThreadPriority(Handle handle, s32 priority) {
|
||||||
Thread* thread = nullptr;
|
Thread* thread = nullptr;
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
|
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
|
||||||
} else {
|
} else {
|
||||||
thread = g_object_pool.GetFast<Thread>(handle);
|
thread = g_object_pool.Get<Thread>(handle);
|
||||||
|
if (thread == nullptr) {
|
||||||
|
return InvalidHandle(ErrorModule::Kernel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
|
||||||
|
|
||||||
|
@ -417,7 +417,7 @@ Result SetThreadPriority(Handle handle, s32 priority) {
|
||||||
thread_ready_queue.push_back(thread->current_priority, handle);
|
thread_ready_queue.push_back(thread->current_priority, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets up the primary application thread
|
/// Sets up the primary application thread
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
enum ThreadPriority {
|
enum ThreadPriority {
|
||||||
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
|
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
|
||||||
|
@ -55,7 +56,7 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
|
||||||
void Reschedule();
|
void Reschedule();
|
||||||
|
|
||||||
/// Stops the current thread
|
/// Stops the current thread
|
||||||
void StopThread(Handle thread, const char* reason);
|
ResultCode StopThread(Handle thread, const char* reason);
|
||||||
|
|
||||||
/// Resumes a thread from waiting by marking it as "ready"
|
/// Resumes a thread from waiting by marking it as "ready"
|
||||||
void ResumeThreadFromWait(Handle handle);
|
void ResumeThreadFromWait(Handle handle);
|
||||||
|
@ -80,10 +81,10 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHa
|
||||||
void WaitThread_Synchronization();
|
void WaitThread_Synchronization();
|
||||||
|
|
||||||
/// Get the priority of the thread specified by handle
|
/// Get the priority of the thread specified by handle
|
||||||
u32 GetThreadPriority(const Handle handle);
|
ResultVal<u32> GetThreadPriority(const Handle handle);
|
||||||
|
|
||||||
/// Set the priority of the thread specified by handle
|
/// Set the priority of the thread specified by handle
|
||||||
Result SetThreadPriority(Handle handle, s32 priority);
|
ResultCode SetThreadPriority(Handle handle, s32 priority);
|
||||||
|
|
||||||
/// Initialize threading
|
/// Initialize threading
|
||||||
void ThreadingInit();
|
void ThreadingInit();
|
||||||
|
|
400
src/core/hle/result.h
Normal file
400
src/core/hle/result.h
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
// Copyright 2014 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/bit_field.h"
|
||||||
|
|
||||||
|
// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
|
||||||
|
|
||||||
|
/// Detailed description of the error. This listing is likely incomplete.
|
||||||
|
enum class ErrorDescription : u32 {
|
||||||
|
Success = 0,
|
||||||
|
InvalidSection = 1000,
|
||||||
|
TooLarge = 1001,
|
||||||
|
NotAuthorized = 1002,
|
||||||
|
AlreadyDone = 1003,
|
||||||
|
InvalidSize = 1004,
|
||||||
|
InvalidEnumValue = 1005,
|
||||||
|
InvalidCombination = 1006,
|
||||||
|
NoData = 1007,
|
||||||
|
Busy = 1008,
|
||||||
|
MisalignedAddress = 1009,
|
||||||
|
MisalignedSize = 1010,
|
||||||
|
OutOfMemory = 1011,
|
||||||
|
NotImplemented = 1012,
|
||||||
|
InvalidAddress = 1013,
|
||||||
|
InvalidPointer = 1014,
|
||||||
|
InvalidHandle = 1015,
|
||||||
|
NotInitialized = 1016,
|
||||||
|
AlreadyInitialized = 1017,
|
||||||
|
NotFound = 1018,
|
||||||
|
CancelRequested = 1019,
|
||||||
|
AlreadyExists = 1020,
|
||||||
|
OutOfRange = 1021,
|
||||||
|
Timeout = 1022,
|
||||||
|
InvalidResultValue = 1023,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the module which caused the error. Error codes can be propagated through a call
|
||||||
|
* chain, meaning that this doesn't always correspond to the module where the API call made is
|
||||||
|
* contained.
|
||||||
|
*/
|
||||||
|
enum class ErrorModule : u32 {
|
||||||
|
Common = 0,
|
||||||
|
Kernel = 1,
|
||||||
|
Util = 2,
|
||||||
|
FileServer = 3,
|
||||||
|
LoaderServer = 4,
|
||||||
|
TCB = 5,
|
||||||
|
OS = 6,
|
||||||
|
DBG = 7,
|
||||||
|
DMNT = 8,
|
||||||
|
PDN = 9,
|
||||||
|
GX = 10,
|
||||||
|
I2C = 11,
|
||||||
|
GPIO = 12,
|
||||||
|
DD = 13,
|
||||||
|
CODEC = 14,
|
||||||
|
SPI = 15,
|
||||||
|
PXI = 16,
|
||||||
|
FS = 17,
|
||||||
|
DI = 18,
|
||||||
|
HID = 19,
|
||||||
|
CAM = 20,
|
||||||
|
PI = 21,
|
||||||
|
PM = 22,
|
||||||
|
PM_LOW = 23,
|
||||||
|
FSI = 24,
|
||||||
|
SRV = 25,
|
||||||
|
NDM = 26,
|
||||||
|
NWM = 27,
|
||||||
|
SOC = 28,
|
||||||
|
LDR = 29,
|
||||||
|
ACC = 30,
|
||||||
|
RomFS = 31,
|
||||||
|
AM = 32,
|
||||||
|
HIO = 33,
|
||||||
|
Updater = 34,
|
||||||
|
MIC = 35,
|
||||||
|
FND = 36,
|
||||||
|
MP = 37,
|
||||||
|
MPWL = 38,
|
||||||
|
AC = 39,
|
||||||
|
HTTP = 40,
|
||||||
|
DSP = 41,
|
||||||
|
SND = 42,
|
||||||
|
DLP = 43,
|
||||||
|
HIO_LOW = 44,
|
||||||
|
CSND = 45,
|
||||||
|
SSL = 46,
|
||||||
|
AM_LOW = 47,
|
||||||
|
NEX = 48,
|
||||||
|
Friends = 49,
|
||||||
|
RDT = 50,
|
||||||
|
Applet = 51,
|
||||||
|
NIM = 52,
|
||||||
|
PTM = 53,
|
||||||
|
MIDI = 54,
|
||||||
|
MC = 55,
|
||||||
|
SWC = 56,
|
||||||
|
FatFS = 57,
|
||||||
|
NGC = 58,
|
||||||
|
CARD = 59,
|
||||||
|
CARDNOR = 60,
|
||||||
|
SDMC = 61,
|
||||||
|
BOSS = 62,
|
||||||
|
DBM = 63,
|
||||||
|
Config = 64,
|
||||||
|
PS = 65,
|
||||||
|
CEC = 66,
|
||||||
|
IR = 67,
|
||||||
|
UDS = 68,
|
||||||
|
PL = 69,
|
||||||
|
CUP = 70,
|
||||||
|
Gyroscope = 71,
|
||||||
|
MCU = 72,
|
||||||
|
NS = 73,
|
||||||
|
News = 74,
|
||||||
|
RO_1 = 75,
|
||||||
|
GD = 76,
|
||||||
|
CardSPI = 77,
|
||||||
|
EC = 78,
|
||||||
|
RO_2 = 79,
|
||||||
|
WebBrowser = 80,
|
||||||
|
Test = 81,
|
||||||
|
ENC = 82,
|
||||||
|
PIA = 83,
|
||||||
|
|
||||||
|
Application = 254,
|
||||||
|
InvalidResult = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A less specific error cause.
|
||||||
|
enum class ErrorSummary : u32 {
|
||||||
|
Success = 0,
|
||||||
|
NothingHappened = 1,
|
||||||
|
WouldBlock = 2,
|
||||||
|
OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to
|
||||||
|
///< execute the operation.
|
||||||
|
NotFound = 4, ///< A file or resource was not found.
|
||||||
|
InvalidState = 5,
|
||||||
|
NotSupported = 6, ///< The operation is not supported or not implemented.
|
||||||
|
InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime
|
||||||
|
///< context. (Invalid handle, out-of-bounds pointer or size, etc.)
|
||||||
|
WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use
|
||||||
|
///< with the function. (E.g. Invalid enum value)
|
||||||
|
Canceled = 9,
|
||||||
|
StatusChanged = 10,
|
||||||
|
Internal = 11,
|
||||||
|
|
||||||
|
InvalidResult = 63
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The severity of the error.
|
||||||
|
enum class ErrorLevel : u32 {
|
||||||
|
Success = 0,
|
||||||
|
Info = 1,
|
||||||
|
|
||||||
|
Status = 25,
|
||||||
|
Temporary = 26,
|
||||||
|
Permanent = 27,
|
||||||
|
Usage = 28,
|
||||||
|
Reinitialize = 29,
|
||||||
|
Reset = 30,
|
||||||
|
Fatal = 31
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
|
||||||
|
union ResultCode {
|
||||||
|
u32 raw;
|
||||||
|
|
||||||
|
BitField<0, 10, ErrorDescription> description;
|
||||||
|
BitField<10, 8, ErrorModule> module;
|
||||||
|
|
||||||
|
BitField<21, 6, ErrorSummary> summary;
|
||||||
|
BitField<27, 5, ErrorLevel> level;
|
||||||
|
|
||||||
|
// The last bit of `level` is checked by apps and the kernel to determine if a result code is an error
|
||||||
|
BitField<31, 1, u32> is_error;
|
||||||
|
|
||||||
|
explicit ResultCode(u32 raw) : raw(raw) {}
|
||||||
|
ResultCode(ErrorDescription description_, ErrorModule module_,
|
||||||
|
ErrorSummary summary_, ErrorLevel level_) : raw(0) {
|
||||||
|
description = description_;
|
||||||
|
module = module_;
|
||||||
|
summary = summary_;
|
||||||
|
level = level_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
|
||||||
|
|
||||||
|
bool IsSuccess() const {
|
||||||
|
return is_error == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsError() const {
|
||||||
|
return is_error == 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const ResultCode a, const ResultCode b) {
|
||||||
|
return a.raw == b.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const ResultCode a, const ResultCode b) {
|
||||||
|
return a.raw != b.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience functions for creating some common kinds of errors:
|
||||||
|
|
||||||
|
/// The default success `ResultCode`.
|
||||||
|
const ResultCode RESULT_SUCCESS(0);
|
||||||
|
|
||||||
|
/// Might be returned instead of a dummy success for unimplemented APIs.
|
||||||
|
inline ResultCode UnimplementedFunction(ErrorModule module) {
|
||||||
|
return ResultCode(ErrorDescription::NotImplemented, module,
|
||||||
|
ErrorSummary::NotSupported, ErrorLevel::Permanent);
|
||||||
|
}
|
||||||
|
/// Returned when a function is passed an invalid handle.
|
||||||
|
inline ResultCode InvalidHandle(ErrorModule module) {
|
||||||
|
return ResultCode(ErrorDescription::InvalidHandle, module,
|
||||||
|
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
|
||||||
|
* also holds a result of type `T`. If the code is an error code then trying to access the inner
|
||||||
|
* value fails, thus ensuring that the ResultCode of functions is always checked properly before
|
||||||
|
* their return value is used. It is similar in concept to the `std::optional` type
|
||||||
|
* (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
|
||||||
|
* C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
|
||||||
|
*
|
||||||
|
* An example of how it could be used:
|
||||||
|
* \code
|
||||||
|
* ResultVal<int> Frobnicate(float strength) {
|
||||||
|
* if (strength < 0.f || strength > 1.0f) {
|
||||||
|
* // Can't frobnicate too weakly or too strongly
|
||||||
|
* return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
|
||||||
|
* ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||||
|
* } else {
|
||||||
|
* // Frobnicated! Give caller a cookie
|
||||||
|
* return MakeResult<int>(42);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* ResultVal<int> frob_result = Frobnicate(0.75f);
|
||||||
|
* if (frob_result) {
|
||||||
|
* // Frobbed ok
|
||||||
|
* printf("My cookie is %d\n", *frob_result);
|
||||||
|
* } else {
|
||||||
|
* printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class ResultVal {
|
||||||
|
public:
|
||||||
|
/// Constructs an empty `ResultVal` with the given error code. The code must not be a success code.
|
||||||
|
ResultVal(ResultCode error_code = ResultCode(-1))
|
||||||
|
: result_code(error_code)
|
||||||
|
{
|
||||||
|
assert(error_code.IsError());
|
||||||
|
UpdateDebugPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to the non-member function `MakeResult`, with the exception that you can manually
|
||||||
|
* specify the success code. `success_code` must not be an error code.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
static ResultVal WithCode(ResultCode success_code, Args&&... args) {
|
||||||
|
ResultVal<T> result;
|
||||||
|
result.emplace(success_code, std::forward<Args>(args)...);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal(const ResultVal& o)
|
||||||
|
: result_code(o.result_code)
|
||||||
|
{
|
||||||
|
if (!o.empty()) {
|
||||||
|
new (&storage) T(*o.GetPointer());
|
||||||
|
}
|
||||||
|
UpdateDebugPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal(ResultVal&& o)
|
||||||
|
: result_code(o.result_code)
|
||||||
|
{
|
||||||
|
if (!o.empty()) {
|
||||||
|
new (&storage) T(std::move(*o.GetPointer()));
|
||||||
|
}
|
||||||
|
UpdateDebugPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ResultVal() {
|
||||||
|
if (!empty()) {
|
||||||
|
GetPointer()->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal& operator=(const ResultVal& o) {
|
||||||
|
if (*this) {
|
||||||
|
if (o) {
|
||||||
|
*GetPointer() = *o.GetPointer();
|
||||||
|
} else {
|
||||||
|
GetPointer()->~T();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (o) {
|
||||||
|
new (&storage) T(*o.GetPointer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_code = o.result_code;
|
||||||
|
UpdateDebugPtr();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the current result with a new constructed result value in-place. The code must not
|
||||||
|
* be an error code.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
void emplace(ResultCode success_code, Args&&... args) {
|
||||||
|
assert(success_code.IsSuccess());
|
||||||
|
if (!empty()) {
|
||||||
|
GetPointer()->~T();
|
||||||
|
}
|
||||||
|
new (&storage) T(std::forward<Args>(args)...);
|
||||||
|
result_code = success_code;
|
||||||
|
UpdateDebugPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the `ResultVal` contains an error code and no value.
|
||||||
|
bool empty() const { return result_code.IsError(); }
|
||||||
|
|
||||||
|
/// Returns true if the `ResultVal` contains a return value.
|
||||||
|
bool Succeeded() const { return result_code.IsSuccess(); }
|
||||||
|
/// Returns true if the `ResultVal` contains an error code and no value.
|
||||||
|
bool Failed() const { return empty(); }
|
||||||
|
|
||||||
|
ResultCode Code() const { return result_code; }
|
||||||
|
|
||||||
|
const T& operator* () const { return *GetPointer(); }
|
||||||
|
T& operator* () { return *GetPointer(); }
|
||||||
|
const T* operator->() const { return GetPointer(); }
|
||||||
|
T* operator->() { return GetPointer(); }
|
||||||
|
|
||||||
|
/// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
|
||||||
|
template <typename U>
|
||||||
|
T ValueOr(U&& value) const {
|
||||||
|
return !empty() ? *GetPointer() : std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
|
||||||
|
|
||||||
|
StorageType storage;
|
||||||
|
ResultCode result_code;
|
||||||
|
#if _DEBUG
|
||||||
|
// The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
|
||||||
|
// need to cast `storage` to a pointer or pay attention to `result_code`.
|
||||||
|
const T* debug_ptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void UpdateDebugPtr() {
|
||||||
|
#if _DEBUG
|
||||||
|
debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* GetPointer() const {
|
||||||
|
assert(!empty());
|
||||||
|
return static_cast<const T*>(static_cast<const void*>(&storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
T* GetPointer() {
|
||||||
|
assert(!empty());
|
||||||
|
return static_cast<T*>(static_cast<void*>(&storage));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
|
||||||
|
* `T` with and creates a success `ResultVal` contained the constructed value.
|
||||||
|
*/
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
ResultVal<T> MakeResult(Args&&... args) {
|
||||||
|
return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
|
||||||
|
}
|
|
@ -4,26 +4,24 @@
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
#include "fs_user.h"
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/settings.h"
|
|
||||||
#include "core/hle/kernel/archive.h"
|
#include "core/hle/kernel/archive.h"
|
||||||
|
#include "core/hle/kernel/archive.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/fs_user.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace FS_User
|
// Namespace FS_User
|
||||||
|
|
||||||
namespace FS_User {
|
namespace FS_User {
|
||||||
|
|
||||||
// We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it
|
|
||||||
// puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make
|
|
||||||
// sure we don't mislead the application into thinking something worked.
|
|
||||||
|
|
||||||
static void Initialize(Service::Interface* self) {
|
static void Initialize(Service::Interface* self) {
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
|
||||||
// TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
|
// TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
|
||||||
// http://3dbrew.org/wiki/FS:Initialize#Request
|
// http://3dbrew.org/wiki/FS:Initialize#Request
|
||||||
cmd_buff[1] = 0;
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "called");
|
DEBUG_LOG(KERNEL, "called");
|
||||||
}
|
}
|
||||||
|
@ -59,14 +57,12 @@ static void OpenFile(Service::Interface* self) {
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "path=%s, mode=%d attrs=%d", file_path.DebugStr().c_str(), mode, attributes);
|
DEBUG_LOG(KERNEL, "path=%s, mode=%d attrs=%d", file_path.DebugStr().c_str(), mode, attributes);
|
||||||
|
|
||||||
Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode);
|
ResultVal<Handle> handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode);
|
||||||
if (handle) {
|
cmd_buff[1] = handle.Code().raw;
|
||||||
cmd_buff[1] = 0;
|
if (handle.Succeeded()) {
|
||||||
cmd_buff[3] = handle;
|
cmd_buff[3] = *handle;
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
|
ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
|
||||||
// TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
|
|
||||||
cmd_buff[1] = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "called");
|
DEBUG_LOG(KERNEL, "called");
|
||||||
|
@ -111,27 +107,27 @@ static void OpenFileDirectly(Service::Interface* self) {
|
||||||
|
|
||||||
if (archive_path.GetType() != FileSys::Empty) {
|
if (archive_path.GetType() != FileSys::Empty) {
|
||||||
ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
|
ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
|
||||||
cmd_buff[1] = -1;
|
cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it
|
// TODO(Link Mauve): Check if we should even get a handle for the archive, and don't leak it
|
||||||
Handle archive_handle = Kernel::OpenArchive(archive_id);
|
// TODO(yuriks): Why is there all this duplicate (and seemingly useless) code up here?
|
||||||
if (!archive_handle) {
|
ResultVal<Handle> archive_handle = Kernel::OpenArchive(archive_id);
|
||||||
|
cmd_buff[1] = archive_handle.Code().raw;
|
||||||
|
if (archive_handle.Failed()) {
|
||||||
ERROR_LOG(KERNEL, "failed to get a handle for archive");
|
ERROR_LOG(KERNEL, "failed to get a handle for archive");
|
||||||
// TODO(Link Mauve): Check for the actual error values, this one was just chosen arbitrarily
|
|
||||||
cmd_buff[1] = -1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// cmd_buff[2] isn't used according to 3dmoo's implementation.
|
||||||
|
cmd_buff[3] = *archive_handle;
|
||||||
|
|
||||||
Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_path, mode);
|
ResultVal<Handle> handle = Kernel::OpenFileFromArchive(*archive_handle, file_path, mode);
|
||||||
if (handle) {
|
cmd_buff[1] = handle.Code().raw;
|
||||||
cmd_buff[1] = 0;
|
if (handle.Succeeded()) {
|
||||||
cmd_buff[3] = handle;
|
cmd_buff[3] = *handle;
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
|
ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_path.DebugStr().c_str());
|
||||||
// TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
|
|
||||||
cmd_buff[1] = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "called");
|
DEBUG_LOG(KERNEL, "called");
|
||||||
|
@ -243,14 +239,12 @@ static void OpenDirectory(Service::Interface* self) {
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
|
DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
|
||||||
|
|
||||||
Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path);
|
ResultVal<Handle> handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_path);
|
||||||
if (handle) {
|
cmd_buff[1] = handle.Code().raw;
|
||||||
cmd_buff[1] = 0;
|
if (handle.Succeeded()) {
|
||||||
cmd_buff[3] = handle;
|
cmd_buff[3] = *handle;
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(KERNEL, "failed to get a handle for directory");
|
ERROR_LOG(KERNEL, "failed to get a handle for directory");
|
||||||
// TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
|
|
||||||
cmd_buff[1] = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "called");
|
DEBUG_LOG(KERNEL, "called");
|
||||||
|
@ -282,19 +276,17 @@ static void OpenArchive(Service::Interface* self) {
|
||||||
|
|
||||||
if (archive_path.GetType() != FileSys::Empty) {
|
if (archive_path.GetType() != FileSys::Empty) {
|
||||||
ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
|
ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
|
||||||
cmd_buff[1] = -1;
|
cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle handle = Kernel::OpenArchive(archive_id);
|
ResultVal<Handle> handle = Kernel::OpenArchive(archive_id);
|
||||||
if (handle) {
|
cmd_buff[1] = handle.Code().raw;
|
||||||
cmd_buff[1] = 0;
|
if (handle.Succeeded()) {
|
||||||
// cmd_buff[2] isn't used according to 3dmoo's implementation.
|
// cmd_buff[2] isn't used according to 3dmoo's implementation.
|
||||||
cmd_buff[3] = handle;
|
cmd_buff[3] = *handle;
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(KERNEL, "failed to get a handle for archive");
|
ERROR_LOG(KERNEL, "failed to get a handle for archive");
|
||||||
// TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
|
|
||||||
cmd_buff[1] = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG(KERNEL, "called");
|
DEBUG_LOG(KERNEL, "called");
|
||||||
|
|
|
@ -28,28 +28,23 @@ u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1
|
||||||
|
|
||||||
/// Gets a pointer to a thread command buffer in GSP shared memory
|
/// Gets a pointer to a thread command buffer in GSP shared memory
|
||||||
static inline u8* GetCommandBuffer(u32 thread_id) {
|
static inline u8* GetCommandBuffer(u32 thread_id) {
|
||||||
if (0 == g_shared_memory)
|
ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
|
||||||
return nullptr;
|
return ptr.ValueOr(nullptr);
|
||||||
|
|
||||||
return Kernel::GetSharedMemoryPointer(g_shared_memory,
|
|
||||||
0x800 + (thread_id * sizeof(CommandBuffer)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
|
static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
|
||||||
if (0 == g_shared_memory)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
_dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
|
_dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
|
||||||
|
|
||||||
// For each thread there are two FrameBufferUpdate fields
|
// For each thread there are two FrameBufferUpdate fields
|
||||||
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
|
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
|
||||||
return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
|
ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
|
||||||
|
return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a pointer to the interrupt relay queue for a given thread index
|
/// Gets a pointer to the interrupt relay queue for a given thread index
|
||||||
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
|
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
|
||||||
return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
|
ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
|
||||||
sizeof(InterruptRelayQueue) * thread_id);
|
return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
|
static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
|
||||||
|
|
|
@ -34,10 +34,7 @@ static s16 next_circle_y = 0;
|
||||||
* Gets a pointer to the PadData structure inside HID shared memory
|
* Gets a pointer to the PadData structure inside HID shared memory
|
||||||
*/
|
*/
|
||||||
static inline PadData* GetPadData() {
|
static inline PadData* GetPadData() {
|
||||||
if (0 == shared_mem)
|
return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,7 +62,7 @@ void Manager::DeleteService(const std::string& port_name) {
|
||||||
|
|
||||||
/// Get a Service Interface from its Handle
|
/// Get a Service Interface from its Handle
|
||||||
Interface* Manager::FetchFromHandle(Handle handle) {
|
Interface* Manager::FetchFromHandle(Handle handle) {
|
||||||
return Kernel::g_object_pool.GetFast<Interface>(handle);
|
return Kernel::g_object_pool.Get<Interface>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a Service Interface from its port
|
/// Get a Service Interface from its port
|
||||||
|
|
|
@ -75,12 +75,7 @@ public:
|
||||||
m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
|
m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> SyncRequest() override {
|
||||||
* Synchronize kernel object
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result SyncRequest(bool* wait) override {
|
|
||||||
u32* cmd_buff = GetCommandBuffer();
|
u32* cmd_buff = GetCommandBuffer();
|
||||||
auto itr = m_functions.find(cmd_buff[0]);
|
auto itr = m_functions.find(cmd_buff[0]);
|
||||||
|
|
||||||
|
@ -91,7 +86,7 @@ public:
|
||||||
// TODO(bunnei): Hack - ignore error
|
// TODO(bunnei): Hack - ignore error
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
cmd_buff[1] = 0;
|
cmd_buff[1] = 0;
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
if (itr->second.func == nullptr) {
|
if (itr->second.func == nullptr) {
|
||||||
ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
|
ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
|
||||||
|
@ -100,23 +95,18 @@ public:
|
||||||
// TODO(bunnei): Hack - ignore error
|
// TODO(bunnei): Hack - ignore error
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
cmd_buff[1] = 0;
|
cmd_buff[1] = 0;
|
||||||
return 0;
|
return MakeResult<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second.func(this);
|
itr->second.func(this);
|
||||||
|
|
||||||
return 0; // TODO: Implement return from actual function
|
return MakeResult<bool>(false); // TODO: Implement return from actual function
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
ResultVal<bool> WaitSynchronization() override {
|
||||||
* Wait for kernel object to synchronize
|
|
||||||
* @param wait Boolean wait set if current thread should wait as a result of sync operation
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
Result WaitSynchronization(bool* wait) override {
|
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
ERROR_LOG(OSHLE, "unimplemented function");
|
ERROR_LOG(OSHLE, "unimplemented function");
|
||||||
return 0;
|
return UnimplementedFunction(ErrorModule::OS);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -35,7 +35,7 @@ static void GetProcSemaphore(Service::Interface* self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GetServiceHandle(Service::Interface* self) {
|
static void GetServiceHandle(Service::Interface* self) {
|
||||||
Result res = 0;
|
ResultCode res = RESULT_SUCCESS;
|
||||||
u32* cmd_buff = Service::GetCommandBuffer();
|
u32* cmd_buff = Service::GetCommandBuffer();
|
||||||
|
|
||||||
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
|
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
|
||||||
|
@ -46,9 +46,9 @@ static void GetServiceHandle(Service::Interface* self) {
|
||||||
DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
|
DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
|
||||||
} else {
|
} else {
|
||||||
ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
|
ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
|
||||||
res = -1;
|
res = UnimplementedFunction(ErrorModule::SRV);
|
||||||
}
|
}
|
||||||
cmd_buff[1] = res;
|
cmd_buff[1] = res.raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
#include "core/hle/function_wrappers.h"
|
#include "core/hle/function_wrappers.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -86,18 +87,22 @@ static Result ConnectToPort(Handle* out, const char* port_name) {
|
||||||
|
|
||||||
/// Synchronize to an OS service
|
/// Synchronize to an OS service
|
||||||
static Result SendSyncRequest(Handle handle) {
|
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)) {
|
||||||
|
return InvalidHandle(ErrorModule::Kernel).raw;
|
||||||
|
}
|
||||||
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
|
|
||||||
_assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
|
_assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
|
||||||
DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
|
||||||
|
|
||||||
bool wait = false;
|
ResultVal<bool> wait = object->SyncRequest();
|
||||||
Result res = object->SyncRequest(&wait);
|
if (wait.Succeeded() && *wait) {
|
||||||
if (wait) {
|
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return wait.Code().raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close a handle
|
/// Close a handle
|
||||||
|
@ -110,25 +115,25 @@ static Result CloseHandle(Handle handle) {
|
||||||
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
|
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
|
||||||
static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
||||||
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
||||||
bool wait = false;
|
|
||||||
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
||||||
|
|
||||||
|
if (!Kernel::g_object_pool.IsValid(handle)) {
|
||||||
|
return InvalidHandle(ErrorModule::Kernel).raw;
|
||||||
|
}
|
||||||
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
|
_dbg_assert_(KERNEL, object != nullptr);
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
|
||||||
object->GetName().c_str(), nano_seconds);
|
object->GetName().c_str(), nano_seconds);
|
||||||
|
|
||||||
_assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
|
ResultVal<bool> wait = object->WaitSynchronization();
|
||||||
|
|
||||||
Result res = object->WaitSynchronization(&wait);
|
|
||||||
|
|
||||||
// Check for next thread to schedule
|
// Check for next thread to schedule
|
||||||
if (wait) {
|
if (wait.Succeeded() && *wait) {
|
||||||
HLE::Reschedule(__func__);
|
HLE::Reschedule(__func__);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return wait.Code().raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||||
|
@ -143,20 +148,21 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
|
||||||
|
|
||||||
// Iterate through each handle, synchronize kernel object
|
// Iterate through each handle, synchronize kernel object
|
||||||
for (s32 i = 0; i < handle_count; i++) {
|
for (s32 i = 0; i < handle_count; i++) {
|
||||||
bool wait = false;
|
if (!Kernel::g_object_pool.IsValid(handles[i])) {
|
||||||
|
return InvalidHandle(ErrorModule::Kernel).raw;
|
||||||
|
}
|
||||||
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
|
||||||
|
|
||||||
_assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
|
|
||||||
"is nullptr!", handles[i]);
|
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
|
DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
|
||||||
object->GetName().c_str());
|
object->GetName().c_str());
|
||||||
|
|
||||||
Result res = object->WaitSynchronization(&wait);
|
// TODO(yuriks): Verify how the real function behaves when an error happens here
|
||||||
|
ResultVal<bool> wait_result = object->WaitSynchronization();
|
||||||
|
bool wait = wait_result.Succeeded() && *wait_result;
|
||||||
|
|
||||||
if (!wait && !wait_all) {
|
if (!wait && !wait_all) {
|
||||||
*out = i;
|
*out = i;
|
||||||
return 0;
|
return RESULT_SUCCESS.raw;
|
||||||
} else {
|
} else {
|
||||||
unlock_all = false;
|
unlock_all = false;
|
||||||
}
|
}
|
||||||
|
@ -164,13 +170,13 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
|
||||||
|
|
||||||
if (wait_all && unlock_all) {
|
if (wait_all && unlock_all) {
|
||||||
*out = handle_count;
|
*out = handle_count;
|
||||||
return 0;
|
return RESULT_SUCCESS.raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for next thread to schedule
|
// Check for next thread to schedule
|
||||||
HLE::Reschedule(__func__);
|
HLE::Reschedule(__func__);
|
||||||
|
|
||||||
return 0;
|
return RESULT_SUCCESS.raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an address arbiter (to allocate access to shared resources)
|
/// Create an address arbiter (to allocate access to shared resources)
|
||||||
|
@ -183,8 +189,8 @@ static Result CreateAddressArbiter(u32* arbiter) {
|
||||||
|
|
||||||
/// Arbitrate address
|
/// Arbitrate address
|
||||||
static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
|
static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
|
||||||
return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
|
return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
|
||||||
value);
|
address, value).raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||||
|
@ -246,13 +252,16 @@ static u32 ExitThread() {
|
||||||
|
|
||||||
/// Gets the priority for the specified thread
|
/// Gets the priority for the specified thread
|
||||||
static Result GetThreadPriority(s32* priority, Handle handle) {
|
static Result GetThreadPriority(s32* priority, Handle handle) {
|
||||||
*priority = Kernel::GetThreadPriority(handle);
|
ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
|
||||||
return 0;
|
if (priority_result.Succeeded()) {
|
||||||
|
*priority = *priority_result;
|
||||||
|
}
|
||||||
|
return priority_result.Code().raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the priority for the specified thread
|
/// Sets the priority for the specified thread
|
||||||
static Result SetThreadPriority(Handle handle, s32 priority) {
|
static Result SetThreadPriority(Handle handle, s32 priority) {
|
||||||
return Kernel::SetThreadPriority(handle, priority);
|
return Kernel::SetThreadPriority(handle, priority).raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mutex
|
/// Create a mutex
|
||||||
|
@ -266,9 +275,8 @@ static Result CreateMutex(Handle* mutex, u32 initial_locked) {
|
||||||
/// Release a mutex
|
/// Release a mutex
|
||||||
static Result ReleaseMutex(Handle handle) {
|
static Result ReleaseMutex(Handle handle) {
|
||||||
DEBUG_LOG(SVC, "called handle=0x%08X", handle);
|
DEBUG_LOG(SVC, "called handle=0x%08X", handle);
|
||||||
_assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
|
ResultCode res = Kernel::ReleaseMutex(handle);
|
||||||
Kernel::ReleaseMutex(handle);
|
return res.raw;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current thread ID
|
/// Get current thread ID
|
||||||
|
@ -310,16 +318,14 @@ static Result DuplicateHandle(Handle* out, Handle handle) {
|
||||||
|
|
||||||
/// Signals an event
|
/// Signals an event
|
||||||
static Result SignalEvent(Handle evt) {
|
static Result SignalEvent(Handle evt) {
|
||||||
Result res = Kernel::SignalEvent(evt);
|
|
||||||
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
||||||
return res;
|
return Kernel::SignalEvent(evt).raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears an event
|
/// Clears an event
|
||||||
static Result ClearEvent(Handle evt) {
|
static Result ClearEvent(Handle evt) {
|
||||||
Result res = Kernel::ClearEvent(evt);
|
|
||||||
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
||||||
return res;
|
return Kernel::ClearEvent(evt).raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sleep the current thread
|
/// Sleep the current thread
|
||||||
|
|
Loading…
Reference in a new issue