Merge pull request #6094 from leoetlino/drop-wad-hack
Drop the direct WAD launch hack
This commit is contained in:
commit
c4914fbb8b
|
@ -46,7 +46,6 @@
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
BootParameters::BootParameters(Parameters&& parameters_) : parameters(std::move(parameters_))
|
BootParameters::BootParameters(Parameters&& parameters_) : parameters(std::move(parameters_))
|
||||||
|
@ -100,8 +99,8 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::stri
|
||||||
if (extension == ".dff")
|
if (extension == ".dff")
|
||||||
return std::make_unique<BootParameters>(DFF{path});
|
return std::make_unique<BootParameters>(DFF{path});
|
||||||
|
|
||||||
if (DiscIO::NANDContentManager::Access().GetNANDLoader(path).IsValid())
|
if (extension == ".wad")
|
||||||
return std::make_unique<BootParameters>(NAND{path});
|
return std::make_unique<BootParameters>(DiscIO::WiiWAD{path});
|
||||||
|
|
||||||
PanicAlertT("Could not recognize file %s", path.c_str());
|
PanicAlertT("Could not recognize file %s", path.c_str());
|
||||||
return {};
|
return {};
|
||||||
|
@ -339,7 +338,8 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
|
||||||
HID4.SBE = 1;
|
HID4.SBE = 1;
|
||||||
// Because there is no TMD to get the requested system (IOS) version from,
|
// Because there is no TMD to get the requested system (IOS) version from,
|
||||||
// we default to IOS58, which is the version used by the Homebrew Channel.
|
// we default to IOS58, which is the version used by the Homebrew Channel.
|
||||||
SetupWiiMemory(0x000000010000003a);
|
SetupWiiMemory();
|
||||||
|
IOS::HLE::GetIOS()->BootIOS(Titles::IOS(58));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -356,11 +356,16 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(const BootParameters::NAND& nand) const
|
bool operator()(const DiscIO::WiiWAD& wad) const
|
||||||
{
|
{
|
||||||
NOTICE_LOG(BOOT, "Booting from NAND: %s", nand.content_path.c_str());
|
|
||||||
SetDefaultDisc();
|
SetDefaultDisc();
|
||||||
return Boot_WiiWAD(nand.content_path);
|
return Boot_WiiWAD(wad);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const BootParameters::NANDTitle& nand_title) const
|
||||||
|
{
|
||||||
|
SetDefaultDisc();
|
||||||
|
return BootNANDTitle(nand_title.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(const BootParameters::IPL& ipl) const
|
bool operator()(const BootParameters::IPL& ipl) const
|
||||||
|
|
|
@ -13,8 +13,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "DiscIO/Blob.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
#include "DiscIO/WiiWad.h"
|
||||||
|
|
||||||
namespace File
|
namespace File
|
||||||
{
|
{
|
||||||
|
@ -45,9 +47,9 @@ struct BootParameters
|
||||||
std::unique_ptr<BootExecutableReader> reader;
|
std::unique_ptr<BootExecutableReader> reader;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NAND
|
struct NANDTitle
|
||||||
{
|
{
|
||||||
std::string content_path;
|
u64 id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPL
|
struct IPL
|
||||||
|
@ -67,7 +69,7 @@ struct BootParameters
|
||||||
|
|
||||||
static std::unique_ptr<BootParameters> GenerateFromFile(const std::string& path);
|
static std::unique_ptr<BootParameters> GenerateFromFile(const std::string& path);
|
||||||
|
|
||||||
using Parameters = std::variant<Disc, Executable, NAND, IPL, DFF>;
|
using Parameters = std::variant<Disc, Executable, DiscIO::WiiWAD, NANDTitle, IPL, DFF>;
|
||||||
BootParameters(Parameters&& parameters_);
|
BootParameters(Parameters&& parameters_);
|
||||||
|
|
||||||
Parameters parameters;
|
Parameters parameters;
|
||||||
|
@ -98,7 +100,8 @@ private:
|
||||||
|
|
||||||
static void UpdateDebugger_MapLoaded();
|
static void UpdateDebugger_MapLoaded();
|
||||||
|
|
||||||
static bool Boot_WiiWAD(const std::string& filename);
|
static bool Boot_WiiWAD(const DiscIO::WiiWAD& wad);
|
||||||
|
static bool BootNANDTitle(u64 title_id);
|
||||||
|
|
||||||
static void SetupMSR();
|
static void SetupMSR();
|
||||||
static void SetupBAT(bool is_wii);
|
static void SetupBAT(bool is_wii);
|
||||||
|
@ -109,7 +112,7 @@ private:
|
||||||
static bool Load_BS2(const std::string& boot_rom_filename);
|
static bool Load_BS2(const std::string& boot_rom_filename);
|
||||||
|
|
||||||
static void SetupGCMemory();
|
static void SetupGCMemory();
|
||||||
static bool SetupWiiMemory(u64 ios_title_id);
|
static bool SetupWiiMemory();
|
||||||
};
|
};
|
||||||
|
|
||||||
class BootExecutableReader
|
class BootExecutableReader
|
||||||
|
|
|
@ -213,7 +213,7 @@ bool CBoot::EmulatedBS2_GC(const DiscIO::Volume& volume)
|
||||||
return RunApploader(/*is_wii*/ false, volume);
|
return RunApploader(/*is_wii*/ false, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CBoot::SetupWiiMemory(u64 ios_title_id)
|
bool CBoot::SetupWiiMemory()
|
||||||
{
|
{
|
||||||
static const std::map<DiscIO::Region, const RegionSetting> region_settings = {
|
static const std::map<DiscIO::Region, const RegionSetting> region_settings = {
|
||||||
{DiscIO::Region::NTSC_J, {"JPN", "NTSC", "JP", "LJ"}},
|
{DiscIO::Region::NTSC_J, {"JPN", "NTSC", "JP", "LJ"}},
|
||||||
|
@ -308,9 +308,6 @@ bool CBoot::SetupWiiMemory(u64 ios_title_id)
|
||||||
// It is fine to always use the latest value as apploaders work with all versions.
|
// It is fine to always use the latest value as apploaders work with all versions.
|
||||||
Memory::Write_U16(0x0113, 0x0000315e);
|
Memory::Write_U16(0x0113, 0x0000315e);
|
||||||
|
|
||||||
if (!IOS::HLE::GetIOS()->BootIOS(ios_title_id))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Memory::Write_U8(0x80, 0x0000315c); // OSInit
|
Memory::Write_U8(0x80, 0x0000315c); // OSInit
|
||||||
Memory::Write_U16(0x0000, 0x000030e0); // PADInit
|
Memory::Write_U16(0x0000, 0x000030e0); // PADInit
|
||||||
Memory::Write_U32(0x80000000, 0x00003184); // GameID Address
|
Memory::Write_U32(0x80000000, 0x00003184); // GameID Address
|
||||||
|
@ -367,7 +364,7 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::Volume& volume)
|
||||||
Memory::Write_U32(0, 0x3194);
|
Memory::Write_U32(0, 0x3194);
|
||||||
Memory::Write_U32(static_cast<u32>(data_partition.offset >> 2), 0x3198);
|
Memory::Write_U32(static_cast<u32>(data_partition.offset >> 2), 0x3198);
|
||||||
|
|
||||||
if (!SetupWiiMemory(tmd.GetIOSId()))
|
if (!SetupWiiMemory() || !IOS::HLE::GetIOS()->BootIOS(tmd.GetIOSId()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DVDRead(volume, 0x00000000, 0x00000000, 0x20, DiscIO::PARTITION_NONE); // Game Code
|
DVDRead(volume, 0x00000000, 0x00000000, 0x20, DiscIO::PARTITION_NONE); // Game Code
|
||||||
|
@ -392,7 +389,7 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::Volume& volume)
|
||||||
|
|
||||||
// Warning: This call will set incorrect running game metadata if our volume parameter
|
// Warning: This call will set incorrect running game metadata if our volume parameter
|
||||||
// doesn't point to the same disc as the one that's inserted in the emulated disc drive!
|
// doesn't point to the same disc as the one that's inserted in the emulated disc drive!
|
||||||
IOS::HLE::Device::ES::DIVerify(tmd, volume.GetTicket(partition));
|
IOS::HLE::GetIOS()->GetES()->DIVerify(tmd, volume.GetTicket(partition));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,49 +18,30 @@
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/IOS/FS/FileIO.h"
|
#include "Core/IOS/FS/FileIO.h"
|
||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
|
#include "Core/WiiUtils.h"
|
||||||
|
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DiscIO/WiiWad.h"
|
||||||
|
|
||||||
bool CBoot::Boot_WiiWAD(const std::string& _pFilename)
|
bool CBoot::BootNANDTitle(const u64 title_id)
|
||||||
{
|
{
|
||||||
UpdateStateFlags([](StateFlags* state) {
|
UpdateStateFlags([](StateFlags* state) {
|
||||||
state->type = 0x03; // TYPE_RETURN
|
state->type = 0x03; // TYPE_RETURN
|
||||||
});
|
});
|
||||||
|
|
||||||
const DiscIO::NANDContentLoader& ContentLoader =
|
if (title_id == Titles::SYSTEM_MENU)
|
||||||
DiscIO::NANDContentManager::Access().GetNANDLoader(_pFilename);
|
IOS::HLE::CreateVirtualFATFilesystem();
|
||||||
if (!ContentLoader.IsValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u64 titleID = ContentLoader.GetTMD().GetTitleId();
|
SetupWiiMemory();
|
||||||
|
auto* ios = IOS::HLE::GetIOS();
|
||||||
|
return ios->GetES()->LaunchTitle(title_id);
|
||||||
|
}
|
||||||
|
|
||||||
if (!IOS::ES::IsChannel(titleID))
|
bool CBoot::Boot_WiiWAD(const DiscIO::WiiWAD& wad)
|
||||||
|
{
|
||||||
|
if (!WiiUtils::InstallWAD(*IOS::HLE::GetIOS(), wad, WiiUtils::InstallType::Temporary))
|
||||||
{
|
{
|
||||||
PanicAlertT("This WAD is not bootable.");
|
PanicAlertT("Cannot boot this WAD because it could not be installed to the NAND.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return BootNANDTitle(wad.GetTMD().GetTitleId());
|
||||||
// create data directory
|
|
||||||
File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT));
|
|
||||||
|
|
||||||
if (titleID == Titles::SYSTEM_MENU)
|
|
||||||
IOS::HLE::CreateVirtualFATFilesystem();
|
|
||||||
// setup Wii memory
|
|
||||||
|
|
||||||
if (!SetupWiiMemory(ContentLoader.GetTMD().GetIOSId()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
IOS::HLE::Device::ES::LoadWAD(_pFilename);
|
|
||||||
|
|
||||||
// TODO: kill these manual calls and just use ES_Launch here, as soon as the direct WAD
|
|
||||||
// launch hack is dropped.
|
|
||||||
auto* ios = IOS::HLE::GetIOS();
|
|
||||||
IOS::ES::UIDSys uid_map{Common::FROM_SESSION_ROOT};
|
|
||||||
ios->SetUidForPPC(uid_map.GetOrInsertUIDForTitle(titleID));
|
|
||||||
ios->SetGidForPPC(ContentLoader.GetTMD().GetGroupId());
|
|
||||||
|
|
||||||
if (!ios->BootstrapPPC(ContentLoader))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,16 @@ constexpr u64 BOOT2 = 0x0000000100000001;
|
||||||
|
|
||||||
constexpr u64 SYSTEM_MENU = 0x0000000100000002;
|
constexpr u64 SYSTEM_MENU = 0x0000000100000002;
|
||||||
|
|
||||||
// IOS used by the latest System Menu (4.3). Corresponds to IOS80.
|
|
||||||
constexpr u64 SYSTEM_MENU_IOS = 0x0000000100000050;
|
|
||||||
|
|
||||||
constexpr u64 BC = 0x0000000100000100;
|
|
||||||
constexpr u64 MIOS = 0x0000000100000101;
|
|
||||||
|
|
||||||
constexpr u64 SHOP = 0x0001000248414241;
|
constexpr u64 SHOP = 0x0001000248414241;
|
||||||
|
|
||||||
|
constexpr u64 IOS(u32 major_version)
|
||||||
|
{
|
||||||
|
return 0x0000000100000000 | major_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IOS used by the latest System Menu (4.3). Corresponds to IOS80.
|
||||||
|
constexpr u64 SYSTEM_MENU_IOS = IOS(80);
|
||||||
|
|
||||||
|
constexpr u64 BC = IOS(0x100);
|
||||||
|
constexpr u64 MIOS = IOS(0x101);
|
||||||
} // namespace Titles
|
} // namespace Titles
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
#include "VideoCommon/HiresTextures.h"
|
#include "VideoCommon/HiresTextures.h"
|
||||||
|
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
#include "DiscIO/WiiWad.h"
|
||||||
|
|
||||||
SConfig* SConfig::m_Instance;
|
SConfig* SConfig::m_Instance;
|
||||||
|
|
||||||
|
@ -888,14 +888,35 @@ struct SetGameMetadata
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(const BootParameters::NAND& nand) const
|
bool operator()(const DiscIO::WiiWAD& wad) const
|
||||||
{
|
{
|
||||||
const auto& loader = DiscIO::NANDContentManager::Access().GetNANDLoader(nand.content_path);
|
if (!wad.IsValid() || !wad.GetTMD().IsValid())
|
||||||
if (!loader.IsValid())
|
{
|
||||||
|
PanicAlertT("This WAD is not valid.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (!IOS::ES::IsChannel(wad.GetTMD().GetTitleId()))
|
||||||
|
{
|
||||||
|
PanicAlertT("This WAD is not bootable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const IOS::ES::TMDReader& tmd = loader.GetTMD();
|
const IOS::ES::TMDReader& tmd = wad.GetTMD();
|
||||||
|
config->SetRunningGameMetadata(tmd);
|
||||||
|
config->bWii = true;
|
||||||
|
*region = tmd.GetRegion();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const BootParameters::NANDTitle& nand_title) const
|
||||||
|
{
|
||||||
|
IOS::HLE::Kernel ios;
|
||||||
|
const IOS::ES::TMDReader tmd = ios.GetES()->FindInstalledTMD(nand_title.id);
|
||||||
|
if (!tmd.IsValid() || !IOS::ES::IsChannel(nand_title.id))
|
||||||
|
{
|
||||||
|
PanicAlertT("This title cannot be booted.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
config->SetRunningGameMetadata(tmd);
|
config->SetRunningGameMetadata(tmd);
|
||||||
config->bWii = true;
|
config->bWii = true;
|
||||||
*region = tmd.GetRegion();
|
*region = tmd.GetRegion();
|
||||||
|
|
|
@ -113,7 +113,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request)
|
||||||
const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition);
|
const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition);
|
||||||
const std::vector<u8>& raw_tmd = tmd.GetBytes();
|
const std::vector<u8>& raw_tmd = tmd.GetBytes();
|
||||||
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
||||||
ES::DIVerify(tmd, DVDThread::GetTicket(partition));
|
m_ios.GetES()->DIVerify(tmd, DVDThread::GetTicket(partition));
|
||||||
|
|
||||||
return_value = 1;
|
return_value = 1;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/IOS/IOSC.h"
|
#include "Core/IOS/IOSC.h"
|
||||||
#include "Core/ec_wii.h"
|
#include "Core/ec_wii.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -35,10 +34,6 @@ namespace HLE
|
||||||
{
|
{
|
||||||
namespace Device
|
namespace Device
|
||||||
{
|
{
|
||||||
// TODO: drop this and convert the title context into a member once the WAD launch hack is gone.
|
|
||||||
static std::string s_content_file;
|
|
||||||
static TitleContext s_title_context;
|
|
||||||
|
|
||||||
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
|
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
|
||||||
static u64 s_title_to_launch;
|
static u64 s_title_to_launch;
|
||||||
|
|
||||||
|
@ -84,9 +79,6 @@ ES::ES(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
|
||||||
|
|
||||||
FinishAllStaleImports();
|
FinishAllStaleImports();
|
||||||
|
|
||||||
s_content_file = "";
|
|
||||||
s_title_context = TitleContext{};
|
|
||||||
|
|
||||||
if (s_title_to_launch != 0)
|
if (s_title_to_launch != 0)
|
||||||
{
|
{
|
||||||
NOTICE_LOG(IOS, "Re-launching title after IOS reload.");
|
NOTICE_LOG(IOS, "Re-launching title after IOS reload.");
|
||||||
|
@ -95,11 +87,6 @@ ES::ES(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleContext& ES::GetTitleContext()
|
|
||||||
{
|
|
||||||
return s_title_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleContext::Clear()
|
void TitleContext::Clear()
|
||||||
{
|
{
|
||||||
ticket.SetBytes({});
|
ticket.SetBytes({});
|
||||||
|
@ -114,13 +101,6 @@ void TitleContext::DoState(PointerWrap& p)
|
||||||
p.Do(active);
|
p.Do(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TitleContext::Update(const DiscIO::NANDContentLoader& content_loader)
|
|
||||||
{
|
|
||||||
if (!content_loader.IsValid())
|
|
||||||
return;
|
|
||||||
Update(content_loader.GetTMD(), content_loader.GetTicket());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TitleContext::Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_)
|
void TitleContext::Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_)
|
||||||
{
|
{
|
||||||
if (!tmd_.IsValid() || !ticket_.IsValid())
|
if (!tmd_.IsValid() || !ticket_.IsValid())
|
||||||
|
@ -141,16 +121,6 @@ void TitleContext::Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES::LoadWAD(const std::string& _rContentFile)
|
|
||||||
{
|
|
||||||
s_content_file = _rContentFile;
|
|
||||||
// XXX: Ideally, this should be done during a launch, but because we support launching WADs
|
|
||||||
// without installing them (which is a bit of a hack), we have to do this manually here.
|
|
||||||
const auto& content_loader = DiscIO::NANDContentManager::Access().GetNANDLoader(s_content_file);
|
|
||||||
s_title_context.Update(content_loader);
|
|
||||||
INFO_LOG(IOS_ES, "LoadWAD: Title context changed: %016" PRIx64, s_title_context.tmd.GetTitleId());
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(1, 1))
|
if (!request.HasNumberOfValidVectors(1, 1))
|
||||||
|
@ -167,9 +137,9 @@ IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
|
||||||
|
|
||||||
ReturnCode ES::GetTitleId(u64* title_id) const
|
ReturnCode ES::GetTitleId(u64* title_id) const
|
||||||
{
|
{
|
||||||
if (!s_title_context.active)
|
if (!m_title_context.active)
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
*title_id = s_title_context.tmd.GetTitleId();
|
*title_id = m_title_context.tmd.GetTitleId();
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,16 +212,11 @@ IPCCommandResult ES::SetUID(u32 uid, const IOCtlVRequest& request)
|
||||||
|
|
||||||
bool ES::LaunchTitle(u64 title_id, bool skip_reload)
|
bool ES::LaunchTitle(u64 title_id, bool skip_reload)
|
||||||
{
|
{
|
||||||
s_title_context.Clear();
|
m_title_context.Clear();
|
||||||
INFO_LOG(IOS_ES, "ES_Launch: Title context changed: (none)");
|
INFO_LOG(IOS_ES, "ES_Launch: Title context changed: (none)");
|
||||||
|
|
||||||
NOTICE_LOG(IOS_ES, "Launching title %016" PRIx64 "...", title_id);
|
NOTICE_LOG(IOS_ES, "Launching title %016" PRIx64 "...", title_id);
|
||||||
|
|
||||||
// ES_Launch should probably reset the whole state, which at least means closing all open files.
|
|
||||||
// leaving them open through ES_Launch may cause hangs and other funky behavior
|
|
||||||
// (supposedly when trying to re-open those files).
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
|
|
||||||
u32 device_id;
|
u32 device_id;
|
||||||
if (title_id == Titles::SHOP &&
|
if (title_id == Titles::SHOP &&
|
||||||
(GetDeviceId(&device_id) != IPC_SUCCESS || device_id == DEFAULT_WII_DEVICE_ID))
|
(GetDeviceId(&device_id) != IPC_SUCCESS || device_id == DEFAULT_WII_DEVICE_ID))
|
||||||
|
@ -275,13 +240,48 @@ bool ES::LaunchTitle(u64 title_id, bool skip_reload)
|
||||||
|
|
||||||
bool ES::LaunchIOS(u64 ios_title_id)
|
bool ES::LaunchIOS(u64 ios_title_id)
|
||||||
{
|
{
|
||||||
|
// A real Wii goes through several steps before getting to MIOS.
|
||||||
|
//
|
||||||
|
// * The System Menu detects a GameCube disc and launches BC (1-100) instead of the game.
|
||||||
|
// * BC (similar to boot1) lowers the clock speed to the Flipper's and then launches boot2.
|
||||||
|
// * boot2 sees the lowered clock speed and launches MIOS (1-101) instead of the System Menu.
|
||||||
|
//
|
||||||
|
// Because we don't have boot1 and boot2, and BC is only ever used to launch MIOS
|
||||||
|
// (indirectly via boot2), we can just launch MIOS when BC is launched.
|
||||||
|
if (ios_title_id == Titles::BC)
|
||||||
|
{
|
||||||
|
NOTICE_LOG(IOS, "BC: Launching MIOS...");
|
||||||
|
return LaunchIOS(Titles::MIOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IOS checks whether the system title is installed and returns an error if it isn't.
|
||||||
|
// Unfortunately, we can't rely on titles being installed as we don't require system titles,
|
||||||
|
// so only have this check for MIOS (for which having the binary is *required*).
|
||||||
|
if (ios_title_id == Titles::MIOS)
|
||||||
|
{
|
||||||
|
const IOS::ES::TMDReader tmd = FindInstalledTMD(ios_title_id);
|
||||||
|
const IOS::ES::TicketReader ticket = FindSignedTicket(ios_title_id);
|
||||||
|
IOS::ES::Content content;
|
||||||
|
if (!tmd.IsValid() || !ticket.IsValid() || !tmd.GetContent(tmd.GetBootIndex(), &content) ||
|
||||||
|
!m_ios.BootIOS(ios_title_id, GetContentPath(ios_title_id, content)))
|
||||||
|
{
|
||||||
|
PanicAlertT("Could not launch IOS %016" PRIx64 " because it is missing from the NAND.\n"
|
||||||
|
"The emulated software will likely hang now.",
|
||||||
|
ios_title_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return m_ios.BootIOS(ios_title_id);
|
return m_ios.BootIOS(ios_title_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload)
|
bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload)
|
||||||
{
|
{
|
||||||
const DiscIO::NANDContentLoader& content_loader = AccessContentDevice(title_id);
|
const IOS::ES::TMDReader tmd = FindInstalledTMD(title_id);
|
||||||
if (!content_loader.IsValid())
|
const IOS::ES::TicketReader ticket = FindSignedTicket(title_id);
|
||||||
|
|
||||||
|
if (!tmd.IsValid() || !ticket.IsValid())
|
||||||
{
|
{
|
||||||
if (title_id == Titles::SYSTEM_MENU)
|
if (title_id == Titles::SYSTEM_MENU)
|
||||||
{
|
{
|
||||||
|
@ -297,33 +297,33 @@ bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content_loader.GetTMD().IsValid() || !content_loader.GetTicket().IsValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Before launching a title, IOS first reads the TMD and reloads into the specified IOS version,
|
// Before launching a title, IOS first reads the TMD and reloads into the specified IOS version,
|
||||||
// even when that version is already running. After it has reloaded, ES_Launch will be called
|
// even when that version is already running. After it has reloaded, ES_Launch will be called
|
||||||
// again with the reload skipped, and the PPC will be bootstrapped then.
|
// again with the reload skipped, and the PPC will be bootstrapped then.
|
||||||
if (!skip_reload)
|
if (!skip_reload)
|
||||||
{
|
{
|
||||||
s_title_to_launch = title_id;
|
s_title_to_launch = title_id;
|
||||||
const u64 required_ios = content_loader.GetTMD().GetIOSId();
|
const u64 required_ios = tmd.GetIOSId();
|
||||||
return LaunchTitle(required_ios);
|
return LaunchTitle(required_ios);
|
||||||
}
|
}
|
||||||
|
|
||||||
s_title_context.Update(content_loader);
|
m_title_context.Update(tmd, ticket);
|
||||||
INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: %016" PRIx64,
|
INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: %016" PRIx64, tmd.GetTitleId());
|
||||||
s_title_context.tmd.GetTitleId());
|
|
||||||
|
|
||||||
// Note: the UID/GID is also updated for IOS titles, but since we have no guarantee IOS titles
|
// Note: the UID/GID is also updated for IOS titles, but since we have no guarantee IOS titles
|
||||||
// are installed, we can only do this for PPC titles.
|
// are installed, we can only do this for PPC titles.
|
||||||
if (!UpdateUIDAndGID(m_ios, s_title_context.tmd))
|
if (!UpdateUIDAndGID(m_ios, m_title_context.tmd))
|
||||||
{
|
{
|
||||||
s_title_context.Clear();
|
m_title_context.Clear();
|
||||||
INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: (none)");
|
INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: (none)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_ios.BootstrapPPC(content_loader);
|
IOS::ES::Content content;
|
||||||
|
if (!tmd.GetContent(tmd.GetBootIndex(), &content))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_ios.BootstrapPPC(GetContentPath(tmd.GetTitleId(), content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES::Context::DoState(PointerWrap& p)
|
void ES::Context::DoState(PointerWrap& p)
|
||||||
|
@ -339,9 +339,21 @@ void ES::Context::DoState(PointerWrap& p)
|
||||||
void ES::DoState(PointerWrap& p)
|
void ES::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
Device::DoState(p);
|
Device::DoState(p);
|
||||||
p.Do(s_content_file);
|
|
||||||
p.Do(m_content_table);
|
for (auto& entry : m_content_table)
|
||||||
s_title_context.DoState(p);
|
{
|
||||||
|
p.Do(entry.m_opened);
|
||||||
|
p.Do(entry.m_title_id);
|
||||||
|
p.Do(entry.m_content);
|
||||||
|
p.Do(entry.m_position);
|
||||||
|
p.Do(entry.m_uid);
|
||||||
|
if (entry.m_opened)
|
||||||
|
entry.m_opened = entry.m_file.Open(GetContentPath(entry.m_title_id, entry.m_content), "rb");
|
||||||
|
else
|
||||||
|
entry.m_file.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_title_context.DoState(p);
|
||||||
|
|
||||||
for (auto& context : m_contexts)
|
for (auto& context : m_contexts)
|
||||||
context.DoState(p);
|
context.DoState(p);
|
||||||
|
@ -383,8 +395,6 @@ ReturnCode ES::Close(u32 fd)
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "ES: Close");
|
INFO_LOG(IOS_ES, "ES: Close");
|
||||||
m_is_active = false;
|
m_is_active = false;
|
||||||
// clear the NAND content cache to make sure nothing remains open.
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,21 +613,6 @@ IPCCommandResult ES::LaunchBC(const IOCtlVRequest& request)
|
||||||
return GetNoReply();
|
return GetNoReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
const DiscIO::NANDContentLoader& ES::AccessContentDevice(u64 title_id)
|
|
||||||
{
|
|
||||||
// for WADs, the passed title id and the stored title id match; along with s_content_file
|
|
||||||
// being set to the actual WAD file name. We cannot simply get a NAND Loader for the title id
|
|
||||||
// in those cases, since the WAD need not be installed in the NAND, but it could be opened
|
|
||||||
// directly from a WAD file anywhere on disk.
|
|
||||||
if (s_title_context.active && s_title_context.tmd.GetTitleId() == title_id &&
|
|
||||||
!s_content_file.empty())
|
|
||||||
{
|
|
||||||
return DiscIO::NANDContentManager::Access().GetNANDLoader(s_content_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DiscIO::NANDContentManager::Access().GetNANDLoader(title_id, Common::FROM_SESSION_ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is technically an ioctlv in IOS's ES, but it is an internal API which cannot be
|
// This is technically an ioctlv in IOS's ES, but it is an internal API which cannot be
|
||||||
// used from the PowerPC (for unpatched and up-to-date IOSes anyway).
|
// used from the PowerPC (for unpatched and up-to-date IOSes anyway).
|
||||||
// So we block access to it from the IPC interface.
|
// So we block access to it from the IPC interface.
|
||||||
|
@ -628,7 +623,7 @@ IPCCommandResult ES::DIVerify(const IOCtlVRequest& request)
|
||||||
|
|
||||||
s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket)
|
s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket)
|
||||||
{
|
{
|
||||||
s_title_context.Clear();
|
m_title_context.Clear();
|
||||||
INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: (none)");
|
INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: (none)");
|
||||||
|
|
||||||
if (!tmd.IsValid() || !ticket.IsValid())
|
if (!tmd.IsValid() || !ticket.IsValid())
|
||||||
|
@ -637,7 +632,7 @@ s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& tic
|
||||||
if (tmd.GetTitleId() != ticket.GetTitleId())
|
if (tmd.GetTitleId() != ticket.GetTitleId())
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
s_title_context.Update(tmd, ticket);
|
m_title_context.Update(tmd, ticket);
|
||||||
INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: %016" PRIx64, tmd.GetTitleId());
|
INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: %016" PRIx64, tmd.GetTitleId());
|
||||||
|
|
||||||
std::string tmd_path = Common::GetTMDFileName(tmd.GetTitleId(), Common::FROM_SESSION_ROOT);
|
std::string tmd_path = Common::GetTMDFileName(tmd.GetTitleId(), Common::FROM_SESSION_ROOT);
|
||||||
|
@ -655,11 +650,8 @@ s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& tic
|
||||||
if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size()))
|
if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size()))
|
||||||
ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND.");
|
ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND.");
|
||||||
}
|
}
|
||||||
// DI_VERIFY writes to title.tmd, which is read and cached inside the NAND Content Manager.
|
|
||||||
// clear the cache to avoid content access mismatches.
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
|
|
||||||
if (!UpdateUIDAndGID(*GetIOS(), s_title_context.tmd))
|
if (!UpdateUIDAndGID(*GetIOS(), m_title_context.tmd))
|
||||||
{
|
{
|
||||||
return ES_SHORT_READ;
|
return ES_SHORT_READ;
|
||||||
}
|
}
|
||||||
|
@ -724,7 +716,7 @@ ReturnCode ES::SetUpStreamKey(const u32 uid, const u8* ticket_view, const IOS::E
|
||||||
// Find a signed ticket from the view.
|
// Find a signed ticket from the view.
|
||||||
const u64 ticket_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, ticket_id)]);
|
const u64 ticket_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, ticket_id)]);
|
||||||
const u64 title_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, title_id)]);
|
const u64 title_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, title_id)]);
|
||||||
const IOS::ES::TicketReader installed_ticket = DiscIO::FindSignedTicket(title_id);
|
const IOS::ES::TicketReader installed_ticket = FindSignedTicket(title_id);
|
||||||
// Unlike the other "get ticket from view" function, this returns a FS error, not ES_NO_TICKET.
|
// Unlike the other "get ticket from view" function, this returns a FS error, not ES_NO_TICKET.
|
||||||
if (!installed_ticket.IsValid())
|
if (!installed_ticket.IsValid())
|
||||||
return FS_ENOENT;
|
return FS_ENOENT;
|
||||||
|
@ -803,10 +795,10 @@ IPCCommandResult ES::DeleteStreamKey(const IOCtlVRequest& request)
|
||||||
|
|
||||||
bool ES::IsActiveTitlePermittedByTicket(const u8* ticket_view) const
|
bool ES::IsActiveTitlePermittedByTicket(const u8* ticket_view) const
|
||||||
{
|
{
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const u32 title_identifier = static_cast<u32>(GetTitleContext().tmd.GetTitleId());
|
const u32 title_identifier = static_cast<u32>(m_title_context.tmd.GetTitleId());
|
||||||
const u32 permitted_title_mask =
|
const u32 permitted_title_mask =
|
||||||
Common::swap32(ticket_view + offsetof(IOS::ES::TicketView, permitted_title_mask));
|
Common::swap32(ticket_view + offsetof(IOS::ES::TicketView, permitted_title_mask));
|
||||||
const u32 permitted_title_id =
|
const u32 permitted_title_id =
|
||||||
|
@ -831,6 +823,9 @@ bool ES::IsIssuerCorrect(VerifyContainerType type, const IOS::ES::CertReader& is
|
||||||
|
|
||||||
ReturnCode ES::ReadCertStore(std::vector<u8>* buffer) const
|
ReturnCode ES::ReadCertStore(std::vector<u8>* buffer) const
|
||||||
{
|
{
|
||||||
|
if (!SConfig::GetInstance().m_enable_signature_checks)
|
||||||
|
return IPC_SUCCESS;
|
||||||
|
|
||||||
const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys";
|
const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys";
|
||||||
File::IOFile store_file{store_path, "rb"};
|
File::IOFile store_file{store_path, "rb"};
|
||||||
if (!store_file)
|
if (!store_file)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/File.h"
|
||||||
#include "Core/IOS/Device.h"
|
#include "Core/IOS/Device.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
|
@ -17,11 +18,6 @@
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
namespace DiscIO
|
|
||||||
{
|
|
||||||
class NANDContentLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
|
@ -32,7 +28,6 @@ struct TitleContext
|
||||||
{
|
{
|
||||||
void Clear();
|
void Clear();
|
||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p);
|
||||||
void Update(const DiscIO::NANDContentLoader& content_loader);
|
|
||||||
void Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_);
|
void Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_);
|
||||||
|
|
||||||
IOS::ES::TicketReader ticket;
|
IOS::ES::TicketReader ticket;
|
||||||
|
@ -46,8 +41,7 @@ class ES final : public Device
|
||||||
public:
|
public:
|
||||||
ES(Kernel& ios, const std::string& device_name);
|
ES(Kernel& ios, const std::string& device_name);
|
||||||
|
|
||||||
static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
|
s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
|
||||||
static void LoadWAD(const std::string& _rContentFile);
|
|
||||||
bool LaunchTitle(u64 title_id, bool skip_reload = false);
|
bool LaunchTitle(u64 title_id, bool skip_reload = false);
|
||||||
|
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
@ -87,6 +81,7 @@ public:
|
||||||
|
|
||||||
IOS::ES::TMDReader FindImportTMD(u64 title_id) const;
|
IOS::ES::TMDReader FindImportTMD(u64 title_id) const;
|
||||||
IOS::ES::TMDReader FindInstalledTMD(u64 title_id) const;
|
IOS::ES::TMDReader FindInstalledTMD(u64 title_id) const;
|
||||||
|
IOS::ES::TicketReader FindSignedTicket(u64 title_id) const;
|
||||||
|
|
||||||
// Get installed titles (in /title) without checking for TMDs at all.
|
// Get installed titles (in /title) without checking for TMDs at all.
|
||||||
std::vector<u64> GetInstalledTitles() const;
|
std::vector<u64> GetInstalledTitles() const;
|
||||||
|
@ -306,7 +301,6 @@ private:
|
||||||
|
|
||||||
bool LaunchIOS(u64 ios_title_id);
|
bool LaunchIOS(u64 ios_title_id);
|
||||||
bool LaunchPPCTitle(u64 title_id, bool skip_reload);
|
bool LaunchPPCTitle(u64 title_id, bool skip_reload);
|
||||||
static TitleContext& GetTitleContext();
|
|
||||||
bool IsActiveTitlePermittedByTicket(const u8* ticket_view) const;
|
bool IsActiveTitlePermittedByTicket(const u8* ticket_view) const;
|
||||||
|
|
||||||
ReturnCode CheckStreamKeyPermissions(u32 uid, const u8* ticket_view,
|
ReturnCode CheckStreamKeyPermissions(u32 uid, const u8* ticket_view,
|
||||||
|
@ -341,12 +335,15 @@ private:
|
||||||
void FinishStaleImport(u64 title_id);
|
void FinishStaleImport(u64 title_id);
|
||||||
void FinishAllStaleImports();
|
void FinishAllStaleImports();
|
||||||
|
|
||||||
static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id);
|
std::string GetContentPath(u64 title_id, const IOS::ES::Content& content,
|
||||||
|
const IOS::ES::SharedContentMap& map = IOS::ES::SharedContentMap{
|
||||||
|
Common::FROM_SESSION_ROOT}) const;
|
||||||
|
|
||||||
// TODO: reuse the FS code.
|
// TODO: reuse the FS code.
|
||||||
struct OpenedContent
|
struct OpenedContent
|
||||||
{
|
{
|
||||||
bool m_opened = false;
|
bool m_opened = false;
|
||||||
|
File::IOFile m_file;
|
||||||
u64 m_title_id = 0;
|
u64 m_title_id = 0;
|
||||||
IOS::ES::Content m_content;
|
IOS::ES::Content m_content;
|
||||||
u32 m_position = 0;
|
u32 m_position = 0;
|
||||||
|
@ -357,6 +354,7 @@ private:
|
||||||
ContentTable m_content_table;
|
ContentTable m_content_table;
|
||||||
|
|
||||||
ContextArray m_contexts;
|
ContextArray m_contexts;
|
||||||
|
TitleContext m_title_context{};
|
||||||
};
|
};
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
|
|
|
@ -110,11 +110,11 @@ IPCCommandResult ES::Sign(const IOCtlVRequest& request)
|
||||||
u32 data_size = request.in_vectors[0].size;
|
u32 data_size = request.in_vectors[0].size;
|
||||||
u8* sig_out = Memory::GetPointer(request.io_vectors[0].address);
|
u8* sig_out = Memory::GetPointer(request.io_vectors[0].address);
|
||||||
|
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const EcWii& ec = EcWii::GetInstance();
|
const EcWii& ec = EcWii::GetInstance();
|
||||||
MakeAPSigAndCert(sig_out, ap_cert_out, GetTitleContext().tmd.GetTitleId(), data, data_size,
|
MakeAPSigAndCert(sig_out, ap_cert_out, m_title_context.tmd.GetTitleId(), data, data_size,
|
||||||
ec.GetNGPriv(), ec.GetNGID());
|
ec.GetNGPriv(), ec.GetNGID());
|
||||||
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
|
|
|
@ -50,6 +50,20 @@ IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const
|
||||||
return FindTMD(title_id, Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT));
|
return FindTMD(title_id, Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IOS::ES::TicketReader ES::FindSignedTicket(u64 title_id) const
|
||||||
|
{
|
||||||
|
const std::string path = Common::GetTicketFileName(title_id, Common::FROM_SESSION_ROOT);
|
||||||
|
File::IOFile ticket_file(path, "rb");
|
||||||
|
if (!ticket_file)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<u8> signed_ticket(ticket_file.GetSize());
|
||||||
|
if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size()))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return IOS::ES::TicketReader{std::move(signed_ticket)};
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsValidPartOfTitleID(const std::string& string)
|
static bool IsValidPartOfTitleID(const std::string& string)
|
||||||
{
|
{
|
||||||
if (string.length() != 8)
|
if (string.length() != 8)
|
||||||
|
@ -154,21 +168,15 @@ std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea
|
||||||
if (!tmd.IsValid())
|
if (!tmd.IsValid())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const IOS::ES::SharedContentMap shared{Common::FROM_SESSION_ROOT};
|
const IOS::ES::SharedContentMap map{Common::FROM_SESSION_ROOT};
|
||||||
const std::vector<IOS::ES::Content> contents = tmd.GetContents();
|
const std::vector<IOS::ES::Content> contents = tmd.GetContents();
|
||||||
|
|
||||||
std::vector<IOS::ES::Content> stored_contents;
|
std::vector<IOS::ES::Content> stored_contents;
|
||||||
|
|
||||||
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
||||||
[&tmd, &shared](const auto& content) {
|
[this, &tmd, &map](const IOS::ES::Content& content) {
|
||||||
if (content.IsShared())
|
const std::string path = GetContentPath(tmd.GetTitleId(), content, map);
|
||||||
{
|
return !path.empty() && File::Exists(path);
|
||||||
const auto path = shared.GetFilenameFromSHA1(content.sha1);
|
|
||||||
return path && File::Exists(*path);
|
|
||||||
}
|
|
||||||
return File::Exists(
|
|
||||||
Common::GetTitleContentPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) +
|
|
||||||
StringFromFormat("%08x.app", content.id));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return stored_contents;
|
return stored_contents;
|
||||||
|
@ -289,6 +297,16 @@ void ES::FinishAllStaleImports()
|
||||||
File::DeleteDirRecursively(import_dir);
|
File::DeleteDirRecursively(import_dir);
|
||||||
File::CreateDir(import_dir);
|
File::CreateDir(import_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ES::GetContentPath(const u64 title_id, const IOS::ES::Content& content,
|
||||||
|
const IOS::ES::SharedContentMap& content_map) const
|
||||||
|
{
|
||||||
|
if (content.IsShared())
|
||||||
|
return content_map.GetFilenameFromSHA1(content.sha1).value_or("");
|
||||||
|
|
||||||
|
return Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
|
StringFromFormat("%08x.app", content.id);
|
||||||
|
}
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace IOS
|
} // namespace IOS
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -23,14 +22,10 @@ namespace Device
|
||||||
s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid)
|
s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid)
|
||||||
{
|
{
|
||||||
const u64 title_id = tmd.GetTitleId();
|
const u64 title_id = tmd.GetTitleId();
|
||||||
const DiscIO::NANDContentLoader& loader = AccessContentDevice(title_id);
|
|
||||||
|
|
||||||
if (!loader.IsValid())
|
IOS::ES::Content content;
|
||||||
return FS_ENOENT;
|
if (!tmd.GetContent(content_index, &content))
|
||||||
|
return ES_EINVAL;
|
||||||
const DiscIO::NANDContent* content = loader.GetContentByIndex(content_index);
|
|
||||||
if (!content)
|
|
||||||
return FS_ENOENT;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_content_table.size(); ++i)
|
for (size_t i = 0; i < m_content_table.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -38,9 +33,12 @@ s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid)
|
||||||
if (entry.m_opened)
|
if (entry.m_opened)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!entry.m_file.Open(GetContentPath(title_id, content), "rb"))
|
||||||
|
return FS_ENOENT;
|
||||||
|
|
||||||
entry.m_opened = true;
|
entry.m_opened = true;
|
||||||
entry.m_position = 0;
|
entry.m_position = 0;
|
||||||
entry.m_content = content->m_metadata;
|
entry.m_content = content;
|
||||||
entry.m_title_id = title_id;
|
entry.m_title_id = title_id;
|
||||||
entry.m_uid = uid;
|
entry.m_uid = uid;
|
||||||
INFO_LOG(IOS_ES, "OpenContent: title ID %016" PRIx64 ", UID 0x%x, CFD %zu", title_id, uid, i);
|
INFO_LOG(IOS_ES, "OpenContent: title ID %016" PRIx64 ", UID 0x%x, CFD %zu", title_id, uid, i);
|
||||||
|
@ -77,15 +75,15 @@ IPCCommandResult ES::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest&
|
||||||
|
|
||||||
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
|
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
|
||||||
|
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
IOS::ES::UIDSys uid_map{Common::FROM_SESSION_ROOT};
|
IOS::ES::UIDSys uid_map{Common::FROM_SESSION_ROOT};
|
||||||
const u32 uid = uid_map.GetOrInsertUIDForTitle(GetTitleContext().tmd.GetTitleId());
|
const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId());
|
||||||
if (caller_uid != 0 && caller_uid != uid)
|
if (caller_uid != 0 && caller_uid != uid)
|
||||||
return GetDefaultReply(ES_EACCES);
|
return GetDefaultReply(ES_EACCES);
|
||||||
|
|
||||||
return GetDefaultReply(OpenContent(GetTitleContext().tmd, content_index, caller_uid));
|
return GetDefaultReply(OpenContent(m_title_context.tmd, content_index, caller_uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 ES::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
|
s32 ES::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
|
||||||
|
@ -102,20 +100,14 @@ s32 ES::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
|
||||||
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
|
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
|
||||||
// instead of all this duplicated filesystem logic.
|
// instead of all this duplicated filesystem logic.
|
||||||
|
|
||||||
if (entry.m_position + size > entry.m_content.size)
|
if (entry.m_position + size > entry.m_file.GetSize())
|
||||||
size = static_cast<u32>(entry.m_content.size) - entry.m_position;
|
size = static_cast<u32>(entry.m_file.GetSize()) - entry.m_position;
|
||||||
|
|
||||||
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
|
entry.m_file.Seek(entry.m_position, SEEK_SET);
|
||||||
// ContentLoader should never be invalid; rContent has been created by it.
|
if (!entry.m_file.ReadBytes(buffer, size))
|
||||||
if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
|
|
||||||
{
|
{
|
||||||
const DiscIO::NANDContent* pContent = ContentLoader.GetContentByIndex(entry.m_content.index);
|
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
|
||||||
pContent->m_Data->Open();
|
return ES_SHORT_READ;
|
||||||
if (!pContent->m_Data->GetRange(entry.m_position, size, buffer))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
|
|
||||||
return ES_SHORT_READ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.m_position += size;
|
entry.m_position += size;
|
||||||
|
@ -145,15 +137,6 @@ ReturnCode ES::CloseContent(u32 cfd, u32 uid)
|
||||||
if (!entry.m_opened)
|
if (!entry.m_opened)
|
||||||
return IPC_EINVAL;
|
return IPC_EINVAL;
|
||||||
|
|
||||||
// XXX: again, this should be a simple IOS_Close.
|
|
||||||
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
|
|
||||||
// ContentLoader should never be invalid; we shouldn't be here if ES_OPENCONTENT failed before.
|
|
||||||
if (ContentLoader.IsValid())
|
|
||||||
{
|
|
||||||
const DiscIO::NANDContent* content = ContentLoader.GetContentByIndex(entry.m_content.index);
|
|
||||||
content->m_Data->Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = {};
|
entry = {};
|
||||||
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
|
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/ec_wii.h"
|
#include "Core/ec_wii.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -160,8 +159,8 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
|
||||||
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
|
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
|
||||||
return ES_EIO;
|
return ES_EIO;
|
||||||
|
|
||||||
ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
|
ret =
|
||||||
&context.title_import_export.key_handle);
|
InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -220,7 +219,7 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
|
const auto ticket = FindSignedTicket(context.title_import_export.tmd.GetTitleId());
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
return ES_NO_TICKET;
|
return ES_NO_TICKET;
|
||||||
|
|
||||||
|
@ -506,8 +505,6 @@ ReturnCode ES::DeleteTitle(u64 title_id)
|
||||||
ERROR_LOG(IOS_ES, "DeleteTitle: Failed to delete title directory: %s", title_dir.c_str());
|
ERROR_LOG(IOS_ES, "DeleteTitle: Failed to delete title directory: %s", title_dir.c_str());
|
||||||
return FS_EACCESS;
|
return FS_EACCESS;
|
||||||
}
|
}
|
||||||
// XXX: ugly, but until we drop NANDContentManager everywhere, this is going to be needed.
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
|
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -528,7 +525,7 @@ ReturnCode ES::DeleteTicket(const u8* ticket_view)
|
||||||
if (!CanDeleteTitle(title_id))
|
if (!CanDeleteTitle(title_id))
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
auto ticket = DiscIO::FindSignedTicket(title_id);
|
auto ticket = FindSignedTicket(title_id);
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
return FS_ENOENT;
|
return FS_ENOENT;
|
||||||
|
|
||||||
|
@ -639,8 +636,8 @@ ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u3
|
||||||
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
ResetTitleImportContext(&context, m_ios.GetIOSC());
|
||||||
context.title_import_export.tmd = tmd;
|
context.title_import_export.tmd = tmd;
|
||||||
|
|
||||||
const ReturnCode ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
|
const ReturnCode ret =
|
||||||
&context.title_import_export.key_handle);
|
InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle);
|
||||||
if (ret != IPC_SUCCESS)
|
if (ret != IPC_SUCCESS)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -49,10 +48,10 @@ IPCCommandResult ES::GetTicketViewCount(const IOCtlVRequest& request)
|
||||||
|
|
||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
|
|
||||||
const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
|
const IOS::ES::TicketReader ticket = FindSignedTicket(TitleID);
|
||||||
u32 view_count = ticket.IsValid() ? static_cast<u32>(ticket.GetNumberOfTickets()) : 0;
|
u32 view_count = ticket.IsValid() ? static_cast<u32>(ticket.GetNumberOfTickets()) : 0;
|
||||||
|
|
||||||
if (ShouldReturnFakeViewsForIOSes(TitleID, GetTitleContext()))
|
if (ShouldReturnFakeViewsForIOSes(TitleID, m_title_context))
|
||||||
{
|
{
|
||||||
view_count = 1;
|
view_count = 1;
|
||||||
WARN_LOG(IOS_ES, "GetViewCount: Faking IOS title %016" PRIx64 " being present", TitleID);
|
WARN_LOG(IOS_ES, "GetViewCount: Faking IOS title %016" PRIx64 " being present", TitleID);
|
||||||
|
@ -73,7 +72,7 @@ IPCCommandResult ES::GetTicketViews(const IOCtlVRequest& request)
|
||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
u32 maxViews = Memory::Read_U32(request.in_vectors[1].address);
|
u32 maxViews = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
|
|
||||||
const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
|
const IOS::ES::TicketReader ticket = FindSignedTicket(TitleID);
|
||||||
|
|
||||||
if (ticket.IsValid())
|
if (ticket.IsValid())
|
||||||
{
|
{
|
||||||
|
@ -85,7 +84,7 @@ IPCCommandResult ES::GetTicketViews(const IOCtlVRequest& request)
|
||||||
ticket_view.data(), ticket_view.size());
|
ticket_view.data(), ticket_view.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ShouldReturnFakeViewsForIOSes(TitleID, GetTitleContext()))
|
else if (ShouldReturnFakeViewsForIOSes(TitleID, m_title_context))
|
||||||
{
|
{
|
||||||
Memory::Memset(request.io_vectors[0].address, 0, sizeof(IOS::ES::TicketView));
|
Memory::Memset(request.io_vectors[0].address, 0, sizeof(IOS::ES::TicketView));
|
||||||
WARN_LOG(IOS_ES, "GetViews: Faking IOS title %016" PRIx64 " being present", TitleID);
|
WARN_LOG(IOS_ES, "GetViews: Faking IOS title %016" PRIx64 " being present", TitleID);
|
||||||
|
@ -102,7 +101,7 @@ ReturnCode ES::GetV0TicketFromView(const u8* ticket_view, u8* ticket) const
|
||||||
const u64 title_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, title_id)]);
|
const u64 title_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, title_id)]);
|
||||||
const u64 ticket_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, ticket_id)]);
|
const u64 ticket_id = Common::swap64(&ticket_view[offsetof(IOS::ES::TicketView, ticket_id)]);
|
||||||
|
|
||||||
const auto installed_ticket = DiscIO::FindSignedTicket(title_id);
|
const auto installed_ticket = FindSignedTicket(title_id);
|
||||||
// TODO: when we get std::optional, check for presence instead of validity.
|
// TODO: when we get std::optional, check for presence instead of validity.
|
||||||
// This is close enough, though.
|
// This is close enough, though.
|
||||||
if (!installed_ticket.IsValid())
|
if (!installed_ticket.IsValid())
|
||||||
|
@ -112,11 +111,11 @@ ReturnCode ES::GetV0TicketFromView(const u8* ticket_view, u8* ticket) const
|
||||||
if (ticket_bytes.empty())
|
if (ticket_bytes.empty())
|
||||||
return ES_NO_TICKET;
|
return ES_NO_TICKET;
|
||||||
|
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
// Check for permission to export the ticket.
|
// Check for permission to export the ticket.
|
||||||
const u32 title_identifier = static_cast<u32>(GetTitleContext().tmd.GetTitleId());
|
const u32 title_identifier = static_cast<u32>(m_title_context.tmd.GetTitleId());
|
||||||
const u32 permitted_title_mask =
|
const u32 permitted_title_mask =
|
||||||
Common::swap32(ticket_bytes.data() + offsetof(IOS::ES::Ticket, permitted_title_mask));
|
Common::swap32(ticket_bytes.data() + offsetof(IOS::ES::Ticket, permitted_title_mask));
|
||||||
const u32 permitted_title_id =
|
const u32 permitted_title_id =
|
||||||
|
@ -276,10 +275,10 @@ IPCCommandResult ES::DIGetTMDViewSize(const IOCtlVRequest& request)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If no TMD was passed in and no title is active, IOS returns -1017.
|
// If no TMD was passed in and no title is active, IOS returns -1017.
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
tmd_view_size = GetTitleContext().tmd.GetRawView().size();
|
tmd_view_size = m_title_context.tmd.GetRawView().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory::Write_U32(static_cast<u32>(tmd_view_size), request.io_vectors[0].address);
|
Memory::Write_U32(static_cast<u32>(tmd_view_size), request.io_vectors[0].address);
|
||||||
|
@ -319,10 +318,10 @@ IPCCommandResult ES::DIGetTMDView(const IOCtlVRequest& request)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If no TMD was passed in and no title is active, IOS returns -1017.
|
// If no TMD was passed in and no title is active, IOS returns -1017.
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
tmd_view = GetTitleContext().tmd.GetRawView();
|
tmd_view = m_title_context.tmd.GetRawView();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmd_view.size() > request.io_vectors[0].size)
|
if (tmd_view.size() > request.io_vectors[0].size)
|
||||||
|
@ -352,10 +351,10 @@ IPCCommandResult ES::DIGetTicketView(const IOCtlVRequest& request)
|
||||||
// Of course, this returns -1017 if no title is active and no ticket is passed.
|
// Of course, this returns -1017 if no title is active and no ticket is passed.
|
||||||
if (!has_ticket_vector)
|
if (!has_ticket_vector)
|
||||||
{
|
{
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
view = GetTitleContext().ticket.GetRawTicketView(0);
|
view = m_title_context.ticket.GetRawTicketView(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -375,10 +374,10 @@ IPCCommandResult ES::DIGetTMDSize(const IOCtlVRequest& request)
|
||||||
if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != sizeof(u32))
|
if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != sizeof(u32))
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
Memory::Write_U32(static_cast<u32>(GetTitleContext().tmd.GetBytes().size()),
|
Memory::Write_U32(static_cast<u32>(m_title_context.tmd.GetBytes().size()),
|
||||||
request.io_vectors[0].address);
|
request.io_vectors[0].address);
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -392,10 +391,10 @@ IPCCommandResult ES::DIGetTMD(const IOCtlVRequest& request)
|
||||||
if (tmd_size != request.io_vectors[0].size)
|
if (tmd_size != request.io_vectors[0].size)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
if (!GetTitleContext().active)
|
if (!m_title_context.active)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
||||||
const std::vector<u8>& tmd_bytes = GetTitleContext().tmd.GetBytes();
|
const std::vector<u8>& tmd_bytes = m_title_context.tmd.GetBytes();
|
||||||
|
|
||||||
if (static_cast<u32>(tmd_bytes.size()) > tmd_size)
|
if (static_cast<u32>(tmd_bytes.size()) > tmd_size)
|
||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Core/Boot/DolReader.h"
|
#include "Core/Boot/DolReader.h"
|
||||||
|
#include "Core/Boot/ElfReader.h"
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
@ -54,7 +55,6 @@
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/WiiRoot.h"
|
#include "Core/WiiRoot.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -275,23 +275,17 @@ u16 Kernel::GetGidForPPC() const
|
||||||
|
|
||||||
// This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC.
|
// This corresponds to syscall 0x41, which loads a binary from the NAND and bootstraps the PPC.
|
||||||
// Unlike 0x42, IOS will set up some constants in memory before booting the PPC.
|
// Unlike 0x42, IOS will set up some constants in memory before booting the PPC.
|
||||||
bool Kernel::BootstrapPPC(const DiscIO::NANDContentLoader& content_loader)
|
bool Kernel::BootstrapPPC(const std::string& boot_content_path)
|
||||||
{
|
{
|
||||||
if (!content_loader.IsValid())
|
const DolReader dol{boot_content_path};
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto* content = content_loader.GetContentByIndex(content_loader.GetTMD().GetBootIndex());
|
if (!dol.IsValid())
|
||||||
if (!content)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto dol_loader = std::make_unique<DolReader>(content->m_Data->Get());
|
|
||||||
if (!dol_loader->IsValid())
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!SetupMemory(m_title_id, MemorySetupType::Full))
|
if (!SetupMemory(m_title_id, MemorySetupType::Full))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!dol_loader->LoadIntoMemory())
|
if (!dol.LoadIntoMemory())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// NAND titles start with address translation off at 0x3400 (via the PPC bootstub)
|
// NAND titles start with address translation off at 0x3400 (via the PPC bootstub)
|
||||||
|
@ -303,23 +297,60 @@ bool Kernel::BootstrapPPC(const DiscIO::NANDContentLoader& content_loader)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ARMBinary final
|
||||||
|
{
|
||||||
|
explicit ARMBinary(std::vector<u8>&& bytes) : m_bytes(std::move(bytes)) {}
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
// The header is at least 0x10.
|
||||||
|
if (m_bytes.size() < 0x10)
|
||||||
|
return false;
|
||||||
|
return m_bytes.size() >= (GetHeaderSize() + GetElfOffset() + GetElfSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> GetElf() const
|
||||||
|
{
|
||||||
|
const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset();
|
||||||
|
return std::vector<u8>(iterator, iterator + GetElfSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetHeaderSize() const { return Common::swap32(m_bytes.data()); }
|
||||||
|
u32 GetElfOffset() const { return Common::swap32(m_bytes.data() + 0x4); }
|
||||||
|
u32 GetElfSize() const { return Common::swap32(m_bytes.data() + 0x8); }
|
||||||
|
private:
|
||||||
|
std::vector<u8> m_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
// Similar to syscall 0x42 (ios_boot); this is used to change the current active IOS.
|
// Similar to syscall 0x42 (ios_boot); this is used to change the current active IOS.
|
||||||
// IOS writes the new version to 0x3140 before restarting, but it does *not* poke any
|
// IOS writes the new version to 0x3140 before restarting, but it does *not* poke any
|
||||||
// of the other constants to the memory. Warning: this resets the kernel instance.
|
// of the other constants to the memory. Warning: this resets the kernel instance.
|
||||||
bool Kernel::BootIOS(const u64 ios_title_id)
|
//
|
||||||
|
// Passing a boot content path is optional because we do not require IOSes
|
||||||
|
// to be installed at the moment. If one is passed, the boot binary must exist
|
||||||
|
// on the NAND, or the call will fail like on a Wii.
|
||||||
|
bool Kernel::BootIOS(const u64 ios_title_id, const std::string& boot_content_path)
|
||||||
{
|
{
|
||||||
// A real Wii goes through several steps before getting to MIOS.
|
if (!boot_content_path.empty())
|
||||||
//
|
|
||||||
// * The System Menu detects a GameCube disc and launches BC (1-100) instead of the game.
|
|
||||||
// * BC (similar to boot1) lowers the clock speed to the Flipper's and then launches boot2.
|
|
||||||
// * boot2 sees the lowered clock speed and launches MIOS (1-101) instead of the System Menu.
|
|
||||||
//
|
|
||||||
// Because we currently don't have boot1 and boot2, and BC is only ever used to launch MIOS
|
|
||||||
// (indirectly via boot2), we can just launch MIOS when BC is launched.
|
|
||||||
if (ios_title_id == Titles::BC)
|
|
||||||
{
|
{
|
||||||
NOTICE_LOG(IOS, "BC: Launching MIOS...");
|
// Load the ARM binary to memory (if possible).
|
||||||
return BootIOS(Titles::MIOS);
|
// Because we do not actually emulate the Starlet, only load the sections that are in MEM1.
|
||||||
|
|
||||||
|
File::IOFile file{boot_content_path, "rb"};
|
||||||
|
// TODO: should return IPC_ERROR_MAX.
|
||||||
|
if (file.GetSize() > 0xB00000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<u8> data(file.GetSize());
|
||||||
|
if (!file.ReadBytes(data.data(), data.size()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ARMBinary binary{std::move(data)};
|
||||||
|
if (!binary.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ElfReader elf{binary.GetElf()};
|
||||||
|
if (!elf.LoadIntoMemory(true))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shut down the active IOS first before switching to the new one.
|
// Shut down the active IOS first before switching to the new one.
|
||||||
|
|
|
@ -19,11 +19,6 @@
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
namespace DiscIO
|
|
||||||
{
|
|
||||||
class NANDContentLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
|
@ -113,8 +108,8 @@ public:
|
||||||
void SetGidForPPC(u16 gid);
|
void SetGidForPPC(u16 gid);
|
||||||
u16 GetGidForPPC() const;
|
u16 GetGidForPPC() const;
|
||||||
|
|
||||||
bool BootstrapPPC(const DiscIO::NANDContentLoader& content_loader);
|
bool BootstrapPPC(const std::string& boot_content_path);
|
||||||
bool BootIOS(u64 ios_title_id);
|
bool BootIOS(u64 ios_title_id, const std::string& boot_content_path = "");
|
||||||
u32 GetVersion() const;
|
u32 GetVersion() const;
|
||||||
|
|
||||||
IOSC& GetIOSC();
|
IOSC& GetIOSC();
|
||||||
|
|
|
@ -6,16 +6,12 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/NandPaths.h"
|
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
#include "Core/Boot/ElfReader.h"
|
|
||||||
#include "Core/CommonTitles.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/DSPEmulator.h"
|
#include "Core/DSPEmulator.h"
|
||||||
|
@ -24,10 +20,8 @@
|
||||||
#include "Core/HW/DVD/DVDInterface.h"
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
|
||||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
|
@ -35,73 +29,6 @@ namespace HLE
|
||||||
{
|
{
|
||||||
namespace MIOS
|
namespace MIOS
|
||||||
{
|
{
|
||||||
// Source: https://wiibrew.org/wiki/ARM_Binaries
|
|
||||||
struct ARMBinary final
|
|
||||||
{
|
|
||||||
explicit ARMBinary(const std::vector<u8>& bytes);
|
|
||||||
explicit ARMBinary(std::vector<u8>&& bytes);
|
|
||||||
|
|
||||||
bool IsValid() const;
|
|
||||||
std::vector<u8> GetElf() const;
|
|
||||||
u32 GetHeaderSize() const;
|
|
||||||
u32 GetElfOffset() const;
|
|
||||||
u32 GetElfSize() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<u8> m_bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
ARMBinary::ARMBinary(const std::vector<u8>& bytes) : m_bytes(bytes)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ARMBinary::ARMBinary(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ARMBinary::IsValid() const
|
|
||||||
{
|
|
||||||
// The header is at least 0x10.
|
|
||||||
if (m_bytes.size() < 0x10)
|
|
||||||
return false;
|
|
||||||
return m_bytes.size() >= (GetHeaderSize() + GetElfOffset() + GetElfSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> ARMBinary::GetElf() const
|
|
||||||
{
|
|
||||||
const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset();
|
|
||||||
return std::vector<u8>(iterator, iterator + GetElfSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ARMBinary::GetHeaderSize() const
|
|
||||||
{
|
|
||||||
return Common::swap32(m_bytes.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ARMBinary::GetElfOffset() const
|
|
||||||
{
|
|
||||||
return Common::swap32(m_bytes.data() + 0x4);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ARMBinary::GetElfSize() const
|
|
||||||
{
|
|
||||||
return Common::swap32(m_bytes.data() + 0x8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<u8> GetMIOSBinary()
|
|
||||||
{
|
|
||||||
const auto& loader =
|
|
||||||
DiscIO::NANDContentManager::Access().GetNANDLoader(Titles::MIOS, Common::FROM_SESSION_ROOT);
|
|
||||||
if (!loader.IsValid())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex());
|
|
||||||
if (!content)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return content->m_Data->Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ReinitHardware()
|
static void ReinitHardware()
|
||||||
{
|
{
|
||||||
SConfig::GetInstance().bWii = false;
|
SConfig::GetInstance().bWii = false;
|
||||||
|
@ -125,22 +52,6 @@ bool Load()
|
||||||
Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
|
Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
|
||||||
Memory::Write_U32(0x09142001, 0x3180);
|
Memory::Write_U32(0x09142001, 0x3180);
|
||||||
|
|
||||||
ARMBinary mios{GetMIOSBinary()};
|
|
||||||
if (!mios.IsValid())
|
|
||||||
{
|
|
||||||
PanicAlertT("Failed to load MIOS. It is required for launching GameCube titles from Wii mode.");
|
|
||||||
Core::QueueHostJob(Core::Stop);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ElfReader elf{mios.GetElf()};
|
|
||||||
if (!elf.LoadIntoMemory(true))
|
|
||||||
{
|
|
||||||
PanicAlertT("Failed to load MIOS ELF into memory.");
|
|
||||||
Core::QueueHostJob(Core::Stop);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReinitHardware();
|
ReinitHardware();
|
||||||
NOTICE_LOG(IOS, "Reinitialised hardware.");
|
NOTICE_LOG(IOS, "Reinitialised hardware.");
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "Core/IOS/ES/ES.h"
|
#include "Core/IOS/ES/ES.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -157,7 +156,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
|
||||||
Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size);
|
Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size);
|
||||||
m_tmd.SetBytes(std::move(tmd_bytes));
|
m_tmd.SetBytes(std::move(tmd_bytes));
|
||||||
|
|
||||||
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_tmd.GetTitleId());
|
IOS::ES::TicketReader ticket = m_ios.GetES()->FindSignedTicket(m_tmd.GetTitleId());
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
{
|
{
|
||||||
return_error_code = -11028;
|
return_error_code = -11028;
|
||||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
static const u32 STATE_VERSION = 90; // Last changed in PR 6077
|
static const u32 STATE_VERSION = 91; // Last changed in PR 6094
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "Common/NandPaths.h"
|
#include "Common/NandPaths.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
|
#include "Common/SysConf.h"
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/IOS/Device.h"
|
#include "Core/IOS/Device.h"
|
||||||
|
@ -38,7 +39,6 @@
|
||||||
#include "DiscIO/DiscExtractor.h"
|
#include "DiscIO/DiscExtractor.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/Filesystem.h"
|
#include "DiscIO/Filesystem.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/VolumeFileBlobReader.h"
|
#include "DiscIO/VolumeFileBlobReader.h"
|
||||||
#include "DiscIO/VolumeWii.h"
|
#include "DiscIO/VolumeWii.h"
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
namespace WiiUtils
|
namespace WiiUtils
|
||||||
{
|
{
|
||||||
static bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad)
|
static bool ImportWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad)
|
||||||
{
|
{
|
||||||
if (!wad.IsValid())
|
if (!wad.IsValid())
|
||||||
{
|
{
|
||||||
|
@ -76,8 +76,9 @@ static bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret != IOS::HLE::IOSC_FAIL_CHECKVALUE)
|
||||||
|
PanicAlertT("WAD installation failed: Could not initialise title import (error %d).", ret);
|
||||||
SConfig::GetInstance().m_enable_signature_checks = checks_enabled;
|
SConfig::GetInstance().m_enable_signature_checks = checks_enabled;
|
||||||
PanicAlertT("WAD installation failed: Could not initialise title import.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SConfig::GetInstance().m_enable_signature_checks = checks_enabled;
|
SConfig::GetInstance().m_enable_signature_checks = checks_enabled;
|
||||||
|
@ -109,14 +110,54 @@ static bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad, InstallType install_type)
|
||||||
|
{
|
||||||
|
if (!wad.GetTMD().IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Skip the install if the WAD is already installed.
|
||||||
|
const auto installed_contents = ios.GetES()->GetStoredContentsFromTMD(wad.GetTMD());
|
||||||
|
if (wad.GetTMD().GetContents() == installed_contents)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If a different version is currently installed, warn the user to make sure
|
||||||
|
// they don't overwrite the current version by mistake.
|
||||||
|
const u64 title_id = wad.GetTMD().GetTitleId();
|
||||||
|
const IOS::ES::TMDReader installed_tmd = ios.GetES()->FindInstalledTMD(title_id);
|
||||||
|
const bool has_another_version =
|
||||||
|
installed_tmd.IsValid() && installed_tmd.GetTitleVersion() != wad.GetTMD().GetTitleVersion();
|
||||||
|
if (has_another_version &&
|
||||||
|
!AskYesNoT("A different version of this title is already installed on the NAND.\n\n"
|
||||||
|
"Installed version: %u\nWAD version: %u\n\n"
|
||||||
|
"Installing this WAD will replace it irreversibly. Continue?",
|
||||||
|
installed_tmd.GetTitleVersion(), wad.GetTMD().GetTitleVersion()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a previous temporary title, if it exists.
|
||||||
|
SysConf sysconf{Common::FROM_SESSION_ROOT};
|
||||||
|
SysConf::Entry* tid_entry = sysconf.GetOrAddEntry("IPL.TID", SysConf::Entry::Type::LongLong);
|
||||||
|
if (const u64 previous_temporary_title_id = Common::swap64(tid_entry->GetData<u64>(0)))
|
||||||
|
ios.GetES()->DeleteTitleContent(previous_temporary_title_id);
|
||||||
|
|
||||||
|
if (!ImportWAD(ios, wad))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Keep track of the title ID so this title can be removed to make room for any future install.
|
||||||
|
// We use the same mechanism as the System Menu for temporary SD card title data.
|
||||||
|
if (!has_another_version && install_type == InstallType::Temporary)
|
||||||
|
tid_entry->SetData<u64>(Common::swap64(title_id));
|
||||||
|
else
|
||||||
|
tid_entry->SetData<u64>(0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool InstallWAD(const std::string& wad_path)
|
bool InstallWAD(const std::string& wad_path)
|
||||||
{
|
{
|
||||||
IOS::HLE::Kernel ios;
|
IOS::HLE::Kernel ios;
|
||||||
const DiscIO::WiiWAD wad{wad_path};
|
return InstallWAD(ios, DiscIO::WiiWAD{wad_path}, InstallType::Permanent);
|
||||||
const bool result = InstallWAD(ios, wad);
|
|
||||||
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common functionality for system updaters.
|
// Common functionality for system updaters.
|
||||||
|
@ -648,7 +689,7 @@ UpdateResult DiscSystemUpdater::ProcessEntry(u32 type, std::bitset<32> attrs,
|
||||||
return UpdateResult::AlreadyUpToDate;
|
return UpdateResult::AlreadyUpToDate;
|
||||||
|
|
||||||
const IOS::ES::TMDReader tmd = m_ios.GetES()->FindInstalledTMD(title.id);
|
const IOS::ES::TMDReader tmd = m_ios.GetES()->FindInstalledTMD(title.id);
|
||||||
const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(title.id);
|
const IOS::ES::TicketReader ticket = m_ios.GetES()->FindSignedTicket(title.id);
|
||||||
|
|
||||||
// Optional titles can be skipped if the ticket is present, even when the title isn't installed.
|
// Optional titles can be skipped if the ticket is present, even when the title isn't installed.
|
||||||
if (attrs.test(16) && ticket.IsValid())
|
if (attrs.test(16) && ticket.IsValid())
|
||||||
|
@ -667,23 +708,19 @@ UpdateResult DiscSystemUpdater::ProcessEntry(u32 type, std::bitset<32> attrs,
|
||||||
return UpdateResult::DiscReadFailed;
|
return UpdateResult::DiscReadFailed;
|
||||||
}
|
}
|
||||||
const DiscIO::WiiWAD wad{std::move(blob)};
|
const DiscIO::WiiWAD wad{std::move(blob)};
|
||||||
return InstallWAD(m_ios, wad) ? UpdateResult::Succeeded : UpdateResult::ImportFailed;
|
return ImportWAD(m_ios, wad) ? UpdateResult::Succeeded : UpdateResult::ImportFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& region)
|
UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& region)
|
||||||
{
|
{
|
||||||
OnlineSystemUpdater updater{std::move(update_callback), region};
|
OnlineSystemUpdater updater{std::move(update_callback), region};
|
||||||
const UpdateResult result = updater.DoOnlineUpdate();
|
return updater.DoOnlineUpdate();
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path)
|
UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path)
|
||||||
{
|
{
|
||||||
DiscSystemUpdater updater{std::move(update_callback), image_path};
|
DiscSystemUpdater updater{std::move(update_callback), image_path};
|
||||||
const UpdateResult result = updater.DoDiscUpdate();
|
return updater.DoDiscUpdate();
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios)
|
NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios)
|
||||||
|
@ -713,7 +750,7 @@ NANDCheckResult CheckNAND(IOS::HLE::Kernel& ios)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for incomplete title installs (missing ticket, TMD or contents).
|
// Check for incomplete title installs (missing ticket, TMD or contents).
|
||||||
const auto ticket = DiscIO::FindSignedTicket(title_id);
|
const auto ticket = es->FindSignedTicket(title_id);
|
||||||
if (!IOS::ES::IsDiscTitle(title_id) && !ticket.IsValid())
|
if (!IOS::ES::IsDiscTitle(title_id) && !ticket.IsValid())
|
||||||
{
|
{
|
||||||
ERROR_LOG(CORE, "CheckNAND: Missing ticket for title %016" PRIx64, title_id);
|
ERROR_LOG(CORE, "CheckNAND: Missing ticket for title %016" PRIx64, title_id);
|
||||||
|
@ -779,7 +816,7 @@ bool RepairNAND(IOS::HLE::Kernel& ios)
|
||||||
const auto content_files = File::ScanDirectoryTree(content_dir, false).children;
|
const auto content_files = File::ScanDirectoryTree(content_dir, false).children;
|
||||||
const bool has_no_tmd_but_contents =
|
const bool has_no_tmd_but_contents =
|
||||||
!es->FindInstalledTMD(title_id).IsValid() && !content_files.empty();
|
!es->FindInstalledTMD(title_id).IsValid() && !content_files.empty();
|
||||||
if (has_no_tmd_but_contents || !DiscIO::FindSignedTicket(title_id).IsValid())
|
if (has_no_tmd_but_contents || !es->FindSignedTicket(title_id).IsValid())
|
||||||
{
|
{
|
||||||
const std::string title_dir = Common::GetTitlePath(title_id, Common::FROM_CONFIGURED_ROOT);
|
const std::string title_dir = Common::GetTitlePath(title_id, Common::FROM_CONFIGURED_ROOT);
|
||||||
File::DeleteDirRecursively(title_dir);
|
File::DeleteDirRecursively(title_dir);
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
|
|
||||||
// Small utility functions for common Wii related tasks.
|
// Small utility functions for common Wii related tasks.
|
||||||
|
|
||||||
|
namespace DiscIO
|
||||||
|
{
|
||||||
|
class WiiWAD;
|
||||||
|
}
|
||||||
namespace IOS
|
namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
|
@ -23,6 +27,15 @@ class Kernel;
|
||||||
|
|
||||||
namespace WiiUtils
|
namespace WiiUtils
|
||||||
{
|
{
|
||||||
|
enum class InstallType
|
||||||
|
{
|
||||||
|
Permanent,
|
||||||
|
Temporary,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad, InstallType type);
|
||||||
|
// Same as the above, but constructs a temporary IOS and WiiWAD instance for importing
|
||||||
|
// and does a permanent install.
|
||||||
bool InstallWAD(const std::string& wad_path);
|
bool InstallWAD(const std::string& wad_path);
|
||||||
|
|
||||||
enum class UpdateResult
|
enum class UpdateResult
|
||||||
|
|
|
@ -11,7 +11,6 @@ set(SRCS
|
||||||
FileBlob.cpp
|
FileBlob.cpp
|
||||||
FileSystemGCWii.cpp
|
FileSystemGCWii.cpp
|
||||||
Filesystem.cpp
|
Filesystem.cpp
|
||||||
NANDContentLoader.cpp
|
|
||||||
NANDImporter.cpp
|
NANDImporter.cpp
|
||||||
TGCBlob.cpp
|
TGCBlob.cpp
|
||||||
Volume.cpp
|
Volume.cpp
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
<ClCompile Include="FileBlob.cpp" />
|
<ClCompile Include="FileBlob.cpp" />
|
||||||
<ClCompile Include="Filesystem.cpp" />
|
<ClCompile Include="Filesystem.cpp" />
|
||||||
<ClCompile Include="FileSystemGCWii.cpp" />
|
<ClCompile Include="FileSystemGCWii.cpp" />
|
||||||
<ClCompile Include="NANDContentLoader.cpp" />
|
|
||||||
<ClCompile Include="NANDImporter.cpp" />
|
<ClCompile Include="NANDImporter.cpp" />
|
||||||
<ClCompile Include="TGCBlob.cpp" />
|
<ClCompile Include="TGCBlob.cpp" />
|
||||||
<ClCompile Include="Volume.cpp" />
|
<ClCompile Include="Volume.cpp" />
|
||||||
|
@ -70,7 +69,6 @@
|
||||||
<ClInclude Include="FileBlob.h" />
|
<ClInclude Include="FileBlob.h" />
|
||||||
<ClInclude Include="Filesystem.h" />
|
<ClInclude Include="Filesystem.h" />
|
||||||
<ClInclude Include="FileSystemGCWii.h" />
|
<ClInclude Include="FileSystemGCWii.h" />
|
||||||
<ClInclude Include="NANDContentLoader.h" />
|
|
||||||
<ClInclude Include="NANDImporter.h" />
|
<ClInclude Include="NANDImporter.h" />
|
||||||
<ClInclude Include="TGCBlob.h" />
|
<ClInclude Include="TGCBlob.h" />
|
||||||
<ClInclude Include="Volume.h" />
|
<ClInclude Include="Volume.h" />
|
||||||
|
|
|
@ -33,9 +33,6 @@
|
||||||
<ClCompile Include="WiiWad.cpp">
|
<ClCompile Include="WiiWad.cpp">
|
||||||
<Filter>NAND</Filter>
|
<Filter>NAND</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="NANDContentLoader.cpp">
|
|
||||||
<Filter>NAND</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="NANDImporter.cpp">
|
<ClCompile Include="NANDImporter.cpp">
|
||||||
<Filter>NAND</Filter>
|
<Filter>NAND</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -98,9 +95,6 @@
|
||||||
<ClInclude Include="WiiWad.h">
|
<ClInclude Include="WiiWad.h">
|
||||||
<Filter>NAND</Filter>
|
<Filter>NAND</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="NANDContentLoader.h">
|
|
||||||
<Filter>NAND</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="NANDImporter.h">
|
<ClInclude Include="NANDImporter.h">
|
||||||
<Filter>NAND</Filter>
|
<Filter>NAND</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -1,268 +0,0 @@
|
||||||
// Copyright 2009 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/Align.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/Crypto/AES.h"
|
|
||||||
#include "Common/File.h"
|
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
#include "Common/NandPaths.h"
|
|
||||||
#include "Common/StringUtil.h"
|
|
||||||
#include "Common/Swap.h"
|
|
||||||
// TODO: kill this dependency.
|
|
||||||
#include "Core/IOS/ES/Formats.h"
|
|
||||||
|
|
||||||
#include "DiscIO/WiiWad.h"
|
|
||||||
|
|
||||||
namespace DiscIO
|
|
||||||
{
|
|
||||||
NANDContentData::~NANDContentData() = default;
|
|
||||||
|
|
||||||
NANDContentDataFile::NANDContentDataFile(const std::string& filename) : m_filename{filename}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NANDContentDataFile::~NANDContentDataFile() = default;
|
|
||||||
|
|
||||||
void NANDContentDataFile::EnsureOpen()
|
|
||||||
{
|
|
||||||
if (!m_file)
|
|
||||||
m_file = std::make_unique<File::IOFile>(m_filename, "rb");
|
|
||||||
else if (!m_file->IsOpen())
|
|
||||||
m_file->Open(m_filename, "rb");
|
|
||||||
}
|
|
||||||
void NANDContentDataFile::Open()
|
|
||||||
{
|
|
||||||
EnsureOpen();
|
|
||||||
}
|
|
||||||
std::vector<u8> NANDContentDataFile::Get()
|
|
||||||
{
|
|
||||||
EnsureOpen();
|
|
||||||
|
|
||||||
if (!m_file->IsGood())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
u64 size = m_file->GetSize();
|
|
||||||
if (size == 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::vector<u8> result(size);
|
|
||||||
m_file->ReadBytes(result.data(), result.size());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NANDContentDataFile::GetRange(u32 start, u32 size, u8* buffer)
|
|
||||||
{
|
|
||||||
EnsureOpen();
|
|
||||||
if (!m_file->IsGood())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!m_file->Seek(start, SEEK_SET))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return m_file->ReadBytes(buffer, static_cast<size_t>(size));
|
|
||||||
}
|
|
||||||
void NANDContentDataFile::Close()
|
|
||||||
{
|
|
||||||
if (m_file && m_file->IsOpen())
|
|
||||||
m_file->Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer)
|
|
||||||
{
|
|
||||||
if (start + size > m_buffer.size())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::copy_n(&m_buffer[start], size, buffer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
NANDContentLoader::NANDContentLoader(const std::string& content_name, Common::FromWhichRoot from)
|
|
||||||
: m_root(from)
|
|
||||||
{
|
|
||||||
m_Valid = Initialize(content_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
NANDContentLoader::~NANDContentLoader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NANDContentLoader::IsValid() const
|
|
||||||
{
|
|
||||||
return m_Valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANDContent* NANDContentLoader::GetContentByID(u32 id) const
|
|
||||||
{
|
|
||||||
const auto iterator = std::find_if(m_Content.begin(), m_Content.end(), [id](const auto& content) {
|
|
||||||
return content.m_metadata.id == id;
|
|
||||||
});
|
|
||||||
return iterator != m_Content.end() ? &*iterator : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANDContent* NANDContentLoader::GetContentByIndex(int index) const
|
|
||||||
{
|
|
||||||
for (auto& Content : m_Content)
|
|
||||||
{
|
|
||||||
if (Content.m_metadata.index == index)
|
|
||||||
{
|
|
||||||
return &Content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NANDContentLoader::Initialize(const std::string& name)
|
|
||||||
{
|
|
||||||
if (name.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_Path = name;
|
|
||||||
|
|
||||||
WiiWAD wad(name);
|
|
||||||
std::vector<u8> data_app;
|
|
||||||
|
|
||||||
if (wad.IsValid())
|
|
||||||
{
|
|
||||||
m_IsWAD = true;
|
|
||||||
m_ticket = wad.GetTicket();
|
|
||||||
m_tmd = wad.GetTMD();
|
|
||||||
data_app = wad.GetDataApp();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string tmd_filename(m_Path);
|
|
||||||
|
|
||||||
if (tmd_filename.back() == '/')
|
|
||||||
tmd_filename += "title.tmd";
|
|
||||||
else
|
|
||||||
m_Path = tmd_filename.substr(0, tmd_filename.find("title.tmd"));
|
|
||||||
|
|
||||||
File::IOFile tmd_file(tmd_filename, "rb");
|
|
||||||
if (!tmd_file)
|
|
||||||
{
|
|
||||||
WARN_LOG(DISCIO, "CreateFromDirectory: error opening %s", tmd_filename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> bytes(tmd_file.GetSize());
|
|
||||||
tmd_file.ReadBytes(bytes.data(), bytes.size());
|
|
||||||
m_tmd.SetBytes(std::move(bytes));
|
|
||||||
|
|
||||||
m_ticket = FindSignedTicket(m_tmd.GetTitleId());
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeContentEntries(data_app);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NANDContentLoader::InitializeContentEntries(const std::vector<u8>& data_app)
|
|
||||||
{
|
|
||||||
if (!m_ticket.IsValid())
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_ES, "No valid ticket for title %016" PRIx64, m_tmd.GetTitleId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<IOS::ES::Content> contents = m_tmd.GetContents();
|
|
||||||
m_Content.resize(contents.size());
|
|
||||||
|
|
||||||
u32 data_app_offset = 0;
|
|
||||||
const std::array<u8, 16> title_key = m_ticket.GetTitleKey();
|
|
||||||
IOS::ES::SharedContentMap shared_content{m_root};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < contents.size(); ++i)
|
|
||||||
{
|
|
||||||
const auto& content = contents.at(i);
|
|
||||||
|
|
||||||
if (m_IsWAD)
|
|
||||||
{
|
|
||||||
// The content index is used as IV (2 bytes); the remaining 14 bytes are zeroes.
|
|
||||||
std::array<u8, 16> iv{};
|
|
||||||
iv[0] = static_cast<u8>(content.index >> 8) & 0xFF;
|
|
||||||
iv[1] = static_cast<u8>(content.index) & 0xFF;
|
|
||||||
|
|
||||||
u32 rounded_size = Common::AlignUp(static_cast<u32>(content.size), 0x40);
|
|
||||||
|
|
||||||
m_Content[i].m_Data = std::make_unique<NANDContentDataBuffer>(Common::AES::Decrypt(
|
|
||||||
title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size));
|
|
||||||
data_app_offset += rounded_size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string filename;
|
|
||||||
if (content.IsShared())
|
|
||||||
filename = *shared_content.GetFilenameFromSHA1(content.sha1);
|
|
||||||
else
|
|
||||||
filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.id);
|
|
||||||
|
|
||||||
m_Content[i].m_Data = std::make_unique<NANDContentDataFile>(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Content[i].m_metadata = std::move(content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NANDContentManager::~NANDContentManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANDContentLoader& NANDContentManager::GetNANDLoader(const std::string& content_path,
|
|
||||||
Common::FromWhichRoot from)
|
|
||||||
{
|
|
||||||
auto it = m_map.find(content_path);
|
|
||||||
if (it != m_map.end())
|
|
||||||
return *it->second;
|
|
||||||
return *m_map
|
|
||||||
.emplace_hint(it, std::make_pair(content_path, std::make_unique<NANDContentLoader>(
|
|
||||||
content_path, from)))
|
|
||||||
->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANDContentLoader& NANDContentManager::GetNANDLoader(u64 title_id, Common::FromWhichRoot from)
|
|
||||||
{
|
|
||||||
std::string path = Common::GetTitleContentPath(title_id, from);
|
|
||||||
return GetNANDLoader(path, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NANDContentManager::ClearCache()
|
|
||||||
{
|
|
||||||
m_map.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
IOS::ES::TicketReader 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 IOS::ES::TicketReader{};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> signed_ticket(ticket_file.GetSize());
|
|
||||||
if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size()))
|
|
||||||
{
|
|
||||||
return IOS::ES::TicketReader{};
|
|
||||||
}
|
|
||||||
|
|
||||||
return IOS::ES::TicketReader{std::move(signed_ticket)};
|
|
||||||
}
|
|
||||||
} // namespace end
|
|
|
@ -1,126 +0,0 @@
|
||||||
// Copyright 2009 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/NandPaths.h"
|
|
||||||
#include "Core/IOS/ES/Formats.h"
|
|
||||||
|
|
||||||
namespace File
|
|
||||||
{
|
|
||||||
class IOFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DiscIO
|
|
||||||
{
|
|
||||||
enum class Region;
|
|
||||||
|
|
||||||
// TODO: move some of these to Core/IOS/ES.
|
|
||||||
IOS::ES::TicketReader FindSignedTicket(u64 title_id);
|
|
||||||
|
|
||||||
class NANDContentData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~NANDContentData() = 0;
|
|
||||||
virtual void Open() {}
|
|
||||||
virtual std::vector<u8> Get() = 0;
|
|
||||||
virtual bool GetRange(u32 start, u32 size, u8* buffer) = 0;
|
|
||||||
virtual void Close() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class NANDContentDataFile final : public NANDContentData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit NANDContentDataFile(const std::string& filename);
|
|
||||||
~NANDContentDataFile();
|
|
||||||
|
|
||||||
void Open() override;
|
|
||||||
std::vector<u8> Get() override;
|
|
||||||
bool GetRange(u32 start, u32 size, u8* buffer) override;
|
|
||||||
void Close() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void EnsureOpen();
|
|
||||||
|
|
||||||
const std::string m_filename;
|
|
||||||
std::unique_ptr<File::IOFile> m_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NANDContentDataBuffer final : public NANDContentData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit NANDContentDataBuffer(const std::vector<u8>& buffer) : m_buffer(buffer) {}
|
|
||||||
std::vector<u8> Get() override { return m_buffer; }
|
|
||||||
bool GetRange(u32 start, u32 size, u8* buffer) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::vector<u8> m_buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NANDContent
|
|
||||||
{
|
|
||||||
IOS::ES::Content m_metadata;
|
|
||||||
std::unique_ptr<NANDContentData> m_Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Instances of this class must be created by NANDContentManager
|
|
||||||
class NANDContentLoader final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit NANDContentLoader(const std::string& content_name, Common::FromWhichRoot from);
|
|
||||||
~NANDContentLoader();
|
|
||||||
|
|
||||||
bool IsValid() const;
|
|
||||||
const NANDContent* GetContentByID(u32 id) const;
|
|
||||||
const NANDContent* GetContentByIndex(int index) const;
|
|
||||||
const IOS::ES::TMDReader& GetTMD() const { return m_tmd; }
|
|
||||||
const IOS::ES::TicketReader& GetTicket() const { return m_ticket; }
|
|
||||||
const std::vector<NANDContent>& GetContent() const { return m_Content; }
|
|
||||||
private:
|
|
||||||
bool Initialize(const std::string& name);
|
|
||||||
void InitializeContentEntries(const std::vector<u8>& data_app);
|
|
||||||
|
|
||||||
bool m_Valid = false;
|
|
||||||
bool m_IsWAD = false;
|
|
||||||
Common::FromWhichRoot m_root;
|
|
||||||
std::string m_Path;
|
|
||||||
IOS::ES::TMDReader m_tmd;
|
|
||||||
IOS::ES::TicketReader m_ticket;
|
|
||||||
|
|
||||||
std::vector<NANDContent> m_Content;
|
|
||||||
};
|
|
||||||
|
|
||||||
// we open the NAND Content files too often... let's cache them
|
|
||||||
class NANDContentManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static NANDContentManager& Access()
|
|
||||||
{
|
|
||||||
static NANDContentManager instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANDContentLoader& GetNANDLoader(const std::string& content_path,
|
|
||||||
Common::FromWhichRoot from = Common::FROM_CONFIGURED_ROOT);
|
|
||||||
const NANDContentLoader& GetNANDLoader(u64 title_id,
|
|
||||||
Common::FromWhichRoot from = Common::FROM_CONFIGURED_ROOT);
|
|
||||||
void ClearCache();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NANDContentManager() {}
|
|
||||||
~NANDContentManager();
|
|
||||||
|
|
||||||
NANDContentManager(NANDContentManager const&) = delete;
|
|
||||||
void operator=(NANDContentManager const&) = delete;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<NANDContentLoader>> m_map;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
|
@ -44,9 +43,6 @@ void NANDImporter::ImportNANDBin(const std::string& path_to_bin,
|
||||||
ProcessEntry(0, nand_root);
|
ProcessEntry(0, nand_root);
|
||||||
ExportKeys(nand_root);
|
ExportKeys(nand_root);
|
||||||
ExtractCertificates(nand_root);
|
ExtractCertificates(nand_root);
|
||||||
|
|
||||||
// We have to clear the cache so the new NAND takes effect
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NANDImporter::ReadNANDBin(const std::string& path_to_bin)
|
bool NANDImporter::ReadNANDBin(const std::string& path_to_bin)
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
#include "DiscIO/Filesystem.h"
|
#include "DiscIO/Filesystem.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
// --- this volume type is used for GC disc images ---
|
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
class BlobReader;
|
class BlobReader;
|
||||||
|
|
|
@ -14,10 +14,6 @@
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
// --- this volume type is used for Wad files ---
|
|
||||||
// Some of this code might look redundant with the NANDContentLoader class, however,
|
|
||||||
// We do not do any decryption here, we do raw read, so things are -Faster-
|
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
class BlobReader;
|
class BlobReader;
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
#include "DiscIO/Filesystem.h"
|
#include "DiscIO/Filesystem.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
// --- this volume type is used for Wii disc images ---
|
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
class BlobReader;
|
class BlobReader;
|
||||||
|
|
|
@ -20,6 +20,8 @@ class WiiWAD
|
||||||
public:
|
public:
|
||||||
explicit WiiWAD(const std::string& name);
|
explicit WiiWAD(const std::string& name);
|
||||||
explicit WiiWAD(std::unique_ptr<BlobReader> blob_reader);
|
explicit WiiWAD(std::unique_ptr<BlobReader> blob_reader);
|
||||||
|
WiiWAD(WiiWAD&&) = default;
|
||||||
|
WiiWAD& operator=(WiiWAD&&) = default;
|
||||||
~WiiWAD();
|
~WiiWAD();
|
||||||
|
|
||||||
bool IsValid() const { return m_valid; }
|
bool IsValid() const { return m_valid; }
|
||||||
|
|
|
@ -360,14 +360,26 @@ int main(int argc, char* argv[])
|
||||||
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
||||||
std::vector<std::string> args = parser->args();
|
std::vector<std::string> args = parser->args();
|
||||||
|
|
||||||
std::string boot_filename;
|
std::unique_ptr<BootParameters> boot;
|
||||||
if (options.is_set("exec"))
|
if (options.is_set("exec"))
|
||||||
{
|
{
|
||||||
boot_filename = static_cast<const char*>(options.get("exec"));
|
boot = BootParameters::GenerateFromFile(static_cast<const char*>(options.get("exec")));
|
||||||
|
}
|
||||||
|
else if (options.is_set("nand_title"))
|
||||||
|
{
|
||||||
|
const std::string hex_string = static_cast<const char*>(options.get("nand_title"));
|
||||||
|
if (hex_string.length() != 16)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Invalid title ID\n");
|
||||||
|
parser->print_help();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const u64 title_id = std::stoull(hex_string, nullptr, 16);
|
||||||
|
boot = std::make_unique<BootParameters>(BootParameters::NANDTitle{title_id});
|
||||||
}
|
}
|
||||||
else if (args.size())
|
else if (args.size())
|
||||||
{
|
{
|
||||||
boot_filename = args.front();
|
boot = BootParameters::GenerateFromFile(args.front());
|
||||||
args.erase(args.begin());
|
args.erase(args.begin());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -408,9 +420,9 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
||||||
|
|
||||||
if (!BootManager::BootCore(BootParameters::GenerateFromFile(boot_filename)))
|
if (!BootManager::BootCore(std::move(boot)))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Could not boot %s\n", boot_filename.c_str());
|
fprintf(stderr, "Could not boot the specified file\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "Core/WiiUtils.h"
|
#include "Core/WiiUtils.h"
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DolphinQt2/GameList/GameFile.h"
|
#include "DolphinQt2/GameList/GameFile.h"
|
||||||
#include "DolphinQt2/Resources.h"
|
#include "DolphinQt2/Resources.h"
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Core/Analytics.h"
|
#include "Core/Analytics.h"
|
||||||
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/BootManager.h"
|
#include "Core/BootManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "DolphinQt2/Host.h"
|
#include "DolphinQt2/Host.h"
|
||||||
|
@ -84,6 +85,21 @@ int main(int argc, char* argv[])
|
||||||
QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock,
|
QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock,
|
||||||
&app, &Core::HostDispatchJobs);
|
&app, &Core::HostDispatchJobs);
|
||||||
|
|
||||||
|
std::unique_ptr<BootParameters> boot;
|
||||||
|
if (options.is_set("nand_title"))
|
||||||
|
{
|
||||||
|
const std::string hex_string = static_cast<const char*>(options.get("nand_title"));
|
||||||
|
if (hex_string.length() == 16)
|
||||||
|
{
|
||||||
|
const u64 title_id = std::stoull(hex_string, nullptr, 16);
|
||||||
|
boot = std::make_unique<BootParameters>(BootParameters::NANDTitle{title_id});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("Invalid title ID."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (SConfig::GetInstance().m_show_development_warning)
|
if (SConfig::GetInstance().m_show_development_warning)
|
||||||
|
@ -95,7 +111,7 @@ int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
DolphinAnalytics::Instance()->ReportDolphinStart("qt");
|
DolphinAnalytics::Instance()->ReportDolphinStart("qt");
|
||||||
|
|
||||||
MainWindow win;
|
MainWindow win{std::move(boot)};
|
||||||
win.show();
|
win.show();
|
||||||
|
|
||||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
#include "UICommon/X11Utils.h"
|
#include "UICommon/X11Utils.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MainWindow::MainWindow() : QMainWindow(nullptr)
|
MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters) : QMainWindow(nullptr)
|
||||||
{
|
{
|
||||||
setWindowTitle(QString::fromStdString(Common::scm_rev_str));
|
setWindowTitle(QString::fromStdString(Common::scm_rev_str));
|
||||||
setWindowIcon(QIcon(Resources::GetMisc(Resources::LOGO_SMALL)));
|
setWindowIcon(QIcon(Resources::GetMisc(Resources::LOGO_SMALL)));
|
||||||
|
@ -84,6 +84,9 @@ MainWindow::MainWindow() : QMainWindow(nullptr)
|
||||||
InitCoreCallbacks();
|
InitCoreCallbacks();
|
||||||
|
|
||||||
NetPlayInit();
|
NetPlayInit();
|
||||||
|
|
||||||
|
if (boot_parameters)
|
||||||
|
StartGame(std::move(boot_parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
@ -652,8 +655,7 @@ void MainWindow::PerformOnlineUpdate(const std::string& region)
|
||||||
|
|
||||||
void MainWindow::BootWiiSystemMenu()
|
void MainWindow::BootWiiSystemMenu()
|
||||||
{
|
{
|
||||||
StartGame(QString::fromStdString(
|
StartGame(std::make_unique<BootParameters>(BootParameters::NANDTitle{Titles::SYSTEM_MENU}));
|
||||||
Common::GetTitleContentPath(Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::NetPlayInit()
|
void MainWindow::NetPlayInit()
|
||||||
|
|
|
@ -35,7 +35,7 @@ class MainWindow final : public QMainWindow
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow();
|
explicit MainWindow(std::unique_ptr<BootParameters> boot_parameters);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
bool eventFilter(QObject* object, QEvent* event) override;
|
bool eventFilter(QObject* object, QEvent* event) override;
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DolphinWX/Config/ConfigMain.h"
|
#include "DolphinWX/Config/ConfigMain.h"
|
||||||
#include "DolphinWX/Frame.h"
|
#include "DolphinWX/Frame.h"
|
||||||
#include "DolphinWX/WxEventUtils.h"
|
#include "DolphinWX/WxEventUtils.h"
|
||||||
|
@ -202,8 +201,6 @@ void PathConfigPane::OnNANDRootChanged(wxCommandEvent& event)
|
||||||
File::SetUserPath(D_WIIROOT_IDX, nand_path);
|
File::SetUserPath(D_WIIROOT_IDX, nand_path);
|
||||||
m_nand_root_dirpicker->SetPath(StrToWxStr(nand_path));
|
m_nand_root_dirpicker->SetPath(StrToWxStr(nand_path));
|
||||||
|
|
||||||
DiscIO::NANDContentManager::Access().ClearCache();
|
|
||||||
|
|
||||||
wxCommandEvent update_event{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, GetId()};
|
wxCommandEvent update_event{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, GetId()};
|
||||||
update_event.SetEventObject(this);
|
update_event.SetEventObject(this);
|
||||||
AddPendingEvent(update_event);
|
AddPendingEvent(update_event);
|
||||||
|
|
|
@ -501,7 +501,6 @@ void CFrame::BindEvents()
|
||||||
Bind(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, &CFrame::OnReloadThemeBitmaps, this);
|
Bind(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, &CFrame::OnReloadThemeBitmaps, this);
|
||||||
Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &CFrame::OnRefreshGameList, this);
|
Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &CFrame::OnRefreshGameList, this);
|
||||||
Bind(DOLPHIN_EVT_RESCAN_GAMELIST, &CFrame::OnRescanGameList, this);
|
Bind(DOLPHIN_EVT_RESCAN_GAMELIST, &CFrame::OnRescanGameList, this);
|
||||||
Bind(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, &CFrame::OnUpdateLoadWiiMenuItem, this);
|
|
||||||
Bind(DOLPHIN_EVT_BOOT_SOFTWARE, &CFrame::OnPlay, this);
|
Bind(DOLPHIN_EVT_BOOT_SOFTWARE, &CFrame::OnPlay, this);
|
||||||
Bind(DOLPHIN_EVT_STOP_SOFTWARE, &CFrame::OnStop, this);
|
Bind(DOLPHIN_EVT_STOP_SOFTWARE, &CFrame::OnStop, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,7 @@ public:
|
||||||
void StatusBarMessage(const char* format, ...);
|
void StatusBarMessage(const char* format, ...);
|
||||||
void ClearStatusBar();
|
void ClearStatusBar();
|
||||||
void BootGame(const std::string& filename);
|
void BootGame(const std::string& filename);
|
||||||
|
void StartGame(std::unique_ptr<BootParameters> boot);
|
||||||
bool RendererHasFocus();
|
bool RendererHasFocus();
|
||||||
bool RendererIsFullscreen();
|
bool RendererIsFullscreen();
|
||||||
void OpenGeneralConfiguration(wxWindowID tab_id = wxID_ANY);
|
void OpenGeneralConfiguration(wxWindowID tab_id = wxID_ANY);
|
||||||
|
@ -193,7 +194,6 @@ private:
|
||||||
void InitializeTASDialogs();
|
void InitializeTASDialogs();
|
||||||
void InitializeCoreCallbacks();
|
void InitializeCoreCallbacks();
|
||||||
|
|
||||||
void StartGame(std::unique_ptr<BootParameters> boot);
|
|
||||||
void SetDebuggerStartupParameters() const;
|
void SetDebuggerStartupParameters() const;
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
|
@ -275,9 +275,6 @@ private:
|
||||||
|
|
||||||
void OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event);
|
void OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event);
|
||||||
|
|
||||||
void OnUpdateLoadWiiMenuItem(wxCommandEvent&);
|
|
||||||
void UpdateLoadWiiMenuItem() const;
|
|
||||||
|
|
||||||
void OnOpen(wxCommandEvent& event); // File menu
|
void OnOpen(wxCommandEvent& event); // File menu
|
||||||
void OnRefresh(wxCommandEvent& event);
|
void OnRefresh(wxCommandEvent& event);
|
||||||
void OnBootDrive(wxCommandEvent& event);
|
void OnBootDrive(wxCommandEvent& event);
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
#include "Core/WiiUtils.h"
|
#include "Core/WiiUtils.h"
|
||||||
|
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
|
||||||
#include "DiscIO/NANDImporter.h"
|
#include "DiscIO/NANDImporter.h"
|
||||||
#include "DiscIO/VolumeWad.h"
|
#include "DiscIO/VolumeWad.h"
|
||||||
|
|
||||||
|
@ -652,6 +651,7 @@ void CFrame::StartGame(std::unique_ptr<BootParameters> boot)
|
||||||
if (m_is_game_loading)
|
if (m_is_game_loading)
|
||||||
return;
|
return;
|
||||||
m_is_game_loading = true;
|
m_is_game_loading = true;
|
||||||
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
|
|
||||||
GetToolBar()->EnableTool(IDM_PLAY, false);
|
GetToolBar()->EnableTool(IDM_PLAY, false);
|
||||||
GetMenuBar()->FindItem(IDM_PLAY)->Enable(false);
|
GetMenuBar()->FindItem(IDM_PLAY)->Enable(false);
|
||||||
|
@ -925,6 +925,7 @@ void CFrame::OnStopped()
|
||||||
m_confirm_stop = false;
|
m_confirm_stop = false;
|
||||||
m_is_game_loading = false;
|
m_is_game_loading = false;
|
||||||
m_tried_graceful_shutdown = false;
|
m_tried_graceful_shutdown = false;
|
||||||
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
|
|
||||||
UninhibitScreensaver();
|
UninhibitScreensaver();
|
||||||
|
|
||||||
|
@ -1131,11 +1132,6 @@ void CFrame::OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event)
|
||||||
event.Check(SConfig::GetInstance().iCPUCore == PowerPC::CORE_INTERPRETER);
|
event.Check(SConfig::GetInstance().iCPUCore == PowerPC::CORE_INTERPRETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnUpdateLoadWiiMenuItem(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
UpdateLoadWiiMenuItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CFrame::ClearStatusBar()
|
void CFrame::ClearStatusBar()
|
||||||
{
|
{
|
||||||
if (this->GetStatusBar()->IsEnabled())
|
if (this->GetStatusBar()->IsEnabled())
|
||||||
|
@ -1225,7 +1221,7 @@ void CFrame::OnShowCheatsWindow(wxCommandEvent& WXUNUSED(event))
|
||||||
|
|
||||||
void CFrame::OnLoadWiiMenu(wxCommandEvent& WXUNUSED(event))
|
void CFrame::OnLoadWiiMenu(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
BootGame(Common::GetTitleContentPath(Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT));
|
StartGame(std::make_unique<BootParameters>(BootParameters::NANDTitle{Titles::SYSTEM_MENU}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnInstallWAD(wxCommandEvent& event)
|
void CFrame::OnInstallWAD(wxCommandEvent& event)
|
||||||
|
@ -1260,7 +1256,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
|
||||||
wxPD_REMAINING_TIME | wxPD_SMOOTH);
|
wxPD_REMAINING_TIME | wxPD_SMOOTH);
|
||||||
|
|
||||||
if (WiiUtils::InstallWAD(fileName))
|
if (WiiUtils::InstallWAD(fileName))
|
||||||
UpdateLoadWiiMenuItem();
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnUninstallWAD(wxCommandEvent&)
|
void CFrame::OnUninstallWAD(wxCommandEvent&)
|
||||||
|
@ -1284,7 +1280,7 @@ void CFrame::OnUninstallWAD(wxCommandEvent&)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title_id == Titles::SYSTEM_MENU)
|
if (title_id == Titles::SYSTEM_MENU)
|
||||||
UpdateLoadWiiMenuItem();
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
|
void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
|
||||||
|
@ -1306,7 +1302,7 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
|
||||||
wxProgressDialog dialog(_("Importing NAND backup"), _("Working..."), 100, this,
|
wxProgressDialog dialog(_("Importing NAND backup"), _("Working..."), 100, this,
|
||||||
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH);
|
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH);
|
||||||
DiscIO::NANDImporter().ImportNANDBin(file_name, [&dialog] { dialog.Pulse(); });
|
DiscIO::NANDImporter().ImportNANDBin(file_name, [&dialog] { dialog.Pulse(); });
|
||||||
UpdateLoadWiiMenuItem();
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnCheckNAND(wxCommandEvent&)
|
void CFrame::OnCheckNAND(wxCommandEvent&)
|
||||||
|
@ -1462,7 +1458,7 @@ void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event)
|
||||||
|
|
||||||
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoOnlineUpdate, region);
|
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoOnlineUpdate, region);
|
||||||
ShowUpdateResult(result);
|
ShowUpdateResult(result);
|
||||||
UpdateLoadWiiMenuItem();
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
|
void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
|
||||||
|
@ -1475,12 +1471,7 @@ void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&)
|
||||||
|
|
||||||
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name);
|
const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name);
|
||||||
ShowUpdateResult(result);
|
ShowUpdateResult(result);
|
||||||
UpdateLoadWiiMenuItem();
|
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
|
||||||
}
|
|
||||||
|
|
||||||
void CFrame::UpdateLoadWiiMenuItem() const
|
|
||||||
{
|
|
||||||
GetMenuBar()->Refresh(true, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event))
|
void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event))
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
|
|
||||||
#include "Core/Analytics.h"
|
#include "Core/Analytics.h"
|
||||||
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
|
@ -169,13 +170,24 @@ void DolphinApp::ParseCommandLine()
|
||||||
|
|
||||||
if (options.is_set("exec"))
|
if (options.is_set("exec"))
|
||||||
{
|
{
|
||||||
m_load_file = true;
|
m_boot = BootParameters::GenerateFromFile(static_cast<const char*>(options.get("exec")));
|
||||||
m_file_to_load = static_cast<const char*>(options.get("exec"));
|
}
|
||||||
|
else if (options.is_set("nand_title"))
|
||||||
|
{
|
||||||
|
const std::string hex_string = static_cast<const char*>(options.get("nand_title"));
|
||||||
|
if (hex_string.length() == 16)
|
||||||
|
{
|
||||||
|
const u64 title_id = std::stoull(hex_string, nullptr, 16);
|
||||||
|
m_boot = std::make_unique<BootParameters>(BootParameters::NANDTitle{title_id});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WxUtils::ShowErrorDialog(_("The title ID is invalid."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (args.size())
|
else if (args.size())
|
||||||
{
|
{
|
||||||
m_load_file = true;
|
m_boot = BootParameters::GenerateFromFile(args.front());
|
||||||
m_file_to_load = args.front();
|
|
||||||
args.erase(args.begin());
|
args.erase(args.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,9 +213,7 @@ void DolphinApp::ParseCommandLine()
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
void DolphinApp::MacOpenFile(const wxString& fileName)
|
void DolphinApp::MacOpenFile(const wxString& fileName)
|
||||||
{
|
{
|
||||||
m_file_to_load = fileName;
|
main_frame->StartGame(BootParameters::GenerateFromFile(fileName.ToStdString()));
|
||||||
m_load_file = true;
|
|
||||||
main_frame->BootGame(WxStrToStr(m_file_to_load));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -241,20 +251,16 @@ void DolphinApp::AfterInit()
|
||||||
{
|
{
|
||||||
if (Movie::PlayInput(WxStrToStr(m_movie_file)))
|
if (Movie::PlayInput(WxStrToStr(m_movie_file)))
|
||||||
{
|
{
|
||||||
if (m_load_file && !m_file_to_load.empty())
|
if (m_boot)
|
||||||
{
|
main_frame->StartGame(std::move(m_boot));
|
||||||
main_frame->BootGame(WxStrToStr(m_file_to_load));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
main_frame->BootGame("");
|
main_frame->BootGame("");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// First check if we have an exec command line.
|
// First check if we have an exec command line.
|
||||||
else if (m_load_file && !m_file_to_load.empty())
|
else if (m_boot)
|
||||||
{
|
{
|
||||||
main_frame->BootGame(WxStrToStr(m_file_to_load));
|
main_frame->StartGame(std::move(m_boot));
|
||||||
}
|
}
|
||||||
// If we have selected Automatic Start, start the default ISO,
|
// If we have selected Automatic Start, start the default ISO,
|
||||||
// or if no default ISO exists, start the last loaded ISO
|
// or if no default ISO exists, start the last loaded ISO
|
||||||
|
|
|
@ -13,6 +13,8 @@ class wxLocale;
|
||||||
|
|
||||||
extern CFrame* main_frame;
|
extern CFrame* main_frame;
|
||||||
|
|
||||||
|
struct BootParameters;
|
||||||
|
|
||||||
// Define a new application
|
// Define a new application
|
||||||
class DolphinApp : public wxApp
|
class DolphinApp : public wxApp
|
||||||
{
|
{
|
||||||
|
@ -43,7 +45,6 @@ private:
|
||||||
bool m_batch_mode = false;
|
bool m_batch_mode = false;
|
||||||
bool m_confirm_stop = false;
|
bool m_confirm_stop = false;
|
||||||
bool m_is_active = true;
|
bool m_is_active = true;
|
||||||
bool m_load_file = false;
|
|
||||||
bool m_play_movie = false;
|
bool m_play_movie = false;
|
||||||
bool m_use_debugger = false;
|
bool m_use_debugger = false;
|
||||||
bool m_use_logger = false;
|
bool m_use_logger = false;
|
||||||
|
@ -53,7 +54,7 @@ private:
|
||||||
wxString m_video_backend_name;
|
wxString m_video_backend_name;
|
||||||
wxString m_audio_emulation_name;
|
wxString m_audio_emulation_name;
|
||||||
wxString m_user_path;
|
wxString m_user_path;
|
||||||
wxString m_file_to_load;
|
std::unique_ptr<BootParameters> m_boot;
|
||||||
wxString m_movie_file;
|
wxString m_movie_file;
|
||||||
std::unique_ptr<wxLocale> m_locale;
|
std::unique_ptr<wxLocale> m_locale;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/IOS/ES/ES.h"
|
||||||
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DolphinWX/Frame.h"
|
||||||
#include "DolphinWX/Globals.h"
|
#include "DolphinWX/Globals.h"
|
||||||
#include "DolphinWX/WxUtils.h"
|
#include "DolphinWX/WxUtils.h"
|
||||||
|
|
||||||
|
@ -30,6 +32,7 @@ MainMenuBar::MainMenuBar(MenuType type, long style) : wxMenuBar{style}, m_type{t
|
||||||
{
|
{
|
||||||
BindEvents();
|
BindEvents();
|
||||||
AddMenus();
|
AddMenus();
|
||||||
|
RefreshWiiSystemMenuLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuBar::Refresh(bool erase_background, const wxRect* rect)
|
void MainMenuBar::Refresh(bool erase_background, const wxRect* rect)
|
||||||
|
@ -62,6 +65,7 @@ void MainMenuBar::AddMenus()
|
||||||
void MainMenuBar::BindEvents()
|
void MainMenuBar::BindEvents()
|
||||||
{
|
{
|
||||||
Bind(EVT_POPULATE_PERSPECTIVES_MENU, &MainMenuBar::OnPopulatePerspectivesMenu, this);
|
Bind(EVT_POPULATE_PERSPECTIVES_MENU, &MainMenuBar::OnPopulatePerspectivesMenu, this);
|
||||||
|
Bind(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, &MainMenuBar::OnUpdateWiiMenuTool, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMenu* MainMenuBar::CreateFileMenu() const
|
wxMenu* MainMenuBar::CreateFileMenu() const
|
||||||
|
@ -582,16 +586,11 @@ void MainMenuBar::RefreshWiiToolsLabels() const
|
||||||
// result in the emulated software being confused, or even worse, exported saves having
|
// result in the emulated software being confused, or even worse, exported saves having
|
||||||
// inconsistent data.
|
// inconsistent data.
|
||||||
const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii;
|
const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii;
|
||||||
for (const int index :
|
for (const int index : {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE,
|
||||||
{IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, IDM_IMPORT_NAND, IDM_CHECK_NAND,
|
IDM_IMPORT_NAND, IDM_CHECK_NAND, IDM_EXTRACT_CERTIFICATES})
|
||||||
IDM_EXTRACT_CERTIFICATES, IDM_LOAD_WII_MENU, IDM_PERFORM_ONLINE_UPDATE_CURRENT,
|
|
||||||
IDM_PERFORM_ONLINE_UPDATE_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR,
|
|
||||||
IDM_PERFORM_ONLINE_UPDATE_USA})
|
|
||||||
{
|
{
|
||||||
FindItem(index)->Enable(enable_wii_tools);
|
FindItem(index)->Enable(enable_wii_tools);
|
||||||
}
|
}
|
||||||
if (enable_wii_tools)
|
|
||||||
RefreshWiiSystemMenuLabel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuBar::EnableUpdateMenu(UpdateMenuMode mode) const
|
void MainMenuBar::EnableUpdateMenu(UpdateMenuMode mode) const
|
||||||
|
@ -608,12 +607,23 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const
|
||||||
{
|
{
|
||||||
auto* const item = FindItem(IDM_LOAD_WII_MENU);
|
auto* const item = FindItem(IDM_LOAD_WII_MENU);
|
||||||
|
|
||||||
const auto& sys_menu_loader = DiscIO::NANDContentManager::Access().GetNANDLoader(
|
if (Core::IsRunning())
|
||||||
Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT);
|
|
||||||
|
|
||||||
if (sys_menu_loader.IsValid())
|
|
||||||
{
|
{
|
||||||
const u16 version_number = sys_menu_loader.GetTMD().GetTitleVersion();
|
item->Enable(false);
|
||||||
|
for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR,
|
||||||
|
IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR,
|
||||||
|
IDM_PERFORM_ONLINE_UPDATE_USA})
|
||||||
|
{
|
||||||
|
FindItem(idm)->Enable(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOS::HLE::Kernel ios;
|
||||||
|
const IOS::ES::TMDReader sys_menu_tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU);
|
||||||
|
if (sys_menu_tmd.IsValid())
|
||||||
|
{
|
||||||
|
const u16 version_number = sys_menu_tmd.GetTitleVersion();
|
||||||
const wxString version_string = StrToWxStr(DiscIO::GetSysMenuVersionString(version_number));
|
const wxString version_string = StrToWxStr(DiscIO::GetSysMenuVersionString(version_number));
|
||||||
item->Enable();
|
item->Enable();
|
||||||
item->SetItemLabel(wxString::Format(_("Load Wii System Menu %s"), version_string));
|
item->SetItemLabel(wxString::Format(_("Load Wii System Menu %s"), version_string));
|
||||||
|
@ -627,6 +637,11 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainMenuBar::OnUpdateWiiMenuTool(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
RefreshWiiSystemMenuLabel();
|
||||||
|
}
|
||||||
|
|
||||||
void MainMenuBar::ClearSavedPerspectivesMenu() const
|
void MainMenuBar::ClearSavedPerspectivesMenu() const
|
||||||
{
|
{
|
||||||
while (m_saved_perspectives_menu->GetMenuItemCount() != 0)
|
while (m_saved_perspectives_menu->GetMenuItemCount() != 0)
|
||||||
|
|
|
@ -42,6 +42,7 @@ private:
|
||||||
wxMenu* CreateHelpMenu() const;
|
wxMenu* CreateHelpMenu() const;
|
||||||
|
|
||||||
void OnPopulatePerspectivesMenu(PopulatePerspectivesEvent&);
|
void OnPopulatePerspectivesMenu(PopulatePerspectivesEvent&);
|
||||||
|
void OnUpdateWiiMenuTool(wxCommandEvent&);
|
||||||
|
|
||||||
void RefreshMenuLabels() const;
|
void RefreshMenuLabels() const;
|
||||||
void RefreshPlayMenuLabel() const;
|
void RefreshPlayMenuLabel() const;
|
||||||
|
|
|
@ -73,6 +73,11 @@ std::unique_ptr<optparse::OptionParser> CreateParser(ParserOptions options)
|
||||||
.metavar("<file>")
|
.metavar("<file>")
|
||||||
.type("string")
|
.type("string")
|
||||||
.help("Load the specified file");
|
.help("Load the specified file");
|
||||||
|
parser->add_option("-n", "--nand_title")
|
||||||
|
.action("store")
|
||||||
|
.metavar("<16-character ASCII title ID>")
|
||||||
|
.type("string")
|
||||||
|
.help("Launch a NAND title");
|
||||||
parser->add_option("-C", "--config")
|
parser->add_option("-C", "--config")
|
||||||
.action("append")
|
.action("append")
|
||||||
.metavar("<System>.<Section>.<Key>=<Value>")
|
.metavar("<System>.<Section>.<Key>=<Value>")
|
||||||
|
|
Loading…
Reference in New Issue