Merge pull request #5419 from leoetlino/import-code-reuse

Reuse the IOS code for WAD installation
This commit is contained in:
Leo Lam
2017-05-14 23:35:43 +02:00
committed by GitHub
18 changed files with 531 additions and 401 deletions
+3 -6
View File
@@ -983,13 +983,10 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2)
DiscIO::CNANDContentManager::Access().GetNANDLoader(m_strFilename);
const IOS::ES::TMDReader& tmd = content_loader.GetTMD();
if (content_loader.GetContentByIndex(tmd.GetBootIndex()) == nullptr)
if (!IOS::ES::IsChannel(tmd.GetTitleId()))
{
// WAD is valid yet cannot be booted. Install instead.
u64 installed = DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_strFilename);
if (installed)
SuccessAlertT("The WAD has been installed successfully");
return false; // do not boot
PanicAlertT("This WAD is not bootable.");
return false;
}
SetRegion(tmd.GetRegion(), &set_region_dir);
+8 -8
View File
@@ -383,21 +383,21 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
switch (request.request)
{
case IOCTL_ES_ADDTICKET:
return AddTicket(request);
return ImportTicket(request);
case IOCTL_ES_ADDTMD:
return AddTMD(*context, request);
return ImportTmd(*context, request);
case IOCTL_ES_ADDTITLESTART:
return AddTitleStart(*context, request);
return ImportTitleInit(*context, request);
case IOCTL_ES_ADDCONTENTSTART:
return AddContentStart(*context, request);
return ImportContentBegin(*context, request);
case IOCTL_ES_ADDCONTENTDATA:
return AddContentData(*context, request);
return ImportContentData(*context, request);
case IOCTL_ES_ADDCONTENTFINISH:
return AddContentFinish(*context, request);
return ImportContentEnd(*context, request);
case IOCTL_ES_ADDTITLEFINISH:
return AddTitleFinish(*context, request);
return ImportTitleDone(*context, request);
case IOCTL_ES_ADDTITLECANCEL:
return AddTitleCancel(*context, request);
return ImportTitleCancel(*context, request);
case IOCTL_ES_GETDEVICEID:
return GetConsoleID(request);
case IOCTL_ES_OPENTITLECONTENT:
+67 -63
View File
@@ -55,7 +55,65 @@ public:
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
struct OpenedContent
{
u64 m_title_id;
IOS::ES::Content m_content;
u32 m_position;
};
struct TitleImportContext
{
IOS::ES::TMDReader tmd;
u32 content_id = 0xFFFFFFFF;
std::vector<u8> content_buffer;
};
// TODO: merge this with TitleImportContext. Also reuse the global content table.
struct TitleExportContext
{
struct ExportContent
{
OpenedContent content;
std::array<u8, 16> iv{};
};
bool valid = false;
IOS::ES::TMDReader tmd;
std::vector<u8> title_key;
std::map<u32, ExportContent> contents;
};
struct Context
{
void DoState(PointerWrap& p);
u16 gid = 0;
u32 uid = 0;
TitleImportContext title_import;
TitleExportContext title_export;
bool active = false;
// We use this to associate an IPC fd with an ES context.
u32 ipc_fd = -1;
};
// Title management
ReturnCode ImportTicket(const std::vector<u8>& ticket_bytes);
ReturnCode ImportTmd(Context& context, const std::vector<u8>& tmd_bytes);
ReturnCode ImportTitleInit(Context& context, const std::vector<u8>& tmd_bytes);
ReturnCode ImportContentBegin(Context& context, u64 title_id, u32 content_id);
ReturnCode ImportContentData(Context& context, u32 content_fd, const u8* data, u32 data_size);
ReturnCode ImportContentEnd(Context& context, u32 content_fd);
ReturnCode ImportTitleDone(Context& context);
ReturnCode ImportTitleCancel(Context& context);
ReturnCode ExportTitleInit(Context& context, u64 title_id, u8* tmd, u32 tmd_size);
ReturnCode ExportContentBegin(Context& context, u64 title_id, u32 content_id);
ReturnCode ExportContentData(Context& context, u32 content_fd, u8* data, u32 data_size);
ReturnCode ExportContentEnd(Context& context, u32 content_fd);
ReturnCode ExportTitleDone(Context& context);
ReturnCode DeleteTitle(u64 title_id);
ReturnCode DeleteTitleContent(u64 title_id) const;
ReturnCode DeleteTicket(const u8* ticket_view);
private:
enum
@@ -131,80 +189,26 @@ private:
IOCTL_ES_CHECKKOREAREGION = 0x45,
};
struct OpenedContent
{
u64 m_title_id;
IOS::ES::Content m_content;
u32 m_position;
};
struct ecc_cert_t
{
u32 sig_type;
u8 sig[0x3c];
u8 pad[0x40];
u8 issuer[0x40];
u32 key_type;
u8 key_name[0x40];
u32 ng_key_id;
u8 ecc_pubkey[0x3c];
u8 padding[0x3c];
};
struct TitleImportContext
{
IOS::ES::TMDReader tmd;
u32 content_id = 0xFFFFFFFF;
std::vector<u8> content_buffer;
};
// TODO: merge this with TitleImportContext. Also reuse the global content table.
struct TitleExportContext
{
struct ExportContent
{
OpenedContent content;
std::array<u8, 16> iv{};
};
bool valid = false;
IOS::ES::TMDReader tmd;
std::vector<u8> title_key;
std::map<u32, ExportContent> contents;
};
struct Context
{
void DoState(PointerWrap& p);
u16 gid = 0;
u32 uid = 0;
TitleImportContext title_import;
TitleExportContext title_export;
bool active = false;
// We use this to associate an IPC fd with an ES context.
u32 ipc_fd = -1;
};
// ES can only have 3 contexts at one time.
using ContextArray = std::array<Context, 3>;
// Title management
IPCCommandResult AddTicket(const IOCtlVRequest& request);
IPCCommandResult AddTMD(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleCancel(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportTicket(const IOCtlVRequest& request);
IPCCommandResult ImportTmd(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportTitleInit(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportContentBegin(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportContentEnd(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportTitleDone(Context& context, const IOCtlVRequest& request);
IPCCommandResult ImportTitleCancel(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleInit(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentBegin(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentEnd(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleDone(Context& context, const IOCtlVRequest& request);
IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
// Device identity and encryption
IPCCommandResult GetConsoleID(const IOCtlVRequest& request);
+5 -1
View File
@@ -42,9 +42,13 @@ bool IsDiscTitle(u64 title_id)
bool IsChannel(u64 title_id)
{
if (title_id == TITLEID_SYSMENU)
return true;
return IsTitleType(title_id, TitleType::Channel) ||
IsTitleType(title_id, TitleType::SystemChannel) ||
IsTitleType(title_id, TitleType::GameWithChannel);
IsTitleType(title_id, TitleType::GameWithChannel) ||
IsTitleType(title_id, TitleType::HiddenChannel);
}
bool Content::IsShared() const
File diff suppressed because it is too large Load Diff
-68
View File
@@ -246,74 +246,6 @@ void CNANDContentManager::ClearCache()
m_map.clear();
}
u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
{
if (filename.find(".wad") == std::string::npos)
return 0;
const CNANDContentLoader& content_loader = GetNANDLoader(filename);
if (content_loader.IsValid() == false)
return 0;
const u64 title_id = content_loader.GetTMD().GetTitleId();
// copy WAD's TMD header and contents to content directory
std::string content_path(Common::GetTitleContentPath(title_id, Common::FROM_CONFIGURED_ROOT));
std::string tmd_filename(Common::GetTMDFileName(title_id, Common::FROM_CONFIGURED_ROOT));
File::CreateFullPath(tmd_filename);
File::IOFile tmd_file(tmd_filename, "wb");
if (!tmd_file)
{
PanicAlertT("WAD installation failed: error creating %s", tmd_filename.c_str());
return 0;
}
const auto& raw_tmd = content_loader.GetTMD().GetRawTMD();
tmd_file.WriteBytes(raw_tmd.data(), raw_tmd.size());
IOS::ES::SharedContentMap shared_content{Common::FromWhichRoot::FROM_CONFIGURED_ROOT};
for (const auto& content : content_loader.GetContent())
{
std::string app_filename;
if (content.m_metadata.IsShared())
app_filename = shared_content.AddSharedContent(content.m_metadata.sha1);
else
app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_metadata.id);
if (!File::Exists(app_filename))
{
File::CreateFullPath(app_filename);
File::IOFile app_file(app_filename, "wb");
if (!app_file)
{
PanicAlertT("WAD installation failed: error creating %s", app_filename.c_str());
return 0;
}
app_file.WriteBytes(content.m_Data->Get().data(), content.m_metadata.size);
}
else
{
INFO_LOG(DISCIO, "Content %s already exists.", app_filename.c_str());
}
}
// Extract and copy WAD's ticket to ticket directory
if (!AddTicket(content_loader.GetTicket()))
{
PanicAlertT("WAD installation failed: error creating ticket");
return 0;
}
IOS::ES::UIDSys uid_sys{Common::FromWhichRoot::FROM_CONFIGURED_ROOT};
uid_sys.GetOrInsertUIDForTitle(title_id);
ClearCache();
return title_id;
}
bool AddTicket(const IOS::ES::TicketReader& signed_ticket)
{
if (!signed_ticket.IsValid())
-1
View File
@@ -106,7 +106,6 @@ public:
static CNANDContentManager instance;
return instance;
}
u64 Install_WiiWAD(const std::string& fileName);
const CNANDContentLoader& GetNANDLoader(const std::string& content_path);
const CNANDContentLoader& GetNANDLoader(u64 title_id, Common::FromWhichRoot from);
+28 -11
View File
@@ -44,25 +44,24 @@ bool IsWiiWAD(const CBlobBigEndianReader& reader)
}
} // Anonymous namespace
WiiWAD::WiiWAD(const std::string& name)
WiiWAD::WiiWAD(const std::string& name) : m_reader(CreateBlobReader(name))
{
std::unique_ptr<IBlobReader> reader(CreateBlobReader(name));
if (reader == nullptr || File::IsDirectory(name))
if (m_reader == nullptr || File::IsDirectory(name))
{
m_valid = false;
return;
}
m_valid = ParseWAD(*reader);
m_valid = ParseWAD();
}
WiiWAD::~WiiWAD()
{
}
bool WiiWAD::ParseWAD(IBlobReader& reader)
bool WiiWAD::ParseWAD()
{
CBlobBigEndianReader big_endian_reader(reader);
CBlobBigEndianReader big_endian_reader(*m_reader);
if (!IsWiiWAD(big_endian_reader))
return false;
@@ -86,18 +85,36 @@ bool WiiWAD::ParseWAD(IBlobReader& reader)
_dbg_assert_msg_(BOOT, reserved == 0x00, "WiiWAD: Reserved must be 0x00");
u32 offset = 0x40;
m_certificate_chain = CreateWADEntry(reader, certificate_chain_size, offset);
m_certificate_chain = CreateWADEntry(*m_reader, certificate_chain_size, offset);
offset += Common::AlignUp(certificate_chain_size, 0x40);
m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset));
m_ticket.SetBytes(CreateWADEntry(*m_reader, ticket_size, offset));
offset += Common::AlignUp(ticket_size, 0x40);
m_tmd.SetBytes(CreateWADEntry(reader, tmd_size, offset));
m_tmd.SetBytes(CreateWADEntry(*m_reader, tmd_size, offset));
offset += Common::AlignUp(tmd_size, 0x40);
m_data_app = CreateWADEntry(reader, data_app_size, offset);
m_data_app_offset = offset;
m_data_app = CreateWADEntry(*m_reader, data_app_size, offset);
offset += Common::AlignUp(data_app_size, 0x40);
m_footer = CreateWADEntry(reader, footer_size, offset);
m_footer = CreateWADEntry(*m_reader, footer_size, offset);
offset += Common::AlignUp(footer_size, 0x40);
return true;
}
std::vector<u8> WiiWAD::GetContent(u16 index) const
{
u64 offset = m_data_app_offset;
for (const IOS::ES::Content& content : m_tmd.GetContents())
{
const u64 aligned_size = Common::AlignUp(content.size, 0x40);
if (content.index == index)
{
std::vector<u8> data(aligned_size);
if (!m_reader->Read(offset, aligned_size, data.data()))
return {};
return data;
}
offset += aligned_size;
}
return {};
}
} // namespace DiscIO
+7 -1
View File
@@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
@@ -27,11 +28,16 @@ public:
const IOS::ES::TMDReader& GetTMD() const { return m_tmd; }
const std::vector<u8>& GetDataApp() const { return m_data_app; }
const std::vector<u8>& GetFooter() const { return m_footer; }
std::vector<u8> GetContent(u16 index) const;
private:
bool ParseWAD(IBlobReader& reader);
bool ParseWAD();
bool m_valid;
std::unique_ptr<IBlobReader> m_reader;
u64 m_data_app_offset = 0;
std::vector<u8> m_certificate_chain;
IOS::ES::TicketReader m_ticket;
IOS::ES::TMDReader m_tmd;
+13 -1
View File
@@ -23,6 +23,7 @@
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/Resources.h"
#include "DolphinQt2/Settings.h"
#include "UICommon/WiiUtils.h"
static const int CACHE_VERSION = 13; // Last changed in PR #3261
static const int DATASTREAM_VERSION = QDataStream::Qt_5_5;
@@ -63,6 +64,17 @@ GameFile::GameFile(const QString& path) : m_path(path)
m_valid = true;
}
bool GameFile::IsValid() const
{
if (!m_valid)
return false;
if (m_platform == DiscIO::Platform::WII_WAD && !IOS::ES::IsChannel(m_title_id))
return false;
return true;
}
QString GameFile::GetCacheFileName() const
{
QString folder = QString::fromStdString(File::GetUserPath(D_CACHE_IDX));
@@ -320,7 +332,7 @@ bool GameFile::Install()
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
return DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_path.toStdString());
return WiiUtils::InstallWAD(m_path.toStdString());
}
bool GameFile::Uninstall()
+1 -1
View File
@@ -27,7 +27,7 @@ class GameFile final
public:
explicit GameFile(const QString& path);
bool IsValid() const { return m_valid; }
bool IsValid() const;
// These will be properly initialized before we try to load the file.
QString GetFilePath() const { return m_path; }
QString GetFileName() const { return m_file_name; }
+3 -4
View File
@@ -83,6 +83,8 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "UICommon/WiiUtils.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
@@ -1209,11 +1211,8 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
wxPD_REMAINING_TIME | wxPD_SMOOTH);
u64 titleID = DiscIO::CNANDContentManager::Access().Install_WiiWAD(fileName);
if (titleID == TITLEID_SYSMENU)
{
if (WiiUtils::InstallWAD(fileName))
UpdateLoadWiiMenuItem();
}
}
void CFrame::OnUninstallWAD(wxCommandEvent&)
+12
View File
@@ -28,6 +28,7 @@
#include "Core/Boot/Boot.h"
#include "Core/ConfigManager.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
@@ -177,6 +178,17 @@ GameListItem::~GameListItem()
{
}
bool GameListItem::IsValid() const
{
if (!m_Valid)
return false;
if (m_Platform == DiscIO::Platform::WII_WAD && !IOS::ES::IsChannel(m_title_id))
return false;
return true;
}
void GameListItem::ReloadINI()
{
if (!IsValid())
+1 -1
View File
@@ -35,7 +35,7 @@ public:
// Reload settings after INI changes
void ReloadINI();
bool IsValid() const { return m_Valid; }
bool IsValid() const;
const std::string& GetFileName() const { return m_FileName; }
std::string GetName(DiscIO::Language language) const;
std::string GetName() const;
+1
View File
@@ -3,6 +3,7 @@ set(SRCS
Disassembler.cpp
UICommon.cpp
USBUtils.cpp
WiiUtils.cpp
)
if(USE_X11)
+3 -1
View File
@@ -59,12 +59,14 @@
<ClCompile Include="USBUtils.cpp">
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="WiiUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CommandLineParse.h" />
<ClInclude Include="UICommon.h" />
<ClInclude Include="Disassembler.h" />
<ClInclude Include="USBUtils.h" />
<ClInclude Include="WiiUtils.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(ExternalsDir)cpp-optparse\cpp-optparse.vcxproj">
@@ -74,4 +76,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+64
View File
@@ -0,0 +1,64 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "UICommon/WiiUtils.h"
#include "Common/CommonTypes.h"
#include "Common/MsgHandler.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/IOS.h"
#include "DiscIO/NANDContentLoader.h"
#include "DiscIO/WiiWad.h"
namespace WiiUtils
{
bool InstallWAD(const std::string& wad_path)
{
const DiscIO::WiiWAD wad{wad_path};
if (!wad.IsValid())
{
PanicAlertT("WAD installation failed: The selected file is not a valid WAD.");
return false;
}
const auto tmd = wad.GetTMD();
IOS::HLE::Kernel ios;
const auto es = ios.GetES();
IOS::HLE::Device::ES::Context context;
if (es->ImportTicket(wad.GetTicket().GetRawTicket()) < 0 ||
es->ImportTitleInit(context, tmd.GetRawTMD()) < 0)
{
PanicAlertT("WAD installation failed: Could not initialise title import.");
return false;
}
const bool contents_imported = [&]() {
const u64 title_id = tmd.GetTitleId();
for (const IOS::ES::Content& content : tmd.GetContents())
{
const std::vector<u8> data = wad.GetContent(content.index);
if (es->ImportContentBegin(context, title_id, content.id) < 0 ||
es->ImportContentData(context, 0, data.data(), static_cast<u32>(data.size())) < 0 ||
es->ImportContentEnd(context, 0) < 0)
{
PanicAlertT("WAD installation failed: Could not import content %08x.", content.id);
return false;
}
}
return true;
}();
if ((contents_imported && es->ImportTitleDone(context) < 0) ||
(!contents_imported && es->ImportTitleCancel(context) < 0))
{
PanicAlertT("WAD installation failed: Could not finalise title import.");
return false;
}
DiscIO::CNANDContentManager::Access().ClearCache();
return true;
}
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <string>
// Small utility functions for common Wii related tasks.
namespace WiiUtils
{
bool InstallWAD(const std::string& wad_path);
}