mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	file_sys: add Self NCCH archive
This commit is contained in:
		@@ -24,6 +24,7 @@ set(SRCS
 | 
			
		||||
            file_sys/archive_savedata.cpp
 | 
			
		||||
            file_sys/archive_sdmc.cpp
 | 
			
		||||
            file_sys/archive_sdmcwriteonly.cpp
 | 
			
		||||
            file_sys/archive_selfncch.cpp
 | 
			
		||||
            file_sys/archive_source_sd_savedata.cpp
 | 
			
		||||
            file_sys/archive_systemsavedata.cpp
 | 
			
		||||
            file_sys/disk_archive.cpp
 | 
			
		||||
@@ -201,6 +202,7 @@ set(HEADERS
 | 
			
		||||
            file_sys/archive_savedata.h
 | 
			
		||||
            file_sys/archive_sdmc.h
 | 
			
		||||
            file_sys/archive_sdmcwriteonly.h
 | 
			
		||||
            file_sys/archive_selfncch.h
 | 
			
		||||
            file_sys/archive_source_sd_savedata.h
 | 
			
		||||
            file_sys/archive_systemsavedata.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,
 | 
			
		||||
                                             ErrorModule::FS, ErrorSummary::NotFound,
 | 
			
		||||
                                             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
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ enum class ErrorDescription : u32 {
 | 
			
		||||
    OS_InvalidBufferDescriptor = 48,
 | 
			
		||||
    MaxConnectionsReached = 52,
 | 
			
		||||
    WrongAddress = 53,
 | 
			
		||||
    FS_RomFSNotFound = 100,
 | 
			
		||||
    FS_ArchiveNotMounted = 101,
 | 
			
		||||
    FS_FileNotFound = 112,
 | 
			
		||||
    FS_PathNotFound = 113,
 | 
			
		||||
@@ -35,10 +36,13 @@ enum class ErrorDescription : u32 {
 | 
			
		||||
    OutofRangeOrMisalignedAddress =
 | 
			
		||||
        513, // TODO(purpasmart): Check if this name fits its actual usage
 | 
			
		||||
    GPU_FirstInitialization = 519,
 | 
			
		||||
    FS_ExeFSSectionNotFound = 567,
 | 
			
		||||
    FS_CommandNotAllowed = 630,
 | 
			
		||||
    FS_InvalidReadFlag = 700,
 | 
			
		||||
    FS_InvalidPath = 702,
 | 
			
		||||
    FS_WriteBeyondEnd = 705,
 | 
			
		||||
    FS_UnsupportedOpenFlags = 760,
 | 
			
		||||
    FS_IncorrectExeFSReadSize = 761,
 | 
			
		||||
    FS_UnexpectedFileOrDirectory = 770,
 | 
			
		||||
    InvalidSection = 1000,
 | 
			
		||||
    TooLarge = 1001,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user