diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 9b6934cd89..7b576f9577 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -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
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 7f4b1b3208..b9f950a2eb 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -197,6 +197,8 @@
+
+
@@ -417,6 +419,8 @@
+
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 37653a0520..555cf460a6 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -133,6 +133,9 @@
{4a090016-76d5-43dd-95a4-abedfc11ef31}
+
+ {f11746cf-277a-4d58-bcf1-578a45348b07}
+
{8352be4d-d37d-4f55-adec-b940a9712802}
@@ -631,6 +634,12 @@
IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote
+
+ IPC HLE %28IOS/Starlet%29\WFS
+
+
+ IPC HLE %28IOS/Starlet%29\WFS
+
IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote
@@ -1243,6 +1252,12 @@
IPC HLE %28IOS/Starlet%29\USB/BT/Wiimote
+
+ IPC HLE %28IOS/Starlet%29\WFS
+
+
+ IPC HLE %28IOS/Starlet%29\WFS
+
HW %28Flipper/Hollywood%29\Wiimote
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
index 1167294875..fdca1f408c 100644
--- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
@@ -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("/dev/usb/hid");
#endif
AddDevice("/dev/usb/oh1");
+ AddDevice("/dev/usb/wfssrv");
+ AddDevice("/dev/wfsi");
}
void Init()
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.cpp
new file mode 100644
index 0000000000..4fabc7200f
--- /dev/null
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.cpp
@@ -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
+#include
+
+#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(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(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(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);
+}
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h
new file mode 100644
index 0000000000..f722bde402
--- /dev/null
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_usb_wfssrv.h
@@ -0,0 +1,76 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+#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 m_fds;
+
+ FileDescriptor* FindFileDescriptor(u16 fd);
+ u16 GetNewFileDescriptor();
+ void ReleaseFileDescriptor(u16 fd);
+};
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.cpp
new file mode 100644
index 0000000000..040ec001c0
--- /dev/null
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.cpp
@@ -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
+#include
+#include
+#include
+#include
+
+#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& 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(nodes_directory + nodes_count * NODE_SIZE);
+
+ std::stack> 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 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 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 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 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& 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(m_tmd.GetTitleId() >> 24), static_cast(m_tmd.GetTitleId() >> 16),
+ static_cast(m_tmd.GetTitleId() >> 8), static_cast(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();
+}
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.h
new file mode 100644
index 0000000000..d5afa62a3a
--- /dev/null
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_wfsi.h
@@ -0,0 +1,73 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+#include
+
+#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& bytes);
+
+ using WriteCallback = std::function&)>;
+ void Extract(const WriteCallback& callback);
+
+private:
+ std::vector 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,
+ };
+};