mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-28 03:30:54 +00:00
815325ccec
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- A new NSO compression method was introduced in Switch 22.0.0. This is a customized variant of zstd and is used when NSO flags have bit 7 set. Key characteristics: - ZSTD_MAGICNUMBER is set to 0x4349425A (b'ZBIC') instead of 0xFD2FB528 - ZSTD_LEGACY_SUPPORT is set to 0 - ZSTD_TRACE is set to 1, zstd version used is 1.5.7 (10507) - FSE_readNCount is replaced with a BIC (Binary Interpolative Coding) version which improves compression of entropy tables significantly Source: https://switchbrew.org/wiki/22.0.0 Implementation: - Detect ZBIC segments via NSO flag bit 7 (NsoFlags_UseZbicCompression) and/or ZBIC magic scan in nso.cpp - Fall back to LZ4 when ZBIC is not detected or returns unexpected size - Handle segment 0 alternate offset (0x100 vs 0x108) for both ZBIC and LZ4 References: - Atmosphère loader/strat zstd-zbic integration: https://github.com/Atmosphere-NX/Atmosphere/commit/082115187a0509cb6b8a757de639a4e4741b8712 - nxdumptool ZBIC segment compression support: https://github.com/DarkMatterCore/nxdumptool/commit/441e5c0904f29427987433be4eb057a2843d222a - STORM_SWITCH ZBIC implementation: https://github.com/ReiKatari/STORM_SWITCH/commit/1e09eb82b760aaf340b810031400991c0740c091 Tested with firmware 23.0.0 and ZBIC-compressed NSOs. Co-authored-by: xbzk <xbzk@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4482 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: lizzie <lizzie@eden-emu.dev> Reviewed-by: Maufeat <sahyno1996@gmail.com>
114 lines
3.2 KiB
C++
114 lines
3.2 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <optional>
|
|
#include <type_traits>
|
|
#include "common/common_types.h"
|
|
#include "common/swap.h"
|
|
#include "core/file_sys/patch_manager.h"
|
|
#include "core/loader/loader.h"
|
|
|
|
namespace Core {
|
|
class System;
|
|
}
|
|
|
|
namespace Core::NCE {
|
|
class Patcher;
|
|
}
|
|
|
|
namespace Kernel {
|
|
class KProcess;
|
|
}
|
|
|
|
namespace Loader {
|
|
|
|
struct NSOSegmentHeader {
|
|
u32_le offset;
|
|
u32_le location;
|
|
u32_le size;
|
|
union {
|
|
u32_le alignment;
|
|
u32_le bss_size;
|
|
};
|
|
};
|
|
static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
|
|
|
|
struct NSOHeader {
|
|
using SHA256Hash = std::array<u8, 0x20>;
|
|
|
|
struct RODataRelativeExtent {
|
|
u32_le data_offset;
|
|
u32_le size;
|
|
};
|
|
|
|
u32_le magic;
|
|
u32_le version;
|
|
u32 reserved;
|
|
u32_le flags;
|
|
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
|
|
std::array<u8, 0x20> build_id;
|
|
std::array<u32_le, 3> segments_compressed_size;
|
|
std::array<u8, 0x1C> padding;
|
|
RODataRelativeExtent api_info_extent;
|
|
RODataRelativeExtent dynstr_extent;
|
|
RODataRelativeExtent dynsyn_extent;
|
|
std::array<SHA256Hash, 3> segment_hashes;
|
|
|
|
bool IsSegmentCompressed(size_t segment_num) const;
|
|
bool IsZBICCompressed() const {
|
|
return ((flags >> 7) & 1) != 0;
|
|
}
|
|
};
|
|
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
|
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
|
|
|
constexpr u32 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
|
|
|
struct NSOArgumentHeader {
|
|
u32_le allocated_size;
|
|
u32_le actual_size;
|
|
INSERT_PADDING_BYTES(0x18);
|
|
};
|
|
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
|
|
|
|
/// Loads an NSO file
|
|
class AppLoader_NSO final : public AppLoader {
|
|
public:
|
|
explicit AppLoader_NSO(FileSys::VirtualFile file_);
|
|
|
|
/**
|
|
* Identifies whether or not the given file is a form of NSO file.
|
|
*
|
|
* @param in_file The file to be identified.
|
|
*
|
|
* @return FileType::NSO if found, or FileType::Error if some other type of file.
|
|
*/
|
|
static FileType IdentifyType(const FileSys::VirtualFile& in_file);
|
|
|
|
FileType GetFileType() const override {
|
|
return IdentifyType(file);
|
|
}
|
|
|
|
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
|
const FileSys::VfsFile& nso_file, VAddr load_base,
|
|
bool should_pass_arguments, bool load_into_process,
|
|
std::optional<FileSys::PatchManager> pm = {},
|
|
std::vector<Core::NCE::Patcher>* patches = nullptr,
|
|
s32 patch_index = -1);
|
|
|
|
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
|
|
|
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
|
|
|
private:
|
|
Modules modules;
|
|
};
|
|
|
|
} // namespace Loader
|