Fix loading DLC using IOCTL_ES_OPENTITLECONTENT & /dev/es state save.

(Intertwined enough that's it's easier to do in one patch.)

(1) /dev/es did not support state save, which could cause crashes and
    incorrect behavior after loading.

(2) NANDContentLoader tried to read all of a title's contents into
    memory when it was first opened.  Two issues:

- If any contents were missing, it bailed out.  However, with DLC,
  only some of the contents may be downloaded, as determined by the
  permission bits in the ticket.  Instead, return an appropriate error
  when a content is accessed that doesn't exist on the filesystem
  (don't bother checking the permission bits though).

- Everything was loaded into memory - even if it consisted of 3 GB of
  songs, which caused Dolphin to lag out for quite a while (and would
  fail on 32-bit).  Instead, open content on demand.
This commit is contained in:
comex 2013-08-29 18:25:12 -04:00
parent 4d6d4a97e4
commit 04c41c1d38
5 changed files with 143 additions and 65 deletions

View File

@ -143,7 +143,7 @@ bool ReadFileToString(bool text_file, const char *filename, std::string &str);
// simple wrapper for cstdlib file functions to // simple wrapper for cstdlib file functions to
// hopefully will make error checking easier // hopefully will make error checking easier
// and make forgetting an fclose() harder // and make forgetting an fclose() harder
class IOFile class IOFile : public NonCopyable
{ {
public: public:
IOFile(); IOFile();
@ -209,10 +209,6 @@ public:
// clear error state // clear error state
void Clear() { m_good = true; std::clearerr(m_file); } void Clear() { m_good = true; std::clearerr(m_file); }
private:
IOFile(const IOFile&) /*= delete*/;
IOFile& operator=(const IOFile&) /*= delete*/;
std::FILE* m_file; std::FILE* m_file;
bool m_good; bool m_good;
}; };

View File

@ -60,7 +60,7 @@ CWII_IPC_HLE_Device_es::CWII_IPC_HLE_Device_es(u32 _DeviceID, const std::string&
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
, m_pContentLoader(NULL) , m_pContentLoader(NULL)
, m_TitleID(-1) , m_TitleID(-1)
, AccessIdentID(0x6000000) , m_AccessIdentID(0x6000000)
{ {
} }
@ -91,7 +91,7 @@ void CWII_IPC_HLE_Device_es::LoadWAD(const std::string& _rContentFile)
m_ContentFile = _rContentFile; m_ContentFile = _rContentFile;
} }
bool CWII_IPC_HLE_Device_es::Open(u32 _CommandAddress, u32 _Mode) void CWII_IPC_HLE_Device_es::OpenInternal()
{ {
m_pContentLoader = &DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile); m_pContentLoader = &DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile);
@ -119,6 +119,57 @@ bool CWII_IPC_HLE_Device_es::Open(u32 _CommandAddress, u32 _Mode)
} }
INFO_LOG(WII_IPC_ES, "Set default title to %08x/%08x", (u32)(m_TitleID>>32), (u32)m_TitleID); INFO_LOG(WII_IPC_ES, "Set default title to %08x/%08x", (u32)(m_TitleID>>32), (u32)m_TitleID);
}
void CWII_IPC_HLE_Device_es::DoState(PointerWrap& p)
{
IWII_IPC_HLE_Device::DoState(p);
p.Do(m_ContentFile);
OpenInternal();
p.Do(m_AccessIdentID);
p.Do(m_TitleIDs);
u32 Count = m_ContentAccessMap.size();
p.Do(Count);
u32 CFD, Position;
u64 TitleID;
u16 Index;
if (p.GetMode() == PointerWrap::MODE_READ)
{
for (u32 i = 0; i < Count; i++)
{
p.Do(CFD);
p.Do(Position);
p.Do(TitleID);
p.Do(Index);
CFD = OpenTitleContent(CFD, TitleID, Index);
if (CFD != 0xffffffff)
{
m_ContentAccessMap[CFD].m_Position = Position;
}
}
}
else
{
for (auto itr = m_ContentAccessMap.begin(); itr != m_ContentAccessMap.end(); ++itr)
{
CFD = itr->first;
SContentAccess& Access = itr->second;
Position = Access.m_Position;
TitleID = Access.m_TitleID;
Index = Access.m_pContent->m_Index;
p.Do(CFD);
p.Do(Position);
p.Do(TitleID);
p.Do(Index);
}
}
}
bool CWII_IPC_HLE_Device_es::Open(u32 _CommandAddress, u32 _Mode)
{
OpenInternal();
Memory::Write_U32(GetDeviceID(), _CommandAddress+4); Memory::Write_U32(GetDeviceID(), _CommandAddress+4);
if (m_Active) if (m_Active)
@ -135,7 +186,7 @@ bool CWII_IPC_HLE_Device_es::Close(u32 _CommandAddress, bool _bForce)
m_pContentLoader = NULL; m_pContentLoader = NULL;
m_TitleIDs.clear(); m_TitleIDs.clear();
m_TitleID = -1; m_TitleID = -1;
AccessIdentID = 0x6000000; m_AccessIdentID = 0x6000000;
INFO_LOG(WII_IPC_ES, "ES: Close"); INFO_LOG(WII_IPC_ES, "ES: Close");
if (!_bForce) if (!_bForce)
@ -144,6 +195,37 @@ bool CWII_IPC_HLE_Device_es::Close(u32 _CommandAddress, bool _bForce)
return true; return true;
} }
u32 CWII_IPC_HLE_Device_es::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
{
const DiscIO::SNANDContent* pContent = AccessContentDevice(TitleID).GetContentByIndex(Index);
if (pContent == NULL)
{
return 0xffffffff; //TODO: what is the correct error value here?
}
SContentAccess Access;
Access.m_Position = 0;
Access.m_pContent = pContent;
Access.m_TitleID = TitleID;
if (!pContent->m_pData)
{
std::string Filename = pContent->m_Filename;
INFO_LOG(WII_IPC_ES, "ES: load %s", Filename.c_str());
Access.m_File.Open(Filename, "rb");
if (!Access.m_File.IsGood())
{
WARN_LOG(WII_IPC_ES, "ES: couldn't load %s", Filename.c_str());
return 0xffffffff;
}
}
m_ContentAccessMap[CFD] = std::move(Access);
return CFD;
}
bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress) bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
{ {
SIOCtlVBuffer Buffer(_CommandAddress); SIOCtlVBuffer Buffer(_CommandAddress);
@ -242,16 +324,11 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address); u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
u32 Index = Memory::Read_U32(Buffer.InBuffer[2].m_Address); u32 Index = Memory::Read_U32(Buffer.InBuffer[2].m_Address);
u32 CFD = AccessIdentID++; u32 CFD = OpenTitleContent(m_AccessIdentID++, TitleID, Index);
m_ContentAccessMap[CFD].m_Position = 0;
m_ContentAccessMap[CFD].m_pContent = AccessContentDevice(TitleID).GetContentByIndex(Index);
_dbg_assert_msg_(WII_IPC_ES, m_ContentAccessMap[CFD].m_pContent != NULL, "No Content for TitleID: %08x/%08x at Index %x", (u32)(TitleID>>32), (u32)TitleID, Index);
// Fix for DLC by itsnotmailmail
if (m_ContentAccessMap[CFD].m_pContent == NULL)
CFD = 0xffffffff; //TODO: what is the correct error value here?
Memory::Write_U32(CFD, _CommandAddress + 0x4); Memory::Write_U32(CFD, _CommandAddress + 0x4);
INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %08x/%08x Index %i -> got CFD %x", (u32)(TitleID>>32), (u32)TitleID, Index, CFD); INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %08x/%08x Index %i -> got CFD %x", (u32)(TitleID>>32), (u32)TitleID, Index, CFD);
return true; return true;
} }
break; break;
@ -260,19 +337,12 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
{ {
_dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1); _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1);
_dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0); _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0);
u32 CFD = AccessIdentID++;
u32 Index = Memory::Read_U32(Buffer.InBuffer[0].m_Address); u32 Index = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
m_ContentAccessMap[CFD].m_Position = 0; u32 CFD = OpenTitleContent(m_AccessIdentID++, m_TitleID, Index);
m_ContentAccessMap[CFD].m_pContent = AccessContentDevice(m_TitleID).GetContentByIndex(Index);
if (m_ContentAccessMap[CFD].m_pContent == NULL)
CFD = 0xffffffff; //TODO: what is the correct error value here?
Memory::Write_U32(CFD, _CommandAddress + 0x4); Memory::Write_U32(CFD, _CommandAddress + 0x4);
INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD); INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD);
return true; return true;
} }
break; break;
@ -286,12 +356,16 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
u32 Size = Buffer.PayloadBuffer[0].m_Size; u32 Size = Buffer.PayloadBuffer[0].m_Size;
u32 Addr = Buffer.PayloadBuffer[0].m_Address; u32 Addr = Buffer.PayloadBuffer[0].m_Address;
_dbg_assert_(WII_IPC_ES, m_ContentAccessMap.find(CFD) != m_ContentAccessMap.end()); auto itr = m_ContentAccessMap.find(CFD);
SContentAccess& rContent = m_ContentAccessMap[CFD]; if (itr == m_ContentAccessMap.end())
{
Memory::Write_U32(-1, _CommandAddress + 0x4);
return true;
}
SContentAccess& rContent = itr->second;
_dbg_assert_(WII_IPC_ES, rContent.m_pContent->m_pData != NULL); _dbg_assert_(WII_IPC_ES, rContent.m_pContent->m_pData != NULL);
u8* pSrc = &rContent.m_pContent->m_pData[rContent.m_Position];
u8* pDest = Memory::GetPointer(Addr); u8* pDest = Memory::GetPointer(Addr);
if (rContent.m_Position + Size > rContent.m_pContent->m_Size) if (rContent.m_Position + Size > rContent.m_pContent->m_Size)
@ -302,7 +376,17 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
if (Size > 0) if (Size > 0)
{ {
if (pDest) { if (pDest) {
if (rContent.m_pContent->m_pData)
{
u8* pSrc = &rContent.m_pContent->m_pData[rContent.m_Position];
memcpy(pDest, pSrc, Size); memcpy(pDest, pSrc, Size);
}
else
{
File::IOFile* pFile = &rContent.m_File;
pFile->Seek(rContent.m_Position, SEEK_SET);
pFile->ReadBytes(pDest, Size);
}
rContent.m_Position += Size; rContent.m_Position += Size;
} else { } else {
PanicAlertT("IOCTL_ES_READCONTENT - bad destination"); PanicAlertT("IOCTL_ES_READCONTENT - bad destination");
@ -323,8 +407,7 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
u32 CFD = Memory::Read_U32(Buffer.InBuffer[0].m_Address); u32 CFD = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
CContentAccessMap::iterator itr = m_ContentAccessMap.find(CFD); m_ContentAccessMap.erase(CFD);
m_ContentAccessMap.erase(itr);
INFO_LOG(WII_IPC_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD); INFO_LOG(WII_IPC_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD);
@ -342,8 +425,13 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
u32 Addr = Memory::Read_U32(Buffer.InBuffer[1].m_Address); u32 Addr = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
u32 Mode = Memory::Read_U32(Buffer.InBuffer[2].m_Address); u32 Mode = Memory::Read_U32(Buffer.InBuffer[2].m_Address);
_dbg_assert_(WII_IPC_ES, m_ContentAccessMap.find(CFD) != m_ContentAccessMap.end()); auto itr = m_ContentAccessMap.find(CFD);
SContentAccess& rContent = m_ContentAccessMap[CFD]; if (itr == m_ContentAccessMap.end())
{
Memory::Write_U32(-1, _CommandAddress + 0x4);
return true;
}
SContentAccess& rContent = itr->second;
switch (Mode) switch (Mode)
{ {
@ -908,7 +996,7 @@ bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
const DiscIO::INANDContentLoader& CWII_IPC_HLE_Device_es::AccessContentDevice(u64 _TitleID) const DiscIO::INANDContentLoader& CWII_IPC_HLE_Device_es::AccessContentDevice(u64 _TitleID)
{ {
if (m_pContentLoader->IsValid() && m_pContentLoader->GetTitleID() == _TitleID) if (m_pContentLoader->IsValid() && m_pContentLoader->GetTitleID() == _TitleID)
return* m_pContentLoader; return *m_pContentLoader;
CTitleToContentMap::iterator itr = m_NANDContent.find(_TitleID); CTitleToContentMap::iterator itr = m_NANDContent.find(_TitleID);
if (itr != m_NANDContent.end()) if (itr != m_NANDContent.end())

View File

@ -19,6 +19,10 @@ public:
void LoadWAD(const std::string& _rContentFile); void LoadWAD(const std::string& _rContentFile);
void OpenInternal();
virtual void DoState(PointerWrap& p);
virtual bool Open(u32 _CommandAddress, u32 _Mode); virtual bool Open(u32 _CommandAddress, u32 _Mode);
virtual bool Close(u32 _CommandAddress, bool _bForce); virtual bool Close(u32 _CommandAddress, bool _bForce);
@ -108,10 +112,12 @@ private:
ES_HASH_SIZE_WRONG = -2014, // HASH !=20 ES_HASH_SIZE_WRONG = -2014, // HASH !=20
}; };
struct SContentAccess struct SContentAccess : public NonCopyable
{ {
u32 m_Position; u32 m_Position;
u64 m_TitleID;
const DiscIO::SNANDContent* m_pContent; const DiscIO::SNANDContent* m_pContent;
File::IOFile m_File;
}; };
typedef std::map<u32, SContentAccess> CContentAccessMap; typedef std::map<u32, SContentAccess> CContentAccessMap;
@ -124,13 +130,14 @@ private:
std::vector<u64> m_TitleIDs; std::vector<u64> m_TitleIDs;
u64 m_TitleID; u64 m_TitleID;
u32 AccessIdentID; u32 m_AccessIdentID;
static u8 *keyTable[11]; static u8 *keyTable[11];
u64 GetCurrentTitleID() const; u64 GetCurrentTitleID() const;
const DiscIO::INANDContentLoader& AccessContentDevice(u64 _TitleID); const DiscIO::INANDContentLoader& AccessContentDevice(u64 _TitleID);
u32 OpenTitleContent(u32 CFD, u64 TitleID, u16 Index);
bool IsValid(u64 _TitleID) const; bool IsValid(u64 _TitleID) const;

View File

@ -42,7 +42,7 @@ void CSharedContent::UpdateLocation()
CSharedContent::~CSharedContent() CSharedContent::~CSharedContent()
{} {}
std::string CSharedContent::GetFilenameFromSHA1(u8* _pHash) std::string CSharedContent::GetFilenameFromSHA1(const u8* _pHash)
{ {
for (size_t i=0; i<m_Elements.size(); i++) for (size_t i=0; i<m_Elements.size(); i++)
{ {
@ -58,7 +58,7 @@ std::string CSharedContent::GetFilenameFromSHA1(u8* _pHash)
return "unk"; return "unk";
} }
std::string CSharedContent::AddSharedContent(u8* _pHash) std::string CSharedContent::AddSharedContent(const u8* _pHash)
{ {
std::string szFilename = GetFilenameFromSHA1(_pHash); std::string szFilename = GetFilenameFromSHA1(_pHash);
if (strcasecmp(szFilename.c_str(), "unk") == 0) if (strcasecmp(szFilename.c_str(), "unk") == 0)
@ -170,8 +170,11 @@ const SNANDContent* CNANDContentLoader::GetContentByIndex(int _Index) const
{ {
for (size_t i=0; i<m_Content.size(); i++) for (size_t i=0; i<m_Content.size(); i++)
{ {
if (m_Content[i].m_Index == _Index) const SNANDContent* pContent = &m_Content[i];
return &m_Content[i]; if (pContent->m_Index == _Index)
{
return pContent;
}
} }
return NULL; return NULL;
} }
@ -262,36 +265,18 @@ bool CNANDContentLoader::Initialize(const std::string& _rName)
} }
rContent.m_pData = NULL; rContent.m_pData = NULL;
char szFilename[1024];
if (rContent.m_Type & 0x8000) // shared app if (rContent.m_Type & 0x8000) // shared app
{ {
std::string Filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(rContent.m_SHA1Hash); rContent.m_Filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(rContent.m_SHA1Hash);
strcpy(szFilename, Filename.c_str());
} }
else else
{ {
sprintf(szFilename, "%s/%08x.app", m_Path.c_str(), rContent.m_ContentID); rContent.m_Filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), rContent.m_ContentID);
} }
INFO_LOG(DISCIO, "NANDContentLoader: load %s", szFilename); // Be graceful about incorrect tmds.
rContent.m_Size = (u32) File::GetSize(rContent.m_Filename);
File::IOFile pFile(szFilename, "rb");
if (pFile)
{
const u64 ContentSize = File::GetSize(szFilename);
rContent.m_pData = new u8[(u32)ContentSize];
_dbg_assert_msg_(BOOT, rContent.m_Size==ContentSize, "TMDLoader: Incorrect filesize (%s %i). Your NAND dump may be corrupt.", szFilename, i);
pFile.ReadBytes(rContent.m_pData, (size_t)ContentSize);
}
else
{
ERROR_LOG(DISCIO, "NANDContentLoader: error opening %s", szFilename);
delete [] pTMD;
return false;
}
} }
delete [] pTMD; delete [] pTMD;
@ -493,7 +478,7 @@ u64 CNANDContentManager::Install_WiiWAD(std::string &fileName)
for (u32 i = 0; i < ContentLoader.GetContentSize(); i++) for (u32 i = 0; i < ContentLoader.GetContentSize(); i++)
{ {
SNANDContent Content = ContentLoader.GetContent()[i]; const SNANDContent& Content = ContentLoader.GetContent()[i];
pTMDFile.WriteBytes(Content.m_Header, INANDContentLoader::CONTENT_HEADER_SIZE); pTMDFile.WriteBytes(Content.m_Header, INANDContentLoader::CONTENT_HEADER_SIZE);

View File

@ -13,6 +13,7 @@
#include "Blob.h" #include "Blob.h"
#include "Volume.h" #include "Volume.h"
#include "NandPaths.h" #include "NandPaths.h"
#include "FileUtil.h"
namespace DiscIO namespace DiscIO
{ {
@ -26,6 +27,7 @@ struct SNANDContent
u8 m_SHA1Hash[20]; u8 m_SHA1Hash[20];
u8 m_Header[36]; //all of the above u8 m_Header[36]; //all of the above
std::string m_Filename;
u8* m_pData; u8* m_pData;
}; };
@ -95,8 +97,8 @@ public:
static CSharedContent& AccessInstance() { return m_Instance; } static CSharedContent& AccessInstance() { return m_Instance; }
std::string GetFilenameFromSHA1(u8* _pHash); std::string GetFilenameFromSHA1(const u8* _pHash);
std::string AddSharedContent(u8* _pHash); std::string AddSharedContent(const u8* _pHash);
void UpdateLocation(); void UpdateLocation();
private: private: