| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2009 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2013-04-17 23:09:55 -04:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <array>
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include <cstddef>
 | 
					
						
							|  |  |  | #include <cstdio>
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							|  |  |  | #include <functional>
 | 
					
						
							|  |  |  | #include <map>
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include <mbedtls/aes.h>
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include <string>
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-27 11:56:22 +01:00
										 |  |  | #include "Common/Align.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-02 00:33:14 +04:00
										 |  |  | #include "Common/CommonFuncs.h"
 | 
					
						
							| 
									
										
										
										
											2014-09-07 20:06:58 -05:00
										 |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/FileUtil.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-26 17:13:07 -04:00
										 |  |  | #include "Common/MsgHandler.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include "Common/NandPaths.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/StringUtil.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 20:33:05 +02:00
										 |  |  | #include "DiscIO/Enums.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "DiscIO/NANDContentLoader.h"
 | 
					
						
							|  |  |  | #include "DiscIO/WiiWad.h"
 | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace DiscIO | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  | namespace | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | // Strips the signature part of a ticket, which has variable size based on
 | 
					
						
							|  |  |  | // signature type. Returns a new vector which has only the ticket structure
 | 
					
						
							|  |  |  | // itself.
 | 
					
						
							|  |  |  | std::vector<u8> SignedTicketToTicket(const std::vector<u8>& signed_ticket) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   u32 signature_type = Common::swap32(signed_ticket.data()); | 
					
						
							|  |  |  |   u32 entry_offset; | 
					
						
							|  |  |  |   if (signature_type == 0x10000)  // RSA4096
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     entry_offset = 576; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else if (signature_type == 0x10001)  // RSA2048
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     entry_offset = 320; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else if (signature_type == 0x10002)  // ECDSA
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     entry_offset = 128; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "Invalid ticket signature type: %08x", signature_type); | 
					
						
							|  |  |  |     return std::vector<u8>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::vector<u8> ticket(signed_ticket.size() - entry_offset); | 
					
						
							|  |  |  |   std::copy(signed_ticket.begin() + entry_offset, signed_ticket.end(), ticket.begin()); | 
					
						
							|  |  |  |   return ticket; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-02 05:32:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   mbedtls_aes_context aes_ctx; | 
					
						
							|  |  |  |   std::vector<u8> buffer(size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mbedtls_aes_setkey_dec(&aes_ctx, key, 128); | 
					
						
							|  |  |  |   mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return buffer; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-10 04:38:04 +00:00
										 |  |  | CNANDContentData::~CNANDContentData() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-05 07:42:40 +00:00
										 |  |  | CSharedContent::CSharedContent() | 
					
						
							| 
									
										
										
										
											2011-06-05 06:30:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   UpdateLocation(); | 
					
						
							| 
									
										
										
										
											2011-06-05 06:30:01 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-05 07:42:40 +00:00
										 |  |  | void CSharedContent::UpdateLocation() | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_Elements.clear(); | 
					
						
							|  |  |  |   m_LastID = 0; | 
					
						
							|  |  |  |   m_ContentMap = | 
					
						
							|  |  |  |       StringFromFormat("%s/shared1/content.map", File::GetUserPath(D_WIIROOT_IDX).c_str()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   File::IOFile pFile(m_ContentMap, "rb"); | 
					
						
							|  |  |  |   SElement Element; | 
					
						
							|  |  |  |   while (pFile.ReadArray(&Element, 1)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     m_Elements.push_back(Element); | 
					
						
							|  |  |  |     m_LastID++; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CSharedContent::~CSharedContent() | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | std::string CSharedContent::GetFilenameFromSHA1(const u8* hash) | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (auto& Element : m_Elements) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (memcmp(hash, Element.SHA1Hash, 20) == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return StringFromFormat( | 
					
						
							|  |  |  |           "%s/shared1/%c%c%c%c%c%c%c%c.app", File::GetUserPath(D_WIIROOT_IDX).c_str(), | 
					
						
							|  |  |  |           Element.FileName[0], Element.FileName[1], Element.FileName[2], Element.FileName[3], | 
					
						
							|  |  |  |           Element.FileName[4], Element.FileName[5], Element.FileName[6], Element.FileName[7]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return "unk"; | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | std::string CSharedContent::AddSharedContent(const u8* hash) | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::string filename = GetFilenameFromSHA1(hash); | 
					
						
							| 
									
										
										
										
											2014-06-03 01:08:54 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (strcasecmp(filename.c_str(), "unk") == 0) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     std::string id = StringFromFormat("%08x", m_LastID); | 
					
						
							|  |  |  |     SElement Element; | 
					
						
							|  |  |  |     memcpy(Element.FileName, id.c_str(), 8); | 
					
						
							|  |  |  |     memcpy(Element.SHA1Hash, hash, 20); | 
					
						
							|  |  |  |     m_Elements.push_back(Element); | 
					
						
							| 
									
										
										
										
											2010-12-15 14:47:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     File::CreateFullPath(m_ContentMap); | 
					
						
							| 
									
										
										
										
											2011-03-11 10:21:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     File::IOFile pFile(m_ContentMap, "ab"); | 
					
						
							|  |  |  |     pFile.WriteArray(&Element, 1); | 
					
						
							| 
									
										
										
										
											2011-03-11 10:21:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     filename = | 
					
						
							|  |  |  |         StringFromFormat("%s/shared1/%s.app", File::GetUserPath(D_WIIROOT_IDX).c_str(), id.c_str()); | 
					
						
							|  |  |  |     m_LastID++; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-06-03 01:08:54 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return filename; | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-09 17:09:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 20:12:16 -04:00
										 |  |  | CNANDContentDataFile::CNANDContentDataFile(const std::string& filename) : m_filename{filename} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CNANDContentDataFile::~CNANDContentDataFile() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-20 13:09:21 +01:00
										 |  |  | void CNANDContentDataFile::EnsureOpen() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!m_file) | 
					
						
							|  |  |  |     m_file = std::make_unique<File::IOFile>(m_filename, "rb"); | 
					
						
							|  |  |  |   else if (!m_file->IsOpen()) | 
					
						
							|  |  |  |     m_file->Open(m_filename, "rb"); | 
					
						
							| 
									
										
										
										
											2016-03-20 13:09:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | void CNANDContentDataFile::Open() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   EnsureOpen(); | 
					
						
							| 
									
										
										
										
											2016-03-20 13:09:21 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-14 19:15:27 -04:00
										 |  |  | std::vector<u8> CNANDContentDataFile::Get() | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   EnsureOpen(); | 
					
						
							| 
									
										
										
										
											2016-09-14 19:41:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!m_file->IsGood()) | 
					
						
							| 
									
										
										
										
											2016-09-14 19:41:38 -04:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   u64 size = m_file->GetSize(); | 
					
						
							|  |  |  |   if (size == 0) | 
					
						
							| 
									
										
										
										
											2016-09-14 19:41:38 -04:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-14 19:41:38 -04:00
										 |  |  |   std::vector<u8> result(size); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_file->ReadBytes(result.data(), result.size()); | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return result; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CNANDContentDataFile::GetRange(u32 start, u32 size, u8* buffer) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   EnsureOpen(); | 
					
						
							|  |  |  |   if (!m_file->IsGood()) | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!m_file->Seek(start, SEEK_SET)) | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return m_file->ReadBytes(buffer, static_cast<size_t>(size)); | 
					
						
							| 
									
										
										
										
											2016-03-20 13:09:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | void CNANDContentDataFile::Close() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_file && m_file->IsOpen()) | 
					
						
							|  |  |  |     m_file->Close(); | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CNANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (start + size > m_buffer.size()) | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 21:49:26 +01:00
										 |  |  |   std::copy_n(&m_buffer[start], size, buffer); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | CNANDContentLoader::CNANDContentLoader(const std::string& content_name) | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     : m_Valid(false), m_IsWAD(false), m_TitleID(-1), m_IosVersion(0x09), m_BootIndex(-1) | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_Valid = Initialize(content_name); | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CNANDContentLoader::~CNANDContentLoader() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | const SNANDContent* CNANDContentLoader::GetContentByIndex(int index) const | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (auto& Content : m_Content) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (Content.m_Index == index) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return &Content; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return nullptr; | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | bool CNANDContentLoader::Initialize(const std::string& name) | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (name.empty()) | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_Path = name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   WiiWAD wad(name); | 
					
						
							|  |  |  |   std::vector<u8> data_app; | 
					
						
							|  |  |  |   std::vector<u8> tmd; | 
					
						
							|  |  |  |   std::vector<u8> decrypted_title_key; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (wad.IsValid()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     m_IsWAD = true; | 
					
						
							|  |  |  |     m_Ticket = wad.GetTicket(); | 
					
						
							|  |  |  |     decrypted_title_key = GetKeyFromTicket(m_Ticket); | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tmd.resize(static_cast<size_t>(File::GetSize(tmd_filename))); | 
					
						
							|  |  |  |     tmd_file.ReadBytes(tmd.data(), tmd.size()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::copy(&tmd[0], &tmd[TMD_HEADER_SIZE], m_TMDHeader); | 
					
						
							|  |  |  |   std::copy(&tmd[0x180], &tmd[0x180 + TMD_VIEW_SIZE], m_TMDView); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_TitleVersion = Common::swap16(&tmd[0x01DC]); | 
					
						
							|  |  |  |   m_NumEntries = Common::swap16(&tmd[0x01DE]); | 
					
						
							|  |  |  |   m_BootIndex = Common::swap16(&tmd[0x01E0]); | 
					
						
							|  |  |  |   m_TitleID = Common::swap64(&tmd[0x018C]); | 
					
						
							|  |  |  |   m_IosVersion = Common::swap16(&tmd[0x018A]); | 
					
						
							|  |  |  |   m_Country = static_cast<u8>(m_TitleID & 0xFF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (m_Country == 2)  // SYSMENU
 | 
					
						
							|  |  |  |     m_Country = GetSysMenuRegion(m_TitleVersion); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   InitializeContentEntries(tmd, decrypted_title_key, data_app); | 
					
						
							|  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd, | 
					
						
							|  |  |  |                                                   const std::vector<u8>& decrypted_title_key, | 
					
						
							|  |  |  |                                                   const std::vector<u8>& data_app) | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_Content.resize(m_NumEntries); | 
					
						
							| 
									
										
										
										
											2009-07-03 22:34:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::array<u8, 16> iv; | 
					
						
							|  |  |  |   u32 data_app_offset = 0; | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (u32 i = 0; i < m_NumEntries; i++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     const u32 entry_offset = 0x24 * i; | 
					
						
							| 
									
										
										
										
											2013-10-29 01:23:17 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     SNANDContent& content = m_Content[i]; | 
					
						
							|  |  |  |     content.m_ContentID = Common::swap32(&tmd[entry_offset + 0x01E4]); | 
					
						
							|  |  |  |     content.m_Index = Common::swap16(&tmd[entry_offset + 0x01E8]); | 
					
						
							|  |  |  |     content.m_Type = Common::swap16(&tmd[entry_offset + 0x01EA]); | 
					
						
							|  |  |  |     content.m_Size = static_cast<u32>(Common::swap64(&tmd[entry_offset + 0x01EC])); | 
					
						
							| 
									
										
										
										
											2016-01-02 00:33:14 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     const auto header_begin = std::next(tmd.begin(), entry_offset + 0x01E4); | 
					
						
							|  |  |  |     const auto header_end = std::next(header_begin, ArraySize(content.m_Header)); | 
					
						
							|  |  |  |     std::copy(header_begin, header_end, content.m_Header); | 
					
						
							| 
									
										
										
										
											2016-01-02 00:33:14 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     const auto hash_begin = std::next(tmd.begin(), entry_offset + 0x01F4); | 
					
						
							|  |  |  |     const auto hash_end = std::next(hash_begin, ArraySize(content.m_SHA1Hash)); | 
					
						
							|  |  |  |     std::copy(hash_begin, hash_end, content.m_SHA1Hash); | 
					
						
							| 
									
										
										
										
											2013-04-16 23:14:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     if (m_IsWAD) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-11-27 11:56:22 +01:00
										 |  |  |       u32 rounded_size = Common::AlignUp(content.m_Size, 0x40); | 
					
						
							| 
									
										
										
										
											2013-10-29 01:23:17 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       iv.fill(0); | 
					
						
							|  |  |  |       std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       content.m_Data = std::make_unique<CNANDContentDataBuffer>(AESDecode( | 
					
						
							|  |  |  |           decrypted_title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       data_app_offset += rounded_size; | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-07-03 22:34:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     std::string filename; | 
					
						
							|  |  |  |     if (content.m_Type & 0x8000)  // shared app
 | 
					
						
							|  |  |  |       filename = CSharedContent::AccessInstance().GetFilenameFromSHA1(content.m_SHA1Hash); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID); | 
					
						
							| 
									
										
										
										
											2016-03-16 20:08:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     content.m_Data = std::make_unique<CNANDContentDataFile>(filename); | 
					
						
							| 
									
										
										
										
											2009-07-03 22:34:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     // Be graceful about incorrect TMDs.
 | 
					
						
							|  |  |  |     if (File::Exists(filename)) | 
					
						
							|  |  |  |       content.m_Size = static_cast<u32>(File::GetSize(filename)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 18:41:21 +01:00
										 |  |  | DiscIO::Region CNANDContentLoader::GetRegion() const | 
					
						
							| 
									
										
										
										
											2009-03-14 19:13:21 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!IsValid()) | 
					
						
							| 
									
										
										
										
											2016-12-23 18:41:21 +01:00
										 |  |  |     return DiscIO::Region::UNKNOWN_REGION; | 
					
						
							| 
									
										
										
										
											2009-07-03 22:34:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 18:41:21 +01:00
										 |  |  |   return RegionSwitchWii(m_Country); | 
					
						
							| 
									
										
										
										
											2009-07-03 22:34:51 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-03-07 08:35:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-13 16:15:15 +00:00
										 |  |  | CNANDContentManager::~CNANDContentManager() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-21 13:19:52 -04:00
										 |  |  | const CNANDContentLoader& CNANDContentManager::GetNANDLoader(const std::string& content_path) | 
					
						
							| 
									
										
										
										
											2009-03-13 16:15:15 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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<CNANDContentLoader>(content_path))) | 
					
						
							|  |  |  |               ->second; | 
					
						
							| 
									
										
										
										
											2009-03-13 16:15:15 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | const CNANDContentLoader& CNANDContentManager::GetNANDLoader(u64 title_id, | 
					
						
							|  |  |  |                                                              Common::FromWhichRoot from) | 
					
						
							| 
									
										
										
										
											2010-09-06 04:36:58 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::string path = Common::GetTitleContentPath(title_id, from); | 
					
						
							|  |  |  |   return GetNANDLoader(path); | 
					
						
							| 
									
										
										
										
											2010-09-06 04:36:58 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-21 13:19:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool CNANDContentManager::RemoveTitle(u64 title_id, Common::FromWhichRoot from) | 
					
						
							| 
									
										
										
										
											2011-03-05 05:18:21 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   auto& loader = GetNANDLoader(title_id, from); | 
					
						
							|  |  |  |   if (!loader.IsValid()) | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   loader.RemoveTitle(); | 
					
						
							|  |  |  |   return GetNANDLoader(title_id, from).IsValid(); | 
					
						
							| 
									
										
										
										
											2015-06-21 13:19:52 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CNANDContentManager::ClearCache() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_map.clear(); | 
					
						
							| 
									
										
										
										
											2011-03-05 05:18:21 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CNANDContentLoader::RemoveTitle() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   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++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (!(m_Content[i].m_Type & 0x8000))  // skip shared apps
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         std::string filename = | 
					
						
							|  |  |  |             StringFromFormat("%s/%08x.app", m_Path.c_str(), m_Content[i].m_ContentID); | 
					
						
							|  |  |  |         INFO_LOG(DISCIO, "Delete %s", filename.c_str()); | 
					
						
							|  |  |  |         File::Delete(filename); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     CNANDContentManager::Access().ClearCache();  // deletes 'this'
 | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2011-03-05 05:18:21 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-09-06 04:36:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-05 07:42:40 +00:00
										 |  |  | cUIDsys::cUIDsys() | 
					
						
							| 
									
										
										
										
											2011-06-05 06:30:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   UpdateLocation(); | 
					
						
							| 
									
										
										
										
											2011-06-05 06:30:01 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void cUIDsys::UpdateLocation() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_Elements.clear(); | 
					
						
							|  |  |  |   m_LastUID = 0x00001000; | 
					
						
							|  |  |  |   m_UidSys = File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/sys/uid.sys"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   File::IOFile pFile(m_UidSys, "rb"); | 
					
						
							|  |  |  |   SElement Element; | 
					
						
							|  |  |  |   while (pFile.ReadArray(&Element, 1)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     *(u32*)&(Element.UID) = Common::swap32(m_LastUID++); | 
					
						
							|  |  |  |     m_Elements.push_back(Element); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   pFile.Close(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (m_Elements.empty()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     *(u64*)&(Element.titleID) = Common::swap64(TITLEID_SYSMENU); | 
					
						
							|  |  |  |     *(u32*)&(Element.UID) = Common::swap32(m_LastUID++); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     File::CreateFullPath(m_UidSys); | 
					
						
							|  |  |  |     pFile.Open(m_UidSys, "wb"); | 
					
						
							|  |  |  |     if (!pFile.WriteArray(&Element, 1)) | 
					
						
							|  |  |  |       ERROR_LOG(DISCIO, "Failed to write to %s", m_UidSys.c_str()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | cUIDsys::~cUIDsys() | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | u32 cUIDsys::GetUIDFromTitle(u64 title_id) | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (auto& Element : m_Elements) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (Common::swap64(title_id) == *(u64*)&(Element.titleID)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return Common::swap32(Element.UID); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 0; | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | void cUIDsys::AddTitle(u64 title_id) | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (GetUIDFromTitle(title_id)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     INFO_LOG(DISCIO, "Title %08x%08x, already exists in uid.sys", (u32)(title_id >> 32), | 
					
						
							|  |  |  |              (u32)title_id); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-12-15 14:47:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   SElement Element; | 
					
						
							|  |  |  |   *(u64*)&(Element.titleID) = Common::swap64(title_id); | 
					
						
							|  |  |  |   *(u32*)&(Element.UID) = Common::swap32(m_LastUID++); | 
					
						
							|  |  |  |   m_Elements.push_back(Element); | 
					
						
							| 
									
										
										
										
											2011-03-11 10:21:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   File::CreateFullPath(m_UidSys); | 
					
						
							|  |  |  |   File::IOFile pFile(m_UidSys, "ab"); | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!pFile.WriteArray(&Element, 1)) | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "fwrite failed"); | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 21:44:24 +01:00
										 |  |  | void cUIDsys::GetTitleIDs(std::vector<u64>& title_ids, bool owned) | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (auto& Element : m_Elements) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if ((owned && | 
					
						
							|  |  |  |          Common::CheckTitleTIK(Common::swap64(Element.titleID), Common::FROM_SESSION_ROOT)) || | 
					
						
							|  |  |  |         (!owned && | 
					
						
							|  |  |  |          Common::CheckTitleTMD(Common::swap64(Element.titleID), Common::FROM_SESSION_ROOT))) | 
					
						
							|  |  |  |       title_ids.push_back(Common::swap64(Element.titleID)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-05-13 04:50:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (filename.find(".wad") == std::string::npos) | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   const CNANDContentLoader& content_loader = GetNANDLoader(filename); | 
					
						
							|  |  |  |   if (content_loader.IsValid() == false) | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   u64 title_id = content_loader.GetTitleID(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // copy WAD's TMD header and contents to content directory
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::string content_path(Common::GetTitleContentPath(title_id, Common::FROM_CONFIGURED_ROOT)); | 
					
						
							|  |  |  |   std::string tmd_filename(Common::GetTMDFileName(title_id, Common::FROM_CONFIGURED_ROOT)); | 
					
						
							|  |  |  |   File::CreateFullPath(tmd_filename); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   File::IOFile tmd_file(tmd_filename, "wb"); | 
					
						
							|  |  |  |   if (!tmd_file) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlertT("WAD installation failed: error creating %s", tmd_filename.c_str()); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   tmd_file.WriteBytes(content_loader.GetTMDHeader(), CNANDContentLoader::TMD_HEADER_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (u32 i = 0; i < content_loader.GetContentSize(); i++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     const SNANDContent& content = content_loader.GetContent()[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tmd_file.WriteBytes(content.m_Header, CNANDContentLoader::CONTENT_HEADER_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::string app_filename; | 
					
						
							|  |  |  |     if (content.m_Type & 0x8000)  // shared
 | 
					
						
							|  |  |  |       app_filename = CSharedContent::AccessInstance().AddSharedContent(content.m_SHA1Hash); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_ContentID); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!File::Exists(app_filename)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       File::CreateFullPath(app_filename); | 
					
						
							|  |  |  |       File::IOFile app_file(app_filename, "wb"); | 
					
						
							|  |  |  |       if (!app_file) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         PanicAlertT("WAD installation failed: error creating %s", app_filename.c_str()); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       app_file.WriteBytes(content.m_Data->Get().data(), content.m_Size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       INFO_LOG(DISCIO, "Content %s already exists.", app_filename.c_str()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Extract and copy WAD's ticket to ticket directory
 | 
					
						
							| 
									
										
										
										
											2017-01-01 22:09:57 +01:00
										 |  |  |   if (!AddTicket(content_loader.GetTicket())) | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     PanicAlertT("WAD installation failed: error creating ticket"); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   cUIDsys::AccessInstance().AddTitle(title_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ClearCache(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return title_id; | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  | bool AddTicket(const std::vector<u8>& signed_ticket) | 
					
						
							| 
									
										
										
										
											2011-08-21 18:20:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  |   std::vector<u8> ticket = SignedTicketToTicket(signed_ticket); | 
					
						
							|  |  |  |   if (ticket.empty()) | 
					
						
							| 
									
										
										
										
											2017-01-01 22:09:57 +01:00
										 |  |  |   { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  |   u64 title_id = Common::swap64(ticket.data() + 0x9c); | 
					
						
							| 
									
										
										
										
											2017-01-01 22:09:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT); | 
					
						
							|  |  |  |   File::CreateFullPath(ticket_filename); | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   File::IOFile ticket_file(ticket_filename, "wb"); | 
					
						
							|  |  |  |   if (!ticket_file) | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2015-12-19 13:46:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 00:59:21 +01:00
										 |  |  |   return ticket_file.WriteBytes(signed_ticket.data(), signed_ticket.size()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<u8> 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 std::vector<u8>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::vector<u8> signed_ticket(ticket_file.GetSize()); | 
					
						
							|  |  |  |   if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return std::vector<u8>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return signed_ticket; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<u8> FindTicket(u64 title_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   std::vector<u8> signed_ticket = FindSignedTicket(title_id); | 
					
						
							|  |  |  |   if (signed_ticket.empty()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return std::vector<u8>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SignedTicketToTicket(signed_ticket); | 
					
						
							| 
									
										
										
										
											2011-08-21 18:20:22 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-05-09 05:47:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 05:32:08 +01:00
										 |  |  | std::vector<u8> GetKeyFromTicket(const std::vector<u8>& signed_ticket) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, | 
					
						
							|  |  |  |                              0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; | 
					
						
							|  |  |  |   u8 iv[16] = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::copy(&signed_ticket[0x01DC], &signed_ticket[0x01DC + 8], iv); | 
					
						
							|  |  |  |   return AESDecode(common_key, iv, &signed_ticket[0x01BF], 16); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | }  // namespace end
 |