Merge pull request #5930 from delroth/wfs
Fix Dragon Quest X offline mode on Dolphin
This commit is contained in:
commit
9b79e0ac72
|
@ -58,6 +58,35 @@ std::string GetTMDFileName(u64 _titleID, FromWhichRoot from)
|
|||
return GetTitleContentPath(_titleID, from) + "title.tmd";
|
||||
}
|
||||
|
||||
bool IsTitlePath(const std::string& path, FromWhichRoot from, u64* title_id)
|
||||
{
|
||||
std::string expected_prefix = RootUserPath(from) + "/title/";
|
||||
if (!StringBeginsWith(path, expected_prefix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to find a title ID in the remaining path.
|
||||
std::string subdirectory = path.substr(expected_prefix.size());
|
||||
std::vector<std::string> components = SplitString(subdirectory, '/');
|
||||
if (components.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 title_id_high, title_id_low;
|
||||
if (!AsciiToHex(components[0], title_id_high) || !AsciiToHex(components[1], title_id_low))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (title_id != nullptr)
|
||||
{
|
||||
*title_id = (static_cast<u64>(title_id_high) << 32) | title_id_low;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string EscapeFileName(const std::string& filename)
|
||||
{
|
||||
// Prevent paths from containing special names like ., .., ..., ...., and so on
|
||||
|
|
|
@ -29,6 +29,9 @@ std::string GetTitleDataPath(u64 _titleID, FromWhichRoot from);
|
|||
std::string GetTitleContentPath(u64 _titleID, FromWhichRoot from);
|
||||
std::string GetTMDFileName(u64 _titleID, FromWhichRoot from);
|
||||
|
||||
// Returns whether a path is within an installed title's directory.
|
||||
bool IsTitlePath(const std::string& path, FromWhichRoot from, u64* title_id = nullptr);
|
||||
|
||||
// Escapes characters that are invalid or have special meanings in the host file system
|
||||
std::string EscapeFileName(const std::string& filename);
|
||||
// Escapes characters that are invalid or have special meanings in the host file system
|
||||
|
|
|
@ -186,6 +186,7 @@ set(SRCS
|
|||
IOS/USB/OH0/OH0.cpp
|
||||
IOS/USB/OH0/OH0Device.cpp
|
||||
IOS/USB/USB_HID/HIDv4.cpp
|
||||
IOS/USB/USB_HID/HIDv5.cpp
|
||||
IOS/USB/USB_VEN/VEN.cpp
|
||||
IOS/USB/USBV0.cpp
|
||||
IOS/USB/USBV4.cpp
|
||||
|
|
|
@ -221,6 +221,7 @@
|
|||
<ClCompile Include="IOS\USB\OH0\OH0.cpp" />
|
||||
<ClCompile Include="IOS\USB\OH0\OH0Device.cpp" />
|
||||
<ClCompile Include="IOS\USB\USB_HID\HIDv4.cpp" />
|
||||
<ClCompile Include="IOS\USB\USB_HID\HIDv5.cpp" />
|
||||
<ClCompile Include="IOS\USB\USB_VEN\VEN.cpp" />
|
||||
<ClCompile Include="IOS\USB\USBV0.cpp" />
|
||||
<ClCompile Include="IOS\USB\USBV4.cpp" />
|
||||
|
@ -468,6 +469,7 @@
|
|||
<ClInclude Include="IOS\USB\OH0\OH0.h" />
|
||||
<ClInclude Include="IOS\USB\OH0\OH0Device.h" />
|
||||
<ClInclude Include="IOS\USB\USB_HID\HIDv4.h" />
|
||||
<ClInclude Include="IOS\USB\USB_HID\HIDv5.h" />
|
||||
<ClInclude Include="IOS\USB\USB_VEN\VEN.h" />
|
||||
<ClInclude Include="IOS\USB\USBV0.h" />
|
||||
<ClInclude Include="IOS\USB\USBV4.h" />
|
||||
|
|
|
@ -826,6 +826,9 @@
|
|||
<ClCompile Include="IOS\USB\USB_HID\HIDv4.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\USB_HID\HIDv5.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IOS\USB\USB_VEN\VEN.cpp">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1469,6 +1472,9 @@
|
|||
<ClInclude Include="IOS\USB\USB_HID\HIDv4.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\USB_HID\HIDv5.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IOS\USB\USB_VEN\VEN.h">
|
||||
<Filter>IOS\USB</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -165,15 +165,24 @@ IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
|
|||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetTitleID(const IOCtlVRequest& request)
|
||||
ReturnCode ES::GetTitleId(u64* title_id) const
|
||||
{
|
||||
if (!s_title_context.active)
|
||||
return ES_EINVAL;
|
||||
*title_id = s_title_context.tmd.GetTitleId();
|
||||
return IPC_SUCCESS;
|
||||
}
|
||||
|
||||
IPCCommandResult ES::GetTitleId(const IOCtlVRequest& request)
|
||||
{
|
||||
if (!request.HasNumberOfValidVectors(0, 1))
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
|
||||
if (!s_title_context.active)
|
||||
return GetDefaultReply(ES_EINVAL);
|
||||
u64 title_id;
|
||||
const ReturnCode ret = GetTitleId(&title_id);
|
||||
if (ret != IPC_SUCCESS)
|
||||
return GetDefaultReply(ret);
|
||||
|
||||
const u64 title_id = s_title_context.tmd.GetTitleId();
|
||||
Memory::Write_U64(title_id, request.io_vectors[0].address);
|
||||
INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", static_cast<u32>(title_id >> 32),
|
||||
static_cast<u32>(title_id));
|
||||
|
@ -421,7 +430,7 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
|
|||
case IOCTL_ES_GETTITLEDIR:
|
||||
return GetTitleDirectory(request);
|
||||
case IOCTL_ES_GETTITLEID:
|
||||
return GetTitleID(request);
|
||||
return GetTitleId(request);
|
||||
case IOCTL_ES_SETUID:
|
||||
return SetUID(context->uid, request);
|
||||
case IOCTL_ES_DIVERIFY:
|
||||
|
|
|
@ -127,6 +127,7 @@ public:
|
|||
ReturnCode DeleteContent(u64 title_id, u32 content_id) const;
|
||||
|
||||
ReturnCode GetDeviceId(u32* device_id) const;
|
||||
ReturnCode GetTitleId(u64* device_id) const;
|
||||
|
||||
// Views
|
||||
ReturnCode GetV0TicketFromView(const u8* ticket_view, u8* ticket) const;
|
||||
|
@ -243,7 +244,7 @@ private:
|
|||
// Misc
|
||||
IPCCommandResult SetUID(u32 uid, const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTitleID(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetTitleId(const IOCtlVRequest& request);
|
||||
IPCCommandResult GetConsumption(const IOCtlVRequest& request);
|
||||
IPCCommandResult Launch(const IOCtlVRequest& request);
|
||||
IPCCommandResult LaunchBC(const IOCtlVRequest& request);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "Common/NandPaths.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/IOS/ES/ES.h"
|
||||
#include "Core/IOS/ES/Formats.h"
|
||||
#include "Core/IOS/FS/FileIO.h"
|
||||
|
||||
namespace IOS
|
||||
|
@ -326,6 +328,18 @@ IPCCommandResult FS::GetAttribute(const IOCtlRequest& request)
|
|||
u8 OtherPerm = 0x3; // read/write
|
||||
u8 Attributes = 0x00; // no attributes
|
||||
|
||||
// Hack: if the path that is being accessed is within an installed title directory, get the
|
||||
// UID/GID from the installed title TMD.
|
||||
u64 title_id;
|
||||
if (IsTitlePath(Filename, Common::FROM_SESSION_ROOT, &title_id))
|
||||
{
|
||||
IOS::ES::TMDReader tmd = GetIOS()->GetES()->FindInstalledTMD(title_id);
|
||||
if (tmd.IsValid())
|
||||
{
|
||||
GroupID = tmd.GetGroupId();
|
||||
}
|
||||
}
|
||||
|
||||
if (File::IsDirectory(Filename))
|
||||
{
|
||||
INFO_LOG(IOS_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set",
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "Core/IOS/USB/OH0/OH0.h"
|
||||
#include "Core/IOS/USB/OH0/OH0Device.h"
|
||||
#include "Core/IOS/USB/USB_HID/HIDv4.h"
|
||||
#include "Core/IOS/USB/USB_HID/HIDv5.h"
|
||||
#include "Core/IOS/USB/USB_KBD.h"
|
||||
#include "Core/IOS/USB/USB_VEN/VEN.h"
|
||||
#include "Core/IOS/WFS/WFSI.h"
|
||||
|
@ -361,7 +362,10 @@ void Kernel::AddStaticDevices()
|
|||
AddDevice(std::make_unique<Device::USB_KBD>(*this, "/dev/usb/kbd"));
|
||||
AddDevice(std::make_unique<Device::SDIOSlot0>(*this, "/dev/sdio/slot0"));
|
||||
AddDevice(std::make_unique<Device::Stub>(*this, "/dev/sdio/slot1"));
|
||||
AddDevice(std::make_unique<Device::USB_HIDv4>(*this, "/dev/usb/hid"));
|
||||
if (GetVersion() == 59)
|
||||
AddDevice(std::make_unique<Device::USB_HIDv5>(*this, "/dev/usb/hid"));
|
||||
else
|
||||
AddDevice(std::make_unique<Device::USB_HIDv4>(*this, "/dev/usb/hid"));
|
||||
AddDevice(std::make_unique<Device::OH0>(*this, "/dev/usb/oh0"));
|
||||
AddDevice(std::make_unique<Device::Stub>(*this, "/dev/usb/oh1"));
|
||||
AddDevice(std::make_unique<Device::USB_VEN>(*this, "/dev/usb/ven"));
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/IOS/USB/USB_HID/HIDv5.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IOS/Device.h"
|
||||
#include "Core/IOS/USB/Common.h"
|
||||
#include "Core/IOS/USB/USBV5.h"
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
USB_HIDv5::USB_HIDv5(Kernel& ios, const std::string& device_name) : USBHost(ios, device_name)
|
||||
{
|
||||
}
|
||||
|
||||
USB_HIDv5::~USB_HIDv5()
|
||||
{
|
||||
StopThreads();
|
||||
}
|
||||
|
||||
IPCCommandResult USB_HIDv5::IOCtl(const IOCtlRequest& request)
|
||||
{
|
||||
request.Log(GetDeviceName(), LogTypes::IOS_USB);
|
||||
switch (request.request)
|
||||
{
|
||||
case USB::IOCTL_USBV5_GETVERSION:
|
||||
Memory::Write_U32(VERSION, request.buffer_out);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
case USB::IOCTL_USBV5_SHUTDOWN:
|
||||
if (m_hanging_request)
|
||||
{
|
||||
IOCtlRequest hanging_request{m_hanging_request};
|
||||
m_ios.EnqueueIPCReply(hanging_request, IPC_SUCCESS);
|
||||
}
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
case USB::IOCTL_USBV5_GETDEVICECHANGE:
|
||||
if (m_devicechange_replied)
|
||||
{
|
||||
m_hanging_request = request.address;
|
||||
return GetNoReply();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_devicechange_replied = true;
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
default:
|
||||
request.DumpUnknown(GetDeviceName(), LogTypes::IOS_USB);
|
||||
return GetDefaultReply(IPC_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Host.h"
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
namespace HLE
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
// Stub implementation that only gets DQX to boot.
|
||||
class USB_HIDv5 : public USBHost
|
||||
{
|
||||
public:
|
||||
USB_HIDv5(Kernel& ios, const std::string& device_name);
|
||||
~USB_HIDv5() override;
|
||||
|
||||
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 VERSION = 0x50001;
|
||||
|
||||
u32 m_hanging_request = 0;
|
||||
bool m_devicechange_replied = false;
|
||||
};
|
||||
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
} // namespace IOS
|
|
@ -15,6 +15,7 @@
|
|||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IOS/ES/ES.h"
|
||||
#include "Core/IOS/ES/Formats.h"
|
||||
#include "Core/IOS/WFS/WFSSRV.h"
|
||||
#include "DiscIO/NANDContentLoader.h"
|
||||
|
@ -101,7 +102,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
constexpr u32 MAX_TMD_SIZE = 0x4000;
|
||||
if (tmd_size > MAX_TMD_SIZE)
|
||||
{
|
||||
ERROR_LOG(IOS, "IOCTL_WFSI_INIT: TMD size too large (%d)", tmd_size);
|
||||
ERROR_LOG(IOS, "IOCTL_WFSI_PREPARE_DEVICE: TMD size too large (%d)", tmd_size);
|
||||
return_error_code = IPC_EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -215,9 +216,23 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
|
||||
case IOCTL_WFSI_INIT:
|
||||
// Nothing to do.
|
||||
{
|
||||
INFO_LOG(IOS, "IOCTL_WFSI_INIT");
|
||||
if (GetIOS()->GetES()->GetTitleId(&m_title_id) < 0)
|
||||
{
|
||||
ERROR_LOG(IOS, "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);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_SET_DEVICE_NAME:
|
||||
INFO_LOG(IOS, "IOCTL_WFSI_SET_DEVICE_NAME");
|
||||
|
@ -227,14 +242,55 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
|||
case IOCTL_WFSI_APPLY_TITLE_PROFILE:
|
||||
INFO_LOG(IOS, "IOCTL_WFSI_APPLY_TITLE_PROFILE");
|
||||
|
||||
m_base_extract_path = StringFromFormat(
|
||||
"/vol/%s/_install/%c%c%c%c/content", m_device_name.c_str(),
|
||||
static_cast<char>(m_tmd.GetTitleId() >> 24), static_cast<char>(m_tmd.GetTitleId() >> 16),
|
||||
static_cast<char>(m_tmd.GetTitleId() >> 8), static_cast<char>(m_tmd.GetTitleId()));
|
||||
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));
|
||||
|
||||
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());
|
||||
|
||||
u32 dol_addr = Memory::Read_U32(request.buffer_in + 0x18);
|
||||
u32 max_dol_size = Memory::Read_U32(request.buffer_in + 0x14);
|
||||
u16 dol_extension_id = Memory::Read_U16(request.buffer_in + 0x1e);
|
||||
|
||||
if (dol_extension_id == 0)
|
||||
{
|
||||
path += "/default.dol";
|
||||
}
|
||||
else
|
||||
{
|
||||
path += StringFromFormat("/extension%d.dol", dol_extension_id);
|
||||
}
|
||||
|
||||
INFO_LOG(IOS, "IOCTL_WFSI_LOAD_DOL: loading %s at address %08x (size %d)", path.c_str(),
|
||||
dol_addr, max_dol_size);
|
||||
|
||||
File::IOFile fp(WFS::NativePath(path), "rb");
|
||||
if (!fp)
|
||||
{
|
||||
WARN_LOG(IOS, "IOCTL_WFSI_LOAD_DOL: no such file or directory: %s", path.c_str());
|
||||
return_error_code = WFSI_ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 real_dol_size = fp.GetSize();
|
||||
if (dol_addr == 0)
|
||||
{
|
||||
// Write the expected size to the size parameter, in the input.
|
||||
Memory::Write_U32(real_dol_size, request.buffer_in + 0x14);
|
||||
}
|
||||
else
|
||||
{
|
||||
fp.ReadBytes(Memory::GetPointer(dol_addr), max_dol_size);
|
||||
}
|
||||
Memory::Write_U32(real_dol_size, request.buffer_out);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(wfs): Should be returning an error. However until we have
|
||||
// everything properly stubbed it's easier to simulate the methods
|
||||
|
|
|
@ -52,9 +52,18 @@ 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;
|
||||
|
||||
ARCUnpacker m_arc_unpacker;
|
||||
|
||||
enum
|
||||
{
|
||||
WFSI_ENOENT = -12000,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFSI_PREPARE_DEVICE = 0x02,
|
||||
|
@ -74,6 +83,8 @@ private:
|
|||
IOCTL_WFSI_FINALIZE_PROFILE = 0x88,
|
||||
|
||||
IOCTL_WFSI_APPLY_TITLE_PROFILE = 0x89,
|
||||
|
||||
IOCTL_WFSI_LOAD_DOL = 0x90,
|
||||
};
|
||||
};
|
||||
} // namespace Device
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Core/IOS/WFS/WFSSRV.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -44,6 +45,27 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
INFO_LOG(IOS, "IOCTL_WFS_INIT");
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_UNKNOWN_8:
|
||||
// TODO(wfs): Figure out what this actually does.
|
||||
INFO_LOG(IOS, "IOCTL_WFS_UNKNOWN_8");
|
||||
Memory::Write_U8(7, request.buffer_out);
|
||||
Memory::CopyToEmu(request.buffer_out + 1, "msc01\x00\x00\x00", 8);
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_SHUTDOWN:
|
||||
INFO_LOG(IOS, "IOCTL_WFS_SHUTDOWN");
|
||||
|
||||
// Close all hanging attach/detach ioctls with an appropriate error code.
|
||||
for (auto address : m_hanging)
|
||||
{
|
||||
IOCtlRequest hanging_request{address};
|
||||
Memory::Write_U32(0x80000000, hanging_request.buffer_out);
|
||||
Memory::Write_U32(0, hanging_request.buffer_out + 4);
|
||||
Memory::Write_U32(0, hanging_request.buffer_out + 8);
|
||||
m_ios.EnqueueIPCReply(hanging_request, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_DEVICE_INFO:
|
||||
INFO_LOG(IOS, "IOCTL_WFS_DEVICE_INFO");
|
||||
Memory::Write_U64(16ull << 30, request.buffer_out); // 16GB storage.
|
||||
|
@ -68,11 +90,16 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
|
||||
case IOCTL_WFS_ATTACH_DETACH:
|
||||
INFO_LOG(IOS, "IOCTL_WFS_ATTACH_DETACH(%u)", request.request);
|
||||
Memory::Write_U32(1, request.buffer_out);
|
||||
Memory::Write_U32(0, request.buffer_out + 4);
|
||||
Memory::Write_U32(0, request.buffer_out + 8);
|
||||
|
||||
// Leave hanging, but we need to acknowledge the request at shutdown time.
|
||||
m_hanging.push_back(request.address);
|
||||
return GetNoReply();
|
||||
|
||||
case IOCTL_WFS_FLUSH:
|
||||
// Nothing to do.
|
||||
INFO_LOG(IOS, "IOCTL_WFS_FLUSH: doing nothing");
|
||||
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:
|
||||
|
@ -83,7 +110,7 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
|
||||
case IOCTL_WFS_GLOB_NEXT:
|
||||
INFO_LOG(IOS, "IOCTL_WFS_GLOB_NEXT(%u)", request.request);
|
||||
return_error_code = WFS_EEMPTY;
|
||||
return_error_code = WFS_ENOENT;
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GLOB_END:
|
||||
|
@ -91,12 +118,32 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
Memory::Memset(request.buffer_out, 0, request.buffer_out_size);
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_SET_HOMEDIR:
|
||||
m_home_directory =
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
INFO_LOG(IOS, "IOCTL_WFS_SET_HOMEDIR: %s", m_home_directory.c_str());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_CHDIR:
|
||||
m_current_directory =
|
||||
Memory::GetString(request.buffer_in + 2, Memory::Read_U16(request.buffer_in));
|
||||
INFO_LOG(IOS, "IOCTL_WFS_CHDIR: %s", m_current_directory.c_str());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GET_HOMEDIR:
|
||||
INFO_LOG(IOS, "IOCTL_WFS_GET_HOMEDIR: %s", m_home_directory.c_str());
|
||||
Memory::Write_U16(static_cast<u16>(m_home_directory.size()), request.buffer_out);
|
||||
Memory::CopyToEmu(request.buffer_out + 2, m_home_directory.data(), m_home_directory.size());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_OPEN:
|
||||
{
|
||||
u32 mode = Memory::Read_U32(request.buffer_in);
|
||||
u16 path_len = Memory::Read_U16(request.buffer_in + 0x20);
|
||||
std::string path = Memory::GetString(request.buffer_in + 0x22, path_len);
|
||||
|
||||
path = NormalizePath(path);
|
||||
|
||||
u16 fd = GetNewFileDescriptor();
|
||||
FileDescriptor* fd_obj = &m_fds[fd];
|
||||
fd_obj->in_use = true;
|
||||
|
@ -108,7 +155,7 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
{
|
||||
ERROR_LOG(IOS, "IOCTL_WFS_OPEN(%s, %d): error opening file", path.c_str(), mode);
|
||||
ReleaseFileDescriptor(fd);
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
return_error_code = WFS_ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -117,6 +164,28 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_GET_SIZE:
|
||||
{
|
||||
u16 fd = Memory::Read_U16(request.buffer_in);
|
||||
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||
if (fd_obj == nullptr)
|
||||
{
|
||||
ERROR_LOG(IOS, "IOCTL_WFS_GET_SIZE: invalid file descriptor %d", fd);
|
||||
return_error_code = WFS_EBADFD;
|
||||
break;
|
||||
}
|
||||
|
||||
u64 size = fd_obj->file.GetSize();
|
||||
u32 truncated_size = static_cast<u32>(size);
|
||||
INFO_LOG(IOS, "IOCTL_WFS_GET_SIZE(%d) -> %d", fd, truncated_size);
|
||||
if (size != truncated_size)
|
||||
{
|
||||
ERROR_LOG(IOS, "IOCTL_WFS_GET_SIZE: file %d too large (%" PRIu64 ")", fd, size);
|
||||
}
|
||||
Memory::Write_U32(truncated_size, request.buffer_out);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_CLOSE:
|
||||
{
|
||||
u16 fd = Memory::Read_U16(request.buffer_in + 0x4);
|
||||
|
@ -126,26 +195,39 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
}
|
||||
|
||||
case IOCTL_WFS_READ:
|
||||
case IOCTL_WFS_READ_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_READ_ABSOLUTE;
|
||||
|
||||
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||
if (fd_obj == nullptr)
|
||||
{
|
||||
ERROR_LOG(IOS, "IOCTL_WFS_READ: invalid file descriptor %d", fd);
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
return_error_code = WFS_EBADFD;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t read_bytes;
|
||||
if (!fd_obj->file.ReadArray(Memory::GetPointer(addr), size, &read_bytes))
|
||||
u64 previous_position = fd_obj->file.Tell();
|
||||
if (absolute)
|
||||
{
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
break;
|
||||
fd_obj->file.Seek(position, SEEK_SET);
|
||||
}
|
||||
size_t read_bytes;
|
||||
fd_obj->file.ReadArray(Memory::GetPointer(addr), size, &read_bytes);
|
||||
// TODO(wfs): Handle read errors.
|
||||
if (absolute)
|
||||
{
|
||||
fd_obj->file.Seek(previous_position, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
fd_obj->position += read_bytes;
|
||||
}
|
||||
fd_obj->position += read_bytes;
|
||||
|
||||
INFO_LOG(IOS, "IOCTL_WFS_READ: read %zd bytes from FD %d (%s)", read_bytes, fd,
|
||||
fd_obj->path.c_str());
|
||||
|
@ -164,6 +246,42 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request)
|
|||
return GetDefaultReply(return_error_code);
|
||||
}
|
||||
|
||||
std::string WFSSRV::NormalizePath(const std::string& path) const
|
||||
{
|
||||
std::string expanded;
|
||||
if (!path.empty() && path[0] == '~')
|
||||
{
|
||||
expanded = m_home_directory + "/" + path.substr(1);
|
||||
}
|
||||
else if (path.empty() || path[0] != '/')
|
||||
{
|
||||
expanded = m_current_directory + "/" + path;
|
||||
}
|
||||
else
|
||||
{
|
||||
expanded = path;
|
||||
}
|
||||
|
||||
std::vector<std::string> components = SplitString(expanded, '/');
|
||||
std::vector<std::string> normalized_components;
|
||||
for (const auto& component : components)
|
||||
{
|
||||
if (component.empty() || component == ".")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (component == ".." && !normalized_components.empty())
|
||||
{
|
||||
normalized_components.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized_components.push_back(component);
|
||||
}
|
||||
}
|
||||
return "/" + JoinStrings(normalized_components, "/");
|
||||
}
|
||||
|
||||
WFSSRV::FileDescriptor* WFSSRV::FindFileDescriptor(u16 fd)
|
||||
{
|
||||
if (fd >= m_fds.size() || !m_fds[fd].in_use)
|
||||
|
|
|
@ -35,12 +35,22 @@ private:
|
|||
// WFS device name, e.g. msc01/msc02.
|
||||
std::string m_device_name;
|
||||
|
||||
// Home / current directories.
|
||||
std::string m_home_directory;
|
||||
std::string m_current_directory;
|
||||
|
||||
std::string NormalizePath(const std::string& path) const;
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFS_INIT = 0x02,
|
||||
IOCTL_WFS_SHUTDOWN = 0x03,
|
||||
IOCTL_WFS_DEVICE_INFO = 0x04,
|
||||
IOCTL_WFS_GET_DEVICE_NAME = 0x05,
|
||||
IOCTL_WFS_UNMOUNT_VOLUME = 0x06,
|
||||
IOCTL_WFS_UNKNOWN_8 = 0x08,
|
||||
IOCTL_WFS_FLUSH = 0x0a,
|
||||
IOCTL_WFS_MKDIR = 0x0c,
|
||||
IOCTL_WFS_GLOB_START = 0x0d,
|
||||
IOCTL_WFS_GLOB_NEXT = 0x0e,
|
||||
IOCTL_WFS_GLOB_END = 0x0f,
|
||||
|
@ -51,16 +61,19 @@ private:
|
|||
IOCTL_WFS_DELETE = 0x15,
|
||||
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
||||
IOCTL_WFS_OPEN = 0x1A,
|
||||
IOCTL_WFS_GET_SIZE = 0x1B,
|
||||
IOCTL_WFS_CLOSE = 0x1E,
|
||||
IOCTL_WFS_READ = 0x20,
|
||||
IOCTL_WFS_WRITE = 0x22,
|
||||
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
||||
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
||||
IOCTL_WFS_READ_ABSOLUTE = 0x48,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
WFS_EEMPTY = -10028, // Directory is empty of iteration completed.
|
||||
WFS_EBADFD = -10026, // Invalid file descriptor.
|
||||
WFS_ENOENT = -10028, // No such file or directory.
|
||||
};
|
||||
|
||||
struct FileDescriptor
|
||||
|
@ -78,6 +91,10 @@ private:
|
|||
FileDescriptor* FindFileDescriptor(u16 fd);
|
||||
u16 GetNewFileDescriptor();
|
||||
void ReleaseFileDescriptor(u16 fd);
|
||||
|
||||
// List of addresses of IPC requests left hanging that need closing at
|
||||
// shutdown time.
|
||||
std::vector<u32> m_hanging;
|
||||
};
|
||||
} // namespace Device
|
||||
} // namespace HLE
|
||||
|
|
Loading…
Reference in New Issue