diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 46176ecf5c..6e0ffebba6 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -278,8 +278,6 @@ bool CBoot::BootUp() PanicAlertT("Warning - starting ISO in wrong console mode!"); } - IOS::HLE::ES_DIVerify(pVolume.GetTMD()); - _StartupPara.bWii = pVolume.GetVolumeType() == DiscIO::Platform::WII_DISC; // HLE BS2 or not diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 605579f597..26b44593ad 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -311,6 +311,11 @@ bool CBoot::EmulatedBS2_Wii() if (DVDInterface::GetVolume().GetVolumeType() != DiscIO::Platform::WII_DISC) return false; + const IOS::ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); + + if (!SetupWiiMemory(tmd.GetIOSId())) + return false; + // This is some kind of consistency check that is compared to the 0x00 // values as the game boots. This location keeps the 4 byte ID for as long // as the game is running. The 6 byte ID at 0x00 is overwritten sometime @@ -346,11 +351,6 @@ bool CBoot::EmulatedBS2_Wii() PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer - IOS::ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); - - if (!SetupWiiMemory(tmd.GetIOSId())) - return false; - // Execute the apploader const u32 apploader_offset = 0x2440; // 0x1c40; @@ -383,11 +383,7 @@ bool CBoot::EmulatedBS2_Wii() PowerPC::ppcState.gpr[3] = 0x81300000; RunFunction(iAppLoaderInit); - // Let the apploader load the exe to memory. At this point I get an unknown IPC command - // (command zero) when I load Wii Sports or other games a second time. I don't notice - // any side effects however. It's a little disconcerting however that Start after Stop - // behaves differently than the first Start after starting Dolphin. It means something - // was not reset correctly. + // Let the apploader load the exe to memory DEBUG_LOG(BOOT, "Run iAppLoaderMain"); do { @@ -413,6 +409,8 @@ bool CBoot::EmulatedBS2_Wii() // Load patches and run startup patches PatchEngine::LoadPatches(); + IOS::HLE::ES_DIVerify(tmd, DVDInterface::GetVolume().GetTicket()); + // return PC = PowerPC::ppcState.gpr[3]; return true; diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index bd91eb11d5..59c0de4b52 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -109,7 +109,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) const ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); const std::vector raw_tmd = tmd.GetRawTMD(); Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); - ES_DIVerify(tmd); + ES_DIVerify(tmd, DVDInterface::GetVolume().GetTicket()); return_value = 1; break; diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 79a69e2317..d2b30b6da7 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -39,6 +39,7 @@ namespace HLE namespace Device { std::string ES::m_ContentFile; +ES::TitleContext ES::m_title_context; constexpr u8 s_key_sd[0x10] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d}; @@ -65,11 +66,58 @@ constexpr const u8* s_key_table[11] = { ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name) { + m_title_context.Clear(); + + m_TitleIDs.clear(); + DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; + uid_sys.GetTitleIDs(m_TitleIDs); + + // uncomment if ES_GetOwnedTitlesCount / ES_GetOwnedTitles is implemented + // m_TitleIDsOwned.clear(); + // DiscIO::cUIDsys::AccessInstance().GetTitleIDs(m_TitleIDsOwned, true); +} + +void ES::TitleContext::Clear() +{ + ticket.SetBytes({}); + tmd.SetBytes({}); + active = false; +} + +void ES::TitleContext::DoState(PointerWrap& p) +{ + ticket.DoState(p); + tmd.DoState(p); + p.Do(active); +} + +void ES::TitleContext::Update(const DiscIO::CNANDContentLoader& content_loader) +{ + if (!content_loader.IsValid()) + return; + Update(content_loader.GetTMD(), content_loader.GetTicket()); +} + +void ES::TitleContext::Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_) +{ + if (!tmd_.IsValid() || !ticket_.IsValid()) + { + ERROR_LOG(IOS_ES, "TMD or ticket is not valid -- refusing to update title context"); + return; + } + + ticket = ticket_; + tmd = tmd_; + active = true; } void ES::LoadWAD(const std::string& _rContentFile) { m_ContentFile = _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::CNANDContentManager::Access().GetNANDLoader(m_ContentFile); + m_title_context.Update(content_loader); } void ES::DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output) @@ -82,6 +130,8 @@ void ES::DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, bool ES::LaunchTitle(u64 title_id, bool skip_reload) const { + m_title_context.Clear(); + 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. @@ -120,47 +170,18 @@ bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload) const return LaunchTitle(required_ios); } + m_title_context.Update(content_loader); SetDefaultContentFile(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT)); return BootstrapPPC(content_loader); } -void ES::OpenInternal() -{ - auto& contentLoader = DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile); - - // check for cd ... - if (contentLoader.IsValid()) - { - m_TitleID = contentLoader.GetTMD().GetTitleId(); - - m_TitleIDs.clear(); - DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; - uid_sys.GetTitleIDs(m_TitleIDs); - // uncomment if ES_GetOwnedTitlesCount / ES_GetOwnedTitles is implemented - // m_TitleIDsOwned.clear(); - // DiscIO::cUIDsys::AccessInstance().GetTitleIDs(m_TitleIDsOwned, true); - } - else if (DVDInterface::VolumeIsValid()) - { - // blindly grab the titleID from the disc - it's unencrypted at: - // offset 0x0F8001DC and 0x0F80044C - DVDInterface::GetVolume().GetTitleID(&m_TitleID); - } - else - { - m_TitleID = ((u64)0x00010000 << 32) | 0xF00DBEEF; - } - - INFO_LOG(IOS_ES, "Set default title to %08x/%08x", (u32)(m_TitleID >> 32), (u32)m_TitleID); -} - void ES::DoState(PointerWrap& p) { Device::DoState(p); p.Do(m_ContentFile); - OpenInternal(); p.Do(m_AccessIdentID); p.Do(m_TitleIDs); + m_title_context.DoState(p); m_addtitle_tmd.DoState(p); p.Do(m_addtitle_content_id); @@ -197,8 +218,6 @@ void ES::DoState(PointerWrap& p) ReturnCode ES::Open(const OpenRequest& request) { - OpenInternal(); - if (m_is_active) INFO_LOG(IOS_ES, "Device was re-opened."); return Device::Open(request); @@ -206,9 +225,8 @@ ReturnCode ES::Open(const OpenRequest& request) void ES::Close() { + // XXX: does IOS really clear the content access map here? m_ContentAccessMap.clear(); - m_TitleIDs.clear(); - m_TitleID = -1; m_AccessIdentID = 0; INFO_LOG(IOS_ES, "ES: Close"); @@ -628,7 +646,10 @@ IPCCommandResult ES::OpenContent(const IOCtlVRequest& request) return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); u32 Index = Memory::Read_U32(request.in_vectors[0].address); - s32 CFD = OpenTitleContent(m_AccessIdentID++, m_TitleID, Index); + if (!m_title_context.active) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + + s32 CFD = OpenTitleContent(m_AccessIdentID++, m_title_context.tmd.GetTitleId(), Index); INFO_LOG(IOS_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD); return GetDefaultReply(CFD); @@ -771,8 +792,13 @@ IPCCommandResult ES::GetTitleID(const IOCtlVRequest& request) if (!request.HasNumberOfValidVectors(0, 1)) return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - Memory::Write_U64(m_TitleID, request.io_vectors[0].address); - INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", (u32)(m_TitleID >> 32), (u32)m_TitleID); + if (!m_title_context.active) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + + const u64 title_id = m_title_context.tmd.GetTitleId(); + Memory::Write_U64(title_id, request.io_vectors[0].address); + INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", static_cast(title_id >> 32), + static_cast(title_id)); return GetDefaultReply(IPC_SUCCESS); } @@ -1327,8 +1353,12 @@ 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 (!m_title_context.active) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + const EcWii& ec = EcWii::GetInstance(); - MakeAPSigAndCert(sig_out, ap_cert_out, m_TitleID, data, data_size, ec.GetNGPriv(), ec.GetNGID()); + MakeAPSigAndCert(sig_out, ap_cert_out, m_title_context.tmd.GetTitleId(), data, data_size, + ec.GetNGPriv(), ec.GetNGID()); return GetDefaultReply(IPC_SUCCESS); } @@ -1373,28 +1403,27 @@ const DiscIO::CNANDContentLoader& ES::AccessContentDevice(u64 title_id) const // the WAD // need not be installed in the NAND, but it could be opened directly from a WAD file anywhere on // disk. - if (m_TitleID == title_id && !m_ContentFile.empty()) + if (m_title_context.active && m_title_context.tmd.GetTitleId() == title_id && + !m_ContentFile.empty()) return DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile); return DiscIO::CNANDContentManager::Access().GetNANDLoader(title_id, Common::FROM_SESSION_ROOT); } -u32 ES::ES_DIVerify(const IOS::ES::TMDReader& tmd) +s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket) { - if (!tmd.IsValid()) - return -1; + m_title_context.Clear(); - u64 title_id = 0xDEADBEEFDEADBEEFull; - u64 tmd_title_id = tmd.GetTitleId(); + if (!tmd.IsValid() || !ticket.IsValid()) + return ES_PARAMETER_SIZE_OR_ALIGNMENT; - DVDInterface::GetVolume().GetTitleID(&title_id); - if (title_id != tmd_title_id) - return -1; + if (tmd.GetTitleId() != ticket.GetTitleId()) + return ES_PARAMETER_SIZE_OR_ALIGNMENT; - std::string tmd_path = Common::GetTMDFileName(tmd_title_id, Common::FROM_SESSION_ROOT); + std::string tmd_path = Common::GetTMDFileName(tmd.GetTitleId(), Common::FROM_SESSION_ROOT); File::CreateFullPath(tmd_path); - File::CreateFullPath(Common::GetTitleDataPath(tmd_title_id, Common::FROM_SESSION_ROOT)); + File::CreateFullPath(Common::GetTitleDataPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT)); if (!File::Exists(tmd_path)) { @@ -1404,11 +1433,13 @@ u32 ES::ES_DIVerify(const IOS::ES::TMDReader& tmd) ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND."); } DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; - uid_sys.AddTitle(tmd_title_id); + uid_sys.AddTitle(tmd.GetTitleId()); // 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::CNANDContentManager::Access().ClearCache(); - return 0; + + m_title_context.Update(tmd, ticket); + return IPC_SUCCESS; } } // namespace Device } // namespace HLE diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 6521eaa360..120effcd8c 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -40,15 +40,13 @@ public: // Internal implementation of the ES_DECRYPT ioctlv. void DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output); - void OpenInternal(); - void DoState(PointerWrap& p) override; ReturnCode Open(const OpenRequest& request) override; void Close() override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - static u32 ES_DIVerify(const IOS::ES::TMDReader& tmd); + static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket); // This should only be cleared on power reset static std::string m_ContentFile; @@ -211,9 +209,21 @@ private: ContentAccessMap m_ContentAccessMap; std::vector m_TitleIDs; - u64 m_TitleID = -1; u32 m_AccessIdentID = 0; + // Shared across all ES instances. + static struct TitleContext + { + void Clear(); + void DoState(PointerWrap& p); + void Update(const DiscIO::CNANDContentLoader& content_loader); + void Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_); + + IOS::ES::TicketReader ticket; + IOS::ES::TMDReader tmd; + bool active = false; + } m_title_context; + // For title installation (ioctls IOCTL_ES_ADDTITLE*). IOS::ES::TMDReader m_addtitle_tmd; u32 m_addtitle_content_id = 0xFFFFFFFF; diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 7e94798305..9efc47c846 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -212,6 +212,11 @@ bool TicketReader::IsValid() const return true; } +void TicketReader::DoState(PointerWrap& p) +{ + p.Do(m_bytes); +} + u32 TicketReader::GetNumberOfTickets() const { return static_cast(m_bytes.size() / (GetOffset() + sizeof(Ticket))); diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 268506bec3..1dea544cc8 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -160,6 +160,7 @@ public: void SetBytes(std::vector&& bytes); bool IsValid() const; + void DoState(PointerWrap& p); const std::vector& GetRawTicket() const; u32 GetNumberOfTickets() const; diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 1ebedf5d3d..a8295db851 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -752,13 +752,13 @@ bool BootstrapPPC(const DiscIO::CNANDContentLoader& content_loader) void SetDefaultContentFile(const std::string& file_name) { std::lock_guard lock(s_device_map_mutex); - for (const auto& es : s_es_handles) - es->LoadWAD(file_name); + s_es_handles[0]->LoadWAD(file_name); } -void ES_DIVerify(const ES::TMDReader& tmd) +// XXX: also pass certificate chains? +void ES_DIVerify(const ES::TMDReader& tmd, const ES::TicketReader& ticket) { - Device::ES::ES_DIVerify(tmd); + Device::ES::DIVerify(tmd, ticket); } void SDIO_EventNotify() diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h index 39acad15fa..ae2c7470d9 100644 --- a/Source/Core/Core/IOS/IPC.h +++ b/Source/Core/Core/IOS/IPC.h @@ -24,6 +24,7 @@ namespace IOS namespace ES { class TMDReader; +class TicketReader; } namespace HLE @@ -75,7 +76,7 @@ void DoState(PointerWrap& p); // Set default content file void SetDefaultContentFile(const std::string& file_name); -void ES_DIVerify(const ES::TMDReader& tmd); +void ES_DIVerify(const ES::TMDReader& tmd, const ES::TicketReader& ticket); void SDIO_EventNotify(); diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index f8ae519dc3..209afb1865 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -71,7 +71,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 = 78; // Last changed in PR 49XX +static const u32 STATE_VERSION = 79; // Last changed in PR 4981 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 1190565004..f3f530e5e4 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -37,6 +37,7 @@ public: } virtual bool GetTitleID(u64*) const { return false; } + virtual IOS::ES::TicketReader GetTicket() const { return {}; } virtual IOS::ES::TMDReader GetTMD() const { return {}; } virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual std::string GetGameID() const = 0; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index ef291fa237..71e50252db 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -114,6 +114,13 @@ bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const return true; } +IOS::ES::TicketReader CVolumeWiiCrypted::GetTicket() const +{ + std::vector buffer(0x2a4); + Read(m_VolumeOffset, buffer.size(), buffer.data(), false); + return IOS::ES::TicketReader{std::move(buffer)}; +} + IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const { u32 tmd_size; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index dfeb74ad66..d7e7c4bddb 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -33,6 +33,7 @@ public: ~CVolumeWiiCrypted(); bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool GetTitleID(u64* buffer) const override; + IOS::ES::TicketReader GetTicket() const override; IOS::ES::TMDReader GetTMD() const override; u64 PartitionOffsetToRawOffset(u64 offset) const override; std::string GetGameID() const override;