commit
187e65f495
|
@ -521,7 +521,7 @@ bool DeleteDirRecursively(const std::string& directory)
|
|||
}
|
||||
|
||||
// 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)
|
||||
return;
|
||||
|
@ -562,10 +562,16 @@ void CopyDir(const std::string& source_path, const std::string& dest_path)
|
|||
{
|
||||
if (!Exists(dest))
|
||||
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
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
|
|
|
@ -156,8 +156,9 @@ bool DeleteDirRecursively(const std::string& directory);
|
|||
// Returns the current directory
|
||||
std::string GetCurrentDir();
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
// Create directory and copy contents (optionally overwrites existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path,
|
||||
bool destructive = false);
|
||||
|
||||
// Set the current directory to given directory
|
||||
bool SetCurrentDir(const std::string& directory);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Core/IOS/WFS/WFSI.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
@ -20,6 +21,20 @@
|
|||
#include "Core/IOS/WFS/WFSSRV.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 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)
|
||||
{
|
||||
s32 return_error_code = IPC_SUCCESS;
|
||||
|
||||
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_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;
|
||||
if (tmd_size > MAX_TMD_SIZE)
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE_INIT: patch type %d, continue install: %s",
|
||||
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;
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -175,12 +228,12 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_FINALIZE_PROFILE:
|
||||
case IOCTL_WFSI_FINALIZE_CONTENT:
|
||||
case IOCTL_WFSI_IMPORT_CONTENT_END:
|
||||
case IOCTL_WFSI_IMPORT_PROFILE_END:
|
||||
{
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_FINALIZE_PROFILE ?
|
||||
"IOCTL_WFSI_FINALIZE_PROFILE" :
|
||||
"IOCTL_WFSI_FINALIZE_CONTENT";
|
||||
const char* ioctl_name = request.request == IOCTL_WFSI_IMPORT_PROFILE_END ?
|
||||
"IOCTL_WFSI_IMPORT_PROFILE_END" :
|
||||
"IOCTL_WFSI_IMPORT_CONTENT_END";
|
||||
INFO_LOG(IOS_WFS, "%s", ioctl_name);
|
||||
|
||||
auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) {
|
||||
|
@ -204,6 +257,53 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
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:
|
||||
// Bytes 0-4: ??
|
||||
// Bytes 4-8: game id
|
||||
|
@ -211,26 +311,40 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_DELETE_TITLE: unimplemented");
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_IMPORT_TITLE:
|
||||
WARN_LOG(IOS_WFS, "IOCTL_WFSI_IMPORT_TITLE: unimplemented");
|
||||
case IOCTL_WFSI_GET_VERSION:
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFSI_GET_VERSION");
|
||||
Memory::Write_U32(0x20, request.buffer_out);
|
||||
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:
|
||||
{
|
||||
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.");
|
||||
return_error_code = IPC_EINVAL;
|
||||
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);
|
||||
m_group_id = tmd.GetGroupId();
|
||||
m_group_id_str = StringFromFormat("%c%c", m_group_id >> 8, m_group_id & 0xFF);
|
||||
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(tid);
|
||||
SetCurrentTitleIdAndGroupId(tmd.GetTitleId(), tmd.GetGroupId());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -240,18 +354,89 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
|
||||
case 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(),
|
||||
m_title_id_str.c_str());
|
||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||
if (m_patch_type == NOT_A_PATCH)
|
||||
{
|
||||
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;
|
||||
|
||||
case IOCTL_WFSI_LOAD_DOL:
|
||||
{
|
||||
std::string path = StringFromFormat("/vol/%s/title/%s/%s/content", m_device_name.c_str(),
|
||||
m_group_id_str.c_str(), m_title_id_str.c_str());
|
||||
std::string path =
|
||||
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 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14);
|
||||
|
@ -273,7 +458,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
if (!fp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -291,6 +476,23 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
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:
|
||||
// TODO(wfs): Should be returning an error. However until we have
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 HLE
|
||||
} // namespace IOS
|
||||
|
|
|
@ -44,6 +44,14 @@ public:
|
|||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
|
||||
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;
|
||||
|
||||
mbedtls_aes_context m_aes_ctx;
|
||||
|
@ -52,39 +60,69 @@ private:
|
|||
|
||||
IOS::ES::TMDReader m_tmd;
|
||||
std::string m_base_extract_path;
|
||||
u64 m_title_id;
|
||||
std::string m_title_id_str;
|
||||
u16 m_group_id;
|
||||
std::string m_group_id_str;
|
||||
|
||||
u64 m_current_title_id;
|
||||
std::string m_current_title_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;
|
||||
|
||||
enum
|
||||
{
|
||||
WFSI_ENOENT = -12000,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFSI_PREPARE_DEVICE = 0x02,
|
||||
IOCTL_WFSI_IMPORT_TITLE_INIT = 0x02,
|
||||
|
||||
IOCTL_WFSI_PREPARE_CONTENT = 0x03,
|
||||
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_IMPORT_TITLE = 0x2f,
|
||||
|
||||
IOCTL_WFSI_GET_VERSION = 0x1b,
|
||||
|
||||
IOCTL_WFSI_IMPORT_TITLE_CANCEL = 0x2f,
|
||||
|
||||
IOCTL_WFSI_INIT = 0x81,
|
||||
IOCTL_WFSI_SET_DEVICE_NAME = 0x82,
|
||||
|
||||
IOCTL_WFSI_PREPARE_PROFILE = 0x86,
|
||||
IOCTL_WFSI_IMPORT_PROFILE = 0x87,
|
||||
IOCTL_WFSI_FINALIZE_PROFILE = 0x88,
|
||||
IOCTL_WFSI_IMPORT_PROFILE_END = 0x88,
|
||||
|
||||
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_FINALIZE_PATCH_INSTALL = 0x91,
|
||||
|
||||
IOCTL_WFSI_CHECK_HAS_SPACE = 0x95,
|
||||
};
|
||||
};
|
||||
} // namespace Device
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Core/IOS/WFS/WFSSRV.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -100,6 +101,29 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
INFO_LOG(IOS_WFS, "IOCTL_WFS_FLUSH: doing nothing");
|
||||
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
|
||||
// (listing /vol/*) which is required to get the installer to work.
|
||||
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());
|
||||
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:
|
||||
{
|
||||
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);
|
||||
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())
|
||||
{
|
||||
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);
|
||||
return_error_code = WFS_ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
INFO_LOG(IOS_WFS, "IOCTL_WFS_OPEN(%s, %d) -> %d", path.c_str(), mode, fd);
|
||||
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
||||
INFO_LOG(IOS_WFS, "%s(%s, %d) -> %d", ioctl_name, path.c_str(), mode, fd);
|
||||
if (request.request == IOCTL_WFS_OPEN)
|
||||
{
|
||||
Memory::Write_U16(fd, request.buffer_out + 0x14);
|
||||
}
|
||||
else
|
||||
{
|
||||
Memory::Write_U16(fd, request.buffer_out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -194,6 +264,16 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
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_ABSOLUTE:
|
||||
{
|
||||
|
@ -235,6 +315,45 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
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:
|
||||
// TODO(wfs): Should be returning -3. However until we have everything
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 expanded;
|
||||
|
|
|
@ -22,6 +22,15 @@ namespace WFS
|
|||
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
|
||||
{
|
||||
class WFSSRV : public Device
|
||||
|
@ -31,6 +40,8 @@ public:
|
|||
|
||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
|
||||
s32 Rename(std::string source, std::string dest) const;
|
||||
|
||||
private:
|
||||
// WFS device name, e.g. msc01/msc02.
|
||||
std::string m_device_name;
|
||||
|
@ -59,7 +70,9 @@ private:
|
|||
IOCTL_WFS_GET_HOMEDIR = 0x12,
|
||||
IOCTL_WFS_GETCWD = 0x13,
|
||||
IOCTL_WFS_DELETE = 0x15,
|
||||
IOCTL_WFS_RENAME = 0x16,
|
||||
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
||||
IOCTL_WFS_CREATE_OPEN = 0x19,
|
||||
IOCTL_WFS_OPEN = 0x1A,
|
||||
IOCTL_WFS_GET_SIZE = 0x1B,
|
||||
IOCTL_WFS_CLOSE = 0x1E,
|
||||
|
@ -67,13 +80,10 @@ private:
|
|||
IOCTL_WFS_WRITE = 0x22,
|
||||
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
||||
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
||||
IOCTL_WFS_RENAME_2 = 0x41,
|
||||
IOCTL_WFS_CLOSE_2 = 0x47,
|
||||
IOCTL_WFS_READ_ABSOLUTE = 0x48,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
WFS_EBADFD = -10026, // Invalid file descriptor.
|
||||
WFS_ENOENT = -10028, // No such file or directory.
|
||||
IOCTL_WFS_WRITE_ABSOLUTE = 0x49,
|
||||
};
|
||||
|
||||
struct FileDescriptor
|
||||
|
|
Loading…
Reference in New Issue