IOS/ES: Keep track of the active title properly
This changes ES to keep track of the active title properly, just like IOS: * It is NOT changed on resource manager open/close. * It is reset on IOS reload. * It is changed by ES_DIVerify and ES_Launch. IOS stores the active title in a structure like this: struct ESTitleContext { Ticket* ticket; TMD* tmd; u32 active; }; With this commit, we also do keep the Ticket and TMD around. This makes some of the DI ioctlvs (which return data about the current active title) trivial to implement in the future. This fixes the System Menu not being able to see update partitions and also allows us to change Dolphin's active game info in the future.
This commit is contained in:
parent
78e7f4aae3
commit
4d776ffa8f
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -109,7 +109,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request)
|
|||
const ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD();
|
||||
const std::vector<u8> 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;
|
||||
|
|
|
@ -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<u32>(title_id >> 32),
|
||||
static_cast<u32>(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
|
||||
|
|
|
@ -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<u64> 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;
|
||||
|
|
|
@ -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<u32>(m_bytes.size() / (GetOffset() + sizeof(Ticket)));
|
||||
|
|
|
@ -160,6 +160,7 @@ public:
|
|||
void SetBytes(std::vector<u8>&& bytes);
|
||||
|
||||
bool IsValid() const;
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
const std::vector<u8>& GetRawTicket() const;
|
||||
u32 GetNumberOfTickets() const;
|
||||
|
|
|
@ -752,13 +752,13 @@ bool BootstrapPPC(const DiscIO::CNANDContentLoader& content_loader)
|
|||
void SetDefaultContentFile(const std::string& file_name)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -114,6 +114,13 @@ bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const
|
|||
return true;
|
||||
}
|
||||
|
||||
IOS::ES::TicketReader CVolumeWiiCrypted::GetTicket() const
|
||||
{
|
||||
std::vector<u8> 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue