mirror of
https://github.com/azahar-emu/azahar.git
synced 2025-03-03 17:25:21 +01:00
Encrypt downloaded apps with a console unique key
This commit is contained in:
parent
b8e138679c
commit
8ad53ca705
@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -126,7 +126,7 @@ void ConfigureDebug::SetConfiguration() {
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
|
||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||
Settings::values.gdbstub_port = static_cast<u16>(ui->gdbport_spinbox->value());
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Settings::values.log_regex_filter = ui->log_regex_filter_edit->text().toStdString();
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -412,7 +412,7 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
s32 new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
|
||||
s32 new_birthday = ui->combo_birthday->currentIndex() + 1;
|
||||
if (birthmonth != new_birthmonth || birthday != new_birthday) {
|
||||
cfg->SetBirthday(new_birthmonth, new_birthday);
|
||||
cfg->SetBirthday(static_cast<u8>(new_birthmonth), static_cast<u8>(new_birthday));
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -142,8 +142,8 @@ void HostRoomWindow::Host() {
|
||||
}
|
||||
if (auto room = Network::GetRoom().lock()) {
|
||||
bool created = room->Create(ui->room_name->text().toStdString(),
|
||||
ui->room_description->toPlainText().toStdString(), "", port,
|
||||
password, ui->max_player->value(),
|
||||
ui->room_description->toPlainText().toStdString(), "",
|
||||
static_cast<u16>(port), password, ui->max_player->value(),
|
||||
NetSettings::values.citra_username, game_name.toStdString(),
|
||||
game_id, CreateVerifyBackend(is_public), ban_list);
|
||||
if (!created) {
|
||||
@ -193,7 +193,8 @@ void HostRoomWindow::Host() {
|
||||
}
|
||||
#endif
|
||||
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
|
||||
"127.0.0.1", port, 0, Network::NoPreferredMac, password, token);
|
||||
"127.0.0.1", static_cast<u16>(port), 0, Network::NoPreferredMac, password,
|
||||
token);
|
||||
|
||||
// Store settings
|
||||
UISettings::values.room_nickname = ui->username->text();
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -174,8 +174,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
||||
}
|
||||
#endif
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(), port, 0,
|
||||
Network::NoPreferredMac, password, token);
|
||||
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(),
|
||||
static_cast<u16>(port), 0, Network::NoPreferredMac, password, token);
|
||||
}
|
||||
});
|
||||
watcher->setFuture(f);
|
||||
|
@ -187,7 +187,7 @@ endif()
|
||||
create_target_directory_groups(citra_common)
|
||||
|
||||
target_link_libraries(citra_common PUBLIC fmt library-headers microprofile Boost::boost Boost::serialization Boost::iostreams)
|
||||
target_link_libraries(citra_common PRIVATE zstd)
|
||||
target_link_libraries(citra_common PRIVATE cryptopp zstd)
|
||||
|
||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(citra_common PRIVATE xbyak)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -13,7 +13,10 @@
|
||||
#include <unordered_map>
|
||||
#include <boost/iostreams/device/file_descriptor.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_paths.h"
|
||||
@ -1136,7 +1139,7 @@ u64 IOFile::GetSize() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IOFile::Seek(s64 off, int origin) {
|
||||
bool IOFile::SeekImpl(s64 off, int origin) {
|
||||
if (!IsOpen() || 0 != fseeko(m_file, off, origin))
|
||||
m_good = false;
|
||||
|
||||
@ -1240,6 +1243,107 @@ bool IOFile::Resize(u64 size) {
|
||||
return m_good;
|
||||
}
|
||||
|
||||
struct CryptoIOFileImpl {
|
||||
|
||||
std::vector<u8> key;
|
||||
std::vector<u8> iv;
|
||||
|
||||
CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d;
|
||||
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption e;
|
||||
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
std::size_t ReadImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size) {
|
||||
std::size_t res = f.IOFile::ReadImpl(data, length, data_size);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
||||
e.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) {
|
||||
std::size_t res = f.IOFile::ReadAtImpl(data, length, data_size, offset);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.Seek(offset);
|
||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
||||
e.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t WriteImpl(CryptoIOFile& f, const void* data, std::size_t length,
|
||||
std::size_t data_size) {
|
||||
if (write_buffer.size() < length * data_size) {
|
||||
write_buffer.resize(length * data_size);
|
||||
}
|
||||
e.ProcessData(write_buffer.data(), reinterpret_cast<const CryptoPP::byte*>(data),
|
||||
length * data_size);
|
||||
std::size_t res = f.IOFile::WriteImpl(write_buffer.data(), length, data_size);
|
||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||
d.Seek(f.IOFile::Tell());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SeekImpl(CryptoIOFile& f, s64 off, int origin) {
|
||||
bool res = f.IOFile::SeekImpl(off, origin);
|
||||
if (res) {
|
||||
u64 pos = f.IOFile::Tell();
|
||||
d.Seek(pos);
|
||||
e.Seek(pos);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
CryptoIOFile::CryptoIOFile() : IOFile() {
|
||||
impl = std::make_unique<CryptoIOFileImpl>();
|
||||
}
|
||||
|
||||
CryptoIOFile::CryptoIOFile(const std::string& filename, const char openmode[],
|
||||
const std::vector<u8>& aes_key, const std::vector<u8>& aes_iv, int flags)
|
||||
: IOFile(filename, openmode, flags) {
|
||||
impl = std::make_unique<CryptoIOFileImpl>();
|
||||
impl->key = aes_key;
|
||||
impl->iv = aes_iv;
|
||||
impl->d.SetKeyWithIV(aes_key.data(), aes_key.size(), aes_iv.data());
|
||||
impl->e.SetKeyWithIV(aes_key.data(), aes_key.size(), aes_iv.data());
|
||||
}
|
||||
|
||||
CryptoIOFile::~CryptoIOFile() {}
|
||||
|
||||
std::size_t CryptoIOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) {
|
||||
return impl->ReadImpl(*this, data, length, data_size);
|
||||
}
|
||||
|
||||
std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) {
|
||||
return impl->ReadAtImpl(*this, data, length, data_size, offset);
|
||||
}
|
||||
|
||||
std::size_t CryptoIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
||||
return impl->WriteImpl(*this, data, length, data_size);
|
||||
}
|
||||
|
||||
bool CryptoIOFile::SeekImpl(s64 off, int origin) {
|
||||
return impl->SeekImpl(*this, off, origin);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void CryptoIOFile::serialize(Archive& ar, const unsigned int) {
|
||||
ar & impl->key;
|
||||
ar & impl->iv;
|
||||
if (Archive::is_loading::value) {
|
||||
impl->e.SetKeyWithIV(impl->key.data(), impl->key.size(), impl->iv.data());
|
||||
impl->d.SetKeyWithIV(impl->key.data(), impl->key.size(), impl->iv.data());
|
||||
}
|
||||
ar& boost::serialization::base_object<IOFile>(*this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using boost_iostreams = boost::iostreams::stream<T>;
|
||||
|
||||
@ -1271,3 +1375,6 @@ void OpenFStream<std::ios_base::out>(
|
||||
fstream.open(file_descriptor_sink);
|
||||
}
|
||||
} // namespace FileUtil
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileUtil::IOFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileUtil::CryptoIOFile)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -12,14 +12,18 @@
|
||||
#include <functional>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/wrapper.hpp>
|
||||
#include "common/common_types.h"
|
||||
#ifdef _MSC_VER
|
||||
@ -267,6 +271,8 @@ enum class DirectorySeparator {
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
struct CryptoIOFileImpl;
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
// and make forgetting an fclose() harder
|
||||
@ -279,7 +285,7 @@ public:
|
||||
// isn't considered "locked" while citra is open and people can open the log file and view it
|
||||
IOFile(const std::string& filename, const char openmode[], int flags = 0);
|
||||
|
||||
~IOFile();
|
||||
virtual ~IOFile();
|
||||
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
@ -369,14 +375,10 @@ public:
|
||||
* @returns Count of T data successfully read.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
|
||||
[[nodiscard]] size_t ReadSpan(std::span<T> data) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fread(data.data(), sizeof(T), data.size(), m_file);
|
||||
return ReadImpl(data.data(), data.size(), sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -395,14 +397,10 @@ public:
|
||||
* @returns Count of T data successfully written.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
|
||||
[[nodiscard]] size_t WriteSpan(std::span<const T> data) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fwrite(data.data(), sizeof(T), data.size(), m_file);
|
||||
return WriteImpl(data.data(), data.size(), sizeof(T));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsOpen() const {
|
||||
@ -426,7 +424,9 @@ public:
|
||||
return IsGood();
|
||||
}
|
||||
|
||||
bool Seek(s64 off, int origin);
|
||||
bool Seek(s64 off, int origin) {
|
||||
return SeekImpl(off, origin);
|
||||
}
|
||||
[[nodiscard]] u64 Tell() const;
|
||||
[[nodiscard]] u64 GetSize() const;
|
||||
bool Resize(u64 size);
|
||||
@ -438,12 +438,24 @@ public:
|
||||
std::clearerr(m_file);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset);
|
||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||
virtual bool IsCrypto() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& Filename() const {
|
||||
return filename;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend struct CryptoIOFileImpl;
|
||||
virtual std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||
virtual std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset);
|
||||
virtual std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||
|
||||
virtual bool SeekImpl(s64 off, int origin);
|
||||
|
||||
private:
|
||||
bool Open();
|
||||
|
||||
std::FILE* m_file = nullptr;
|
||||
@ -472,6 +484,37 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class CryptoIOFile : public IOFile {
|
||||
public:
|
||||
CryptoIOFile();
|
||||
|
||||
// flags is used for windows specific file open mode flags, which
|
||||
// allows citra to open the logs in shared write mode, so that the file
|
||||
// isn't considered "locked" while citra is open and people can open the log file and view it
|
||||
CryptoIOFile(const std::string& filename, const char openmode[], const std::vector<u8>& aes_key,
|
||||
const std::vector<u8>& aes_iv, int flags = 0);
|
||||
|
||||
bool IsCrypto() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
~CryptoIOFile() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CryptoIOFileImpl> impl;
|
||||
|
||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
||||
std::size_t offset) override;
|
||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
||||
|
||||
bool SeekImpl(s64 off, int origin) override;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
template <std::ios_base::openmode o, typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename);
|
||||
} // namespace FileUtil
|
||||
@ -485,3 +528,6 @@ void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmod
|
||||
fstream.open(filename, openmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileUtil::IOFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileUtil::CryptoIOFile)
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -174,7 +174,7 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co
|
||||
u32 tmp = (first ? cheat_lines[state.current_line_nr].first
|
||||
: cheat_lines[state.current_line_nr].value) >>
|
||||
bit_offset;
|
||||
system.Memory().Write8(addr, tmp);
|
||||
system.Memory().Write8(addr, static_cast<u8>(tmp));
|
||||
addr += 1;
|
||||
num_bytes -= 1;
|
||||
bit_offset += 8;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "core/file_sys/patch.h"
|
||||
#include "core/file_sys/seed_db.h"
|
||||
#include "core/hw/aes/key.h"
|
||||
#include "core/hw/unique_data.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
@ -112,7 +113,7 @@ static bool LZSS_Decompress(std::span<const u8> compressed, std::span<u8> decomp
|
||||
|
||||
NCCHContainer::NCCHContainer(const std::string& filepath, u32 ncch_offset, u32 partition)
|
||||
: ncch_offset(ncch_offset), partition(partition), filepath(filepath) {
|
||||
file = FileUtil::IOFile(filepath, "rb");
|
||||
file = std::make_unique<FileUtil::IOFile>(filepath, "rb");
|
||||
}
|
||||
|
||||
Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath_, u32 ncch_offset_,
|
||||
@ -120,9 +121,9 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath_, u32 n
|
||||
filepath = filepath_;
|
||||
ncch_offset = ncch_offset_;
|
||||
partition = partition_;
|
||||
file = FileUtil::IOFile(filepath_, "rb");
|
||||
file = std::make_unique<FileUtil::IOFile>(filepath_, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
if (!file->IsOpen()) {
|
||||
LOG_WARNING(Service_FS, "Failed to open {}", filepath);
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
@ -135,33 +136,47 @@ Loader::ResultStatus NCCHContainer::LoadHeader() {
|
||||
if (has_header) {
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
if (!file.IsOpen()) {
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!file->IsOpen()) {
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
// Reset read pointer in case this file has been read before.
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
|
||||
if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) {
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
NCSD_Header ncsd_header;
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
file->ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
LOG_ERROR(Service_FS, "{}", ncch_offset);
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
}
|
||||
|
||||
// Verify we are loading the correct file type...
|
||||
if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) {
|
||||
// We may be loading a crypto file, try again
|
||||
if (i == 0) {
|
||||
file.reset();
|
||||
file = HW::UniqueData::OpenUniqueCryptoFile(
|
||||
filepath, "rb", HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
} else {
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset read pointer in case this file has been read before.
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
|
||||
if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) {
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
NCSD_Header ncsd_header;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
LOG_ERROR(Service_FS, "{}", ncch_offset);
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
}
|
||||
|
||||
// Verify we are loading the correct file type...
|
||||
if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) {
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
if (file->IsCrypto()) {
|
||||
LOG_DEBUG(Service_FS, "NCCH file has console unique crypto");
|
||||
}
|
||||
|
||||
has_header = true;
|
||||
@ -174,30 +189,45 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
|
||||
int block_size = kBlockSize;
|
||||
|
||||
if (file.IsOpen()) {
|
||||
size_t file_size = file.GetSize();
|
||||
if (file->IsOpen()) {
|
||||
size_t file_size;
|
||||
|
||||
// Reset read pointer in case this file has been read before.
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
file_size = file->GetSize();
|
||||
|
||||
if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
// Reset read pointer in case this file has been read before.
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
NCSD_Header ncsd_header;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
file.Seek(ncch_offset, SEEK_SET);
|
||||
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
||||
if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
|
||||
NCSD_Header ncsd_header;
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
file->ReadBytes(&ncsd_header, sizeof(NCSD_Header));
|
||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||
ASSERT(partition < 8);
|
||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||
file->Seek(ncch_offset, SEEK_SET);
|
||||
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||
}
|
||||
|
||||
// Verify we are loading the correct file type...
|
||||
if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) {
|
||||
// We may be loading a crypto file, try again
|
||||
if (i == 0) {
|
||||
file = HW::UniqueData::OpenUniqueCryptoFile(
|
||||
filepath, "rb", HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
} else {
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we are loading the correct file type...
|
||||
if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
if (file->IsCrypto()) {
|
||||
LOG_DEBUG(Service_FS, "NCCH file has console unique crypto");
|
||||
}
|
||||
|
||||
has_header = true;
|
||||
|
||||
@ -215,12 +245,12 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
// System archives and DLC don't have an extended header but have RomFS
|
||||
// Proto apps don't have an ext header size
|
||||
if (ncch_header.extended_header_size || is_proto) {
|
||||
auto read_exheader = [this](FileUtil::IOFile& file) {
|
||||
auto read_exheader = [this](FileUtil::IOFile* file) {
|
||||
const std::size_t size = sizeof(exheader_header);
|
||||
return file && file.ReadBytes(&exheader_header, size) == size;
|
||||
return file && file->ReadBytes(&exheader_header, size) == size;
|
||||
};
|
||||
|
||||
if (!read_exheader(file)) {
|
||||
if (!read_exheader(file.get())) {
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
@ -235,7 +265,7 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
bool has_exheader_override = false;
|
||||
for (const auto& path : exheader_override_paths) {
|
||||
FileUtil::IOFile exheader_override_file{path, "rb"};
|
||||
if (read_exheader(exheader_override_file)) {
|
||||
if (read_exheader(&exheader_override_file)) {
|
||||
has_exheader_override = true;
|
||||
break;
|
||||
}
|
||||
@ -289,11 +319,17 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
LOG_DEBUG(Service_FS, "ExeFS offset: 0x{:08X}", exefs_offset);
|
||||
LOG_DEBUG(Service_FS, "ExeFS size: 0x{:08X}", exefs_size);
|
||||
|
||||
file.Seek(exefs_offset + ncch_offset, SEEK_SET);
|
||||
if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
|
||||
file->Seek(exefs_offset + ncch_offset, SEEK_SET);
|
||||
if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
exefs_file = FileUtil::IOFile(filepath, "rb");
|
||||
if (file->IsCrypto()) {
|
||||
exefs_file = HW::UniqueData::OpenUniqueCryptoFile(
|
||||
filepath, "rb", HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
} else {
|
||||
exefs_file = std::make_unique<FileUtil::IOFile>(filepath, "rb");
|
||||
}
|
||||
|
||||
has_exefs = true;
|
||||
}
|
||||
|
||||
@ -322,15 +358,15 @@ Loader::ResultStatus NCCHContainer::LoadOverrides() {
|
||||
std::string exefs_override = filepath + ".exefs";
|
||||
std::string exefsdir_override = filepath + ".exefsdir/";
|
||||
if (FileUtil::Exists(exefs_override)) {
|
||||
exefs_file = FileUtil::IOFile(exefs_override, "rb");
|
||||
exefs_file = std::make_unique<FileUtil::IOFile>(exefs_override, "rb");
|
||||
|
||||
if (exefs_file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) == sizeof(ExeFs_Header)) {
|
||||
if (exefs_file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) == sizeof(ExeFs_Header)) {
|
||||
LOG_DEBUG(Service_FS, "Loading ExeFS section from {}", exefs_override);
|
||||
exefs_offset = 0;
|
||||
is_tainted = true;
|
||||
has_exefs = true;
|
||||
} else {
|
||||
exefs_file = FileUtil::IOFile(filepath, "rb");
|
||||
exefs_file = std::make_unique<FileUtil::IOFile>(filepath, "rb");
|
||||
}
|
||||
} else if (FileUtil::Exists(exefsdir_override) && FileUtil::IsDirectory(exefsdir_override)) {
|
||||
is_tainted = true;
|
||||
@ -385,9 +421,9 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
|
||||
std::size_t logo_size = ncch_header.logo_region_size * block_size;
|
||||
|
||||
buffer.resize(logo_size);
|
||||
file.Seek(ncch_offset + logo_offset, SEEK_SET);
|
||||
file->Seek(ncch_offset + logo_offset, SEEK_SET);
|
||||
|
||||
if (file.ReadBytes(buffer.data(), logo_size) != logo_size) {
|
||||
if (file->ReadBytes(buffer.data(), logo_size) != logo_size) {
|
||||
LOG_ERROR(Service_FS, "Could not read NCCH logo");
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
@ -398,7 +434,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
|
||||
}
|
||||
|
||||
// If we don't have any separate files, we'll need a full ExeFS
|
||||
if (!exefs_file.IsOpen())
|
||||
if (!exefs_file->IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
LOG_DEBUG(Service_FS, "{} sections:", kMaxSections);
|
||||
@ -414,14 +450,14 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
|
||||
s64 section_offset =
|
||||
is_proto ? section.offset
|
||||
: (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
|
||||
exefs_file.Seek(section_offset, SEEK_SET);
|
||||
exefs_file->Seek(section_offset, SEEK_SET);
|
||||
|
||||
size_t section_size = is_proto ? Common::AlignUp(section.size, 0x10) : section.size;
|
||||
|
||||
if (strcmp(section.name, ".code") == 0 && is_compressed) {
|
||||
// Section is compressed, read compressed .code section...
|
||||
std::vector<u8> temp_buffer(section_size);
|
||||
if (exefs_file.ReadBytes(temp_buffer.data(), temp_buffer.size()) !=
|
||||
if (exefs_file->ReadBytes(temp_buffer.data(), temp_buffer.size()) !=
|
||||
temp_buffer.size())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
@ -433,7 +469,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
|
||||
} else {
|
||||
// Section is uncompressed...
|
||||
buffer.resize(section_size);
|
||||
if (exefs_file.ReadBytes(buffer.data(), section_size) != section_size)
|
||||
if (exefs_file->ReadBytes(buffer.data(), section_size) != section_size)
|
||||
return Loader::ResultStatus::Error;
|
||||
}
|
||||
|
||||
@ -552,7 +588,7 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf
|
||||
return Loader::ResultStatus::ErrorNotUsed;
|
||||
}
|
||||
|
||||
if (!file.IsOpen())
|
||||
if (!file->IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * block_size) + 0x1000;
|
||||
@ -561,17 +597,22 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf
|
||||
LOG_DEBUG(Service_FS, "RomFS offset: 0x{:08X}", romfs_offset);
|
||||
LOG_DEBUG(Service_FS, "RomFS size: 0x{:08X}", romfs_size);
|
||||
|
||||
if (file.GetSize() < romfs_offset + romfs_size)
|
||||
if (file->GetSize() < romfs_offset + romfs_size)
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
// We reopen the file, to allow its position to be independent from file's
|
||||
FileUtil::IOFile romfs_file_inner(filepath, "rb");
|
||||
if (!romfs_file_inner.IsOpen())
|
||||
std::unique_ptr<FileUtil::IOFile> romfs_file_inner;
|
||||
if (file->IsCrypto()) {
|
||||
romfs_file_inner = HW::UniqueData::OpenUniqueCryptoFile(
|
||||
filepath, "rb", HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
} else {
|
||||
romfs_file_inner = std::make_unique<FileUtil::IOFile>(filepath, "rb");
|
||||
}
|
||||
|
||||
if (!romfs_file_inner->IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
std::shared_ptr<RomFSReader> direct_romfs;
|
||||
|
||||
direct_romfs =
|
||||
std::shared_ptr<RomFSReader> direct_romfs =
|
||||
std::make_shared<DirectRomFSReader>(std::move(romfs_file_inner), romfs_offset, romfs_size);
|
||||
|
||||
const auto path =
|
||||
@ -590,6 +631,9 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf
|
||||
}
|
||||
|
||||
Loader::ResultStatus NCCHContainer::DumpRomFS(const std::string& target_path) {
|
||||
if (file->IsCrypto())
|
||||
return Loader::ResultStatus::ErrorEncrypted;
|
||||
|
||||
std::shared_ptr<RomFSReader> direct_romfs;
|
||||
Loader::ResultStatus result = ReadRomFS(direct_romfs, false);
|
||||
if (result != Loader::ResultStatus::Success)
|
||||
@ -608,12 +652,13 @@ Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr<RomFSReade
|
||||
// Check for RomFS overrides
|
||||
std::string split_filepath = filepath + ".romfs";
|
||||
if (FileUtil::Exists(split_filepath)) {
|
||||
FileUtil::IOFile romfs_file_inner(split_filepath, "rb");
|
||||
if (romfs_file_inner.IsOpen()) {
|
||||
std::unique_ptr<FileUtil::IOFile> romfs_file_inner =
|
||||
std::make_unique<FileUtil::IOFile>(split_filepath, "rb");
|
||||
if (romfs_file_inner->IsOpen()) {
|
||||
LOG_WARNING(Service_FS, "File {} overriding built-in RomFS; LayeredFS not enabled",
|
||||
split_filepath);
|
||||
romfs_file = std::make_shared<DirectRomFSReader>(std::move(romfs_file_inner), 0,
|
||||
romfs_file_inner.GetSize());
|
||||
romfs_file_inner->GetSize());
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -353,8 +353,8 @@ private:
|
||||
u32 partition = 0;
|
||||
|
||||
std::string filepath;
|
||||
FileUtil::IOFile file;
|
||||
FileUtil::IOFile exefs_file;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
std::unique_ptr<FileUtil::IOFile> exefs_file;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -1,3 +1,7 @@
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cryptopp/aes.h>
|
||||
@ -24,7 +28,7 @@ std::size_t DirectRomFSReader::ReadFile(std::size_t offset, std::size_t length,
|
||||
|
||||
// Skip cache if the read is too big
|
||||
if (segments.size() == 1 && segments[0].second > cache_line_size) {
|
||||
length = file.ReadAtBytes(buffer, length, file_offset + offset);
|
||||
length = file->ReadAtBytes(buffer, length, file_offset + offset);
|
||||
LOG_TRACE(Service_FS, "RomFS Cache SKIP: offset={}, length={}", offset, length);
|
||||
return length;
|
||||
}
|
||||
@ -38,7 +42,7 @@ std::size_t DirectRomFSReader::ReadFile(std::size_t offset, std::size_t length,
|
||||
auto cache_entry = cache.request(page);
|
||||
if (!cache_entry.first) {
|
||||
// If not found, read from disk and cache the data
|
||||
read_size = file.ReadAtBytes(cache_entry.second.data(), read_size, file_offset + page);
|
||||
read_size = file->ReadAtBytes(cache_entry.second.data(), read_size, file_offset + page);
|
||||
LOG_TRACE(Service_FS, "RomFS Cache MISS: page={}, length={}, into={}", page, seg.second,
|
||||
(seg.first - page));
|
||||
} else {
|
||||
|
@ -1,3 +1,7 @@
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
@ -41,7 +45,8 @@ private:
|
||||
*/
|
||||
class DirectRomFSReader : public RomFSReader {
|
||||
public:
|
||||
DirectRomFSReader(FileUtil::IOFile&& file, std::size_t file_offset, std::size_t data_size)
|
||||
DirectRomFSReader(std::unique_ptr<FileUtil::IOFile>&& file, std::size_t file_offset,
|
||||
std::size_t data_size)
|
||||
: file(std::move(file)), file_offset(file_offset), data_size(data_size) {}
|
||||
|
||||
~DirectRomFSReader() override = default;
|
||||
@ -57,7 +62,7 @@ public:
|
||||
bool CacheReady(std::size_t file_offset, std::size_t length) override;
|
||||
|
||||
private:
|
||||
FileUtil::IOFile file;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
u64 file_offset;
|
||||
u64 data_size;
|
||||
|
||||
|
@ -95,13 +95,24 @@ public:
|
||||
};
|
||||
|
||||
NCCHCryptoFile::NCCHCryptoFile(const std::string& out_file) {
|
||||
file = FileUtil::IOFile(out_file, "wb");
|
||||
// A console unique crypto file is used to store the decrypted NCCH file. This is done
|
||||
// to prevent Azahar being used as a tool to download easy shareable decrypted contents
|
||||
// from the eshop.
|
||||
file = HW::UniqueData::OpenUniqueCryptoFile(out_file, "wb",
|
||||
HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
if (!file->IsOpen()) {
|
||||
is_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
if (is_error)
|
||||
return;
|
||||
|
||||
if (is_not_ncch) {
|
||||
file->WriteBytes(buffer, length);
|
||||
}
|
||||
|
||||
const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
|
||||
|
||||
if (header_size != sizeof(NCCH_Header)) {
|
||||
@ -114,7 +125,10 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
|
||||
if (!header_parsed && header_size == sizeof(NCCH_Header)) {
|
||||
if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) {
|
||||
is_error = true;
|
||||
// Most likely DS contents, store without additional operations
|
||||
is_not_ncch = true;
|
||||
file->WriteBytes(&ncch_header, sizeof(ncch_header));
|
||||
file->WriteBytes(buffer, length);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -278,7 +292,7 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
|
||||
u8 prev_crypto = ncch_header.no_crypto;
|
||||
ncch_header.no_crypto.Assign(1);
|
||||
file.WriteBytes(&ncch_header, sizeof(ncch_header));
|
||||
file->WriteBytes(&ncch_header, sizeof(ncch_header));
|
||||
written += sizeof(ncch_header);
|
||||
ncch_header.no_crypto.Assign(prev_crypto);
|
||||
}
|
||||
@ -305,7 +319,7 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
if (reg == nullptr) {
|
||||
// This file has no encryption
|
||||
size_t to_write = length;
|
||||
file.WriteBytes(buffer, to_write);
|
||||
file->WriteBytes(buffer, to_write);
|
||||
written += to_write;
|
||||
buffer += to_write;
|
||||
length -= to_write;
|
||||
@ -313,7 +327,7 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
if (written < reg->offset) {
|
||||
// Not inside a crypto region
|
||||
size_t to_write = std::min(length, reg->offset - written);
|
||||
file.WriteBytes(buffer, to_write);
|
||||
file->WriteBytes(buffer, to_write);
|
||||
written += to_write;
|
||||
buffer += to_write;
|
||||
length -= to_write;
|
||||
@ -347,7 +361,7 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
d.Seek(offset);
|
||||
}
|
||||
d.ProcessData(temp.data(), buffer, to_write);
|
||||
file.WriteBytes(temp.data(), to_write);
|
||||
file->WriteBytes(temp.data(), to_write);
|
||||
|
||||
if (reg->type == CryptoRegion::EXEFS_HDR) {
|
||||
if (exefs_header_written != sizeof(ExeFs_Header)) {
|
||||
@ -376,7 +390,7 @@ void NCCHCryptoFile::Write(const u8* buffer, std::size_t length) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file.WriteBytes(buffer, to_write);
|
||||
file->WriteBytes(buffer, to_write);
|
||||
}
|
||||
written += to_write;
|
||||
buffer += to_write;
|
||||
@ -538,7 +552,6 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
||||
// has been written since we might get a written buffer which contains multiple .app
|
||||
// contents or only part of a larger .app's contents.
|
||||
const u64 offset_max = offset + length;
|
||||
bool success = true;
|
||||
for (std::size_t i = 0; i < content_written.size(); i++) {
|
||||
if (content_written[i] < container.GetContentSize(i)) {
|
||||
// The size, minimum unwritten offset, and maximum unwritten offset of this content
|
||||
@ -573,8 +586,11 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
||||
}
|
||||
|
||||
file.Write(temp.data(), temp.size());
|
||||
if (file.IsError())
|
||||
success = false;
|
||||
if (file.IsError()) {
|
||||
// This can never happen in real HW
|
||||
return Result(ErrCodes::InvalidImportState, ErrorModule::AM,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
// Keep tabs on how much of this content ID has been written so new range_min
|
||||
// values can be calculated.
|
||||
@ -584,7 +600,7 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
||||
}
|
||||
}
|
||||
|
||||
return success ? length : 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush,
|
||||
@ -705,13 +721,17 @@ ResultVal<std::size_t> CIAFile::WriteContentDataIndexed(u16 content_index, u64 o
|
||||
}
|
||||
|
||||
file.Write(temp.data(), temp.size());
|
||||
bool success = !file.IsError();
|
||||
if (file.IsError()) {
|
||||
// This can never happen in real HW
|
||||
return Result(ErrCodes::InvalidImportState, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
content_written[content_index] += temp.size();
|
||||
LOG_DEBUG(Service_AM, "Wrote {} to content {}, total {}", temp.size(), content_index,
|
||||
content_written[content_index]);
|
||||
|
||||
return success ? temp.size() : 0;
|
||||
return temp.size();
|
||||
}
|
||||
|
||||
u64 CIAFile::GetSize() const {
|
||||
@ -2313,8 +2333,6 @@ void Module::Interface::GetNumImportTitleContextsImpl(IPC::RequestParser& rp,
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
// TODO: Make this actually do something:
|
||||
/*
|
||||
u32 count = 0;
|
||||
for (auto it = am->import_title_contexts.begin(); it != am->import_title_contexts.end(); it++) {
|
||||
if ((include_installing &&
|
||||
@ -2325,9 +2343,8 @@ void Module::Interface::GetNumImportTitleContextsImpl(IPC::RequestParser& rp,
|
||||
count++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
rb.Push<u32>(static_cast<u32>(am->import_title_contexts.size()));
|
||||
rb.Push<u32>(count);
|
||||
}
|
||||
|
||||
void Module::Interface::GetImportTitleContextListImpl(IPC::RequestParser& rp,
|
||||
@ -3672,6 +3689,8 @@ void Module::Interface::Sign(Kernel::HLERequestContext& ctx) {
|
||||
template <class Archive>
|
||||
void Module::serialize(Archive& ar, const unsigned int) {
|
||||
ar & cia_installing;
|
||||
ar & force_old_device_id;
|
||||
ar & force_new_device_id;
|
||||
ar & am_title_list;
|
||||
ar & system_updater_mutex;
|
||||
}
|
||||
|
@ -121,8 +121,9 @@ public:
|
||||
|
||||
private:
|
||||
friend class CIAFile;
|
||||
FileUtil::IOFile file;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
bool is_error = false;
|
||||
bool is_not_ncch = false;
|
||||
bool decryption_authorized = false;
|
||||
|
||||
std::size_t written = 0;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cryptopp/sha.h>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/certificate.h"
|
||||
@ -105,6 +106,7 @@ SecureDataLoadStatus LoadOTP() {
|
||||
|
||||
const std::string filepath = GetOTPPath();
|
||||
|
||||
HW::AES::InitKeys();
|
||||
auto otp_keyiv = HW::AES::GetOTPKeyIV();
|
||||
|
||||
auto loader_status = otp.Load(filepath, otp_keyiv.first, otp_keyiv.second);
|
||||
@ -226,4 +228,34 @@ void InvalidateSecureData() {
|
||||
movable.Invalidate();
|
||||
}
|
||||
|
||||
std::unique_ptr<FileUtil::IOFile> OpenUniqueCryptoFile(const std::string& filename,
|
||||
const char openmode[], UniqueCryptoFileID id,
|
||||
int flags) {
|
||||
LoadOTP();
|
||||
|
||||
if (!ct_cert.IsValid() || !otp.Valid()) {
|
||||
return std::make_unique<FileUtil::IOFile>();
|
||||
}
|
||||
|
||||
struct {
|
||||
ECC::PublicKey pkey;
|
||||
u32 device_id;
|
||||
u32 id;
|
||||
} hash_data;
|
||||
hash_data.pkey = ct_cert.GetPublicKeyECC();
|
||||
hash_data.device_id = otp.GetDeviceID();
|
||||
hash_data.id = static_cast<u32>(id);
|
||||
|
||||
CryptoPP::SHA256 hash;
|
||||
u8 digest[CryptoPP::SHA256::DIGESTSIZE];
|
||||
hash.CalculateDigest(digest, reinterpret_cast<CryptoPP::byte*>(&hash_data), sizeof(hash_data));
|
||||
|
||||
std::vector<u8> key(0x10);
|
||||
std::vector<u8> ctr(0x10);
|
||||
memcpy(key.data(), digest, 0x10);
|
||||
memcpy(ctr.data(), digest + 0x10, 12);
|
||||
|
||||
return std::make_unique<FileUtil::CryptoIOFile>(filename, openmode, key, ctr, flags);
|
||||
}
|
||||
|
||||
} // namespace HW::UniqueData
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Azahar Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
|
||||
namespace FileSys {
|
||||
class Certificate;
|
||||
@ -129,5 +130,13 @@ FileSys::Certificate& GetCTCert();
|
||||
FileSys::OTP& GetOTP();
|
||||
MovableSedFull& GetMovableSed();
|
||||
|
||||
enum class UniqueCryptoFileID {
|
||||
NCCH = 0,
|
||||
};
|
||||
|
||||
void InvalidateSecureData();
|
||||
|
||||
std::unique_ptr<FileUtil::IOFile> OpenUniqueCryptoFile(const std::string& filename,
|
||||
const char openmode[], UniqueCryptoFileID id,
|
||||
int flags = 0);
|
||||
} // namespace HW::UniqueData
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -314,8 +314,9 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileSys::RomFSReader>
|
||||
LOG_DEBUG(Loader, "RomFS size: {:#010X}", romfs_size);
|
||||
|
||||
// We reopen the file, to allow its position to be independent from file's
|
||||
FileUtil::IOFile romfs_file_inner(filepath, "rb");
|
||||
if (!romfs_file_inner.IsOpen())
|
||||
std::unique_ptr<FileUtil::IOFile> romfs_file_inner =
|
||||
std::make_unique<FileUtil::IOFile>(filepath, "rb");
|
||||
if (!romfs_file_inner->IsOpen())
|
||||
return ResultStatus::Error;
|
||||
|
||||
romfs_file = std::make_shared<FileSys::DirectRomFSReader>(std::move(romfs_file_inner),
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/hw/unique_data.h"
|
||||
#include "core/loader/ncch.h"
|
||||
#include "core/loader/smdh.h"
|
||||
#include "core/memory.h"
|
||||
@ -45,6 +46,19 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
|
||||
if (MakeMagic('N', 'C', 'C', 'H') == magic)
|
||||
return FileType::CXI;
|
||||
|
||||
std::unique_ptr<FileUtil::IOFile> file_crypto = HW::UniqueData::OpenUniqueCryptoFile(
|
||||
file.Filename(), "rb", HW::UniqueData::UniqueCryptoFileID::NCCH);
|
||||
|
||||
file_crypto->Seek(0x100, SEEK_SET);
|
||||
if (1 != file_crypto->ReadArray<u32>(&magic, 1))
|
||||
return FileType::Error;
|
||||
|
||||
if (MakeMagic('N', 'C', 'S', 'D') == magic)
|
||||
return FileType::CCI;
|
||||
|
||||
if (MakeMagic('N', 'C', 'C', 'H') == magic)
|
||||
return FileType::CXI;
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -23,7 +23,8 @@ struct ShaderRegs {
|
||||
|
||||
Common::Vec4<u8> GetIntUniform(u32 index) const {
|
||||
const auto& values = int_uniforms[index];
|
||||
return Common::MakeVec<u8>(values.x, values.y, values.z, values.w);
|
||||
return Common::MakeVec<u8>(static_cast<u8>(values.x), static_cast<u8>(values.y),
|
||||
static_cast<u8>(values.z), static_cast<u8>(values.w));
|
||||
}
|
||||
|
||||
INSERT_PADDING_WORDS(0x4);
|
||||
|
Loading…
x
Reference in New Issue
Block a user