Skip to content
Permalink
Browse files
Merge pull request #5930 from delroth/wfs
Fix Dragon Quest X offline mode on Dolphin
  • Loading branch information
delroth committed Aug 15, 2017
2 parents 8fedd4f + 40c7046 commit 9b79e0a
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 24 deletions.
@@ -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 @@ class ES final : public Device
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 @@ class ES final : public Device
// 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

0 comments on commit 9b79e0a

Please sign in to comment.