mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 18:08:08 +00:00
54cd5fb8eb
- [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. ------------------- - Dynamic shader cache reloading capability for qlaunch - Multi-process improvements (thanks to @frank1734), instead of just using the main application we now respect caller process (also did it for HID devices while I was at it) - Layer stack masks & shared buffer screenshot for video core, added different masks (screenshot, recording, etc.) this was discovered as an issue due to how in qlaunch the transition was not right between applications. May not be perfect but also fixes screenshots while using qlaunch - Reworked overlay display management (input and visibility) - instead of random numbers as I've previously did, I decided to add an AppletZIndex enum for better readability. Also split capability of input by touch and gamepad. - etc. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4238 Reviewed-by: Samuel <lizzie@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
194 lines
5.7 KiB
C++
194 lines
5.7 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
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <optional>
|
|
#include <utility>
|
|
|
|
#include "common/logging.h"
|
|
#include <ranges>
|
|
#include "core/crypto/aes_util.h"
|
|
#include "core/crypto/ctr_encryption_layer.h"
|
|
#include "core/crypto/key_manager.h"
|
|
#include "core/file_sys/content_archive.h"
|
|
#include "core/file_sys/partition_filesystem.h"
|
|
#include "core/file_sys/vfs/vfs_offset.h"
|
|
#include "core/loader/loader.h"
|
|
|
|
#include "core/file_sys/fssystem/fssystem_compression_configuration.h"
|
|
#include "core/file_sys/fssystem/fssystem_crypto_configuration.h"
|
|
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
|
|
|
namespace FileSys {
|
|
|
|
static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
|
|
return std::max<u8>(key_generation, 1) - 1;
|
|
}
|
|
|
|
NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
|
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
|
if (file == nullptr) {
|
|
status = Loader::ResultStatus::ErrorNullFile;
|
|
return;
|
|
}
|
|
|
|
reader = std::make_shared<NcaReader>();
|
|
if (Result rc = reader->Initialize(file, GetCryptoConfiguration(), GetNcaCompressionConfiguration()); R_FAILED(rc)) {
|
|
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
|
return;
|
|
}
|
|
|
|
// Ensure we have the proper key area keys to continue.
|
|
const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration());
|
|
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) {
|
|
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
|
return;
|
|
}
|
|
|
|
RightsId rights_id{};
|
|
reader->GetRightsId(rights_id.data(), rights_id.size());
|
|
if (rights_id != RightsId{}) {
|
|
// External decryption key required; provide it here.
|
|
u128 rights_id_u128;
|
|
std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id));
|
|
|
|
auto titlekey =
|
|
keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id_u128[1], rights_id_u128[0]);
|
|
if (titlekey == Core::Crypto::Key128{}) {
|
|
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
|
return;
|
|
}
|
|
|
|
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
|
|
status = Loader::ResultStatus::ErrorMissingTitlekek;
|
|
return;
|
|
}
|
|
|
|
auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id);
|
|
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB);
|
|
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(),
|
|
Core::Crypto::Op::Decrypt);
|
|
|
|
reader->SetExternalDecryptionKey(titlekey.data(), titlekey.size());
|
|
}
|
|
|
|
const s32 fs_count = reader->GetFsCount();
|
|
NcaFileSystemDriver fs(base_nca ? base_nca->reader : nullptr, reader);
|
|
std::vector<VirtualFile> filesystems(fs_count);
|
|
for (s32 i = 0; i < fs_count; i++) {
|
|
NcaFsHeaderReader header_reader;
|
|
if (Result rc = fs.OpenStorage(&filesystems[i], &header_reader, i); R_FAILED(rc)) {
|
|
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
|
return;
|
|
}
|
|
|
|
if (header_reader.GetFsType() == NcaFsHeader::FsType::RomFs) {
|
|
files.push_back(filesystems[i]);
|
|
romfs = files.back();
|
|
}
|
|
|
|
if (header_reader.GetFsType() == NcaFsHeader::FsType::PartitionFs) {
|
|
auto npfs = std::make_shared<PartitionFilesystem>(filesystems[i]);
|
|
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
|
|
dirs.push_back(npfs);
|
|
if (IsDirectoryExeFS(npfs)) {
|
|
exefs = dirs.back();
|
|
} else if (IsDirectoryLogoPartition(npfs)) {
|
|
logo = dirs.back();
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (header_reader.GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx) {
|
|
is_update = true;
|
|
}
|
|
}
|
|
|
|
if (is_update && base_nca == nullptr && !allow_missing_base) {
|
|
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
|
} else {
|
|
status = Loader::ResultStatus::Success;
|
|
}
|
|
}
|
|
|
|
NCA::~NCA() = default;
|
|
|
|
Loader::ResultStatus NCA::GetStatus() const {
|
|
return status;
|
|
}
|
|
|
|
std::vector<VirtualFile> NCA::GetFiles() const {
|
|
if (status != Loader::ResultStatus::Success) {
|
|
return {};
|
|
}
|
|
return files;
|
|
}
|
|
|
|
std::vector<VirtualDir> NCA::GetSubdirectories() const {
|
|
if (status != Loader::ResultStatus::Success) {
|
|
return {};
|
|
}
|
|
return dirs;
|
|
}
|
|
|
|
std::string NCA::GetName() const {
|
|
return file->GetName();
|
|
}
|
|
|
|
VirtualDir NCA::GetParentDirectory() const {
|
|
return file->GetContainingDirectory();
|
|
}
|
|
|
|
NCAContentType NCA::GetType() const {
|
|
return static_cast<NCAContentType>(reader->GetContentType());
|
|
}
|
|
|
|
u64 NCA::GetTitleId() const {
|
|
if (is_update) {
|
|
return reader->GetProgramId() | 0x800;
|
|
}
|
|
return reader->GetProgramId();
|
|
}
|
|
|
|
RightsId NCA::GetRightsId() const {
|
|
RightsId result;
|
|
reader->GetRightsId(result.data(), result.size());
|
|
return result;
|
|
}
|
|
|
|
u32 NCA::GetSDKVersion() const {
|
|
return reader->GetSdkAddonVersion();
|
|
}
|
|
|
|
u8 NCA::GetKeyGeneration() const {
|
|
return reader->GetKeyGeneration();
|
|
}
|
|
|
|
bool NCA::IsUpdate() const {
|
|
return is_update;
|
|
}
|
|
|
|
VirtualFile NCA::GetRomFS() const {
|
|
return romfs;
|
|
}
|
|
|
|
VirtualDir NCA::GetExeFS() const {
|
|
return exefs;
|
|
}
|
|
|
|
VirtualFile NCA::GetBaseFile() const {
|
|
return file;
|
|
}
|
|
|
|
VirtualDir NCA::GetLogoPartition() const {
|
|
return logo;
|
|
}
|
|
|
|
} // namespace FileSys
|