mirror of
https://github.com/azahar-emu/azahar.git
synced 2025-04-28 13:47:59 +03:00
Fix installing DLCs with encrypted flags for missing contents (#979)
Some checks failed
citra-transifex / transifex (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / ios (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
Some checks failed
citra-transifex / transifex (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / ios (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
This commit is contained in:
parent
493f59cef5
commit
01d7ff7a08
5 changed files with 66 additions and 43 deletions
|
@ -13,11 +13,11 @@
|
|||
namespace FileSys {
|
||||
|
||||
Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) {
|
||||
std::vector<u8> header_data(sizeof(Header));
|
||||
std::vector<u8> header_data(sizeof(CIAHeader));
|
||||
|
||||
// Load the CIA Header
|
||||
ResultVal<std::size_t> read_result = backend.Read(0, sizeof(Header), header_data.data());
|
||||
if (read_result.Failed() || *read_result != sizeof(Header))
|
||||
ResultVal<std::size_t> read_result = backend.Read(0, sizeof(CIAHeader), header_data.data());
|
||||
if (read_result.Failed() || *read_result != sizeof(CIAHeader))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
Loader::ResultStatus result = LoadHeader(header_data);
|
||||
|
@ -65,8 +65,8 @@ Loader::ResultStatus CIAContainer::Load(const std::string& filepath) {
|
|||
return Loader::ResultStatus::Error;
|
||||
|
||||
// Load CIA Header
|
||||
std::vector<u8> header_data(sizeof(Header));
|
||||
if (file.ReadBytes(header_data.data(), sizeof(Header)) != sizeof(Header))
|
||||
std::vector<u8> header_data(sizeof(CIAHeader));
|
||||
if (file.ReadBytes(header_data.data(), sizeof(CIAHeader)) != sizeof(CIAHeader))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
Loader::ResultStatus result = LoadHeader(header_data);
|
||||
|
@ -134,11 +134,12 @@ Loader::ResultStatus CIAContainer::Load(std::span<const u8> file_data) {
|
|||
}
|
||||
|
||||
Loader::ResultStatus CIAContainer::LoadHeader(std::span<const u8> header_data, std::size_t offset) {
|
||||
if (header_data.size() - offset < sizeof(Header)) {
|
||||
if (header_data.size() - offset < sizeof(CIAHeader)) {
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
std::memcpy(&cia_header, header_data.data(), sizeof(Header));
|
||||
std::memcpy(&cia_header, header_data.data(), sizeof(CIAHeader));
|
||||
has_header = true;
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
@ -265,4 +266,7 @@ void CIAContainer::Print() const {
|
|||
LOG_DEBUG(Service_FS, "Content {:x} Offset: 0x{:08x} bytes", i, GetContentOffset(i));
|
||||
}
|
||||
}
|
||||
const CIAHeader* CIAContainer::GetHeader() {
|
||||
return has_header ? &cia_header : nullptr;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
|
|
@ -28,48 +28,7 @@ constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300;
|
|||
constexpr std::size_t CIA_METADATA_SIZE = 0x400;
|
||||
constexpr u32 CIA_SECTION_ALIGNMENT = 0x40;
|
||||
|
||||
/**
|
||||
* 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(std::span<const u8> header_data);
|
||||
|
||||
// Load parts of CIAs (for CIAs streamed in)
|
||||
Loader::ResultStatus LoadHeader(std::span<const u8> header_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTicket(std::span<const u8> ticket_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTicket(const Ticket& ticket);
|
||||
Loader::ResultStatus LoadTitleMetadata(std::span<const u8> tmd_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTitleMetadata(const TitleMetadata& tmd);
|
||||
Loader::ResultStatus LoadMetadata(std::span<const u8> meta_data, std::size_t offset = 0);
|
||||
|
||||
Ticket& GetTicket();
|
||||
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(std::size_t index = 0) const;
|
||||
|
||||
u32 GetCertificateSize() const;
|
||||
u32 GetTicketSize() const;
|
||||
u32 GetTitleMetadataSize() const;
|
||||
u32 GetMetadataSize() const;
|
||||
u64 GetTotalContentSize() const;
|
||||
u64 GetContentSize(std::size_t index = 0) const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
struct Header {
|
||||
struct CIAHeader {
|
||||
u32_le header_size;
|
||||
u16_le type;
|
||||
u16_le version;
|
||||
|
@ -91,7 +50,49 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong");
|
||||
static_assert(sizeof(CIAHeader) == CIA_HEADER_SIZE, "CIA Header structure size is wrong");
|
||||
|
||||
/**
|
||||
* 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(std::span<const u8> header_data);
|
||||
|
||||
// Load parts of CIAs (for CIAs streamed in)
|
||||
Loader::ResultStatus LoadHeader(std::span<const u8> header_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTicket(std::span<const u8> ticket_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTicket(const Ticket& ticket);
|
||||
Loader::ResultStatus LoadTitleMetadata(std::span<const u8> tmd_data, std::size_t offset = 0);
|
||||
Loader::ResultStatus LoadTitleMetadata(const TitleMetadata& tmd);
|
||||
Loader::ResultStatus LoadMetadata(std::span<const u8> meta_data, std::size_t offset = 0);
|
||||
|
||||
const CIAHeader* GetHeader();
|
||||
Ticket& GetTicket();
|
||||
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(std::size_t index = 0) const;
|
||||
|
||||
u32 GetCertificateSize() const;
|
||||
u32 GetTicketSize() const;
|
||||
u32 GetTitleMetadataSize() const;
|
||||
u32 GetMetadataSize() const;
|
||||
u64 GetTotalContentSize() const;
|
||||
u64 GetContentSize(std::size_t index = 0) const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
struct Metadata {
|
||||
|
@ -103,7 +104,8 @@ private:
|
|||
|
||||
static_assert(sizeof(Metadata) == CIA_METADATA_SIZE, "CIA Metadata structure size is wrong");
|
||||
|
||||
Header cia_header;
|
||||
bool has_header = false;
|
||||
CIAHeader cia_header;
|
||||
Metadata cia_metadata;
|
||||
Ticket cia_ticket;
|
||||
TitleMetadata cia_tmd;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#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/signature.h"
|
||||
#include "core/file_sys/title_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
@ -185,9 +186,14 @@ std::array<u8, 16> TitleMetadata::GetContentCTRByIndex(std::size_t index) const
|
|||
return ctr;
|
||||
}
|
||||
|
||||
bool TitleMetadata::HasEncryptedContent() const {
|
||||
return std::any_of(tmd_chunks.begin(), tmd_chunks.end(), [](auto& chunk) {
|
||||
return (static_cast<u16>(chunk.type) & FileSys::TMDContentTypeFlag::Encrypted) != 0;
|
||||
bool TitleMetadata::HasEncryptedContent(const CIAHeader* header) const {
|
||||
return std::any_of(tmd_chunks.begin(), tmd_chunks.end(), [header](auto& chunk) {
|
||||
bool is_crypted =
|
||||
(static_cast<u16>(chunk.type) & FileSys::TMDContentTypeFlag::Encrypted) != 0;
|
||||
if (header) {
|
||||
is_crypted = is_crypted && header->IsContentPresent(static_cast<u16>(chunk.index));
|
||||
}
|
||||
return is_crypted;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ enum class ResultStatus;
|
|||
|
||||
namespace FileSys {
|
||||
|
||||
struct CIAHeader;
|
||||
|
||||
enum TMDContentTypeFlag : u16 {
|
||||
Encrypted = 1 << 0,
|
||||
Disc = 1 << 2,
|
||||
|
@ -99,7 +101,7 @@ public:
|
|||
u64 GetContentSizeByIndex(std::size_t index) const;
|
||||
bool GetContentOptional(std::size_t index) const;
|
||||
std::array<u8, 16> GetContentCTRByIndex(std::size_t index) const;
|
||||
bool HasEncryptedContent() const;
|
||||
bool HasEncryptedContent(const CIAHeader* header = nullptr) const;
|
||||
|
||||
void SetTitleID(u64 title_id);
|
||||
void SetTitleType(u32 type);
|
||||
|
|
|
@ -425,8 +425,8 @@ CIAFile::CIAFile(Core::System& system_, Service::FS::MediaType media_type, bool
|
|||
// If data is being installing from CDN, provide a fake header to the container so that
|
||||
// it's not uninitialized.
|
||||
if (from_cdn) {
|
||||
FileSys::CIAContainer::Header fake_header{
|
||||
.header_size = sizeof(FileSys::CIAContainer::Header),
|
||||
FileSys::CIAHeader fake_header{
|
||||
.header_size = sizeof(FileSys::CIAHeader),
|
||||
.type = 0,
|
||||
.version = 0,
|
||||
.cert_size = 0,
|
||||
|
@ -548,6 +548,11 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
|||
buffer + (range_min - offset) + available_to_write);
|
||||
|
||||
if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) {
|
||||
if (!decryption_authorized) {
|
||||
LOG_ERROR(Service_AM, "Blocked unauthorized encrypted CIA installation.");
|
||||
return Result(ErrorDescription::NotAuthorized, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size());
|
||||
}
|
||||
|
||||
|
@ -663,7 +668,8 @@ Result CIAFile::PrepareToImportContent(const FileSys::TitleMetadata& tmd) {
|
|||
content_file_paths.emplace_back(path);
|
||||
}
|
||||
|
||||
if (container.GetTitleMetadata().HasEncryptedContent()) {
|
||||
if (container.GetTitleMetadata().HasEncryptedContent(from_cdn ? nullptr
|
||||
: container.GetHeader())) {
|
||||
if (!decryption_authorized) {
|
||||
LOG_ERROR(Service_AM, "Blocked unauthorized encrypted CIA installation.");
|
||||
return {ErrorDescription::NotAuthorized, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
|
@ -728,9 +734,7 @@ Result CIAFile::ProvideTMDForAdditionalContent(const FileSys::TitleMetadata& tmd
|
|||
|
||||
is_additional_content = true;
|
||||
|
||||
PrepareToImportContent(container.GetTitleMetadata());
|
||||
|
||||
return ResultSuccess;
|
||||
return PrepareToImportContent(container.GetTitleMetadata());
|
||||
}
|
||||
|
||||
const FileSys::TitleMetadata& CIAFile::GetTMD() {
|
||||
|
@ -762,6 +766,11 @@ ResultVal<std::size_t> CIAFile::WriteContentDataIndexed(u16 content_index, u64 o
|
|||
std::vector<u8> temp(buffer, buffer + std::min(static_cast<u64>(length), remaining_to_write));
|
||||
|
||||
if ((tmd.GetContentTypeByIndex(content_index) & FileSys::TMDContentTypeFlag::Encrypted) != 0) {
|
||||
if (!decryption_authorized) {
|
||||
LOG_ERROR(Service_AM, "Blocked unauthorized encrypted CIA installation.");
|
||||
return Result(ErrorDescription::NotAuthorized, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
decryption_state->content[content_index].ProcessData(temp.data(), temp.data(), temp.size());
|
||||
}
|
||||
|
||||
|
@ -1000,7 +1009,7 @@ InstallStatus InstallCIA(const std::string& path,
|
|||
Core::System::GetInstance(),
|
||||
Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID()));
|
||||
|
||||
if (container.GetTitleMetadata().HasEncryptedContent()) {
|
||||
if (container.GetTitleMetadata().HasEncryptedContent(container.GetHeader())) {
|
||||
LOG_ERROR(Service_AM, "File {} is encrypted! Aborting...", path);
|
||||
return InstallStatus::ErrorEncrypted;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue