mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	file_sys: Add CIA Container
This commit is contained in:
		
							parent
							
								
									79852d3707
								
							
						
					
					
						commit
						b3e87d01fb
					
				
					 3 changed files with 333 additions and 0 deletions
				
			
		|  | @ -24,6 +24,7 @@ set(SRCS | ||||||
|             file_sys/archive_selfncch.cpp |             file_sys/archive_selfncch.cpp | ||||||
|             file_sys/archive_source_sd_savedata.cpp |             file_sys/archive_source_sd_savedata.cpp | ||||||
|             file_sys/archive_systemsavedata.cpp |             file_sys/archive_systemsavedata.cpp | ||||||
|  |             file_sys/cia_container.cpp | ||||||
|             file_sys/disk_archive.cpp |             file_sys/disk_archive.cpp | ||||||
|             file_sys/ivfc_archive.cpp |             file_sys/ivfc_archive.cpp | ||||||
|             file_sys/ncch_container.cpp |             file_sys/ncch_container.cpp | ||||||
|  |  | ||||||
							
								
								
									
										228
									
								
								src/core/file_sys/cia_container.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/core/file_sys/cia_container.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,228 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <cinttypes> | ||||||
|  | #include <cryptopp/sha.h> | ||||||
|  | #include "common/alignment.h" | ||||||
|  | #include "common/file_util.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/file_sys/cia_container.h" | ||||||
|  | #include "core/file_sys/file_backend.h" | ||||||
|  | #include "core/loader/loader.h" | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // FileSys namespace
 | ||||||
|  | 
 | ||||||
|  | namespace FileSys { | ||||||
|  | 
 | ||||||
|  | constexpr u32 CIA_SECTION_ALIGNMENT = 0x40; | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) { | ||||||
|  |     std::vector<u8> header_data(sizeof(Header)); | ||||||
|  | 
 | ||||||
|  |     // Load the CIA Header
 | ||||||
|  |     ResultVal<size_t> read_result = backend.Read(0, sizeof(Header), header_data.data()); | ||||||
|  |     if (read_result.Failed() || *read_result != sizeof(Header)) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     Loader::ResultStatus result = LoadHeader(header_data); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load Title Metadata
 | ||||||
|  |     std::vector<u8> tmd_data(cia_header.tmd_size); | ||||||
|  |     read_result = backend.Read(GetTitleMetadataOffset(), cia_header.tmd_size, tmd_data.data()); | ||||||
|  |     if (read_result.Failed() || *read_result != cia_header.tmd_size) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     result = LoadTitleMetadata(tmd_data); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load CIA Metadata
 | ||||||
|  |     if (cia_header.meta_size) { | ||||||
|  |         std::vector<u8> meta_data(sizeof(Metadata)); | ||||||
|  |         read_result = backend.Read(GetMetadataOffset(), sizeof(Metadata), meta_data.data()); | ||||||
|  |         if (read_result.Failed() || *read_result != sizeof(Metadata)) | ||||||
|  |             return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |         result = LoadMetadata(meta_data); | ||||||
|  |         if (result != Loader::ResultStatus::Success) | ||||||
|  |             return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Loader::ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::Load(const std::string& filepath) { | ||||||
|  |     FileUtil::IOFile file(filepath, "rb"); | ||||||
|  |     if (!file.IsOpen()) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     // Load CIA Header
 | ||||||
|  |     std::vector<u8> header_data(sizeof(Header)); | ||||||
|  |     if (file.ReadBytes(header_data.data(), sizeof(Header)) != sizeof(Header)) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     Loader::ResultStatus result = LoadHeader(header_data); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load Title Metadata
 | ||||||
|  |     std::vector<u8> tmd_data(cia_header.tmd_size); | ||||||
|  |     file.Seek(GetTitleMetadataOffset(), SEEK_SET); | ||||||
|  |     if (!file.ReadBytes(tmd_data.data(), cia_header.tmd_size) != cia_header.tmd_size) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     result = LoadTitleMetadata(tmd_data); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load CIA Metadata
 | ||||||
|  |     if (cia_header.meta_size) { | ||||||
|  |         std::vector<u8> meta_data(sizeof(Metadata)); | ||||||
|  |         file.Seek(GetMetadataOffset(), SEEK_SET); | ||||||
|  |         if (file.ReadBytes(meta_data.data(), sizeof(Metadata)) != sizeof(Metadata)) | ||||||
|  |             return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |         result = LoadMetadata(meta_data); | ||||||
|  |         if (result != Loader::ResultStatus::Success) | ||||||
|  |             return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Loader::ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::Load(const std::vector<u8>& file_data) { | ||||||
|  |     Loader::ResultStatus result = LoadHeader(file_data); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load Title Metadata
 | ||||||
|  |     result = LoadTitleMetadata(file_data, GetTitleMetadataOffset()); | ||||||
|  |     if (result != Loader::ResultStatus::Success) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     // Load CIA Metadata
 | ||||||
|  |     if (cia_header.meta_size) { | ||||||
|  |         result = LoadMetadata(file_data, GetMetadataOffset()); | ||||||
|  |         if (result != Loader::ResultStatus::Success) | ||||||
|  |             return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Loader::ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::LoadHeader(const std::vector<u8>& header_data, size_t offset) { | ||||||
|  |     if (header_data.size() - offset < sizeof(Header)) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     std::memcpy(&cia_header, header_data.data(), sizeof(Header)); | ||||||
|  | 
 | ||||||
|  |     return Loader::ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::LoadTitleMetadata(const std::vector<u8>& tmd_data, | ||||||
|  |                                                      size_t offset) { | ||||||
|  |     return cia_tmd.Load(tmd_data, offset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Loader::ResultStatus CIAContainer::LoadMetadata(const std::vector<u8>& meta_data, size_t offset) { | ||||||
|  |     if (meta_data.size() - offset < sizeof(Metadata)) | ||||||
|  |         return Loader::ResultStatus::Error; | ||||||
|  | 
 | ||||||
|  |     std::memcpy(&cia_metadata, meta_data.data(), sizeof(Metadata)); | ||||||
|  | 
 | ||||||
|  |     return Loader::ResultStatus::Success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const TitleMetadata& CIAContainer::GetTitleMetadata() const { | ||||||
|  |     return cia_tmd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::array<u64, 0x30>& CIAContainer::GetDependencies() { | ||||||
|  |     return cia_metadata.dependencies; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 CIAContainer::GetCoreVersion() const { | ||||||
|  |     return cia_metadata.core_version; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetCertificateOffset() const { | ||||||
|  |     return Common::AlignUp(cia_header.header_size, CIA_SECTION_ALIGNMENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetTicketOffset() const { | ||||||
|  |     return Common::AlignUp(GetCertificateOffset() + cia_header.cert_size, CIA_SECTION_ALIGNMENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetTitleMetadataOffset() const { | ||||||
|  |     return Common::AlignUp(GetTicketOffset() + cia_header.tik_size, CIA_SECTION_ALIGNMENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetMetadataOffset() const { | ||||||
|  |     u64 tmd_end_offset = GetContentOffset(); | ||||||
|  | 
 | ||||||
|  |     // Meta exists after all content in the CIA
 | ||||||
|  |     u64 offset = Common::AlignUp(tmd_end_offset + cia_header.content_size, CIA_SECTION_ALIGNMENT); | ||||||
|  | 
 | ||||||
|  |     return offset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetContentOffset(u16 index) const { | ||||||
|  |     u64 offset = | ||||||
|  |         Common::AlignUp(GetTitleMetadataOffset() + cia_header.tmd_size, CIA_SECTION_ALIGNMENT); | ||||||
|  |     for (u16 i = 0; i < index; i++) { | ||||||
|  |         offset += GetContentSize(i); | ||||||
|  |     } | ||||||
|  |     return offset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 CIAContainer::GetCertificateSize() const { | ||||||
|  |     return cia_header.cert_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 CIAContainer::GetTicketSize() const { | ||||||
|  |     return cia_header.tik_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 CIAContainer::GetTitleMetadataSize() const { | ||||||
|  |     return cia_header.tmd_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 CIAContainer::GetMetadataSize() const { | ||||||
|  |     return cia_header.meta_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetTotalContentSize() const { | ||||||
|  |     return cia_header.content_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u64 CIAContainer::GetContentSize(u16 index) const { | ||||||
|  |     // If the content doesn't exist in the CIA, it doesn't have a size.
 | ||||||
|  |     if (!cia_header.isContentPresent(index)) | ||||||
|  |         return 0; | ||||||
|  | 
 | ||||||
|  |     return cia_tmd.GetContentSizeByIndex(index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CIAContainer::Print() const { | ||||||
|  |     LOG_DEBUG(Service_FS, "Type:               %u", static_cast<u32>(cia_header.type)); | ||||||
|  |     LOG_DEBUG(Service_FS, "Version:            %u\n", static_cast<u32>(cia_header.version)); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_FS, "Certificate Size: 0x%08x bytes", GetCertificateSize()); | ||||||
|  |     LOG_DEBUG(Service_FS, "Ticket Size:      0x%08x bytes", GetTicketSize()); | ||||||
|  |     LOG_DEBUG(Service_FS, "TMD Size:         0x%08x bytes", GetTitleMetadataSize()); | ||||||
|  |     LOG_DEBUG(Service_FS, "Meta Size:        0x%08x bytes", GetMetadataSize()); | ||||||
|  |     LOG_DEBUG(Service_FS, "Content Size:     0x%08x bytes\n", GetTotalContentSize()); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_FS, "Certificate Offset: 0x%08" PRIx64 " bytes", GetCertificateOffset()); | ||||||
|  |     LOG_DEBUG(Service_FS, "Ticket Offset:      0x%08" PRIx64 " bytes", GetTicketOffset()); | ||||||
|  |     LOG_DEBUG(Service_FS, "TMD Offset:         0x%08" PRIx64 " bytes", GetTitleMetadataOffset()); | ||||||
|  |     LOG_DEBUG(Service_FS, "Meta Offset:        0x%08" PRIx64 " bytes", GetMetadataOffset()); | ||||||
|  |     for (u16 i = 0; i < cia_tmd.GetContentCount(); i++) { | ||||||
|  |         LOG_DEBUG(Service_FS, "Content %x Offset:   0x%08" PRIx64 " bytes", i, GetContentOffset(i)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								src/core/file_sys/cia_container.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/core/file_sys/cia_container.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,104 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  | #include "core/file_sys/title_metadata.h" | ||||||
|  | 
 | ||||||
|  | namespace Loader { | ||||||
|  | enum class ResultStatus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // FileSys namespace
 | ||||||
|  | 
 | ||||||
|  | namespace FileSys { | ||||||
|  | 
 | ||||||
|  | class FileBackend; | ||||||
|  | 
 | ||||||
|  | constexpr size_t CIA_CONTENT_MAX_COUNT = 0x10000; | ||||||
|  | constexpr size_t CIA_CONTENT_BITS_SIZE = (CIA_CONTENT_MAX_COUNT / 8); | ||||||
|  | constexpr size_t CIA_HEADER_SIZE = 0x2020; | ||||||
|  | constexpr size_t CIA_DEPENDENCY_SIZE = 0x300; | ||||||
|  | constexpr size_t CIA_METADATA_SIZE = 0x400; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Helper which implements an interface to read and write CTR Installable Archive (CIA) files. | ||||||
|  |  * Data can either be loaded from a FileBackend, a string path, or from a data array. Data can | ||||||
|  |  * also be partially loaded for CIAs which are downloading/streamed in and need some metadata | ||||||
|  |  * read out. | ||||||
|  |  */ | ||||||
|  | class CIAContainer { | ||||||
|  | public: | ||||||
|  |     // Load whole CIAs outright
 | ||||||
|  |     Loader::ResultStatus Load(const FileBackend& backend); | ||||||
|  |     Loader::ResultStatus Load(const std::string& filepath); | ||||||
|  |     Loader::ResultStatus Load(const std::vector<u8>& header_data); | ||||||
|  | 
 | ||||||
|  |     // Load parts of CIAs (for CIAs streamed in)
 | ||||||
|  |     Loader::ResultStatus LoadHeader(const std::vector<u8>& header_data, size_t offset = 0); | ||||||
|  |     Loader::ResultStatus LoadTitleMetadata(const std::vector<u8>& tmd_data, size_t offset = 0); | ||||||
|  |     Loader::ResultStatus LoadMetadata(const std::vector<u8>& meta_data, size_t offset = 0); | ||||||
|  | 
 | ||||||
|  |     const TitleMetadata& GetTitleMetadata() const; | ||||||
|  |     std::array<u64, 0x30>& GetDependencies(); | ||||||
|  |     u32 GetCoreVersion() const; | ||||||
|  | 
 | ||||||
|  |     u64 GetCertificateOffset() const; | ||||||
|  |     u64 GetTicketOffset() const; | ||||||
|  |     u64 GetTitleMetadataOffset() const; | ||||||
|  |     u64 GetMetadataOffset() const; | ||||||
|  |     u64 GetContentOffset(u16 index = 0) const; | ||||||
|  | 
 | ||||||
|  |     u32 GetCertificateSize() const; | ||||||
|  |     u32 GetTicketSize() const; | ||||||
|  |     u32 GetTitleMetadataSize() const; | ||||||
|  |     u32 GetMetadataSize() const; | ||||||
|  |     u64 GetTotalContentSize() const; | ||||||
|  |     u64 GetContentSize(u16 index = 0) const; | ||||||
|  | 
 | ||||||
|  |     void Print() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     struct Header { | ||||||
|  |         u32_le header_size; | ||||||
|  |         u16_le type; | ||||||
|  |         u16_le version; | ||||||
|  |         u32_le cert_size; | ||||||
|  |         u32_le tik_size; | ||||||
|  |         u32_le tmd_size; | ||||||
|  |         u32_le meta_size; | ||||||
|  |         u64_le content_size; | ||||||
|  |         std::array<u8, CIA_CONTENT_BITS_SIZE> content_present; | ||||||
|  | 
 | ||||||
|  |         bool isContentPresent(u16 index) const { | ||||||
|  |             // The content_present is a bit array which defines which content in the TMD
 | ||||||
|  |             // is included in the CIA, so check the bit for this index and add if set.
 | ||||||
|  |             // The bits in the content index are arranged w/ index 0 as the MSB, 7 as the LSB, etc.
 | ||||||
|  |             return (content_present[index >> 3] & (0x80 >> (index & 7))); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong"); | ||||||
|  | 
 | ||||||
|  |     struct Metadata { | ||||||
|  |         std::array<u64_le, 0x30> dependencies; | ||||||
|  |         std::array<u8, 0x180> reserved; | ||||||
|  |         u32_le core_version; | ||||||
|  |         std::array<u8, 0xfc> reserved_2; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     static_assert(sizeof(Metadata) == CIA_METADATA_SIZE, "CIA Metadata structure size is wrong"); | ||||||
|  | 
 | ||||||
|  |     Header cia_header; | ||||||
|  |     Metadata cia_metadata; | ||||||
|  |     TitleMetadata cia_tmd; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace FileSys
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue