diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index a93ba7b577..a3083308ef 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -46,7 +46,6 @@ #include "Core/PowerPC/PowerPC.h" #include "DiscIO/Enums.h" -#include "DiscIO/NANDContentLoader.h" #include "DiscIO/Volume.h" BootParameters::BootParameters(Parameters&& parameters_) : parameters(std::move(parameters_)) @@ -100,8 +99,8 @@ std::unique_ptr BootParameters::GenerateFromFile(const std::stri if (extension == ".dff") return std::make_unique(DFF{path}); - if (DiscIO::NANDContentManager::Access().GetNANDLoader(path).IsValid()) - return std::make_unique(NAND{path}); + if (extension == ".wad") + return std::make_unique(DiscIO::WiiWAD{path}); PanicAlertT("Could not recognize file %s", path.c_str()); return {}; @@ -339,7 +338,8 @@ bool CBoot::BootUp(std::unique_ptr boot) HID4.SBE = 1; // 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. - SetupWiiMemory(0x000000010000003a); + SetupWiiMemory(); + IOS::HLE::GetIOS()->BootIOS(Titles::IOS(58)); } else { @@ -356,11 +356,16 @@ bool CBoot::BootUp(std::unique_ptr boot) 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(); - 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 diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 96dd2002e4..9295244bcb 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -13,8 +13,10 @@ #include #include "Common/CommonTypes.h" +#include "DiscIO/Blob.h" #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" +#include "DiscIO/WiiWad.h" namespace File { @@ -45,9 +47,9 @@ struct BootParameters std::unique_ptr reader; }; - struct NAND + struct NANDTitle { - std::string content_path; + u64 id; }; struct IPL @@ -67,7 +69,7 @@ struct BootParameters static std::unique_ptr GenerateFromFile(const std::string& path); - using Parameters = std::variant; + using Parameters = std::variant; BootParameters(Parameters&& parameters_); Parameters parameters; @@ -98,7 +100,8 @@ private: 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 SetupBAT(bool is_wii); @@ -109,7 +112,7 @@ private: static bool Load_BS2(const std::string& boot_rom_filename); static void SetupGCMemory(); - static bool SetupWiiMemory(u64 ios_title_id); + static bool SetupWiiMemory(); }; class BootExecutableReader diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 794e979aa1..7bc1799878 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -213,7 +213,7 @@ bool CBoot::EmulatedBS2_GC(const DiscIO::Volume& volume) return RunApploader(/*is_wii*/ false, volume); } -bool CBoot::SetupWiiMemory(u64 ios_title_id) +bool CBoot::SetupWiiMemory() { static const std::map region_settings = { {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. Memory::Write_U16(0x0113, 0x0000315e); - if (!IOS::HLE::GetIOS()->BootIOS(ios_title_id)) - return false; - Memory::Write_U8(0x80, 0x0000315c); // OSInit Memory::Write_U16(0x0000, 0x000030e0); // PADInit 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(static_cast(data_partition.offset >> 2), 0x3198); - if (!SetupWiiMemory(tmd.GetIOSId())) + if (!SetupWiiMemory() || !IOS::HLE::GetIOS()->BootIOS(tmd.GetIOSId())) return false; 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 // 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; } diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 44d2b64262..fd62f17cf8 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -18,49 +18,30 @@ #include "Core/IOS/ES/Formats.h" #include "Core/IOS/FS/FileIO.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) { state->type = 0x03; // TYPE_RETURN }); - const DiscIO::NANDContentLoader& ContentLoader = - DiscIO::NANDContentManager::Access().GetNANDLoader(_pFilename); - if (!ContentLoader.IsValid()) - return false; + if (title_id == Titles::SYSTEM_MENU) + IOS::HLE::CreateVirtualFATFilesystem(); - 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; } - - // 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; + return BootNANDTitle(wad.GetTMD().GetTitleId()); } diff --git a/Source/Core/Core/CommonTitles.h b/Source/Core/Core/CommonTitles.h index 5b86db8f17..3ed6176e2f 100644 --- a/Source/Core/Core/CommonTitles.h +++ b/Source/Core/Core/CommonTitles.h @@ -12,11 +12,16 @@ constexpr u64 BOOT2 = 0x0000000100000001; 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 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 diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 2341fdfba3..826e17da42 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -43,8 +43,8 @@ #include "VideoCommon/HiresTextures.h" #include "DiscIO/Enums.h" -#include "DiscIO/NANDContentLoader.h" #include "DiscIO/Volume.h" +#include "DiscIO/WiiWad.h" SConfig* SConfig::m_Instance; @@ -888,14 +888,35 @@ struct SetGameMetadata 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 (!loader.IsValid()) + if (!wad.IsValid() || !wad.GetTMD().IsValid()) + { + PanicAlertT("This WAD is not valid."); 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->bWii = true; *region = tmd.GetRegion(); diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index 39311960fd..af8b5343bd 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -113,7 +113,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition); const std::vector& raw_tmd = tmd.GetBytes(); 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; break; diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index e398d0bb5c..1250651024 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -27,7 +27,6 @@ #include "Core/IOS/ES/Formats.h" #include "Core/IOS/IOSC.h" #include "Core/ec_wii.h" -#include "DiscIO/NANDContentLoader.h" namespace IOS { @@ -35,10 +34,6 @@ namespace HLE { 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). static u64 s_title_to_launch; @@ -84,9 +79,6 @@ ES::ES(Kernel& ios, const std::string& device_name) : Device(ios, device_name) FinishAllStaleImports(); - s_content_file = ""; - s_title_context = TitleContext{}; - if (s_title_to_launch != 0) { 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() { ticket.SetBytes({}); @@ -114,13 +101,6 @@ void TitleContext::DoState(PointerWrap& p) 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_) { 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) { if (!request.HasNumberOfValidVectors(1, 1)) @@ -167,9 +137,9 @@ IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request) ReturnCode ES::GetTitleId(u64* title_id) const { - if (!s_title_context.active) + if (!m_title_context.active) return ES_EINVAL; - *title_id = s_title_context.tmd.GetTitleId(); + *title_id = m_title_context.tmd.GetTitleId(); return IPC_SUCCESS; } @@ -242,16 +212,11 @@ IPCCommandResult ES::SetUID(u32 uid, const IOCtlVRequest& request) 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)"); 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; if (title_id == Titles::SHOP && (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) { + // 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); } bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload) { - const DiscIO::NANDContentLoader& content_loader = AccessContentDevice(title_id); - if (!content_loader.IsValid()) + const IOS::ES::TMDReader tmd = FindInstalledTMD(title_id); + const IOS::ES::TicketReader ticket = FindSignedTicket(title_id); + + if (!tmd.IsValid() || !ticket.IsValid()) { if (title_id == Titles::SYSTEM_MENU) { @@ -297,33 +297,33 @@ bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload) 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, // 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. if (!skip_reload) { s_title_to_launch = title_id; - const u64 required_ios = content_loader.GetTMD().GetIOSId(); + const u64 required_ios = tmd.GetIOSId(); return LaunchTitle(required_ios); } - s_title_context.Update(content_loader); - INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: %016" PRIx64, - s_title_context.tmd.GetTitleId()); + m_title_context.Update(tmd, ticket); + INFO_LOG(IOS_ES, "LaunchPPCTitle: Title context changed: %016" PRIx64, tmd.GetTitleId()); // 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. - 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)"); 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) @@ -339,9 +339,21 @@ void ES::Context::DoState(PointerWrap& p) void ES::DoState(PointerWrap& p) { Device::DoState(p); - p.Do(s_content_file); - p.Do(m_content_table); - s_title_context.DoState(p); + + for (auto& entry : m_content_table) + { + 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) context.DoState(p); @@ -383,8 +395,6 @@ ReturnCode ES::Close(u32 fd) INFO_LOG(IOS_ES, "ES: Close"); m_is_active = false; - // clear the NAND content cache to make sure nothing remains open. - DiscIO::NANDContentManager::Access().ClearCache(); return IPC_SUCCESS; } @@ -603,21 +613,6 @@ IPCCommandResult ES::LaunchBC(const IOCtlVRequest& request) 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 // used from the PowerPC (for unpatched and up-to-date IOSes anyway). // 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) { - s_title_context.Clear(); + m_title_context.Clear(); INFO_LOG(IOS_ES, "ES_DIVerify: Title context changed: (none)"); 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()) 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()); 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())) 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; } @@ -724,7 +716,7 @@ ReturnCode ES::SetUpStreamKey(const u32 uid, const u8* ticket_view, const IOS::E // Find a signed ticket from the view. 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 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. if (!installed_ticket.IsValid()) return FS_ENOENT; @@ -803,10 +795,10 @@ IPCCommandResult ES::DeleteStreamKey(const IOCtlVRequest& request) bool ES::IsActiveTitlePermittedByTicket(const u8* ticket_view) const { - if (!GetTitleContext().active) + if (!m_title_context.active) return false; - const u32 title_identifier = static_cast(GetTitleContext().tmd.GetTitleId()); + const u32 title_identifier = static_cast(m_title_context.tmd.GetTitleId()); const u32 permitted_title_mask = Common::swap32(ticket_view + offsetof(IOS::ES::TicketView, permitted_title_mask)); const u32 permitted_title_id = @@ -831,6 +823,9 @@ bool ES::IsIssuerCorrect(VerifyContainerType type, const IOS::ES::CertReader& is ReturnCode ES::ReadCertStore(std::vector* 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"; File::IOFile store_file{store_path, "rb"}; if (!store_file) diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index d7122ddb1f..3feb3dd954 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -10,6 +10,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/File.h" #include "Core/IOS/Device.h" #include "Core/IOS/ES/Formats.h" #include "Core/IOS/IOS.h" @@ -17,11 +18,6 @@ class PointerWrap; -namespace DiscIO -{ -class NANDContentLoader; -} - namespace IOS { namespace HLE @@ -32,7 +28,6 @@ struct TitleContext { void Clear(); void DoState(PointerWrap& p); - void Update(const DiscIO::NANDContentLoader& content_loader); void Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_); IOS::ES::TicketReader ticket; @@ -46,8 +41,7 @@ class ES final : public Device public: ES(Kernel& ios, const std::string& device_name); - static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket); - static void LoadWAD(const std::string& _rContentFile); + s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket); bool LaunchTitle(u64 title_id, bool skip_reload = false); void DoState(PointerWrap& p) override; @@ -87,6 +81,7 @@ public: IOS::ES::TMDReader FindImportTMD(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. std::vector GetInstalledTitles() const; @@ -306,7 +301,6 @@ private: bool LaunchIOS(u64 ios_title_id); bool LaunchPPCTitle(u64 title_id, bool skip_reload); - static TitleContext& GetTitleContext(); bool IsActiveTitlePermittedByTicket(const u8* ticket_view) const; ReturnCode CheckStreamKeyPermissions(u32 uid, const u8* ticket_view, @@ -341,12 +335,15 @@ private: void FinishStaleImport(u64 title_id); 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. struct OpenedContent { bool m_opened = false; + File::IOFile m_file; u64 m_title_id = 0; IOS::ES::Content m_content; u32 m_position = 0; @@ -357,6 +354,7 @@ private: ContentTable m_content_table; ContextArray m_contexts; + TitleContext m_title_context{}; }; } // namespace Device } // namespace HLE diff --git a/Source/Core/Core/IOS/ES/Identity.cpp b/Source/Core/Core/IOS/ES/Identity.cpp index 9e24a78959..dd6c342a48 100644 --- a/Source/Core/Core/IOS/ES/Identity.cpp +++ b/Source/Core/Core/IOS/ES/Identity.cpp @@ -110,11 +110,11 @@ IPCCommandResult ES::Sign(const IOCtlVRequest& request) u32 data_size = request.in_vectors[0].size; u8* sig_out = Memory::GetPointer(request.io_vectors[0].address); - if (!GetTitleContext().active) + if (!m_title_context.active) return GetDefaultReply(ES_EINVAL); 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()); return GetDefaultReply(IPC_SUCCESS); diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index b82d5daae4..06c6efdf29 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -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)); } +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 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) { if (string.length() != 8) @@ -154,21 +168,15 @@ std::vector ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea if (!tmd.IsValid()) return {}; - const IOS::ES::SharedContentMap shared{Common::FROM_SESSION_ROOT}; + const IOS::ES::SharedContentMap map{Common::FROM_SESSION_ROOT}; const std::vector contents = tmd.GetContents(); std::vector stored_contents; std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents), - [&tmd, &shared](const auto& content) { - if (content.IsShared()) - { - 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)); + [this, &tmd, &map](const IOS::ES::Content& content) { + const std::string path = GetContentPath(tmd.GetTitleId(), content, map); + return !path.empty() && File::Exists(path); }); return stored_contents; @@ -289,6 +297,16 @@ void ES::FinishAllStaleImports() File::DeleteDirRecursively(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 HLE } // namespace IOS diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp index d6cba3fb9f..8e7cbb616e 100644 --- a/Source/Core/Core/IOS/ES/TitleContents.cpp +++ b/Source/Core/Core/IOS/ES/TitleContents.cpp @@ -12,7 +12,6 @@ #include "Common/MsgHandler.h" #include "Core/HW/Memmap.h" #include "Core/IOS/ES/Formats.h" -#include "DiscIO/NANDContentLoader.h" namespace IOS { @@ -23,14 +22,10 @@ namespace Device s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid) { const u64 title_id = tmd.GetTitleId(); - const DiscIO::NANDContentLoader& loader = AccessContentDevice(title_id); - if (!loader.IsValid()) - return FS_ENOENT; - - const DiscIO::NANDContent* content = loader.GetContentByIndex(content_index); - if (!content) - return FS_ENOENT; + IOS::ES::Content content; + if (!tmd.GetContent(content_index, &content)) + return ES_EINVAL; 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) continue; + if (!entry.m_file.Open(GetContentPath(title_id, content), "rb")) + return FS_ENOENT; + entry.m_opened = true; entry.m_position = 0; - entry.m_content = content->m_metadata; + entry.m_content = content; entry.m_title_id = title_id; entry.m_uid = uid; 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); - if (!GetTitleContext().active) + if (!m_title_context.active) return GetDefaultReply(ES_EINVAL); 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) 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) @@ -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 // instead of all this duplicated filesystem logic. - if (entry.m_position + size > entry.m_content.size) - size = static_cast(entry.m_content.size) - entry.m_position; + if (entry.m_position + size > entry.m_file.GetSize()) + size = static_cast(entry.m_file.GetSize()) - entry.m_position; - const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id); - // ContentLoader should never be invalid; rContent has been created by it. - if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid()) + entry.m_file.Seek(entry.m_position, SEEK_SET); + if (!entry.m_file.ReadBytes(buffer, size)) { - const DiscIO::NANDContent* pContent = ContentLoader.GetContentByIndex(entry.m_content.index); - pContent->m_Data->Open(); - 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; - } + ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position); + return ES_SHORT_READ; } entry.m_position += size; @@ -145,15 +137,6 @@ ReturnCode ES::CloseContent(u32 cfd, u32 uid) if (!entry.m_opened) 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 = {}; INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd); return IPC_SUCCESS; diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 4a9e82aeb8..4a7660fd52 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -22,7 +22,6 @@ #include "Core/HW/Memmap.h" #include "Core/IOS/ES/Formats.h" #include "Core/ec_wii.h" -#include "DiscIO/NANDContentLoader.h" namespace IOS { @@ -160,8 +159,8 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector& tmd_bytes) if (!InitImport(context.title_import_export.tmd.GetTitleId())) return ES_EIO; - ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(), - &context.title_import_export.key_handle); + ret = + InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle); if (ret != IPC_SUCCESS) return ret; @@ -220,7 +219,7 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector& tmd_byte if (ret != IPC_SUCCESS) 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()) 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()); return FS_EACCESS; } - // XXX: ugly, but until we drop NANDContentManager everywhere, this is going to be needed. - DiscIO::NANDContentManager::Access().ClearCache(); return IPC_SUCCESS; } @@ -528,7 +525,7 @@ ReturnCode ES::DeleteTicket(const u8* ticket_view) if (!CanDeleteTitle(title_id)) return ES_EINVAL; - auto ticket = DiscIO::FindSignedTicket(title_id); + auto ticket = FindSignedTicket(title_id); if (!ticket.IsValid()) return FS_ENOENT; @@ -639,8 +636,8 @@ ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u3 ResetTitleImportContext(&context, m_ios.GetIOSC()); context.title_import_export.tmd = tmd; - const ReturnCode ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(), - &context.title_import_export.key_handle); + const ReturnCode ret = + InitBackupKey(m_title_context.tmd, m_ios.GetIOSC(), &context.title_import_export.key_handle); if (ret != IPC_SUCCESS) return ret; diff --git a/Source/Core/Core/IOS/ES/Views.cpp b/Source/Core/Core/IOS/ES/Views.cpp index a687419408..f3227b7f0a 100644 --- a/Source/Core/Core/IOS/ES/Views.cpp +++ b/Source/Core/Core/IOS/ES/Views.cpp @@ -17,7 +17,6 @@ #include "Core/Core.h" #include "Core/HW/Memmap.h" #include "Core/IOS/ES/Formats.h" -#include "DiscIO/NANDContentLoader.h" namespace IOS { @@ -49,10 +48,10 @@ IPCCommandResult ES::GetTicketViewCount(const IOCtlVRequest& request) 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(ticket.GetNumberOfTickets()) : 0; - if (ShouldReturnFakeViewsForIOSes(TitleID, GetTitleContext())) + if (ShouldReturnFakeViewsForIOSes(TitleID, m_title_context)) { view_count = 1; 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); 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()) { @@ -85,7 +84,7 @@ IPCCommandResult ES::GetTicketViews(const IOCtlVRequest& request) 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)); 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 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. // This is close enough, though. if (!installed_ticket.IsValid()) @@ -112,11 +111,11 @@ ReturnCode ES::GetV0TicketFromView(const u8* ticket_view, u8* ticket) const if (ticket_bytes.empty()) return ES_NO_TICKET; - if (!GetTitleContext().active) + if (!m_title_context.active) return ES_EINVAL; // Check for permission to export the ticket. - const u32 title_identifier = static_cast(GetTitleContext().tmd.GetTitleId()); + const u32 title_identifier = static_cast(m_title_context.tmd.GetTitleId()); const u32 permitted_title_mask = Common::swap32(ticket_bytes.data() + offsetof(IOS::ES::Ticket, permitted_title_mask)); const u32 permitted_title_id = @@ -276,10 +275,10 @@ IPCCommandResult ES::DIGetTMDViewSize(const IOCtlVRequest& request) else { // 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); - tmd_view_size = GetTitleContext().tmd.GetRawView().size(); + tmd_view_size = m_title_context.tmd.GetRawView().size(); } Memory::Write_U32(static_cast(tmd_view_size), request.io_vectors[0].address); @@ -319,10 +318,10 @@ IPCCommandResult ES::DIGetTMDView(const IOCtlVRequest& request) else { // 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); - tmd_view = GetTitleContext().tmd.GetRawView(); + tmd_view = m_title_context.tmd.GetRawView(); } 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. if (!has_ticket_vector) { - if (!GetTitleContext().active) + if (!m_title_context.active) return GetDefaultReply(ES_EINVAL); - view = GetTitleContext().ticket.GetRawTicketView(0); + view = m_title_context.ticket.GetRawTicketView(0); } else { @@ -375,10 +374,10 @@ IPCCommandResult ES::DIGetTMDSize(const IOCtlVRequest& request) if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != sizeof(u32)) return GetDefaultReply(ES_EINVAL); - if (!GetTitleContext().active) + if (!m_title_context.active) return GetDefaultReply(ES_EINVAL); - Memory::Write_U32(static_cast(GetTitleContext().tmd.GetBytes().size()), + Memory::Write_U32(static_cast(m_title_context.tmd.GetBytes().size()), request.io_vectors[0].address); return GetDefaultReply(IPC_SUCCESS); } @@ -392,10 +391,10 @@ IPCCommandResult ES::DIGetTMD(const IOCtlVRequest& request) if (tmd_size != request.io_vectors[0].size) return GetDefaultReply(ES_EINVAL); - if (!GetTitleContext().active) + if (!m_title_context.active) return GetDefaultReply(ES_EINVAL); - const std::vector& tmd_bytes = GetTitleContext().tmd.GetBytes(); + const std::vector& tmd_bytes = m_title_context.tmd.GetBytes(); if (static_cast(tmd_bytes.size()) > tmd_size) return GetDefaultReply(ES_EINVAL); diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 3d372c7688..667e92e490 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -19,6 +19,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Core/Boot/DolReader.h" +#include "Core/Boot/ElfReader.h" #include "Core/CommonTitles.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -54,7 +55,6 @@ #include "Core/IOS/WFS/WFSSRV.h" #include "Core/PowerPC/PowerPC.h" #include "Core/WiiRoot.h" -#include "DiscIO/NANDContentLoader.h" 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. // 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()) - return false; + const DolReader dol{boot_content_path}; - const auto* content = content_loader.GetContentByIndex(content_loader.GetTMD().GetBootIndex()); - if (!content) - return false; - - const auto dol_loader = std::make_unique(content->m_Data->Get()); - if (!dol_loader->IsValid()) + if (!dol.IsValid()) return false; if (!SetupMemory(m_title_id, MemorySetupType::Full)) return false; - if (!dol_loader->LoadIntoMemory()) + if (!dol.LoadIntoMemory()) return false; // 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; } +struct ARMBinary final +{ + explicit ARMBinary(std::vector&& 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 GetElf() const + { + const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset(); + return std::vector(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 m_bytes; +}; + // 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 // 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. - // - // * 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) + if (!boot_content_path.empty()) { - NOTICE_LOG(IOS, "BC: Launching MIOS..."); - return BootIOS(Titles::MIOS); + // Load the ARM binary to memory (if possible). + // 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 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. diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index aaf6b18671..9280412698 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -19,11 +19,6 @@ class PointerWrap; -namespace DiscIO -{ -class NANDContentLoader; -} - namespace IOS { namespace HLE @@ -113,8 +108,8 @@ public: void SetGidForPPC(u16 gid); u16 GetGidForPPC() const; - bool BootstrapPPC(const DiscIO::NANDContentLoader& content_loader); - bool BootIOS(u64 ios_title_id); + bool BootstrapPPC(const std::string& boot_content_path); + bool BootIOS(u64 ios_title_id, const std::string& boot_content_path = ""); u32 GetVersion() const; IOSC& GetIOSC(); diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index fc0e70541a..2f0f02a422 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -6,16 +6,12 @@ #include #include -#include #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -#include "Common/NandPaths.h" #include "Common/Swap.h" -#include "Core/Boot/ElfReader.h" -#include "Core/CommonTitles.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/DSPEmulator.h" @@ -24,10 +20,8 @@ #include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" -#include "Core/IOS/ES/Formats.h" #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" -#include "DiscIO/NANDContentLoader.h" namespace IOS { @@ -35,73 +29,6 @@ namespace HLE { namespace MIOS { -// Source: https://wiibrew.org/wiki/ARM_Binaries -struct ARMBinary final -{ - explicit ARMBinary(const std::vector& bytes); - explicit ARMBinary(std::vector&& bytes); - - bool IsValid() const; - std::vector GetElf() const; - u32 GetHeaderSize() const; - u32 GetElfOffset() const; - u32 GetElfSize() const; - -private: - std::vector m_bytes; -}; - -ARMBinary::ARMBinary(const std::vector& bytes) : m_bytes(bytes) -{ -} - -ARMBinary::ARMBinary(std::vector&& 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 ARMBinary::GetElf() const -{ - const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset(); - return std::vector(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 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() { SConfig::GetInstance().bWii = false; @@ -125,22 +52,6 @@ bool Load() Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE); 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(); NOTICE_LOG(IOS, "Reinitialised hardware."); diff --git a/Source/Core/Core/IOS/WFS/WFSI.cpp b/Source/Core/Core/IOS/WFS/WFSI.cpp index a0d8610bde..afb6406c2d 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.cpp +++ b/Source/Core/Core/IOS/WFS/WFSI.cpp @@ -19,7 +19,6 @@ #include "Core/IOS/ES/ES.h" #include "Core/IOS/ES/Formats.h" #include "Core/IOS/WFS/WFSSRV.h" -#include "DiscIO/NANDContentLoader.h" namespace { @@ -157,7 +156,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) Memory::CopyFromEmu(tmd_bytes.data(), tmd_addr, tmd_size); 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()) { return_error_code = -11028; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index b17daba6ca..afd6105e7c 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // 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. // Versions after 42 don't need to be added to this list, diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp index 4235c9ba96..bde5e04a10 100644 --- a/Source/Core/Core/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -29,6 +29,7 @@ #include "Common/NandPaths.h" #include "Common/StringUtil.h" #include "Common/Swap.h" +#include "Common/SysConf.h" #include "Core/CommonTitles.h" #include "Core/ConfigManager.h" #include "Core/IOS/Device.h" @@ -38,7 +39,6 @@ #include "DiscIO/DiscExtractor.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" -#include "DiscIO/NANDContentLoader.h" #include "DiscIO/Volume.h" #include "DiscIO/VolumeFileBlobReader.h" #include "DiscIO/VolumeWii.h" @@ -46,7 +46,7 @@ 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()) { @@ -76,8 +76,9 @@ static bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::WiiWAD& wad) 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; - PanicAlertT("WAD installation failed: Could not initialise title import."); return false; } 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; } +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(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(Common::swap64(title_id)); + else + tid_entry->SetData(0); + + return true; +} + bool InstallWAD(const std::string& wad_path) { IOS::HLE::Kernel ios; - const DiscIO::WiiWAD wad{wad_path}; - const bool result = InstallWAD(ios, wad); - - DiscIO::NANDContentManager::Access().ClearCache(); - return result; + return InstallWAD(ios, DiscIO::WiiWAD{wad_path}, InstallType::Permanent); } // Common functionality for system updaters. @@ -648,7 +689,7 @@ UpdateResult DiscSystemUpdater::ProcessEntry(u32 type, std::bitset<32> attrs, return UpdateResult::AlreadyUpToDate; 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. if (attrs.test(16) && ticket.IsValid()) @@ -667,23 +708,19 @@ UpdateResult DiscSystemUpdater::ProcessEntry(u32 type, std::bitset<32> attrs, return UpdateResult::DiscReadFailed; } 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) { OnlineSystemUpdater updater{std::move(update_callback), region}; - const UpdateResult result = updater.DoOnlineUpdate(); - DiscIO::NANDContentManager::Access().ClearCache(); - return result; + return updater.DoOnlineUpdate(); } UpdateResult DoDiscUpdate(UpdateCallback update_callback, const std::string& image_path) { DiscSystemUpdater updater{std::move(update_callback), image_path}; - const UpdateResult result = updater.DoDiscUpdate(); - DiscIO::NANDContentManager::Access().ClearCache(); - return result; + return updater.DoDiscUpdate(); } 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). - const auto ticket = DiscIO::FindSignedTicket(title_id); + const auto ticket = es->FindSignedTicket(title_id); if (!IOS::ES::IsDiscTitle(title_id) && !ticket.IsValid()) { 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 bool has_no_tmd_but_contents = !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); File::DeleteDirRecursively(title_dir); diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 0047e2c63d..b07863fa3b 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -13,6 +13,10 @@ // Small utility functions for common Wii related tasks. +namespace DiscIO +{ +class WiiWAD; +} namespace IOS { namespace HLE @@ -23,6 +27,15 @@ class Kernel; 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); enum class UpdateResult diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index 1b6bb3b51d..9e095f464e 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -11,7 +11,6 @@ set(SRCS FileBlob.cpp FileSystemGCWii.cpp Filesystem.cpp - NANDContentLoader.cpp NANDImporter.cpp TGCBlob.cpp Volume.cpp diff --git a/Source/Core/DiscIO/DiscIO.vcxproj b/Source/Core/DiscIO/DiscIO.vcxproj index b20dae68bc..4c8b542a6f 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj +++ b/Source/Core/DiscIO/DiscIO.vcxproj @@ -47,7 +47,6 @@ - @@ -70,7 +69,6 @@ - diff --git a/Source/Core/DiscIO/DiscIO.vcxproj.filters b/Source/Core/DiscIO/DiscIO.vcxproj.filters index 89cfed8abb..c196ae25aa 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj.filters +++ b/Source/Core/DiscIO/DiscIO.vcxproj.filters @@ -33,9 +33,6 @@ NAND - - NAND - NAND @@ -98,9 +95,6 @@ NAND - - NAND - NAND diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp deleted file mode 100644 index 47ea26c81a..0000000000 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2009 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "DiscIO/NANDContentLoader.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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(m_filename, "rb"); - else if (!m_file->IsOpen()) - m_file->Open(m_filename, "rb"); -} -void NANDContentDataFile::Open() -{ - EnsureOpen(); -} -std::vector NANDContentDataFile::Get() -{ - EnsureOpen(); - - if (!m_file->IsGood()) - return {}; - - u64 size = m_file->GetSize(); - if (size == 0) - return {}; - - std::vector 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)); -} -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 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 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& data_app) -{ - if (!m_ticket.IsValid()) - { - ERROR_LOG(IOS_ES, "No valid ticket for title %016" PRIx64, m_tmd.GetTitleId()); - return; - } - - const std::vector contents = m_tmd.GetContents(); - m_Content.resize(contents.size()); - - u32 data_app_offset = 0; - const std::array 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 iv{}; - iv[0] = static_cast(content.index >> 8) & 0xFF; - iv[1] = static_cast(content.index) & 0xFF; - - u32 rounded_size = Common::AlignUp(static_cast(content.size), 0x40); - - m_Content[i].m_Data = std::make_unique(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(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( - 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 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 diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h deleted file mode 100644 index aa7d1c168e..0000000000 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2009 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include - -#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 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 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 m_file; -}; - -class NANDContentDataBuffer final : public NANDContentData -{ -public: - explicit NANDContentDataBuffer(const std::vector& buffer) : m_buffer(buffer) {} - std::vector Get() override { return m_buffer; } - bool GetRange(u32 start, u32 size, u8* buffer) override; - -private: - const std::vector m_buffer; -}; - -struct NANDContent -{ - IOS::ES::Content m_metadata; - std::unique_ptr 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& GetContent() const { return m_Content; } -private: - bool Initialize(const std::string& name); - void InitializeContentEntries(const std::vector& 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 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> m_map; -}; -} diff --git a/Source/Core/DiscIO/NANDImporter.cpp b/Source/Core/DiscIO/NANDImporter.cpp index 220a241390..5a9983ce03 100644 --- a/Source/Core/DiscIO/NANDImporter.cpp +++ b/Source/Core/DiscIO/NANDImporter.cpp @@ -17,7 +17,6 @@ #include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/IOS/ES/Formats.h" -#include "DiscIO/NANDContentLoader.h" namespace DiscIO { @@ -44,9 +43,6 @@ void NANDImporter::ImportNANDBin(const std::string& path_to_bin, ProcessEntry(0, nand_root); ExportKeys(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) diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index fc065a8d60..4f878c3bc9 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -15,8 +15,6 @@ #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" -// --- this volume type is used for GC disc images --- - namespace DiscIO { class BlobReader; diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index a97f2d078e..33bfe4f7a6 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -14,10 +14,6 @@ #include "Core/IOS/ES/Formats.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 { class BlobReader; diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index 97604887a3..84d58a530c 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -17,8 +17,6 @@ #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" -// --- this volume type is used for Wii disc images --- - namespace DiscIO { class BlobReader; diff --git a/Source/Core/DiscIO/WiiWad.h b/Source/Core/DiscIO/WiiWad.h index 6f3f4a97bc..1bdd40f698 100644 --- a/Source/Core/DiscIO/WiiWad.h +++ b/Source/Core/DiscIO/WiiWad.h @@ -20,6 +20,8 @@ class WiiWAD public: explicit WiiWAD(const std::string& name); explicit WiiWAD(std::unique_ptr blob_reader); + WiiWAD(WiiWAD&&) = default; + WiiWAD& operator=(WiiWAD&&) = default; ~WiiWAD(); bool IsValid() const { return m_valid; } diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index ecd2d3ba88..5619689abc 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -360,14 +360,26 @@ int main(int argc, char* argv[]) optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv); std::vector args = parser->args(); - std::string boot_filename; + std::unique_ptr boot; if (options.is_set("exec")) { - boot_filename = static_cast(options.get("exec")); + boot = BootParameters::GenerateFromFile(static_cast(options.get("exec"))); + } + else if (options.is_set("nand_title")) + { + const std::string hex_string = static_cast(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::NANDTitle{title_id}); } else if (args.size()) { - boot_filename = args.front(); + boot = BootParameters::GenerateFromFile(args.front()); args.erase(args.begin()); } else @@ -408,9 +420,9 @@ int main(int argc, char* argv[]) 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; } diff --git a/Source/Core/DolphinQt2/GameList/GameFile.cpp b/Source/Core/DolphinQt2/GameList/GameFile.cpp index 035028c9f9..9f5314a444 100644 --- a/Source/Core/DolphinQt2/GameList/GameFile.cpp +++ b/Source/Core/DolphinQt2/GameList/GameFile.cpp @@ -18,7 +18,6 @@ #include "Core/WiiUtils.h" #include "DiscIO/Blob.h" #include "DiscIO/Enums.h" -#include "DiscIO/NANDContentLoader.h" #include "DiscIO/Volume.h" #include "DolphinQt2/GameList/GameFile.h" #include "DolphinQt2/Resources.h" diff --git a/Source/Core/DolphinQt2/Main.cpp b/Source/Core/DolphinQt2/Main.cpp index fea2b56379..1a9b66cbae 100644 --- a/Source/Core/DolphinQt2/Main.cpp +++ b/Source/Core/DolphinQt2/Main.cpp @@ -10,6 +10,7 @@ #include "Common/MsgHandler.h" #include "Core/Analytics.h" +#include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/Core.h" #include "DolphinQt2/Host.h" @@ -84,6 +85,21 @@ int main(int argc, char* argv[]) QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock, &app, &Core::HostDispatchJobs); + std::unique_ptr boot; + if (options.is_set("nand_title")) + { + const std::string hex_string = static_cast(options.get("nand_title")); + if (hex_string.length() == 16) + { + const u64 title_id = std::stoull(hex_string, nullptr, 16); + boot = std::make_unique(BootParameters::NANDTitle{title_id}); + } + else + { + QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("Invalid title ID.")); + } + } + int retval = 0; if (SConfig::GetInstance().m_show_development_warning) @@ -95,7 +111,7 @@ int main(int argc, char* argv[]) { DolphinAnalytics::Instance()->ReportDolphinStart("qt"); - MainWindow win; + MainWindow win{std::move(boot)}; win.show(); #if defined(USE_ANALYTICS) && USE_ANALYTICS diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index fbb508f195..304327a4b7 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -65,7 +65,7 @@ #include "UICommon/X11Utils.h" #endif -MainWindow::MainWindow() : QMainWindow(nullptr) +MainWindow::MainWindow(std::unique_ptr boot_parameters) : QMainWindow(nullptr) { setWindowTitle(QString::fromStdString(Common::scm_rev_str)); setWindowIcon(QIcon(Resources::GetMisc(Resources::LOGO_SMALL))); @@ -84,6 +84,9 @@ MainWindow::MainWindow() : QMainWindow(nullptr) InitCoreCallbacks(); NetPlayInit(); + + if (boot_parameters) + StartGame(std::move(boot_parameters)); } MainWindow::~MainWindow() @@ -652,8 +655,7 @@ void MainWindow::PerformOnlineUpdate(const std::string& region) void MainWindow::BootWiiSystemMenu() { - StartGame(QString::fromStdString( - Common::GetTitleContentPath(Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT))); + StartGame(std::make_unique(BootParameters::NANDTitle{Titles::SYSTEM_MENU})); } void MainWindow::NetPlayInit() diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index 93d5431334..d8db4fc01b 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -35,7 +35,7 @@ class MainWindow final : public QMainWindow Q_OBJECT public: - explicit MainWindow(); + explicit MainWindow(std::unique_ptr boot_parameters); ~MainWindow(); bool eventFilter(QObject* object, QEvent* event) override; diff --git a/Source/Core/DolphinWX/Config/PathConfigPane.cpp b/Source/Core/DolphinWX/Config/PathConfigPane.cpp index 0d54738192..ce1c2a78a4 100644 --- a/Source/Core/DolphinWX/Config/PathConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/PathConfigPane.cpp @@ -18,7 +18,6 @@ #include "Common/FileUtil.h" #include "Core/ConfigManager.h" #include "Core/Core.h" -#include "DiscIO/NANDContentLoader.h" #include "DolphinWX/Config/ConfigMain.h" #include "DolphinWX/Frame.h" #include "DolphinWX/WxEventUtils.h" @@ -202,8 +201,6 @@ void PathConfigPane::OnNANDRootChanged(wxCommandEvent& event) File::SetUserPath(D_WIIROOT_IDX, 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()}; update_event.SetEventObject(this); AddPendingEvent(update_event); diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp index c4f9cd7dd1..6a1c3a0c53 100644 --- a/Source/Core/DolphinWX/Frame.cpp +++ b/Source/Core/DolphinWX/Frame.cpp @@ -501,7 +501,6 @@ void CFrame::BindEvents() Bind(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, &CFrame::OnReloadThemeBitmaps, this); Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &CFrame::OnRefreshGameList, 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_STOP_SOFTWARE, &CFrame::OnStop, this); } diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 24cb56a94b..4e33b645e5 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -106,6 +106,7 @@ public: void StatusBarMessage(const char* format, ...); void ClearStatusBar(); void BootGame(const std::string& filename); + void StartGame(std::unique_ptr boot); bool RendererHasFocus(); bool RendererIsFullscreen(); void OpenGeneralConfiguration(wxWindowID tab_id = wxID_ANY); @@ -193,7 +194,6 @@ private: void InitializeTASDialogs(); void InitializeCoreCallbacks(); - void StartGame(std::unique_ptr boot); void SetDebuggerStartupParameters() const; // Utility @@ -275,9 +275,6 @@ private: void OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event); - void OnUpdateLoadWiiMenuItem(wxCommandEvent&); - void UpdateLoadWiiMenuItem() const; - void OnOpen(wxCommandEvent& event); // File menu void OnRefresh(wxCommandEvent& event); void OnBootDrive(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 592f338cd2..05b819a99b 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -60,7 +60,6 @@ #include "Core/WiiUtils.h" #include "DiscIO/Enums.h" -#include "DiscIO/NANDContentLoader.h" #include "DiscIO/NANDImporter.h" #include "DiscIO/VolumeWad.h" @@ -652,6 +651,7 @@ void CFrame::StartGame(std::unique_ptr boot) if (m_is_game_loading) return; m_is_game_loading = true; + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); GetToolBar()->EnableTool(IDM_PLAY, false); GetMenuBar()->FindItem(IDM_PLAY)->Enable(false); @@ -925,6 +925,7 @@ void CFrame::OnStopped() m_confirm_stop = false; m_is_game_loading = false; m_tried_graceful_shutdown = false; + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); UninhibitScreensaver(); @@ -1131,11 +1132,6 @@ void CFrame::OnUpdateInterpreterMenuItem(wxUpdateUIEvent& event) event.Check(SConfig::GetInstance().iCPUCore == PowerPC::CORE_INTERPRETER); } -void CFrame::OnUpdateLoadWiiMenuItem(wxCommandEvent& WXUNUSED(event)) -{ - UpdateLoadWiiMenuItem(); -} - void CFrame::ClearStatusBar() { if (this->GetStatusBar()->IsEnabled()) @@ -1225,7 +1221,7 @@ void CFrame::OnShowCheatsWindow(wxCommandEvent& WXUNUSED(event)) void CFrame::OnLoadWiiMenu(wxCommandEvent& WXUNUSED(event)) { - BootGame(Common::GetTitleContentPath(Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT)); + StartGame(std::make_unique(BootParameters::NANDTitle{Titles::SYSTEM_MENU})); } void CFrame::OnInstallWAD(wxCommandEvent& event) @@ -1260,7 +1256,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event) wxPD_REMAINING_TIME | wxPD_SMOOTH); if (WiiUtils::InstallWAD(fileName)) - UpdateLoadWiiMenuItem(); + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); } void CFrame::OnUninstallWAD(wxCommandEvent&) @@ -1284,7 +1280,7 @@ void CFrame::OnUninstallWAD(wxCommandEvent&) } if (title_id == Titles::SYSTEM_MENU) - UpdateLoadWiiMenuItem(); + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); } void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event)) @@ -1306,7 +1302,7 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event)) wxProgressDialog dialog(_("Importing NAND backup"), _("Working..."), 100, this, wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH); DiscIO::NANDImporter().ImportNANDBin(file_name, [&dialog] { dialog.Pulse(); }); - UpdateLoadWiiMenuItem(); + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); } void CFrame::OnCheckNAND(wxCommandEvent&) @@ -1462,7 +1458,7 @@ void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event) const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoOnlineUpdate, region); ShowUpdateResult(result); - UpdateLoadWiiMenuItem(); + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); } void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&) @@ -1475,12 +1471,7 @@ void CFrame::OnPerformDiscWiiUpdate(wxCommandEvent&) const WiiUtils::UpdateResult result = ShowUpdateProgress(this, WiiUtils::DoDiscUpdate, file_name); ShowUpdateResult(result); - UpdateLoadWiiMenuItem(); -} - -void CFrame::UpdateLoadWiiMenuItem() const -{ - GetMenuBar()->Refresh(true, nullptr); + wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM}); } void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp index bff1c34adb..fd87aaea8e 100644 --- a/Source/Core/DolphinWX/Main.cpp +++ b/Source/Core/DolphinWX/Main.cpp @@ -34,6 +34,7 @@ #include "Common/Version.h" #include "Core/Analytics.h" +#include "Core/Boot/Boot.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/Wiimote.h" @@ -169,13 +170,24 @@ void DolphinApp::ParseCommandLine() if (options.is_set("exec")) { - m_load_file = true; - m_file_to_load = static_cast(options.get("exec")); + m_boot = BootParameters::GenerateFromFile(static_cast(options.get("exec"))); + } + else if (options.is_set("nand_title")) + { + const std::string hex_string = static_cast(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::NANDTitle{title_id}); + } + else + { + WxUtils::ShowErrorDialog(_("The title ID is invalid.")); + } } else if (args.size()) { - m_load_file = true; - m_file_to_load = args.front(); + m_boot = BootParameters::GenerateFromFile(args.front()); args.erase(args.begin()); } @@ -201,9 +213,7 @@ void DolphinApp::ParseCommandLine() #ifdef __APPLE__ void DolphinApp::MacOpenFile(const wxString& fileName) { - m_file_to_load = fileName; - m_load_file = true; - main_frame->BootGame(WxStrToStr(m_file_to_load)); + main_frame->StartGame(BootParameters::GenerateFromFile(fileName.ToStdString())); } #endif @@ -241,20 +251,16 @@ void DolphinApp::AfterInit() { if (Movie::PlayInput(WxStrToStr(m_movie_file))) { - if (m_load_file && !m_file_to_load.empty()) - { - main_frame->BootGame(WxStrToStr(m_file_to_load)); - } + if (m_boot) + main_frame->StartGame(std::move(m_boot)); else - { main_frame->BootGame(""); - } } } // 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, // or if no default ISO exists, start the last loaded ISO diff --git a/Source/Core/DolphinWX/Main.h b/Source/Core/DolphinWX/Main.h index 46f3d80e91..324051fdbd 100644 --- a/Source/Core/DolphinWX/Main.h +++ b/Source/Core/DolphinWX/Main.h @@ -13,6 +13,8 @@ class wxLocale; extern CFrame* main_frame; +struct BootParameters; + // Define a new application class DolphinApp : public wxApp { @@ -43,7 +45,6 @@ private: bool m_batch_mode = false; bool m_confirm_stop = false; bool m_is_active = true; - bool m_load_file = false; bool m_play_movie = false; bool m_use_debugger = false; bool m_use_logger = false; @@ -53,7 +54,7 @@ private: wxString m_video_backend_name; wxString m_audio_emulation_name; wxString m_user_path; - wxString m_file_to_load; + std::unique_ptr m_boot; wxString m_movie_file; std::unique_ptr m_locale; }; diff --git a/Source/Core/DolphinWX/MainMenuBar.cpp b/Source/Core/DolphinWX/MainMenuBar.cpp index fdb6b633c6..18eb27002c 100644 --- a/Source/Core/DolphinWX/MainMenuBar.cpp +++ b/Source/Core/DolphinWX/MainMenuBar.cpp @@ -11,10 +11,12 @@ #include "Core/CommonTitles.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "Core/IOS/ES/ES.h" +#include "Core/IOS/ES/Formats.h" #include "Core/PowerPC/PowerPC.h" #include "Core/State.h" #include "DiscIO/Enums.h" -#include "DiscIO/NANDContentLoader.h" +#include "DolphinWX/Frame.h" #include "DolphinWX/Globals.h" #include "DolphinWX/WxUtils.h" @@ -30,6 +32,7 @@ MainMenuBar::MainMenuBar(MenuType type, long style) : wxMenuBar{style}, m_type{t { BindEvents(); AddMenus(); + RefreshWiiSystemMenuLabel(); } void MainMenuBar::Refresh(bool erase_background, const wxRect* rect) @@ -62,6 +65,7 @@ void MainMenuBar::AddMenus() void MainMenuBar::BindEvents() { Bind(EVT_POPULATE_PERSPECTIVES_MENU, &MainMenuBar::OnPopulatePerspectivesMenu, this); + Bind(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, &MainMenuBar::OnUpdateWiiMenuTool, this); } 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 // inconsistent data. const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii; - for (const int index : - {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, IDM_IMPORT_NAND, IDM_CHECK_NAND, - 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}) + for (const int index : {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, + IDM_IMPORT_NAND, IDM_CHECK_NAND, IDM_EXTRACT_CERTIFICATES}) { FindItem(index)->Enable(enable_wii_tools); } - if (enable_wii_tools) - RefreshWiiSystemMenuLabel(); } void MainMenuBar::EnableUpdateMenu(UpdateMenuMode mode) const @@ -608,12 +607,23 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const { auto* const item = FindItem(IDM_LOAD_WII_MENU); - const auto& sys_menu_loader = DiscIO::NANDContentManager::Access().GetNANDLoader( - Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT); - - if (sys_menu_loader.IsValid()) + if (Core::IsRunning()) { - 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)); item->Enable(); 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 { while (m_saved_perspectives_menu->GetMenuItemCount() != 0) diff --git a/Source/Core/DolphinWX/MainMenuBar.h b/Source/Core/DolphinWX/MainMenuBar.h index d7e3d692dc..ab786c637f 100644 --- a/Source/Core/DolphinWX/MainMenuBar.h +++ b/Source/Core/DolphinWX/MainMenuBar.h @@ -42,6 +42,7 @@ private: wxMenu* CreateHelpMenu() const; void OnPopulatePerspectivesMenu(PopulatePerspectivesEvent&); + void OnUpdateWiiMenuTool(wxCommandEvent&); void RefreshMenuLabels() const; void RefreshPlayMenuLabel() const; diff --git a/Source/Core/UICommon/CommandLineParse.cpp b/Source/Core/UICommon/CommandLineParse.cpp index c5db8b57e0..d76a2052c4 100644 --- a/Source/Core/UICommon/CommandLineParse.cpp +++ b/Source/Core/UICommon/CommandLineParse.cpp @@ -73,6 +73,11 @@ std::unique_ptr CreateParser(ParserOptions options) .metavar("") .type("string") .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") .action("append") .metavar(".
.=")