Merge pull request #4632 from delroth/wfs
Initial WFSI/WFSSRV implementation (+ some ES additions).
This commit is contained in:
commit
be6e7d70c7
|
@ -75,6 +75,7 @@
|
|||
#define ANAGLYPH_DIR "Anaglyph"
|
||||
#define PIPES_DIR "Pipes"
|
||||
#define MEMORYWATCHER_DIR "MemoryWatcher"
|
||||
#define WFSROOT_DIR "WFS"
|
||||
|
||||
// This one is only used to remove it if it was present
|
||||
#define SHADERCACHE_LEGACY_DIR "ShaderCache"
|
||||
|
|
|
@ -780,6 +780,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[D_MAILLOGS_IDX] = s_user_paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP;
|
||||
s_user_paths[D_THEMES_IDX] = s_user_paths[D_USER_IDX] + THEMES_DIR DIR_SEP;
|
||||
s_user_paths[D_PIPES_IDX] = s_user_paths[D_USER_IDX] + PIPES_DIR DIR_SEP;
|
||||
s_user_paths[D_WFSROOT_IDX] = s_user_paths[D_USER_IDX] + WFSROOT_DIR DIR_SEP;
|
||||
s_user_paths[F_DOLPHINCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
|
||||
s_user_paths[F_DEBUGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
|
||||
s_user_paths[F_LOGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + LOGGER_CONFIG;
|
||||
|
|
|
@ -46,6 +46,7 @@ enum
|
|||
D_THEMES_IDX,
|
||||
D_PIPES_IDX,
|
||||
D_MEMORYWATCHER_IDX,
|
||||
D_WFSROOT_IDX,
|
||||
F_DOLPHINCONFIG_IDX,
|
||||
F_DEBUGGERCONFIG_IDX,
|
||||
F_LOGGERCONFIG_IDX,
|
||||
|
|
|
@ -135,6 +135,7 @@ set(SRCS ActionReplay.cpp
|
|||
HW/WiimoteEmu/Speaker.cpp
|
||||
HW/WiimoteReal/WiimoteReal.cpp
|
||||
HW/WiiSaveCrypted.cpp
|
||||
IPC_HLE/ESFormats.cpp
|
||||
IPC_HLE/ICMPLin.cpp
|
||||
IPC_HLE/NWC24Config.cpp
|
||||
IPC_HLE/WII_IPC_HLE.cpp
|
||||
|
@ -154,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
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp" />
|
||||
<ClCompile Include="HW\WII_IPC.cpp" />
|
||||
<ClCompile Include="HW\WiiSaveCrypted.cpp" />
|
||||
<ClCompile Include="IPC_HLE\ESFormats.cpp" />
|
||||
<ClCompile Include="IPC_HLE\ICMPWin.cpp" />
|
||||
<ClCompile Include="IPC_HLE\NWC24Config.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WiiMote_HID_Attr.cpp" />
|
||||
|
@ -196,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" />
|
||||
|
@ -392,6 +395,7 @@
|
|||
<ClInclude Include="HW\WiimoteReal\WiimoteRealBase.h" />
|
||||
<ClInclude Include="HW\WiiSaveCrypted.h" />
|
||||
<ClInclude Include="HW\WII_IPC.h" />
|
||||
<ClInclude Include="IPC_HLE\ESFormats.h" />
|
||||
<ClInclude Include="IPC_HLE\hci.h" />
|
||||
<ClInclude Include="IPC_HLE\ICMP.h" />
|
||||
<ClInclude Include="IPC_HLE\l2cap.h" />
|
||||
|
@ -415,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>
|
||||
|
@ -565,6 +568,9 @@
|
|||
<ClCompile Include="DSP\LabelMap.cpp">
|
||||
<Filter>DSPCore</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IPC_HLE\ESFormats.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\ES</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE.cpp">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29</Filter>
|
||||
</ClCompile>
|
||||
|
@ -628,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>
|
||||
|
@ -1162,6 +1174,9 @@
|
|||
<ClInclude Include="DSP\LabelMap.h">
|
||||
<Filter>DSPCore</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IPC_HLE\ESFormats.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29\ES</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IPC_HLE\WII_IPC_HLE.h">
|
||||
<Filter>IPC HLE %28IOS/Starlet%29</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1237,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>
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/IPC_HLE/ESFormats.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
TMDReader::TMDReader(const std::vector<u8>& bytes) : m_bytes(bytes)
|
||||
{
|
||||
}
|
||||
|
||||
TMDReader::TMDReader(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
|
||||
{
|
||||
}
|
||||
|
||||
void TMDReader::SetBytes(const std::vector<u8>& bytes)
|
||||
{
|
||||
m_bytes = bytes;
|
||||
}
|
||||
|
||||
void TMDReader::SetBytes(std::vector<u8>&& bytes)
|
||||
{
|
||||
m_bytes = std::move(bytes);
|
||||
}
|
||||
|
||||
bool TMDReader::IsValid() const
|
||||
{
|
||||
if (m_bytes.size() < 0x1E4)
|
||||
{
|
||||
// TMD is too small to contain its base fields.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bytes.size() < 0x1E4 + GetNumContents() * 36u)
|
||||
{
|
||||
// TMD is too small to contain all its expected content entries.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 TMDReader::GetTitleId() const
|
||||
{
|
||||
return Common::swap64(m_bytes.data() + 0x18C);
|
||||
}
|
||||
|
||||
u16 TMDReader::GetNumContents() const
|
||||
{
|
||||
return Common::swap16(m_bytes.data() + 0x1DE);
|
||||
}
|
||||
|
||||
bool TMDReader::GetContent(u16 index, Content* content) const
|
||||
{
|
||||
if (index >= GetNumContents())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const u8* content_base = m_bytes.data() + 0x1E4 + index * 36;
|
||||
content->id = Common::swap32(content_base);
|
||||
content->index = Common::swap16(content_base + 4);
|
||||
content->type = Common::swap16(content_base + 6);
|
||||
content->size = Common::swap64(content_base + 8);
|
||||
std::copy(content_base + 16, content_base + 36, content->sha1.begin());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TMDReader::FindContentById(u32 id, Content* content) const
|
||||
{
|
||||
for (u16 index = 0; index < GetNumContents(); ++index)
|
||||
{
|
||||
if (!GetContent(index, content))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (content->id == id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TMDReader::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_bytes);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Utilities to manipulate files and formats from the Wii's ES module: tickets,
|
||||
// TMD, and other title informations.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class TMDReader final
|
||||
{
|
||||
public:
|
||||
TMDReader() = default;
|
||||
explicit TMDReader(const std::vector<u8>& bytes);
|
||||
explicit TMDReader(std::vector<u8>&& bytes);
|
||||
|
||||
void SetBytes(const std::vector<u8>& bytes);
|
||||
void SetBytes(std::vector<u8>&& bytes);
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
u64 GetTitleId() const;
|
||||
|
||||
struct Content
|
||||
{
|
||||
u32 id;
|
||||
u16 index;
|
||||
u16 type;
|
||||
u64 size;
|
||||
std::array<u8, 20> sha1;
|
||||
};
|
||||
u16 GetNumContents() const;
|
||||
bool GetContent(u16 index, Content* content) const;
|
||||
bool FindContentById(u32 id, Content* content) const;
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
private:
|
||||
std::vector<u8> m_bytes;
|
||||
};
|
|
@ -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()
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "Core/HW/DVDInterface.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/IPC_HLE/ESFormats.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_es.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_WiiMote.h"
|
||||
|
@ -105,6 +106,15 @@ void CWII_IPC_HLE_Device_es::LoadWAD(const std::string& _rContentFile)
|
|||
m_ContentFile = _rContentFile;
|
||||
}
|
||||
|
||||
void CWII_IPC_HLE_Device_es::DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv,
|
||||
u8* output)
|
||||
{
|
||||
mbedtls_aes_context AES_ctx;
|
||||
mbedtls_aes_setkey_dec(&AES_ctx, keyTable[key_index], 128);
|
||||
memcpy(new_iv, iv, 16);
|
||||
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, size, new_iv, input, output);
|
||||
}
|
||||
|
||||
void CWII_IPC_HLE_Device_es::OpenInternal()
|
||||
{
|
||||
auto& contentLoader = DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile);
|
||||
|
@ -142,6 +152,10 @@ void CWII_IPC_HLE_Device_es::DoState(PointerWrap& p)
|
|||
p.Do(m_AccessIdentID);
|
||||
p.Do(m_TitleIDs);
|
||||
|
||||
m_addtitle_tmd.DoState(p);
|
||||
p.Do(m_addtitle_content_id);
|
||||
p.Do(m_addtitle_content_buffer);
|
||||
|
||||
u32 Count = (u32)(m_ContentAccessMap.size());
|
||||
p.Do(Count);
|
||||
|
||||
|
@ -255,6 +269,152 @@ IPCCommandResult CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
|||
|
||||
switch (Buffer.Parameter)
|
||||
{
|
||||
case IOCTL_ES_ADDTICKET:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 3,
|
||||
"IOCTL_ES_ADDTICKET wrong number of inputs");
|
||||
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDTICKET");
|
||||
std::vector<u8> ticket(Buffer.InBuffer[0].m_Size);
|
||||
Memory::CopyFromEmu(ticket.data(), Buffer.InBuffer[0].m_Address, Buffer.InBuffer[0].m_Size);
|
||||
DiscIO::AddTicket(ticket);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_ES_ADDTITLESTART:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 4,
|
||||
"IOCTL_ES_ADDTITLESTART wrong number of inputs");
|
||||
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDTITLESTART");
|
||||
std::vector<u8> tmd(Buffer.InBuffer[0].m_Size);
|
||||
Memory::CopyFromEmu(tmd.data(), Buffer.InBuffer[0].m_Address, Buffer.InBuffer[0].m_Size);
|
||||
|
||||
m_addtitle_tmd.SetBytes(tmd);
|
||||
if (!m_addtitle_tmd.IsValid())
|
||||
{
|
||||
ERROR_LOG(WII_IPC_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
|
||||
Memory::Write_U32(ES_INVALID_TMD, _CommandAddress + 0x4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
// Write the TMD to title storage.
|
||||
std::string tmd_path =
|
||||
Common::GetTMDFileName(m_addtitle_tmd.GetTitleId(), Common::FROM_SESSION_ROOT);
|
||||
File::CreateFullPath(tmd_path);
|
||||
|
||||
File::IOFile fp(tmd_path, "wb");
|
||||
fp.WriteBytes(tmd.data(), tmd.size());
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_ES_ADDCONTENTSTART:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2,
|
||||
"IOCTL_ES_ADDCONTENTSTART wrong number of inputs");
|
||||
|
||||
u64 title_id = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
|
||||
u32 content_id = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
|
||||
|
||||
if (m_addtitle_content_id != 0xFFFFFFFF)
|
||||
{
|
||||
ERROR_LOG(WII_IPC_ES, "Trying to add content when we haven't finished adding "
|
||||
"another content. Unsupported.");
|
||||
Memory::Write_U32(ES_WRITE_FAILURE, _CommandAddress + 0x4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
m_addtitle_content_id = content_id;
|
||||
|
||||
m_addtitle_content_buffer.clear();
|
||||
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
|
||||
"content id %08x",
|
||||
title_id, m_addtitle_content_id);
|
||||
|
||||
if (title_id != m_addtitle_tmd.GetTitleId())
|
||||
{
|
||||
ERROR_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
|
||||
"TMD title id %016lx, ignoring",
|
||||
title_id, m_addtitle_tmd.GetTitleId());
|
||||
}
|
||||
|
||||
// We're supposed to return a "content file descriptor" here, which is
|
||||
// passed to further AddContentData / AddContentFinish. But so far there is
|
||||
// no known content installer which performs content addition concurrently.
|
||||
// Instead we just log an error (see above) if this condition is detected.
|
||||
s32 content_fd = 0;
|
||||
Memory::Write_U32(content_fd, _CommandAddress + 0x4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
case IOCTL_ES_ADDCONTENTDATA:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2,
|
||||
"IOCTL_ES_ADDCONTENTDATA wrong number of inputs");
|
||||
|
||||
u32 content_fd = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTDATA: content fd %08x, "
|
||||
"size %d",
|
||||
content_fd, Buffer.InBuffer[1].m_Size);
|
||||
|
||||
u8* data_start = Memory::GetPointer(Buffer.InBuffer[1].m_Address);
|
||||
u8* data_end = data_start + Buffer.InBuffer[1].m_Size;
|
||||
m_addtitle_content_buffer.insert(m_addtitle_content_buffer.end(), data_start, data_end);
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_ES_ADDCONTENTFINISH:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1,
|
||||
"IOCTL_ES_ADDCONTENTFINISH wrong number of inputs");
|
||||
|
||||
u32 content_fd = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
|
||||
|
||||
// Try to find the title key from a pre-installed ticket.
|
||||
std::vector<u8> ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId());
|
||||
if (ticket.size() == 0)
|
||||
{
|
||||
Memory::Write_U32(ES_NO_TICKET_INSTALLED, _CommandAddress + 0x4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
|
||||
mbedtls_aes_context aes_ctx;
|
||||
mbedtls_aes_setkey_dec(&aes_ctx, DiscIO::GetKeyFromTicket(ticket).data(), 128);
|
||||
|
||||
// The IV for title content decryption is the lower two bytes of the
|
||||
// content index, zero extended.
|
||||
TMDReader::Content content_info;
|
||||
if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info))
|
||||
{
|
||||
Memory::Write_U32(ES_INVALID_TMD, _CommandAddress + 0x4);
|
||||
return GetDefaultReply();
|
||||
}
|
||||
u8 iv[16] = {0};
|
||||
iv[0] = (content_info.index >> 8) & 0xFF;
|
||||
iv[1] = content_info.index & 0xFF;
|
||||
std::vector<u8> decrypted_data(m_addtitle_content_buffer.size());
|
||||
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv,
|
||||
m_addtitle_content_buffer.data(), decrypted_data.data());
|
||||
|
||||
std::string path = StringFromFormat(
|
||||
"%s%08x.app",
|
||||
Common::GetTitleContentPath(m_addtitle_tmd.GetTitleId(), Common::FROM_SESSION_ROOT).c_str(),
|
||||
m_addtitle_content_id);
|
||||
|
||||
File::IOFile fp(path, "wb");
|
||||
fp.WriteBytes(decrypted_data.data(), decrypted_data.size());
|
||||
|
||||
m_addtitle_content_id = 0xFFFFFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_ES_ADDTITLEFINISH:
|
||||
{
|
||||
INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDTITLEFINISH");
|
||||
break;
|
||||
}
|
||||
|
||||
case IOCTL_ES_GETDEVICEID:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1,
|
||||
|
@ -914,10 +1074,7 @@ IPCCommandResult CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
|||
u8* newIV = Memory::GetPointer(Buffer.PayloadBuffer[0].m_Address);
|
||||
u8* destination = Memory::GetPointer(Buffer.PayloadBuffer[1].m_Address);
|
||||
|
||||
mbedtls_aes_context AES_ctx;
|
||||
mbedtls_aes_setkey_dec(&AES_ctx, keyTable[keyIndex], 128);
|
||||
memcpy(newIV, IV, 16);
|
||||
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, size, newIV, source, destination);
|
||||
DecryptContent(keyIndex, IV, source, size, newIV, destination);
|
||||
|
||||
_dbg_assert_msg_(WII_IPC_ES, keyIndex == 6,
|
||||
"IOCTL_ES_DECRYPT: Key type is not SD, data will be crap");
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <vector>
|
||||
|
||||
#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"
|
||||
|
||||
|
@ -30,6 +31,9 @@ public:
|
|||
|
||||
void LoadWAD(const std::string& _rContentFile);
|
||||
|
||||
// Internal implementation of the ES_DECRYPT ioctlv.
|
||||
void DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output);
|
||||
|
||||
void OpenInternal();
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
@ -140,6 +144,11 @@ private:
|
|||
|
||||
static u8* keyTable[11];
|
||||
|
||||
// For title installation (ioctls IOCTL_ES_ADDTITLE*).
|
||||
TMDReader m_addtitle_tmd;
|
||||
u32 m_addtitle_content_id = 0xFFFFFFFF;
|
||||
std::vector<u8> m_addtitle_content_buffer;
|
||||
|
||||
const DiscIO::CNANDContentLoader& AccessContentDevice(u64 title_id);
|
||||
u32 OpenTitleContent(u32 CFD, u64 TitleID, u16 Index);
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
|
@ -29,6 +29,50 @@
|
|||
|
||||
namespace DiscIO
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Strips the signature part of a ticket, which has variable size based on
|
||||
// signature type. Returns a new vector which has only the ticket structure
|
||||
// itself.
|
||||
std::vector<u8> SignedTicketToTicket(const std::vector<u8>& signed_ticket)
|
||||
{
|
||||
u32 signature_type = Common::swap32(signed_ticket.data());
|
||||
u32 entry_offset;
|
||||
if (signature_type == 0x10000) // RSA4096
|
||||
{
|
||||
entry_offset = 576;
|
||||
}
|
||||
else if (signature_type == 0x10001) // RSA2048
|
||||
{
|
||||
entry_offset = 320;
|
||||
}
|
||||
else if (signature_type == 0x10002) // ECDSA
|
||||
{
|
||||
entry_offset = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(DISCIO, "Invalid ticket signature type: %08x", signature_type);
|
||||
return std::vector<u8>();
|
||||
}
|
||||
|
||||
std::vector<u8> ticket(signed_ticket.size() - entry_offset);
|
||||
std::copy(signed_ticket.begin() + entry_offset, signed_ticket.end(), ticket.begin());
|
||||
return ticket;
|
||||
}
|
||||
|
||||
std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size)
|
||||
{
|
||||
mbedtls_aes_context aes_ctx;
|
||||
std::vector<u8> buffer(size);
|
||||
|
||||
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
|
||||
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
CNANDContentData::~CNANDContentData() = default;
|
||||
|
||||
CSharedContent::CSharedContent()
|
||||
|
@ -290,27 +334,6 @@ void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd,
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<u8> CNANDContentLoader::AESDecode(const u8* key, u8* iv, const u8* src, u32 size)
|
||||
{
|
||||
mbedtls_aes_context aes_ctx;
|
||||
std::vector<u8> buffer(size);
|
||||
|
||||
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
|
||||
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<u8> CNANDContentLoader::GetKeyFromTicket(const std::vector<u8>& ticket)
|
||||
{
|
||||
const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
|
||||
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
|
||||
u8 iv[16] = {};
|
||||
|
||||
std::copy(&ticket[0x01DC], &ticket[0x01DC + 8], iv);
|
||||
return AESDecode(common_key, iv, &ticket[0x01BF], 16);
|
||||
}
|
||||
|
||||
DiscIO::Region CNANDContentLoader::GetRegion() const
|
||||
{
|
||||
if (!IsValid())
|
||||
|
@ -512,7 +535,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
|
|||
}
|
||||
|
||||
// Extract and copy WAD's ticket to ticket directory
|
||||
if (!AddTicket(title_id, content_loader.GetTicket()))
|
||||
if (!AddTicket(content_loader.GetTicket()))
|
||||
{
|
||||
PanicAlertT("WAD installation failed: error creating ticket");
|
||||
return 0;
|
||||
|
@ -525,8 +548,15 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
|
|||
return title_id;
|
||||
}
|
||||
|
||||
bool AddTicket(u64 title_id, const std::vector<u8>& ticket)
|
||||
bool AddTicket(const std::vector<u8>& signed_ticket)
|
||||
{
|
||||
std::vector<u8> ticket = SignedTicketToTicket(signed_ticket);
|
||||
if (ticket.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
u64 title_id = Common::swap64(ticket.data() + 0x9c);
|
||||
|
||||
std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT);
|
||||
File::CreateFullPath(ticket_filename);
|
||||
|
||||
|
@ -534,7 +564,46 @@ bool AddTicket(u64 title_id, const std::vector<u8>& ticket)
|
|||
if (!ticket_file)
|
||||
return false;
|
||||
|
||||
return ticket_file.WriteBytes(ticket.data(), ticket.size());
|
||||
return ticket_file.WriteBytes(signed_ticket.data(), signed_ticket.size());
|
||||
}
|
||||
|
||||
std::vector<u8> FindSignedTicket(u64 title_id)
|
||||
{
|
||||
std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT);
|
||||
File::IOFile ticket_file(ticket_filename, "rb");
|
||||
if (!ticket_file)
|
||||
{
|
||||
return std::vector<u8>();
|
||||
}
|
||||
|
||||
std::vector<u8> signed_ticket(ticket_file.GetSize());
|
||||
if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size()))
|
||||
{
|
||||
return std::vector<u8>();
|
||||
}
|
||||
|
||||
return signed_ticket;
|
||||
}
|
||||
|
||||
std::vector<u8> FindTicket(u64 title_id)
|
||||
{
|
||||
std::vector<u8> signed_ticket = FindSignedTicket(title_id);
|
||||
if (signed_ticket.empty())
|
||||
{
|
||||
return std::vector<u8>();
|
||||
}
|
||||
|
||||
return SignedTicketToTicket(signed_ticket);
|
||||
}
|
||||
|
||||
std::vector<u8> GetKeyFromTicket(const std::vector<u8>& signed_ticket)
|
||||
{
|
||||
const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
|
||||
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
|
||||
u8 iv[16] = {};
|
||||
|
||||
std::copy(&signed_ticket[0x01DC], &signed_ticket[0x01DC + 8], iv);
|
||||
return AESDecode(common_key, iv, &signed_ticket[0x01BF], 16);
|
||||
}
|
||||
|
||||
} // namespace end
|
||||
|
|
|
@ -22,7 +22,10 @@ namespace DiscIO
|
|||
{
|
||||
enum class Region;
|
||||
|
||||
bool AddTicket(u64 title_id, const std::vector<u8>& ticket);
|
||||
bool AddTicket(const std::vector<u8>& signed_ticket);
|
||||
std::vector<u8> FindSignedTicket(u64 title_id);
|
||||
std::vector<u8> FindTicket(u64 title_id);
|
||||
std::vector<u8> GetKeyFromTicket(const std::vector<u8>& ticket);
|
||||
|
||||
class CNANDContentData
|
||||
{
|
||||
|
@ -110,9 +113,6 @@ private:
|
|||
const std::vector<u8>& decrypted_title_key,
|
||||
const std::vector<u8>& data_app);
|
||||
|
||||
static std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size);
|
||||
static std::vector<u8> GetKeyFromTicket(const std::vector<u8>& ticket);
|
||||
|
||||
bool m_Valid;
|
||||
bool m_IsWAD;
|
||||
std::string m_Path;
|
||||
|
|
Loading…
Reference in New Issue