commit
187e65f495
|
@ -521,7 +521,7 @@ bool DeleteDirRecursively(const std::string& directory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create directory and copy contents (does not overwrite existing files)
|
// Create directory and copy contents (does not overwrite existing files)
|
||||||
void CopyDir(const std::string& source_path, const std::string& dest_path)
|
void CopyDir(const std::string& source_path, const std::string& dest_path, bool destructive)
|
||||||
{
|
{
|
||||||
if (source_path == dest_path)
|
if (source_path == dest_path)
|
||||||
return;
|
return;
|
||||||
|
@ -562,10 +562,16 @@ void CopyDir(const std::string& source_path, const std::string& dest_path)
|
||||||
{
|
{
|
||||||
if (!Exists(dest))
|
if (!Exists(dest))
|
||||||
File::CreateFullPath(dest + DIR_SEP);
|
File::CreateFullPath(dest + DIR_SEP);
|
||||||
CopyDir(source, dest);
|
CopyDir(source, dest, destructive);
|
||||||
|
}
|
||||||
|
else if (!Exists(dest) && !destructive)
|
||||||
|
{
|
||||||
|
Copy(source, dest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Rename(source, dest);
|
||||||
}
|
}
|
||||||
else if (!Exists(dest))
|
|
||||||
File::Copy(source, dest);
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
} while (FindNextFile(hFind, &ffd) != 0);
|
} while (FindNextFile(hFind, &ffd) != 0);
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
|
|
|
@ -156,8 +156,9 @@ bool DeleteDirRecursively(const std::string& directory);
|
||||||
// Returns the current directory
|
// Returns the current directory
|
||||||
std::string GetCurrentDir();
|
std::string GetCurrentDir();
|
||||||
|
|
||||||
// Create directory and copy contents (does not overwrite existing files)
|
// Create directory and copy contents (optionally overwrites existing files)
|
||||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
void CopyDir(const std::string& source_path, const std::string& dest_path,
|
||||||
|
bool destructive = false);
|
||||||
|
|
||||||
// Set the current directory to given directory
|
// Set the current directory to given directory
|
||||||
bool SetCurrentDir(const std::string& directory);
|
bool SetCurrentDir(const std::string& directory);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "Core/IOS/WFS/WFSI.h"
|
#include "Core/IOS/WFS/WFSI.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
#include <mbedtls/aes.h>
|
#include <mbedtls/aes.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -20,6 +21,20 @@
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DiscIO/NANDContentLoader.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string TitleIdStr(u64 tid)
|
||||||
|
{
|
||||||
|
return StringFromFormat("%c%c%c%c", static_cast<char>(tid >> 24), static_cast<char>(tid >> 16),
|
||||||
|
static_cast<char>(tid >> 8), static_cast<char>(tid));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GroupIdStr(u16 gid)
|
||||||
|
{
|
||||||
|
return StringFromFormat("%c%c", gid >> 8, gid & 0xFF);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
|
@ -86,23 +101,54 @@ WFSI::WFSI(Kernel& ios, const std::string& device_name) : Device(ios, device_nam
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WFSI::SetCurrentTitleIdAndGroupId(u64 tid, u16 gid)
|
||||||
|
{
|
||||||
|
m_current_title_id = tid;
|
||||||
|
m_current_group_id = gid;
|
||||||
|
|
||||||
|
m_current_title_id_str = TitleIdStr(tid);
|
||||||
|
m_current_group_id_str = GroupIdStr(gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WFSI::SetImportTitleIdAndGroupId(u64 tid, u16 gid)
|
||||||
|
{
|
||||||
|
m_import_title_id = tid;
|
||||||
|
m_import_group_id = gid;
|
||||||
|
|
||||||
|
m_import_title_id_str = TitleIdStr(tid);
|
||||||
|
m_import_group_id_str = GroupIdStr(gid);
|
||||||
|
}
|
||||||
|
|
||||||
IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
{
|
{
|
||||||
s32 return_error_code = IPC_SUCCESS;
|
s32 return_error_code = IPC_SUCCESS;
|
||||||
|
|
||||||
switch (request.request)
|
switch (request.request)
|
||||||
{
|
{
|
||||||
case IOCTL_WFSI_PREPARE_DEVICE:
|
case IOCTL_WFSI_IMPORT_TITLE_INIT:
|
||||||
{
|
{
|
||||||
u32 tmd_addr = Memory::Read_U32(request.buffer_in);
|
u32 tmd_addr = Memory::Read_U32(request.buffer_in);
|
||||||
u32 tmd_size = Memory::Read_U32(request.buffer_in + 4);
|
u32 tmd_size = Memory::Read_U32(request.buffer_in + 4);
|
||||||
|
|
||||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_PREPARE_DEVICE");
|
m_patch_type = static_cast<PatchType>(Memory::Read_U32(request.buffer_in + 32));
|
||||||
|
m_continue_install = Memory::Read_U32(request.buffer_in + 36);
|
||||||
|
|
||||||
constexpr u32 MAX_TMD_SIZE = 0x4000;
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: patch type %d, continue install: %s",
|
||||||
if (tmd_size > MAX_TMD_SIZE)
|
m_patch_type, m_continue_install ? "true" : "false");
|
||||||
|
|
||||||
|
if (m_patch_type == PatchType::PATCH_TYPE_2)
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_PREPARE_DEVICE: TMD size too large (%d)", tmd_size);
|
const std::string content_dir =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
|
||||||
|
File::Rename(WFS::NativePath(content_dir + "/default.dol"),
|
||||||
|
WFS::NativePath(content_dir + "/_default.dol"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IOS::ES::IsValidTMDSize(tmd_size))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: TMD size too large (%d)", tmd_size);
|
||||||
return_error_code = IPC_EINVAL;
|
return_error_code = IPC_EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +167,13 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
memcpy(m_aes_key, ticket.GetTitleKey(m_ios.GetIOSC()).data(), sizeof(m_aes_key));
|
memcpy(m_aes_key, ticket.GetTitleKey(m_ios.GetIOSC()).data(), sizeof(m_aes_key));
|
||||||
mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128);
|
mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128);
|
||||||
|
|
||||||
|
SetImportTitleIdAndGroupId(m_tmd.GetTitleId(), m_tmd.GetGroupId());
|
||||||
|
|
||||||
|
if (m_patch_type == PatchType::PATCH_TYPE_1)
|
||||||
|
CancelPatchImport(m_continue_install);
|
||||||
|
else if (m_patch_type == PatchType::NOT_A_PATCH)
|
||||||
|
CancelTitleImport(m_continue_install);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,12 +228,12 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case IOCTL_WFSI_FINALIZE_PROFILE:
|
case IOCTL_WFSI_IMPORT_CONTENT_END:
|
||||||
case IOCTL_WFSI_FINALIZE_CONTENT:
|
case IOCTL_WFSI_IMPORT_PROFILE_END:
|
||||||
{
|
{
|
||||||
const char* ioctl_name = request.request == IOCTL_WFSI_FINALIZE_PROFILE ?
|
const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE_END ?
|
||||||
"IOCTL_WFSI_FINALIZE_PROFILE" :
|
"IOCTL_WFSI_IMPORT_PROFILE_END" :
|
||||||
"IOCTL_WFSI_FINALIZE_CONTENT";
|
"IOCTL_WFSI_IMPORT_CONTENT_END";
|
||||||
INFO_LOG(IOS_WFS, "%s", ioctl_name);
|
INFO_LOG(IOS_WFS, "%s", ioctl_name);
|
||||||
|
|
||||||
auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) {
|
auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) {
|
||||||
|
@ -204,6 +257,53 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_FINALIZE_TITLE_INSTALL:
|
||||||
|
{
|
||||||
|
std::string tmd_path;
|
||||||
|
if (m_patch_type == NOT_A_PATCH)
|
||||||
|
{
|
||||||
|
std::string title_install_dir = StringFromFormat("/vol/%s/_install/%s", m_device_name.c_str(),
|
||||||
|
m_import_title_id_str.c_str());
|
||||||
|
std::string title_final_dir =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(),
|
||||||
|
m_import_group_id_str.c_str(), m_import_title_id_str.c_str());
|
||||||
|
File::Rename(WFS::NativePath(title_install_dir), WFS::NativePath(title_final_dir));
|
||||||
|
|
||||||
|
tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd",
|
||||||
|
m_device_name.c_str(), m_import_group_id_str.c_str(),
|
||||||
|
m_import_title_id_str.c_str(), m_import_title_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string patch_dir =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/_patch", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
File::DeleteDirRecursively(WFS::NativePath(patch_dir));
|
||||||
|
|
||||||
|
tmd_path = StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd",
|
||||||
|
m_device_name.c_str(), m_current_group_id_str.c_str(),
|
||||||
|
m_current_title_id_str.c_str(), m_import_title_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
File::IOFile tmd_file(WFS::NativePath(tmd_path), "wb");
|
||||||
|
tmd_file.WriteBytes(m_tmd.GetBytes().data(), m_tmd.GetBytes().size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_FINALIZE_PATCH_INSTALL:
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_FINALIZE_PATCH_INSTALL");
|
||||||
|
if (m_patch_type != NOT_A_PATCH)
|
||||||
|
{
|
||||||
|
std::string current_title_dir =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
std::string patch_dir = current_title_dir + "/_patch";
|
||||||
|
File::CopyDir(WFS::NativePath(patch_dir), WFS::NativePath(current_title_dir), true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IOCTL_WFSI_DELETE_TITLE:
|
case IOCTL_WFSI_DELETE_TITLE:
|
||||||
// Bytes 0-4: ??
|
// Bytes 0-4: ??
|
||||||
// Bytes 4-8: game id
|
// Bytes 4-8: game id
|
||||||
|
@ -211,26 +311,40 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_DELETE_TITLE: unimplemented");
|
WARN_LOG(IOS_WFS, "IOCTL_WFSI_DELETE_TITLE: unimplemented");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IOCTL_WFSI_IMPORT_TITLE:
|
case IOCTL_WFSI_GET_VERSION:
|
||||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE: unimplemented");
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_VERSION");
|
||||||
|
Memory::Write_U32(0x20, request.buffer_out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IOCTL_WFSI_IMPORT_TITLE_CANCEL:
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_CANCEL");
|
||||||
|
|
||||||
|
bool continue_install = Memory::Read_U32(request.buffer_in) != 0;
|
||||||
|
if (m_patch_type == PatchType::NOT_A_PATCH)
|
||||||
|
return_error_code = CancelTitleImport(continue_install);
|
||||||
|
else if (m_patch_type == PatchType::PATCH_TYPE_1 || m_patch_type == PatchType::PATCH_TYPE_2)
|
||||||
|
return_error_code = CancelPatchImport(continue_install);
|
||||||
|
else
|
||||||
|
return_error_code = WFS_EINVAL;
|
||||||
|
|
||||||
|
m_tmd = {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IOCTL_WFSI_INIT:
|
case IOCTL_WFSI_INIT:
|
||||||
{
|
{
|
||||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_INIT");
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_INIT");
|
||||||
if (GetIOS()->GetES()->GetTitleId(&m_title_id) < 0)
|
u64 tid;
|
||||||
|
if (GetIOS()->GetES()->GetTitleId(&tid) < 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_INIT: Could not get title id.");
|
ERROR_LOG(IOS_WFS, "IOCTL_WFSI_INIT: Could not get title id.");
|
||||||
return_error_code = IPC_EINVAL;
|
return_error_code = IPC_EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_title_id_str = StringFromFormat(
|
|
||||||
"%c%c%c%c", static_cast<char>(m_title_id >> 24), static_cast<char>(m_title_id >> 16),
|
|
||||||
static_cast<char>(m_title_id >> 8), static_cast<char>(m_title_id));
|
|
||||||
|
|
||||||
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(m_title_id);
|
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(tid);
|
||||||
m_group_id = tmd.GetGroupId();
|
SetCurrentTitleIdAndGroupId(tmd.GetTitleId(), tmd.GetGroupId());
|
||||||
m_group_id_str = StringFromFormat("%c%c", m_group_id >> 8, m_group_id & 0xFF);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,18 +354,89 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IOCTL_WFSI_APPLY_TITLE_PROFILE:
|
case IOCTL_WFSI_APPLY_TITLE_PROFILE:
|
||||||
|
{
|
||||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_APPLY_TITLE_PROFILE");
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_APPLY_TITLE_PROFILE");
|
||||||
|
|
||||||
m_base_extract_path = StringFromFormat("/vol/%s/_install/%s/content", m_device_name.c_str(),
|
if (m_patch_type == NOT_A_PATCH)
|
||||||
m_title_id_str.c_str());
|
{
|
||||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
std::string install_directory = StringFromFormat("/vol/%s/_install", m_device_name.c_str());
|
||||||
|
if (!m_continue_install && File::IsDirectory(WFS::NativePath(install_directory)))
|
||||||
|
{
|
||||||
|
File::DeleteDirRecursively(WFS::NativePath(install_directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_base_extract_path = StringFromFormat("%s/%s/content", install_directory.c_str(),
|
||||||
|
m_import_title_id_str.c_str());
|
||||||
|
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||||
|
File::CreateDir(WFS::NativePath(m_base_extract_path));
|
||||||
|
|
||||||
|
for (auto dir : {"work", "meta", "save"})
|
||||||
|
{
|
||||||
|
std::string path = StringFromFormat("%s/%s/%s", install_directory.c_str(),
|
||||||
|
m_import_title_id_str.c_str(), dir);
|
||||||
|
File::CreateDir(WFS::NativePath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string group_path = StringFromFormat("/vol/%s/title/%s", m_device_name.c_str(),
|
||||||
|
m_import_group_id_str.c_str());
|
||||||
|
File::CreateFullPath(WFS::NativePath(group_path));
|
||||||
|
File::CreateDir(WFS::NativePath(group_path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_base_extract_path =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/_patch/content", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||||
|
File::CreateDir(WFS::NativePath(m_base_extract_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_GET_TMD:
|
||||||
|
{
|
||||||
|
u64 subtitle_id = Memory::Read_U64(request.buffer_in);
|
||||||
|
u32 address = Memory::Read_U32(request.buffer_in + 24);
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD: subtitle ID %016" PRIx64, subtitle_id);
|
||||||
|
|
||||||
|
u32 tmd_size;
|
||||||
|
return_error_code =
|
||||||
|
GetTmd(m_current_group_id, m_current_title_id, subtitle_id, address, &tmd_size);
|
||||||
|
Memory::Write_U32(tmd_size, request.buffer_out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_GET_TMD_ABSOLUTE:
|
||||||
|
{
|
||||||
|
u64 subtitle_id = Memory::Read_U64(request.buffer_in);
|
||||||
|
u32 address = Memory::Read_U32(request.buffer_in + 24);
|
||||||
|
u16 group_id = Memory::Read_U16(request.buffer_in + 36);
|
||||||
|
u32 title_id = Memory::Read_U32(request.buffer_in + 32);
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_TMD_ABSOLUTE: tid %08x, gid %04x, subtitle ID %016" PRIx64,
|
||||||
|
title_id, group_id, subtitle_id);
|
||||||
|
|
||||||
|
u32 tmd_size;
|
||||||
|
return_error_code = GetTmd(group_id, title_id, subtitle_id, address, &tmd_size);
|
||||||
|
Memory::Write_U32(tmd_size, request.buffer_out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_SET_FST_BUFFER:
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFSI_SET_FST_BUFFER: address %08x, size %08x", request.buffer_in,
|
||||||
|
request.buffer_in_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_NOOP:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IOCTL_WFSI_LOAD_DOL:
|
case IOCTL_WFSI_LOAD_DOL:
|
||||||
{
|
{
|
||||||
std::string path = StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
std::string path =
|
||||||
m_group_id_str.c_str(), m_title_id_str.c_str());
|
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
|
||||||
u32 dol_addr = Memory::Read_U32(request.buffer_in + 0x18);
|
u32 dol_addr = Memory::Read_U32(request.buffer_in + 0x18);
|
||||||
u32 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14);
|
u32 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14);
|
||||||
|
@ -273,7 +458,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
if (!fp)
|
if (!fp)
|
||||||
{
|
{
|
||||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: no such file or directory: %s", path.c_str());
|
WARN_LOG(IOS_WFS, "IOCTL_WFSI_LOAD_DOL: no such file or directory: %s", path.c_str());
|
||||||
return_error_code = WFSI_ENOENT;
|
return_error_code = WFS_ENOENT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,6 +476,23 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFSI_CHECK_HAS_SPACE:
|
||||||
|
WARN_LOG(IOS_WFS, "IOCTL_WFSI_CHECK_HAS_SPACE: returning true");
|
||||||
|
|
||||||
|
// TODO(wfs): implement this properly.
|
||||||
|
// 1 is returned if there is free space, 0 otherwise.
|
||||||
|
//
|
||||||
|
// WFSI builds a path depending on the import state
|
||||||
|
// /vol/VOLUME_ID/title/GROUP_ID/GAME_ID
|
||||||
|
// /vol/VOLUME_ID/_install/GAME_ID
|
||||||
|
// then removes everything after the last path separator ('/')
|
||||||
|
// it then calls WFSISrvGetFreeBlkNum (ioctl 0x5a, aliased to 0x5b) with that path.
|
||||||
|
// If the ioctl fails, WFSI returns 0.
|
||||||
|
// If the ioctl succeeds, WFSI returns 0 or 1 depending on the three u32s in the input buffer
|
||||||
|
// and the three u32s returned by WFSSRV (TODO: figure out what it does)
|
||||||
|
return_error_code = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO(wfs): Should be returning an error. However until we have
|
// TODO(wfs): Should be returning an error. However until we have
|
||||||
// everything properly stubbed it's easier to simulate the methods
|
// everything properly stubbed it's easier to simulate the methods
|
||||||
|
@ -302,6 +504,75 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
|
|
||||||
return GetDefaultReply(return_error_code);
|
return GetDefaultReply(return_error_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 WFSI::GetTmd(u16 group_id, u32 title_id, u64 subtitle_id, u32 address, u32* size) const
|
||||||
|
{
|
||||||
|
std::string path =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/meta/%016" PRIx64 ".tmd", m_device_name.c_str(),
|
||||||
|
GroupIdStr(group_id).c_str(), TitleIdStr(title_id).c_str(), subtitle_id);
|
||||||
|
File::IOFile fp(WFS::NativePath(path), "rb");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
WARN_LOG(IOS_WFS, "GetTmd: no such file or directory: %s", path.c_str());
|
||||||
|
return WFS_ENOENT;
|
||||||
|
}
|
||||||
|
if (address)
|
||||||
|
{
|
||||||
|
fp.ReadBytes(Memory::GetPointer(address), fp.GetSize());
|
||||||
|
}
|
||||||
|
*size = fp.GetSize();
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32 DeleteTemporaryFiles(const std::string& device_name, u64 title_id)
|
||||||
|
{
|
||||||
|
File::Delete(WFS::NativePath(
|
||||||
|
StringFromFormat("/vol/%s/tmp/%016" PRIx64 ".ini", device_name.c_str(), title_id)));
|
||||||
|
File::Delete(WFS::NativePath(
|
||||||
|
StringFromFormat("/vol/%s/tmp/%016" PRIx64 ".ppcini", device_name.c_str(), title_id)));
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 WFSI::CancelTitleImport(bool continue_install)
|
||||||
|
{
|
||||||
|
m_arc_unpacker.Reset();
|
||||||
|
|
||||||
|
if (!continue_install)
|
||||||
|
{
|
||||||
|
File::DeleteDirRecursively(
|
||||||
|
WFS::NativePath(StringFromFormat("/vol/%s/_install", m_device_name.c_str())));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteTemporaryFiles(m_device_name, m_import_title_id);
|
||||||
|
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 WFSI::CancelPatchImport(bool continue_install)
|
||||||
|
{
|
||||||
|
m_arc_unpacker.Reset();
|
||||||
|
|
||||||
|
if (!continue_install)
|
||||||
|
{
|
||||||
|
File::DeleteDirRecursively(WFS::NativePath(
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/_patch", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str())));
|
||||||
|
|
||||||
|
if (m_patch_type == PatchType::PATCH_TYPE_2)
|
||||||
|
{
|
||||||
|
// Move back _default.dol to default.dol.
|
||||||
|
const std::string content_dir =
|
||||||
|
StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||||
|
m_current_group_id_str.c_str(), m_current_title_id_str.c_str());
|
||||||
|
File::Rename(WFS::NativePath(content_dir + "/_default.dol"),
|
||||||
|
WFS::NativePath(content_dir + "/default.dol"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteTemporaryFiles(m_device_name, m_current_title_id);
|
||||||
|
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
|
|
@ -44,6 +44,14 @@ public:
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
u32 GetTmd(u16 group_id, u32 title_id, u64 subtitle_id, u32 address, u32* size) const;
|
||||||
|
|
||||||
|
void SetCurrentTitleIdAndGroupId(u64 tid, u16 gid);
|
||||||
|
void SetImportTitleIdAndGroupId(u64 tid, u16 gid);
|
||||||
|
|
||||||
|
s32 CancelTitleImport(bool continue_install);
|
||||||
|
s32 CancelPatchImport(bool continue_install);
|
||||||
|
|
||||||
std::string m_device_name;
|
std::string m_device_name;
|
||||||
|
|
||||||
mbedtls_aes_context m_aes_ctx;
|
mbedtls_aes_context m_aes_ctx;
|
||||||
|
@ -52,39 +60,69 @@ private:
|
||||||
|
|
||||||
IOS::ES::TMDReader m_tmd;
|
IOS::ES::TMDReader m_tmd;
|
||||||
std::string m_base_extract_path;
|
std::string m_base_extract_path;
|
||||||
u64 m_title_id;
|
|
||||||
std::string m_title_id_str;
|
u64 m_current_title_id;
|
||||||
u16 m_group_id;
|
std::string m_current_title_id_str;
|
||||||
std::string m_group_id_str;
|
u16 m_current_group_id;
|
||||||
|
std::string m_current_group_id_str;
|
||||||
|
u64 m_import_title_id;
|
||||||
|
std::string m_import_title_id_str;
|
||||||
|
u16 m_import_group_id;
|
||||||
|
std::string m_import_group_id_str;
|
||||||
|
|
||||||
|
// Set on IMPORT_TITLE_INIT when the next profile application should not delete
|
||||||
|
// temporary install files.
|
||||||
|
bool m_continue_install = false;
|
||||||
|
|
||||||
|
// Set on IMPORT_TITLE_INIT to indicate that the install is a patch and not a
|
||||||
|
// standalone title.
|
||||||
|
enum PatchType
|
||||||
|
{
|
||||||
|
NOT_A_PATCH,
|
||||||
|
PATCH_TYPE_1,
|
||||||
|
PATCH_TYPE_2,
|
||||||
|
};
|
||||||
|
PatchType m_patch_type = NOT_A_PATCH;
|
||||||
|
|
||||||
ARCUnpacker m_arc_unpacker;
|
ARCUnpacker m_arc_unpacker;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
WFSI_ENOENT = -12000,
|
IOCTL_WFSI_IMPORT_TITLE_INIT = 0x02,
|
||||||
};
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
IOCTL_WFSI_PREPARE_DEVICE = 0x02,
|
|
||||||
|
|
||||||
IOCTL_WFSI_PREPARE_CONTENT = 0x03,
|
IOCTL_WFSI_PREPARE_CONTENT = 0x03,
|
||||||
IOCTL_WFSI_IMPORT_CONTENT = 0x04,
|
IOCTL_WFSI_IMPORT_CONTENT = 0x04,
|
||||||
IOCTL_WFSI_FINALIZE_CONTENT = 0x05,
|
IOCTL_WFSI_IMPORT_CONTENT_END = 0x05,
|
||||||
|
|
||||||
|
IOCTL_WFSI_FINALIZE_TITLE_INSTALL = 0x06,
|
||||||
|
|
||||||
IOCTL_WFSI_DELETE_TITLE = 0x17,
|
IOCTL_WFSI_DELETE_TITLE = 0x17,
|
||||||
IOCTL_WFSI_IMPORT_TITLE = 0x2f,
|
|
||||||
|
IOCTL_WFSI_GET_VERSION = 0x1b,
|
||||||
|
|
||||||
|
IOCTL_WFSI_IMPORT_TITLE_CANCEL = 0x2f,
|
||||||
|
|
||||||
IOCTL_WFSI_INIT = 0x81,
|
IOCTL_WFSI_INIT = 0x81,
|
||||||
IOCTL_WFSI_SET_DEVICE_NAME = 0x82,
|
IOCTL_WFSI_SET_DEVICE_NAME = 0x82,
|
||||||
|
|
||||||
IOCTL_WFSI_PREPARE_PROFILE = 0x86,
|
IOCTL_WFSI_PREPARE_PROFILE = 0x86,
|
||||||
IOCTL_WFSI_IMPORT_PROFILE = 0x87,
|
IOCTL_WFSI_IMPORT_PROFILE = 0x87,
|
||||||
IOCTL_WFSI_FINALIZE_PROFILE = 0x88,
|
IOCTL_WFSI_IMPORT_PROFILE_END = 0x88,
|
||||||
|
|
||||||
IOCTL_WFSI_APPLY_TITLE_PROFILE = 0x89,
|
IOCTL_WFSI_APPLY_TITLE_PROFILE = 0x89,
|
||||||
|
|
||||||
|
IOCTL_WFSI_GET_TMD = 0x8a,
|
||||||
|
IOCTL_WFSI_GET_TMD_ABSOLUTE = 0x8b,
|
||||||
|
|
||||||
|
IOCTL_WFSI_SET_FST_BUFFER = 0x8e,
|
||||||
|
|
||||||
|
IOCTL_WFSI_NOOP = 0x8f,
|
||||||
|
|
||||||
IOCTL_WFSI_LOAD_DOL = 0x90,
|
IOCTL_WFSI_LOAD_DOL = 0x90,
|
||||||
|
|
||||||
|
IOCTL_WFSI_FINALIZE_PATCH_INSTALL = 0x91,
|
||||||
|
|
||||||
|
IOCTL_WFSI_CHECK_HAS_SPACE = 0x95,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -100,6 +101,29 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_FLUSH: doing nothing");
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_FLUSH: doing nothing");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IOCTL_WFS_MKDIR:
|
||||||
|
{
|
||||||
|
std::string path = NormalizePath(
|
||||||
|
Memory::GetString(request.buffer_in + 34, Memory::Read_U16(request.buffer_in + 32)));
|
||||||
|
std::string native_path = WFS::NativePath(path);
|
||||||
|
|
||||||
|
if (File::Exists(native_path))
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): already exists", path.c_str());
|
||||||
|
return_error_code = WFS_EEXIST;
|
||||||
|
}
|
||||||
|
else if (!File::CreateDir(native_path))
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): no such file or directory", path.c_str());
|
||||||
|
return_error_code = WFS_ENOENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_MKDIR(%s): directory created", path.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(wfs): Globbing is not really implemented, we just fake the one case
|
// TODO(wfs): Globbing is not really implemented, we just fake the one case
|
||||||
// (listing /vol/*) which is required to get the installer to work.
|
// (listing /vol/*) which is required to get the installer to work.
|
||||||
case IOCTL_WFS_GLOB_START:
|
case IOCTL_WFS_GLOB_START:
|
||||||
|
@ -136,9 +160,48 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
Memory::CopyToEmu(request.buffer_out + 2, m_home_directory.data(), m_home_directory.size());
|
Memory::CopyToEmu(request.buffer_out + 2, m_home_directory.data(), m_home_directory.size());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IOCTL_WFS_GET_ATTRIBUTES:
|
||||||
|
{
|
||||||
|
std::string path = NormalizePath(
|
||||||
|
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in)));
|
||||||
|
std::string native_path = WFS::NativePath(path);
|
||||||
|
Memory::Memset(0, request.buffer_out, request.buffer_out_size);
|
||||||
|
if (!File::Exists(native_path))
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): no such file or directory", path.c_str());
|
||||||
|
return_error_code = WFS_ENOENT;
|
||||||
|
}
|
||||||
|
else if (File::IsDirectory(native_path))
|
||||||
|
{
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): directory", path.c_str());
|
||||||
|
Memory::Write_U32(0x80000000, request.buffer_out + 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 size = static_cast<u32>(File::GetSize(native_path));
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_GET_ATTRIBUTES(%s): file with size %d", path.c_str(), size);
|
||||||
|
Memory::Write_U32(size, request.buffer_out);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFS_RENAME:
|
||||||
|
case IOCTL_WFS_RENAME_2:
|
||||||
|
{
|
||||||
|
const std::string source_path =
|
||||||
|
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||||
|
const std::string dest_path =
|
||||||
|
Memory::GetString(request.buffer_in + 512 + 2, Memory::Read_U16(request.buffer_in + 512));
|
||||||
|
return_error_code = Rename(source_path, dest_path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFS_CREATE_OPEN:
|
||||||
case IOCTL_WFS_OPEN:
|
case IOCTL_WFS_OPEN:
|
||||||
{
|
{
|
||||||
u32 mode = Memory::Read_U32(request.buffer_in);
|
const char* ioctl_name =
|
||||||
|
request.request == IOCTL_WFS_OPEN ? "IOCTL_WFS_OPEN" : "IOCTL_WFS_CREATE_OPEN";
|
||||||
|
u32 mode = request.request == IOCTL_WFS_OPEN ? Memory::Read_U32(request.buffer_in) : 2;
|
||||||
u16 path_len = Memory::Read_U16(request.buffer_in + 0x20);
|
u16 path_len = Memory::Read_U16(request.buffer_in + 0x20);
|
||||||
std::string path = Memory::GetString(request.buffer_in + 0x22, path_len);
|
std::string path = Memory::GetString(request.buffer_in + 0x22, path_len);
|
||||||
|
|
||||||
|
@ -153,14 +216,21 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
|
|
||||||
if (!fd_obj->Open())
|
if (!fd_obj->Open())
|
||||||
{
|
{
|
||||||
ERROR_LOG(IOS_WFS, "IOCTL_WFS_OPEN(%s, %d): error opening file", path.c_str(), mode);
|
ERROR_LOG(IOS_WFS, "%s(%s, %d): error opening file", ioctl_name, path.c_str(), mode);
|
||||||
ReleaseFileDescriptor(fd);
|
ReleaseFileDescriptor(fd);
|
||||||
return_error_code = WFS_ENOENT;
|
return_error_code = WFS_ENOENT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_OPEN(%s, %d) -> %d", path.c_str(), mode, fd);
|
INFO_LOG(IOS_WFS, "%s(%s, %d) -> %d", ioctl_name, path.c_str(), mode, fd);
|
||||||
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
if (request.request == IOCTL_WFS_OPEN)
|
||||||
|
{
|
||||||
|
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Memory::Write_U16(fd, request.buffer_out);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +264,16 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFS_CLOSE_2:
|
||||||
|
{
|
||||||
|
// TODO(wfs): Figure out the exact semantics difference from the other
|
||||||
|
// close.
|
||||||
|
u16 fd = Memory::Read_U16(request.buffer_in + 0x4);
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_CLOSE_2(%d)", fd);
|
||||||
|
ReleaseFileDescriptor(fd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IOCTL_WFS_READ:
|
case IOCTL_WFS_READ:
|
||||||
case IOCTL_WFS_READ_ABSOLUTE:
|
case IOCTL_WFS_READ_ABSOLUTE:
|
||||||
{
|
{
|
||||||
|
@ -235,6 +315,45 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IOCTL_WFS_WRITE:
|
||||||
|
case IOCTL_WFS_WRITE_ABSOLUTE:
|
||||||
|
{
|
||||||
|
u32 addr = Memory::Read_U32(request.buffer_in);
|
||||||
|
u32 position = Memory::Read_U32(request.buffer_in + 4); // Only for absolute.
|
||||||
|
u16 fd = Memory::Read_U16(request.buffer_in + 0xC);
|
||||||
|
u32 size = Memory::Read_U32(request.buffer_in + 8);
|
||||||
|
|
||||||
|
bool absolute = request.request == IOCTL_WFS_WRITE_ABSOLUTE;
|
||||||
|
|
||||||
|
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||||
|
if (fd_obj == nullptr)
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_WFS, "IOCTL_WFS_WRITE: invalid file descriptor %d", fd);
|
||||||
|
return_error_code = WFS_EBADFD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 previous_position = fd_obj->file.Tell();
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
fd_obj->file.Seek(position, SEEK_SET);
|
||||||
|
}
|
||||||
|
fd_obj->file.WriteArray(Memory::GetPointer(addr), size);
|
||||||
|
// TODO(wfs): Handle write errors.
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
fd_obj->file.Seek(previous_position, SEEK_SET);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fd_obj->position += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_WRITE: written %d bytes from FD %d (%s)", size, fd,
|
||||||
|
fd_obj->path.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO(wfs): Should be returning -3. However until we have everything
|
// TODO(wfs): Should be returning -3. However until we have everything
|
||||||
// properly stubbed it's easier to simulate the methods succeeding.
|
// properly stubbed it's easier to simulate the methods succeeding.
|
||||||
|
@ -246,6 +365,26 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
||||||
return GetDefaultReply(return_error_code);
|
return GetDefaultReply(return_error_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 WFSSRV::Rename(std::string source, std::string dest) const
|
||||||
|
{
|
||||||
|
source = NormalizePath(source);
|
||||||
|
dest = NormalizePath(dest);
|
||||||
|
|
||||||
|
INFO_LOG(IOS_WFS, "IOCTL_WFS_RENAME: %s to %s", source.c_str(), dest.c_str());
|
||||||
|
|
||||||
|
const bool opened = std::any_of(m_fds.begin(), m_fds.end(),
|
||||||
|
[&](const auto& fd) { return fd.in_use && fd.path == source; });
|
||||||
|
|
||||||
|
if (opened)
|
||||||
|
return WFS_FILE_IS_OPENED;
|
||||||
|
|
||||||
|
// TODO(wfs): Handle other rename failures
|
||||||
|
if (!File::Rename(WFS::NativePath(source), WFS::NativePath(dest)))
|
||||||
|
return WFS_ENOENT;
|
||||||
|
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
std::string WFSSRV::NormalizePath(const std::string& path) const
|
std::string WFSSRV::NormalizePath(const std::string& path) const
|
||||||
{
|
{
|
||||||
std::string expanded;
|
std::string expanded;
|
||||||
|
|
|
@ -22,6 +22,15 @@ namespace WFS
|
||||||
std::string NativePath(const std::string& wfs_path);
|
std::string NativePath(const std::string& wfs_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
WFS_EINVAL = -10003, // Invalid argument.
|
||||||
|
WFS_EBADFD = -10026, // Invalid file descriptor.
|
||||||
|
WFS_EEXIST = -10027, // File already exists.
|
||||||
|
WFS_ENOENT = -10028, // No such file or directory.
|
||||||
|
WFS_FILE_IS_OPENED = -10032, // Cannot perform operation on an opened file.
|
||||||
|
};
|
||||||
|
|
||||||
namespace Device
|
namespace Device
|
||||||
{
|
{
|
||||||
class WFSSRV : public Device
|
class WFSSRV : public Device
|
||||||
|
@ -31,6 +40,8 @@ public:
|
||||||
|
|
||||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||||
|
|
||||||
|
s32 Rename(std::string source, std::string dest) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// WFS device name, e.g. msc01/msc02.
|
// WFS device name, e.g. msc01/msc02.
|
||||||
std::string m_device_name;
|
std::string m_device_name;
|
||||||
|
@ -59,7 +70,9 @@ private:
|
||||||
IOCTL_WFS_GET_HOMEDIR = 0x12,
|
IOCTL_WFS_GET_HOMEDIR = 0x12,
|
||||||
IOCTL_WFS_GETCWD = 0x13,
|
IOCTL_WFS_GETCWD = 0x13,
|
||||||
IOCTL_WFS_DELETE = 0x15,
|
IOCTL_WFS_DELETE = 0x15,
|
||||||
|
IOCTL_WFS_RENAME = 0x16,
|
||||||
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
||||||
|
IOCTL_WFS_CREATE_OPEN = 0x19,
|
||||||
IOCTL_WFS_OPEN = 0x1A,
|
IOCTL_WFS_OPEN = 0x1A,
|
||||||
IOCTL_WFS_GET_SIZE = 0x1B,
|
IOCTL_WFS_GET_SIZE = 0x1B,
|
||||||
IOCTL_WFS_CLOSE = 0x1E,
|
IOCTL_WFS_CLOSE = 0x1E,
|
||||||
|
@ -67,13 +80,10 @@ private:
|
||||||
IOCTL_WFS_WRITE = 0x22,
|
IOCTL_WFS_WRITE = 0x22,
|
||||||
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
||||||
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
||||||
|
IOCTL_WFS_RENAME_2 = 0x41,
|
||||||
|
IOCTL_WFS_CLOSE_2 = 0x47,
|
||||||
IOCTL_WFS_READ_ABSOLUTE = 0x48,
|
IOCTL_WFS_READ_ABSOLUTE = 0x48,
|
||||||
};
|
IOCTL_WFS_WRITE_ABSOLUTE = 0x49,
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
WFS_EBADFD = -10026, // Invalid file descriptor.
|
|
||||||
WFS_ENOENT = -10028, // No such file or directory.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileDescriptor
|
struct FileDescriptor
|
||||||
|
|
Loading…
Reference in New Issue