IOS: Add partial wfsi/wfssrv implementations.
The current implementations do many things wrong but work well enough to run the Dragon Quest X installer until the very end. The game itself crashes when being launched from its System Menu channel unfortunately so it is hard to verify whether the install properly worked or not. There are plenty of "TODO(wfs)" sprinkled around this codebase with things that are knowingly done wrong. The most important one right now is that content extraction is done by buffering everything into memory instead of properly streaming the data to disk (and processing asynchronously), which causes freezes. It is likely to not cause any practical issues since only the installer and the updater should use this anyway.
This commit is contained in:
parent
45d27f7fc7
commit
45a1232920
|
@ -155,6 +155,8 @@ set(SRCS ActionReplay.cpp
|
|||
IPC_HLE/WII_IPC_HLE_Device_usb_bt_stub.cpp
|
||||
IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp
|
||||
IPC_HLE/WII_IPC_HLE_Device_usb_ven.cpp
|
||||
IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.cpp
|
||||
IPC_HLE/WII_IPC_HLE_Device_wfsi.cpp
|
||||
IPC_HLE/WII_IPC_HLE_WiiMote.cpp
|
||||
IPC_HLE/WiiMote_HID_Attr.cpp
|
||||
IPC_HLE/WiiNetConfig.cpp
|
||||
|
|
|
@ -197,6 +197,8 @@
|
|||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_bt_stub.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_kbd.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_ven.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_wfssrv.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_wfsi.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_WiiMote.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WII_Socket.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WiiNetConfig.cpp" />
|
||||
|
@ -417,6 +419,8 @@
|
|||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_bt_stub.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_kbd.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_ven.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_wfssrv.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_wfsi.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_WiiMote.h" />
|
||||
<ClInclude Include="IPC_HLE\WII_Socket.h" />
|
||||
<ClInclude Include="IPC_HLE\WiiNetConfig.h" />
|
||||
|
|
|
@ -133,6 +133,9 @@
|
|||
<Filter Include="IPC HLE %28IOS/Starlet%29\USB">
|
||||
<UniqueIdentifier>{4a090016-76d5-43dd-95a4-abedfc11ef31}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="IPC HLE %28IOS/Starlet%29\WFS">
|
||||
<UniqueIdentifier>{f11746cf-277a-4d58-bcf1-578a45348b07}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote">
|
||||
<UniqueIdentifier>{8352be4d-d37d-4f55-adec-b940a9712802}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -631,6 +634,12 @@
|
|||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_bt_stub.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_wfssrv.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\WFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_wfsi.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\WFS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_WiiMote.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1243,6 +1252,12 @@
|
|||
<ClInclude Include="IPC_HLE\WiiMote_HID_Attr.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_wfssrv.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\WFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_wfsi.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\WFS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\Wiimote.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_real.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_ven.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.h"
|
||||
|
||||
namespace CoreTiming
|
||||
{
|
||||
|
@ -157,6 +159,8 @@ void Reinit()
|
|||
AddDevice<CWII_IPC_HLE_Device_stub>("/dev/usb/hid");
|
||||
#endif
|
||||
AddDevice<CWII_IPC_HLE_Device_stub>("/dev/usb/oh1");
|
||||
AddDevice<CWII_IPC_HLE_Device_usb_wfssrv>("/dev/usb/wfssrv");
|
||||
AddDevice<CWII_IPC_HLE_Device_wfsi>("/dev/wfsi");
|
||||
}
|
||||
|
||||
void Init()
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/NandPaths.h"
|
||||
#include "Core/HW/DVDInterface.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
||||
namespace WFS
|
||||
{
|
||||
std::string NativePath(const std::string& wfs_path)
|
||||
{
|
||||
return File::GetUserPath(D_WFSROOT_IDX) + Common::EscapePath(wfs_path);
|
||||
}
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_usb_wfssrv::CWII_IPC_HLE_Device_usb_wfssrv(u32 device_id,
|
||||
const std::string& device_name)
|
||||
: IWII_IPC_HLE_Device(device_id, device_name)
|
||||
{
|
||||
m_device_name = "msc01";
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_usb_wfssrv::IOCtl(u32 command_address)
|
||||
{
|
||||
u32 command = Memory::Read_U32(command_address + 0xC);
|
||||
u32 buffer_in = Memory::Read_U32(command_address + 0x10);
|
||||
u32 buffer_in_size = Memory::Read_U32(command_address + 0x14);
|
||||
u32 buffer_out = Memory::Read_U32(command_address + 0x18);
|
||||
u32 buffer_out_size = Memory::Read_U32(command_address + 0x1C);
|
||||
|
||||
int return_error_code = IPC_SUCCESS;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case IOCTL_WFS_INIT:
|
||||
// TODO(wfs): Implement.
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_INIT");
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_DEVICE_INFO:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_DEVICE_INFO");
|
||||
Memory::Write_U64(16ull << 30, buffer_out); // 16GB storage.
|
||||
Memory::Write_U8(4, buffer_out + 8);
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GET_DEVICE_NAME:
|
||||
{
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_GET_DEVICE_NAME");
|
||||
Memory::Write_U8(static_cast<u8>(m_device_name.size()), buffer_out);
|
||||
Memory::CopyToEmu(buffer_out + 1, m_device_name.data(), m_device_name.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_ATTACH_DETACH_2:
|
||||
// TODO(wfs): Implement.
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_ATTACH_DETACH_2(%d)", command);
|
||||
Memory::Write_U32(1, buffer_out);
|
||||
Memory::Write_U32(0, buffer_out + 4); // device id?
|
||||
Memory::Write_U32(0, buffer_out + 8);
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_ATTACH_DETACH:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_ATTACH_DETACH(%d)", command);
|
||||
Memory::Write_U32(1, buffer_out);
|
||||
Memory::Write_U32(0, buffer_out + 4);
|
||||
Memory::Write_U32(0, buffer_out + 8);
|
||||
return GetNoReply();
|
||||
|
||||
// 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:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_GLOB_START(%d)", command);
|
||||
Memory::Memset(buffer_out, 0, buffer_out_size);
|
||||
memcpy(Memory::GetPointer(buffer_out + 0x14), m_device_name.data(), m_device_name.size());
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GLOB_NEXT:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_GLOB_NEXT(%d)", command);
|
||||
return_error_code = WFS_EEMPTY;
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_GLOB_END:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_GLOB_END(%d)", command);
|
||||
Memory::Memset(buffer_out, 0, buffer_out_size);
|
||||
break;
|
||||
|
||||
case IOCTL_WFS_OPEN:
|
||||
{
|
||||
u32 mode = Memory::Read_U32(buffer_in);
|
||||
u16 path_len = Memory::Read_U16(buffer_in + 0x20);
|
||||
std::string path = Memory::GetString(buffer_in + 0x22, path_len);
|
||||
|
||||
u16 fd = GetNewFileDescriptor();
|
||||
FileDescriptor* fd_obj = &m_fds[fd];
|
||||
fd_obj->in_use = true;
|
||||
fd_obj->path = path;
|
||||
fd_obj->mode = mode;
|
||||
fd_obj->position = 0;
|
||||
|
||||
if (!fd_obj->Open())
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "IOCTL_WFS_OPEN(%s, %d): error opening file", path.c_str(), mode);
|
||||
ReleaseFileDescriptor(fd);
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
break;
|
||||
}
|
||||
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_OPEN(%s, %d) -> %d", path.c_str(), mode, fd);
|
||||
Memory::Write_U16(fd, buffer_out + 0x14);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_CLOSE:
|
||||
{
|
||||
u16 fd = Memory::Read_U16(buffer_in + 0x4);
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_CLOSE(%d)", fd);
|
||||
ReleaseFileDescriptor(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFS_READ:
|
||||
{
|
||||
u32 addr = Memory::Read_U32(buffer_in);
|
||||
u16 fd = Memory::Read_U16(buffer_in + 0xC);
|
||||
u32 size = Memory::Read_U32(buffer_in + 8);
|
||||
|
||||
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||
if (fd_obj == nullptr)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "IOCTL_WFS_READ: invalid file descriptor %d", fd);
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
break;
|
||||
}
|
||||
|
||||
size_t read_bytes;
|
||||
if (!fd_obj->file.ReadArray(Memory::GetPointer(addr), size, &read_bytes))
|
||||
{
|
||||
return_error_code = -1; // TODO(wfs): proper error code.
|
||||
break;
|
||||
}
|
||||
fd_obj->position += read_bytes;
|
||||
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFS_READ: read %zd bytes from FD %d (%s)", read_bytes, fd,
|
||||
fd_obj->path.c_str());
|
||||
return_error_code = static_cast<int>(read_bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(wfs): Should be returning -3. However until we have everything
|
||||
// properly stubbed it's easier to simulate the methods succeeding.
|
||||
WARN_LOG(WII_IPC_HLE, "%s unimplemented IOCtl(0x%08x, size_in=%08x, size_out=%08x)\n%s\n%s",
|
||||
m_name.c_str(), command, buffer_in_size, buffer_out_size,
|
||||
HexDump(Memory::GetPointer(buffer_in), buffer_in_size).c_str(),
|
||||
HexDump(Memory::GetPointer(buffer_out), buffer_out_size).c_str());
|
||||
Memory::Memset(buffer_out, 0, buffer_out_size);
|
||||
break;
|
||||
}
|
||||
|
||||
Memory::Write_U32(return_error_code, command_address + 4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_usb_wfssrv::IOCtlV(u32 command_address)
|
||||
{
|
||||
SIOCtlVBuffer command_buffer(command_address);
|
||||
ERROR_LOG(WII_IPC_HLE, "IOCtlV on /dev/usb/wfssrv -- unsupported");
|
||||
Memory::Write_U32(IPC_EINVAL, command_address + 4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_usb_wfssrv::FileDescriptor*
|
||||
CWII_IPC_HLE_Device_usb_wfssrv::FindFileDescriptor(u16 fd)
|
||||
{
|
||||
if (fd >= m_fds.size() || !m_fds[fd].in_use)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return &m_fds[fd];
|
||||
}
|
||||
|
||||
u16 CWII_IPC_HLE_Device_usb_wfssrv::GetNewFileDescriptor()
|
||||
{
|
||||
for (u32 i = 0; i < m_fds.size(); ++i)
|
||||
{
|
||||
if (!m_fds[i].in_use)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
m_fds.resize(m_fds.size() + 1);
|
||||
return static_cast<u16>(m_fds.size() - 1);
|
||||
}
|
||||
|
||||
void CWII_IPC_HLE_Device_usb_wfssrv::ReleaseFileDescriptor(u16 fd)
|
||||
{
|
||||
FileDescriptor* fd_obj = FindFileDescriptor(fd);
|
||||
if (!fd_obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fd_obj->in_use = false;
|
||||
|
||||
// Garbage collect and shrink the array if possible.
|
||||
while (m_fds.size() > 0 && !m_fds[m_fds.size() - 1].in_use)
|
||||
{
|
||||
m_fds.resize(m_fds.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool CWII_IPC_HLE_Device_usb_wfssrv::FileDescriptor::Open()
|
||||
{
|
||||
const char* mode_string;
|
||||
|
||||
if (mode == 1)
|
||||
{
|
||||
mode_string = "rb";
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
mode_string = "wb";
|
||||
}
|
||||
else if (mode == 3)
|
||||
{
|
||||
mode_string = "rwb";
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "WFSOpen: invalid mode %d", mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return file.Open(WFS::NativePath(path), mode_string);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
namespace WFS
|
||||
{
|
||||
std::string NativePath(const std::string& wfs_path);
|
||||
}
|
||||
|
||||
class CWII_IPC_HLE_Device_usb_wfssrv : public IWII_IPC_HLE_Device
|
||||
{
|
||||
public:
|
||||
CWII_IPC_HLE_Device_usb_wfssrv(u32 device_id, const std::string& device_name);
|
||||
|
||||
IPCCommandResult IOCtl(u32 command_address) override;
|
||||
IPCCommandResult IOCtlV(u32 command_address) override;
|
||||
|
||||
private:
|
||||
// WFS device name, e.g. msc01/msc02.
|
||||
std::string m_device_name;
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFS_INIT = 0x02,
|
||||
IOCTL_WFS_DEVICE_INFO = 0x04,
|
||||
IOCTL_WFS_GET_DEVICE_NAME = 0x05,
|
||||
IOCTL_WFS_FLUSH = 0x0a,
|
||||
IOCTL_WFS_GLOB_START = 0x0d,
|
||||
IOCTL_WFS_GLOB_NEXT = 0x0e,
|
||||
IOCTL_WFS_GLOB_END = 0x0f,
|
||||
IOCTL_WFS_SET_HOMEDIR = 0x10,
|
||||
IOCTL_WFS_CHDIR = 0x11,
|
||||
IOCTL_WFS_GET_HOMEDIR = 0x12,
|
||||
IOCTL_WFS_GETCWD = 0x13,
|
||||
IOCTL_WFS_DELETE = 0x15,
|
||||
IOCTL_WFS_GET_ATTRIBUTES = 0x17,
|
||||
IOCTL_WFS_OPEN = 0x1A,
|
||||
IOCTL_WFS_CLOSE = 0x1E,
|
||||
IOCTL_WFS_READ = 0x20,
|
||||
IOCTL_WFS_WRITE = 0x22,
|
||||
IOCTL_WFS_ATTACH_DETACH = 0x2d,
|
||||
IOCTL_WFS_ATTACH_DETACH_2 = 0x2e,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
WFS_EEMPTY = -10028, // Directory is empty of iteration completed.
|
||||
};
|
||||
|
||||
struct FileDescriptor
|
||||
{
|
||||
bool in_use;
|
||||
std::string path;
|
||||
int mode;
|
||||
size_t position;
|
||||
File::IOFile file;
|
||||
|
||||
bool Open();
|
||||
};
|
||||
std::vector<FileDescriptor> m_fds;
|
||||
|
||||
FileDescriptor* FindFileDescriptor(u16 fd);
|
||||
u16 GetNewFileDescriptor();
|
||||
void ReleaseFileDescriptor(u16 fd);
|
||||
};
|
|
@ -0,0 +1,263 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.h"
|
||||
|
||||
#include <mbedtls/aes.h>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/IPC_HLE/ESFormats.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h"
|
||||
#include "DiscIO/NANDContentLoader.h"
|
||||
|
||||
void ARCUnpacker::Reset()
|
||||
{
|
||||
m_whole_file.clear();
|
||||
}
|
||||
|
||||
void ARCUnpacker::AddBytes(const std::vector<u8>& bytes)
|
||||
{
|
||||
m_whole_file.insert(m_whole_file.end(), bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
void ARCUnpacker::Extract(const WriteCallback& callback)
|
||||
{
|
||||
u32 fourcc = Common::swap32(m_whole_file.data());
|
||||
if (fourcc != 0x55AA382D)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "ARCUnpacker: invalid fourcc (%08x)", fourcc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the root node to get the number of nodes.
|
||||
u8* nodes_directory = m_whole_file.data() + 0x20;
|
||||
u32 nodes_count = Common::swap32(nodes_directory + 8);
|
||||
constexpr u32 NODE_SIZE = 0xC;
|
||||
char* string_table = reinterpret_cast<char*>(nodes_directory + nodes_count * NODE_SIZE);
|
||||
|
||||
std::stack<std::pair<u32, std::string>> directory_stack;
|
||||
directory_stack.emplace(std::make_pair(nodes_count, ""));
|
||||
for (u32 i = 1; i < nodes_count; ++i)
|
||||
{
|
||||
while (i >= directory_stack.top().first)
|
||||
{
|
||||
directory_stack.pop();
|
||||
}
|
||||
const std::string& current_directory = directory_stack.top().second;
|
||||
u8* node = nodes_directory + i * NODE_SIZE;
|
||||
u32 name_offset = (node[1] << 16) | Common::swap16(node + 2);
|
||||
u32 data_offset = Common::swap32(node + 4);
|
||||
u32 size = Common::swap32(node + 8);
|
||||
std::string basename = string_table + name_offset;
|
||||
std::string fullname =
|
||||
current_directory.empty() ? basename : current_directory + "/" + basename;
|
||||
|
||||
u8 flags = *node;
|
||||
if (flags == 1)
|
||||
{
|
||||
directory_stack.emplace(std::make_pair(size, fullname));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<u8> contents(m_whole_file.data() + data_offset,
|
||||
m_whole_file.data() + data_offset + size);
|
||||
callback(fullname, contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_wfsi::CWII_IPC_HLE_Device_wfsi(u32 device_id, const std::string& device_name)
|
||||
: IWII_IPC_HLE_Device(device_id, device_name)
|
||||
{
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_wfsi::Open(u32 command_address, u32 mode)
|
||||
{
|
||||
INFO_LOG(WII_IPC_HLE, "/dev/wfsi: Open");
|
||||
return IWII_IPC_HLE_Device::Open(command_address, mode);
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_wfsi::IOCtl(u32 command_address)
|
||||
{
|
||||
u32 command = Memory::Read_U32(command_address + 0xC);
|
||||
u32 buffer_in = Memory::Read_U32(command_address + 0x10);
|
||||
u32 buffer_in_size = Memory::Read_U32(command_address + 0x14);
|
||||
u32 buffer_out = Memory::Read_U32(command_address + 0x18);
|
||||
u32 buffer_out_size = Memory::Read_U32(command_address + 0x1C);
|
||||
|
||||
u32 return_error_code = IPC_SUCCESS;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case IOCTL_WFSI_PREPARE_DEVICE:
|
||||
{
|
||||
u32 tmd_addr = Memory::Read_U32(buffer_in);
|
||||
u32 tmd_size = Memory::Read_U32(buffer_in + 4);
|
||||
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFSI_PREPARE_DEVICE");
|
||||
|
||||
constexpr u32 MAX_TMD_SIZE = 0x4000;
|
||||
if (tmd_size > MAX_TMD_SIZE)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "IOCTL_WFSI_INIT: TMD size too large (%d)", tmd_size);
|
||||
return_error_code = IPC_EINVAL;
|
||||
break;
|
||||
}
|
||||
std::vector<u8> tmd_bytes;
|
||||
tmd_bytes.resize(tmd_size);
|
||||
Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size);
|
||||
m_tmd.SetBytes(std::move(tmd_bytes));
|
||||
|
||||
std::vector<u8> ticket = DiscIO::FindSignedTicket(m_tmd.GetTitleId());
|
||||
if (ticket.size() == 0)
|
||||
{
|
||||
return_error_code = -11028;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(m_aes_key, DiscIO::GetKeyFromTicket(ticket).data(), sizeof(m_aes_key));
|
||||
mbedtls_aes_setkey_dec(&m_aes_ctx, m_aes_key, 128);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_PREPARE_PROFILE:
|
||||
m_base_extract_path = StringFromFormat("/vol/%s/tmp/", m_device_name.c_str());
|
||||
// Fall through intended.
|
||||
|
||||
case IOCTL_WFSI_PREPARE_CONTENT:
|
||||
{
|
||||
const char* ioctl_name = command == IOCTL_WFSI_PREPARE_PROFILE ? "IOCTL_WFSI_PREPARE_PROFILE" :
|
||||
"IOCTL_WFSI_PREPARE_CONTENT";
|
||||
|
||||
// Initializes the IV from the index of the content in the TMD contents.
|
||||
u32 content_id = Memory::Read_U32(buffer_in + 8);
|
||||
TMDReader::Content content_info;
|
||||
if (!m_tmd.FindContentById(content_id, &content_info))
|
||||
{
|
||||
WARN_LOG(WII_IPC_HLE, "%s: Content id %08x not found", ioctl_name, content_id);
|
||||
return_error_code = -10003;
|
||||
break;
|
||||
}
|
||||
|
||||
memset(m_aes_iv, 0, sizeof(m_aes_iv));
|
||||
m_aes_iv[0] = content_info.index >> 8;
|
||||
m_aes_iv[1] = content_info.index & 0xFF;
|
||||
INFO_LOG(WII_IPC_HLE, "%s: Content id %08x found at index %d", ioctl_name, content_id,
|
||||
content_info.index);
|
||||
|
||||
m_arc_unpacker.Reset();
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_IMPORT_PROFILE:
|
||||
case IOCTL_WFSI_IMPORT_CONTENT:
|
||||
{
|
||||
const char* ioctl_name = command == IOCTL_WFSI_IMPORT_PROFILE ? "IOCTL_WFSI_IMPORT_PROFILE" :
|
||||
"IOCTL_WFSI_IMPORT_CONTENT";
|
||||
|
||||
u32 content_id = Memory::Read_U32(buffer_in + 0xC);
|
||||
u32 input_ptr = Memory::Read_U32(buffer_in + 0x10);
|
||||
u32 input_size = Memory::Read_U32(buffer_in + 0x14);
|
||||
INFO_LOG(WII_IPC_HLE, "%s: %08x bytes of data at %08x from content id %d", ioctl_name,
|
||||
content_id, input_ptr, input_size);
|
||||
|
||||
std::vector<u8> decrypted(input_size);
|
||||
mbedtls_aes_crypt_cbc(&m_aes_ctx, MBEDTLS_AES_DECRYPT, input_size, m_aes_iv,
|
||||
Memory::GetPointer(input_ptr), decrypted.data());
|
||||
|
||||
m_arc_unpacker.AddBytes(decrypted);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_FINALIZE_PROFILE:
|
||||
case IOCTL_WFSI_FINALIZE_CONTENT:
|
||||
{
|
||||
const char* ioctl_name = command == IOCTL_WFSI_FINALIZE_PROFILE ?
|
||||
"IOCTL_WFSI_FINALIZE_PROFILE" :
|
||||
"IOCTL_WFSI_FINALIZE_CONTENT";
|
||||
INFO_LOG(WII_IPC_HLE, "%s", ioctl_name);
|
||||
|
||||
auto callback = [this](const std::string& filename, const std::vector<u8>& bytes) {
|
||||
INFO_LOG(WII_IPC_HLE, "Extract: %s (%zd bytes)", filename.c_str(), bytes.size());
|
||||
|
||||
std::string path = WFS::NativePath(m_base_extract_path + "/" + filename);
|
||||
File::CreateFullPath(path);
|
||||
File::IOFile f(path, "wb");
|
||||
if (!f)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "Could not extract %s to %s", filename.c_str(), path.c_str());
|
||||
return;
|
||||
}
|
||||
f.WriteBytes(bytes.data(), bytes.size());
|
||||
};
|
||||
m_arc_unpacker.Extract(callback);
|
||||
|
||||
// Technically not needed, but let's not keep large buffers in RAM for no
|
||||
// reason if we can avoid it.
|
||||
m_arc_unpacker.Reset();
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_WFSI_DELETE_TITLE:
|
||||
// Bytes 0-4: ??
|
||||
// Bytes 4-8: game id
|
||||
// Bytes 1c-1e: title id?
|
||||
WARN_LOG(WII_IPC_HLE, "IOCTL_WFSI_DELETE_TITLE: unimplemented");
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_IMPORT_TITLE:
|
||||
WARN_LOG(WII_IPC_HLE, "IOCTL_WFSI_IMPORT_TITLE: unimplemented");
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_INIT:
|
||||
// Nothing to do.
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFSI_INIT");
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_SET_DEVICE_NAME:
|
||||
INFO_LOG(WII_IPC_HLE, "IOCTL_WFSI_SET_DEVICE_NAME");
|
||||
m_device_name = Memory::GetString(buffer_in);
|
||||
break;
|
||||
|
||||
case IOCTL_WFSI_APPLY_TITLE_PROFILE:
|
||||
INFO_LOG(WII_IPC_HLE, "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()));
|
||||
File::CreateFullPath(WFS::NativePath(m_base_extract_path));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO(wfs): Should be returning an error. However until we have
|
||||
// everything properly stubbed it's easier to simulate the methods
|
||||
// succeeding.
|
||||
WARN_LOG(WII_IPC_HLE, "%s unimplemented IOCtl(0x%08x, size_in=%08x, size_out=%08x)\n%s\n%s",
|
||||
m_name.c_str(), command, buffer_in_size, buffer_out_size,
|
||||
HexDump(Memory::GetPointer(buffer_in), buffer_in_size).c_str(),
|
||||
HexDump(Memory::GetPointer(buffer_out), buffer_out_size).c_str());
|
||||
Memory::Memset(buffer_out, 0, buffer_out_size);
|
||||
break;
|
||||
}
|
||||
|
||||
Memory::Write_U32(return_error_code, command_address + 4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_wfsi::IOCtlV(u32 command_address)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_HLE, "IOCtlV on /dev/wfsi -- unsupported");
|
||||
Memory::Write_U32(IPC_EINVAL, command_address + 4);
|
||||
return GetDefaultReply();
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <mbedtls/aes.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IPC_HLE/ESFormats.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
|
||||
|
||||
class ARCUnpacker
|
||||
{
|
||||
public:
|
||||
ARCUnpacker() { Reset(); }
|
||||
void Reset();
|
||||
|
||||
void AddBytes(const std::vector<u8>& bytes);
|
||||
|
||||
using WriteCallback = std::function<void(const std::string&, const std::vector<u8>&)>;
|
||||
void Extract(const WriteCallback& callback);
|
||||
|
||||
private:
|
||||
std::vector<u8> m_whole_file;
|
||||
};
|
||||
|
||||
class CWII_IPC_HLE_Device_wfsi : public IWII_IPC_HLE_Device
|
||||
{
|
||||
public:
|
||||
CWII_IPC_HLE_Device_wfsi(u32 device_id, const std::string& device_name);
|
||||
|
||||
IPCCommandResult Open(u32 command_address, u32 mode) override;
|
||||
IPCCommandResult IOCtl(u32 command_address) override;
|
||||
IPCCommandResult IOCtlV(u32 command_address) override;
|
||||
|
||||
private:
|
||||
std::string m_device_name;
|
||||
|
||||
mbedtls_aes_context m_aes_ctx;
|
||||
u8 m_aes_key[0x10] = {};
|
||||
u8 m_aes_iv[0x10] = {};
|
||||
|
||||
TMDReader m_tmd;
|
||||
std::string m_base_extract_path;
|
||||
|
||||
ARCUnpacker m_arc_unpacker;
|
||||
|
||||
enum
|
||||
{
|
||||
IOCTL_WFSI_PREPARE_DEVICE = 0x02,
|
||||
|
||||
IOCTL_WFSI_PREPARE_CONTENT = 0x03,
|
||||
IOCTL_WFSI_IMPORT_CONTENT = 0x04,
|
||||
IOCTL_WFSI_FINALIZE_CONTENT = 0x05,
|
||||
|
||||
IOCTL_WFSI_DELETE_TITLE = 0x17,
|
||||
IOCTL_WFSI_IMPORT_TITLE = 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_APPLY_TITLE_PROFILE = 0x89,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue