// Copyright (C) 2003 Dolphin Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ #include "NANDContentLoader.h" #include #include #include "Crypto/aes.h" #include "MathUtil.h" #include "FileUtil.h" #include "Log.h" #include "WiiWad.h" #include "StringUtil.h" namespace DiscIO { CSharedContent CSharedContent::m_Instance; cUIDsys cUIDsys::m_Instance; CSharedContent::CSharedContent() { UpdateLocation(); } void CSharedContent::UpdateLocation() { m_Elements.clear(); lastID = 0; sprintf(contentMap, "%sshared1/content.map", File::GetUserPath(D_WIIUSER_IDX).c_str()); File::IOFile pFile(contentMap, "rb"); SElement Element; while (pFile.ReadArray(&Element, 1)) { m_Elements.push_back(Element); lastID++; } } CSharedContent::~CSharedContent() {} std::string CSharedContent::GetFilenameFromSHA1(u8* _pHash) { for (size_t i=0; i& GetContent() const { return m_Content; } u16 GetTitleVersion() const {return m_TitleVersion;} u16 GetNumEntries() const {return m_numEntries;} DiscIO::IVolume::ECountry GetCountry() const; u8 GetCountryChar() const {return m_Country; } private: bool m_Valid; bool m_isWAD; std::string m_Path; u64 m_TitleID; u16 m_IosVersion; u32 m_BootIndex; u16 m_numEntries; u16 m_TitleVersion; u8 m_TMDView[TMD_VIEW_SIZE]; u8 m_TMDHeader[TMD_HEADER_SIZE]; u32 m_TIKSize; u8 *m_TIK; u8 m_Country; std::vector m_Content; bool Initialize(const std::string& _rName); void AESDecode(u8* _pKey, u8* _IV, u8* _pSrc, u32 _Size, u8* _pDest); void GetKeyFromTicket(u8* pTicket, u8* pTicketKey); }; CNANDContentLoader::CNANDContentLoader(const std::string& _rName) : m_Valid(false) , m_isWAD(false) , m_TitleID(-1) , m_IosVersion(0x09) , m_BootIndex(-1) , m_TIKSize(0) , m_TIK(NULL) { m_Valid = Initialize(_rName); } CNANDContentLoader::~CNANDContentLoader() { for (size_t i=0; isecond; ++itr; } m_Map.clear(); } const INANDContentLoader& CNANDContentManager::GetNANDLoader(const std::string& _rName, bool forceReload) { CNANDContentMap::iterator lb = m_Map.lower_bound(_rName); if(lb == m_Map.end() || (m_Map.key_comp()(_rName, lb->first))) { m_Map.insert(lb, CNANDContentMap::value_type(_rName, new CNANDContentLoader(_rName))); } else { if (!lb->second->IsValid() || forceReload) { delete lb->second; lb->second = new CNANDContentLoader(_rName); } } return *m_Map[_rName]; } const INANDContentLoader& CNANDContentManager::GetNANDLoader(u64 _titleId, bool forceReload) { std::string _rName = Common::GetTitleContentPath(_titleId); return GetNANDLoader(_rName, forceReload); } bool CNANDContentManager::RemoveTitle(u64 _titleID) { if (!GetNANDLoader(_titleID).IsValid()) return false; GetNANDLoader(_titleID).RemoveTitle(); return GetNANDLoader(_titleID, true).IsValid(); } void CNANDContentLoader::RemoveTitle() const { INFO_LOG(DISCIO, "RemoveTitle %08x/%08x", (u32)(m_TitleID >> 32), (u32)m_TitleID); if(IsValid()) { // remove tmd? for (u32 i = 0; i < m_numEntries; i++) { char szFilename[1024]; if (!(m_Content[i].m_Type & 0x8000)) // skip shared apps { sprintf(szFilename, "%s%08x.app", Common::GetTitleContentPath(m_TitleID).c_str(), m_Content[i].m_ContentID); INFO_LOG(DISCIO, "Delete %s", szFilename); File::Delete(szFilename); } } } } cUIDsys::cUIDsys() { UpdateLocation(); } void cUIDsys::UpdateLocation() { m_Elements.clear(); lastUID = 0x00001000; sprintf(uidSys, "%ssys/uid.sys", File::GetUserPath(D_WIIUSER_IDX).c_str()); File::IOFile pFile(uidSys, "rb"); SElement Element; while (pFile.ReadArray(&Element, 1)) { *(u32*)&(Element.UID) = Common::swap32(lastUID++); m_Elements.push_back(Element); } pFile.Close(); if (m_Elements.empty()) { *(u64*)&(Element.titleID) = Common::swap64(TITLEID_SYSMENU); *(u32*)&(Element.UID) = Common::swap32(lastUID++); File::CreateFullPath(uidSys); pFile.Open(uidSys, "wb"); if (!pFile.WriteArray(&Element, 1)) ERROR_LOG(DISCIO, "Failed to write to %s", uidSys); } } cUIDsys::~cUIDsys() {} u32 cUIDsys::GetUIDFromTitle(u64 _Title) { for (size_t i=0; i> 32), (u32)_TitleID); return; } SElement Element; *(u64*)&(Element.titleID) = Common::swap64(_TitleID); *(u32*)&(Element.UID) = Common::swap32(lastUID++); m_Elements.push_back(Element); File::CreateFullPath(uidSys); File::IOFile pFile(uidSys, "ab"); if (!pFile.WriteArray(&Element, 1)) ERROR_LOG(DISCIO, "fwrite failed"); } void cUIDsys::GetTitleIDs(std::vector& _TitleIDs, bool _owned) { for (size_t i = 0; i < m_Elements.size(); i++) { if ((_owned && Common::CheckTitleTIK(Common::swap64(m_Elements[i].titleID))) || (!_owned && Common::CheckTitleTMD(Common::swap64(m_Elements[i].titleID)))) _TitleIDs.push_back(Common::swap64(m_Elements[i].titleID)); } } u64 CNANDContentManager::Install_WiiWAD(std::string &fileName) { if (fileName.find(".wad") == std::string::npos) return 0; const INANDContentLoader& ContentLoader = GetNANDLoader(fileName); if (ContentLoader.IsValid() == false) return 0; u64 TitleID = ContentLoader.GetTitleID(); //copy WAD's tmd header and contents to content directory std::string ContentPath(Common::GetTitleContentPath(TitleID)); std::string TMDFileName(Common::GetTMDFileName(TitleID)); File::CreateFullPath(TMDFileName); File::IOFile pTMDFile(TMDFileName, "wb"); if (!pTMDFile) { PanicAlertT("WAD installation failed: error creating %s", TMDFileName.c_str()); return 0; } pTMDFile.WriteBytes(ContentLoader.GetTMDHeader(), INANDContentLoader::TMD_HEADER_SIZE); for (u32 i = 0; i < ContentLoader.GetContentSize(); i++) { SNANDContent Content = ContentLoader.GetContent()[i]; pTMDFile.WriteBytes(Content.m_Header, INANDContentLoader::CONTENT_HEADER_SIZE); char APPFileName[1024]; if (Content.m_Type & 0x8000) //shared { sprintf(APPFileName, "%s", CSharedContent::AccessInstance().AddSharedContent(Content.m_SHA1Hash).c_str()); } else { sprintf(APPFileName, "%s%08x.app", ContentPath.c_str(), Content.m_ContentID); } if (!File::Exists(APPFileName)) { File::CreateFullPath(APPFileName); File::IOFile pAPPFile(APPFileName, "wb"); if (!pAPPFile) { PanicAlertT("WAD installation failed: error creating %s", APPFileName); return 0; } pAPPFile.WriteBytes(Content.m_pData, Content.m_Size); } else { INFO_LOG(DISCIO, "Content %s already exists.", APPFileName); } } pTMDFile.Close(); //Extract and copy WAD's ticket to ticket directory if (!Add_Ticket(TitleID, ContentLoader.GetTIK(), ContentLoader.GetTIKSize())) { PanicAlertT("WAD installation failed: error creating ticket"); return 0; } cUIDsys::AccessInstance().AddTitle(TitleID); return TitleID; } bool Add_Ticket(u64 TitleID, const u8 *p_tik, u32 tikSize) { std::string TicketFileName = Common::GetTicketFileName(TitleID); File::CreateFullPath(TicketFileName); File::IOFile pTicketFile(TicketFileName, "wb"); if (!pTicketFile || !p_tik) { //PanicAlertT("WAD installation failed: error creating %s", TicketFileName.c_str()); return false; } return pTicketFile.WriteBytes(p_tik, tikSize); } } // namespace end