mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	file_sys: add Self NCCH archive
This commit is contained in:
		
							parent
							
								
									2889372e47
								
							
						
					
					
						commit
						a0df747325
					
				
					 5 changed files with 318 additions and 0 deletions
				
			
		|  | @ -24,6 +24,7 @@ set(SRCS | ||||||
|             file_sys/archive_savedata.cpp |             file_sys/archive_savedata.cpp | ||||||
|             file_sys/archive_sdmc.cpp |             file_sys/archive_sdmc.cpp | ||||||
|             file_sys/archive_sdmcwriteonly.cpp |             file_sys/archive_sdmcwriteonly.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/disk_archive.cpp |             file_sys/disk_archive.cpp | ||||||
|  | @ -201,6 +202,7 @@ set(HEADERS | ||||||
|             file_sys/archive_savedata.h |             file_sys/archive_savedata.h | ||||||
|             file_sys/archive_sdmc.h |             file_sys/archive_sdmc.h | ||||||
|             file_sys/archive_sdmcwriteonly.h |             file_sys/archive_sdmcwriteonly.h | ||||||
|  |             file_sys/archive_selfncch.h | ||||||
|             file_sys/archive_source_sd_savedata.h |             file_sys/archive_source_sd_savedata.h | ||||||
|             file_sys/archive_systemsavedata.h |             file_sys/archive_systemsavedata.h | ||||||
|             file_sys/directory_backend.h |             file_sys/directory_backend.h | ||||||
|  |  | ||||||
							
								
								
									
										257
									
								
								src/core/file_sys/archive_selfncch.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/core/file_sys/archive_selfncch.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,257 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  | #include "core/file_sys/archive_selfncch.h" | ||||||
|  | #include "core/file_sys/errors.h" | ||||||
|  | #include "core/file_sys/ivfc_archive.h" | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // FileSys namespace
 | ||||||
|  | 
 | ||||||
|  | namespace FileSys { | ||||||
|  | 
 | ||||||
|  | enum class SelfNCCHFilePathType : u32 { | ||||||
|  |     RomFS = 0, | ||||||
|  |     Code = 1, // This is not supported by SelfNCCHArchive but by archive 0x2345678E
 | ||||||
|  |     ExeFS = 2, | ||||||
|  |     UpdateRomFS = 5, // This is presumably for accessing the RomFS of the update patch.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct SelfNCCHFilePath { | ||||||
|  |     u32_le type; | ||||||
|  |     std::array<char, 8> exefs_filename; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!"); | ||||||
|  | 
 | ||||||
|  | // A read-only file created from a block of data. It only allows you to read the entire file at
 | ||||||
|  | // once, in a single read operation.
 | ||||||
|  | class ExeFSSectionFile final : public FileBackend { | ||||||
|  | public: | ||||||
|  |     explicit ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_) : data(std::move(data_)) {} | ||||||
|  | 
 | ||||||
|  |     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override { | ||||||
|  |         if (offset != 0) { | ||||||
|  |             LOG_ERROR(Service_FS, "offset must be zero!"); | ||||||
|  |             return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (length != data->size()) { | ||||||
|  |             LOG_ERROR(Service_FS, "size must match the file size!"); | ||||||
|  |             return ERROR_INCORRECT_EXEFS_READ_SIZE; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         std::memcpy(buffer, data->data(), data->size()); | ||||||
|  |         return MakeResult<size_t>(data->size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultVal<size_t> Write(u64 offset, size_t length, bool flush, | ||||||
|  |                             const u8* buffer) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "The file is read-only!"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u64 GetSize() const override { | ||||||
|  |         return data->size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool SetSize(u64 size) const override { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool Close() const override { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Flush() const override {} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::shared_ptr<std::vector<u8>> data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // SelfNCCHArchive represents the running application itself. From this archive the application can
 | ||||||
|  | // open RomFS and ExeFS, excluding the .code section.
 | ||||||
|  | class SelfNCCHArchive final : public ArchiveBackend { | ||||||
|  | public: | ||||||
|  |     explicit SelfNCCHArchive(const NCCHData& ncch_data_) : ncch_data(ncch_data_) {} | ||||||
|  | 
 | ||||||
|  |     std::string GetName() const override { | ||||||
|  |         return "SelfNCCHArchive"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override { | ||||||
|  |         // Note: SelfNCCHArchive doesn't check the open mode.
 | ||||||
|  | 
 | ||||||
|  |         if (path.GetType() != LowPathType::Binary) { | ||||||
|  |             LOG_ERROR(Service_FS, "Path need to be Binary"); | ||||||
|  |             return ERROR_INVALID_PATH; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         std::vector<u8> binary = path.AsBinary(); | ||||||
|  |         if (binary.size() != sizeof(SelfNCCHFilePath)) { | ||||||
|  |             LOG_ERROR(Service_FS, "Wrong path size %zu", binary.size()); | ||||||
|  |             return ERROR_INVALID_PATH; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         SelfNCCHFilePath file_path; | ||||||
|  |         std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath)); | ||||||
|  | 
 | ||||||
|  |         switch (static_cast<SelfNCCHFilePathType>(file_path.type)) { | ||||||
|  |         case SelfNCCHFilePathType::UpdateRomFS: | ||||||
|  |             LOG_WARNING(Service_FS, "(STUBBED) open update RomFS"); | ||||||
|  |             return OpenRomFS(); | ||||||
|  | 
 | ||||||
|  |         case SelfNCCHFilePathType::RomFS: | ||||||
|  |             return OpenRomFS(); | ||||||
|  | 
 | ||||||
|  |         case SelfNCCHFilePathType::Code: | ||||||
|  |             LOG_ERROR(Service_FS, "Reading the code section is not supported!"); | ||||||
|  |             return ERROR_COMMAND_NOT_ALLOWED; | ||||||
|  | 
 | ||||||
|  |         case SelfNCCHFilePathType::ExeFS: { | ||||||
|  |             const auto& raw = file_path.exefs_filename; | ||||||
|  |             auto end = std::find(raw.begin(), raw.end(), '\0'); | ||||||
|  |             std::string filename(raw.begin(), end); | ||||||
|  |             return OpenExeFS(filename); | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             LOG_ERROR(Service_FS, "Unknown file type %u!", static_cast<u32>(file_path.type)); | ||||||
|  |             return ERROR_INVALID_PATH; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode DeleteFile(const Path& path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode DeleteDirectory(const Path& path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode DeleteDirectoryRecursively(const Path& path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode CreateFile(const Path& path, u64 size) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode CreateDirectory(const Path& path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override { | ||||||
|  |         LOG_ERROR(Service_FS, "Unsupported"); | ||||||
|  |         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u64 GetFreeBytes() const override { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ResultVal<std::unique_ptr<FileBackend>> OpenRomFS() const { | ||||||
|  |         if (ncch_data.romfs_file) { | ||||||
|  |             return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>( | ||||||
|  |                 ncch_data.romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size)); | ||||||
|  |         } else { | ||||||
|  |             LOG_INFO(Service_FS, "Unable to read RomFS"); | ||||||
|  |             return ERROR_ROMFS_NOT_FOUND; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const { | ||||||
|  |         if (filename == "icon") { | ||||||
|  |             if (ncch_data.icon) { | ||||||
|  |                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||||
|  |                     std::make_unique<ExeFSSectionFile>(ncch_data.icon)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             LOG_WARNING(Service_FS, "Unable to read icon"); | ||||||
|  |             return ERROR_EXEFS_SECTION_NOT_FOUND; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (filename == "logo") { | ||||||
|  |             if (ncch_data.logo) { | ||||||
|  |                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||||
|  |                     std::make_unique<ExeFSSectionFile>(ncch_data.logo)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             LOG_WARNING(Service_FS, "Unable to read logo"); | ||||||
|  |             return ERROR_EXEFS_SECTION_NOT_FOUND; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (filename == "banner") { | ||||||
|  |             if (ncch_data.banner) { | ||||||
|  |                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||||
|  |                     std::make_unique<ExeFSSectionFile>(ncch_data.banner)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             LOG_WARNING(Service_FS, "Unable to read banner"); | ||||||
|  |             return ERROR_EXEFS_SECTION_NOT_FOUND; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         LOG_ERROR(Service_FS, "Unknown ExeFS section %s!", filename.c_str()); | ||||||
|  |         return ERROR_INVALID_PATH; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     NCCHData ncch_data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { | ||||||
|  |     std::shared_ptr<FileUtil::IOFile> romfs_file_; | ||||||
|  |     if (Loader::ResultStatus::Success == | ||||||
|  |         app_loader.ReadRomFS(romfs_file_, ncch_data.romfs_offset, ncch_data.romfs_size)) { | ||||||
|  | 
 | ||||||
|  |         ncch_data.romfs_file = std::move(romfs_file_); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> buffer; | ||||||
|  | 
 | ||||||
|  |     if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer)) | ||||||
|  |         ncch_data.icon = std::make_shared<std::vector<u8>>(std::move(buffer)); | ||||||
|  | 
 | ||||||
|  |     buffer.clear(); | ||||||
|  |     if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer)) | ||||||
|  |         ncch_data.logo = std::make_shared<std::vector<u8>>(std::move(buffer)); | ||||||
|  | 
 | ||||||
|  |     buffer.clear(); | ||||||
|  |     if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer)) | ||||||
|  |         ncch_data.banner = std::make_shared<std::vector<u8>>(std::move(buffer)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { | ||||||
|  |     auto archive = std::make_unique<SelfNCCHArchive>(ncch_data); | ||||||
|  |     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) { | ||||||
|  |     LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive."); | ||||||
|  |     return ERROR_INVALID_PATH; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&) const { | ||||||
|  |     LOG_ERROR(Service_FS, "Attempted to get format info of a SelfNCCH archive"); | ||||||
|  |     return ERROR_INVALID_PATH; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace FileSys
 | ||||||
							
								
								
									
										45
									
								
								src/core/file_sys/archive_selfncch.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/core/file_sys/archive_selfncch.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | // 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 "core/file_sys/archive_backend.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
|  | #include "core/loader/loader.h" | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // FileSys namespace
 | ||||||
|  | 
 | ||||||
|  | namespace FileSys { | ||||||
|  | 
 | ||||||
|  | struct NCCHData { | ||||||
|  |     std::shared_ptr<std::vector<u8>> icon; | ||||||
|  |     std::shared_ptr<std::vector<u8>> logo; | ||||||
|  |     std::shared_ptr<std::vector<u8>> banner; | ||||||
|  |     std::shared_ptr<FileUtil::IOFile> romfs_file; | ||||||
|  |     u64 romfs_offset = 0; | ||||||
|  |     u64 romfs_size = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// File system interface to the SelfNCCH archive
 | ||||||
|  | class ArchiveFactory_SelfNCCH final : public ArchiveFactory { | ||||||
|  | public: | ||||||
|  |     explicit ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader); | ||||||
|  | 
 | ||||||
|  |     std::string GetName() const override { | ||||||
|  |         return "SelfNCCH"; | ||||||
|  |     } | ||||||
|  |     ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; | ||||||
|  |     ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||||||
|  |     ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     NCCHData ncch_data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace FileSys
 | ||||||
|  | @ -39,5 +39,15 @@ const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpt | ||||||
| const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted, | const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted, | ||||||
|                                              ErrorModule::FS, ErrorSummary::NotFound, |                                              ErrorModule::FS, ErrorSummary::NotFound, | ||||||
|                                              ErrorLevel::Status); |                                              ErrorLevel::Status); | ||||||
|  | const ResultCode ERROR_INCORRECT_EXEFS_READ_SIZE(ErrorDescription::FS_IncorrectExeFSReadSize, | ||||||
|  |                                                  ErrorModule::FS, ErrorSummary::NotSupported, | ||||||
|  |                                                  ErrorLevel::Usage); | ||||||
|  | const ResultCode ERROR_ROMFS_NOT_FOUND(ErrorDescription::FS_RomFSNotFound, ErrorModule::FS, | ||||||
|  |                                        ErrorSummary::NotFound, ErrorLevel::Status); | ||||||
|  | const ResultCode ERROR_COMMAND_NOT_ALLOWED(ErrorDescription::FS_CommandNotAllowed, ErrorModule::FS, | ||||||
|  |                                            ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||||
|  | const ResultCode ERROR_EXEFS_SECTION_NOT_FOUND(ErrorDescription::FS_ExeFSSectionNotFound, | ||||||
|  |                                                ErrorModule::FS, ErrorSummary::NotFound, | ||||||
|  |                                                ErrorLevel::Status); | ||||||
| 
 | 
 | ||||||
| } // namespace FileSys
 | } // namespace FileSys
 | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ enum class ErrorDescription : u32 { | ||||||
|     OS_InvalidBufferDescriptor = 48, |     OS_InvalidBufferDescriptor = 48, | ||||||
|     MaxConnectionsReached = 52, |     MaxConnectionsReached = 52, | ||||||
|     WrongAddress = 53, |     WrongAddress = 53, | ||||||
|  |     FS_RomFSNotFound = 100, | ||||||
|     FS_ArchiveNotMounted = 101, |     FS_ArchiveNotMounted = 101, | ||||||
|     FS_FileNotFound = 112, |     FS_FileNotFound = 112, | ||||||
|     FS_PathNotFound = 113, |     FS_PathNotFound = 113, | ||||||
|  | @ -35,10 +36,13 @@ enum class ErrorDescription : u32 { | ||||||
|     OutofRangeOrMisalignedAddress = |     OutofRangeOrMisalignedAddress = | ||||||
|         513, // TODO(purpasmart): Check if this name fits its actual usage
 |         513, // TODO(purpasmart): Check if this name fits its actual usage
 | ||||||
|     GPU_FirstInitialization = 519, |     GPU_FirstInitialization = 519, | ||||||
|  |     FS_ExeFSSectionNotFound = 567, | ||||||
|  |     FS_CommandNotAllowed = 630, | ||||||
|     FS_InvalidReadFlag = 700, |     FS_InvalidReadFlag = 700, | ||||||
|     FS_InvalidPath = 702, |     FS_InvalidPath = 702, | ||||||
|     FS_WriteBeyondEnd = 705, |     FS_WriteBeyondEnd = 705, | ||||||
|     FS_UnsupportedOpenFlags = 760, |     FS_UnsupportedOpenFlags = 760, | ||||||
|  |     FS_IncorrectExeFSReadSize = 761, | ||||||
|     FS_UnexpectedFileOrDirectory = 770, |     FS_UnexpectedFileOrDirectory = 770, | ||||||
|     InvalidSection = 1000, |     InvalidSection = 1000, | ||||||
|     TooLarge = 1001, |     TooLarge = 1001, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue