diff --git a/Source/Core/Core/IOS/WFS/WFSI.cpp b/Source/Core/Core/IOS/WFS/WFSI.cpp index cf60c31bb6..6eae92bf72 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.cpp +++ b/Source/Core/Core/IOS/WFS/WFSI.cpp @@ -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" @@ -215,8 +216,13 @@ 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; + } break; case IOCTL_WFSI_SET_DEVICE_NAME: @@ -235,6 +241,51 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) break; + case IOCTL_WFSI_LOAD_DOL: + { + std::string path = StringFromFormat( + "/vol/%s/_install/%c%c%c%c/content", m_device_name.c_str(), + static_cast(m_title_id >> 24), static_cast(m_title_id >> 16), + static_cast(m_title_id >> 8), static_cast(m_title_id)); + + 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 diff --git a/Source/Core/Core/IOS/WFS/WFSI.h b/Source/Core/Core/IOS/WFS/WFSI.h index a6494c4f2c..77e4a827f6 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.h +++ b/Source/Core/Core/IOS/WFS/WFSI.h @@ -52,9 +52,15 @@ private: IOS::ES::TMDReader m_tmd; std::string m_base_extract_path; + u64 m_title_id; ARCUnpacker m_arc_unpacker; + enum + { + WFSI_ENOENT = -12000, + }; + enum { IOCTL_WFSI_PREPARE_DEVICE = 0x02, @@ -74,6 +80,8 @@ private: IOCTL_WFSI_FINALIZE_PROFILE = 0x88, IOCTL_WFSI_APPLY_TITLE_PROFILE = 0x89, + + IOCTL_WFSI_LOAD_DOL = 0x90, }; }; } // namespace Device diff --git a/Source/Core/Core/IOS/WFS/WFSSRV.cpp b/Source/Core/Core/IOS/WFS/WFSSRV.cpp index 81639e69ad..2251b28161 100644 --- a/Source/Core/Core/IOS/WFS/WFSSRV.cpp +++ b/Source/Core/Core/IOS/WFS/WFSSRV.cpp @@ -44,6 +44,20 @@ IPCCommandResult WFSSRV::IOCtl(const IOCtlRequest& request) INFO_LOG(IOS, "IOCTL_WFS_INIT"); 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,9 +82,9 @@ 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(); // TODO(wfs): Globbing is not really implemented, we just fake the one case diff --git a/Source/Core/Core/IOS/WFS/WFSSRV.h b/Source/Core/Core/IOS/WFS/WFSSRV.h index 602d30777e..0741bc1922 100644 --- a/Source/Core/Core/IOS/WFS/WFSSRV.h +++ b/Source/Core/Core/IOS/WFS/WFSSRV.h @@ -38,8 +38,10 @@ private: 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_FLUSH = 0x0a, IOCTL_WFS_GLOB_START = 0x0d, IOCTL_WFS_GLOB_NEXT = 0x0e, @@ -75,6 +77,10 @@ private: }; std::vector m_fds; + // List of addresses of IPC requests left hanging that need closing at + // shutdown time. + std::vector m_hanging; + FileDescriptor* FindFileDescriptor(u16 fd); u16 GetNewFileDescriptor(); void ReleaseFileDescriptor(u16 fd);