mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-07 16:59:07 +00:00
Added encryption scheme display.
This commit is contained in:
+13
-5
@@ -296,7 +296,8 @@ std::vector<ContentSpecifier> SDMCImporter::ListContent() const {
|
|||||||
// Regex for half Title IDs
|
// Regex for half Title IDs
|
||||||
static const std::regex title_regex{"[0-9a-f]{8}"};
|
static const std::regex title_regex{"[0-9a-f]{8}"};
|
||||||
|
|
||||||
std::pair<std::string, u64> SDMCImporter::LoadTitleData(const std::string& path) const {
|
std::tuple<std::string, u64, EncryptionType, bool> SDMCImporter::LoadTitleData(
|
||||||
|
const std::string& path) const {
|
||||||
// Remove trailing '/'
|
// Remove trailing '/'
|
||||||
const auto sdmc_path = config.sdmc_path.substr(0, config.sdmc_path.size() - 1);
|
const auto sdmc_path = config.sdmc_path.substr(0, config.sdmc_path.size() - 1);
|
||||||
|
|
||||||
@@ -356,8 +357,14 @@ std::pair<std::string, u64> SDMCImporter::LoadTitleData(const std::string& path)
|
|||||||
|
|
||||||
u64 extdata_id{};
|
u64 extdata_id{};
|
||||||
ncch.ReadExtdataId(extdata_id);
|
ncch.ReadExtdataId(extdata_id);
|
||||||
return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)),
|
|
||||||
extdata_id};
|
EncryptionType encryption = EncryptionType::None;
|
||||||
|
ncch.ReadEncryptionType(encryption);
|
||||||
|
|
||||||
|
bool seed_crypto{};
|
||||||
|
ncch.ReadSeedCrypto(seed_crypto);
|
||||||
|
return {Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English)), extdata_id,
|
||||||
|
encryption, seed_crypto};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
|
void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
|
||||||
@@ -385,11 +392,12 @@ void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
|
|||||||
if (FileUtil::Exists(directory + virtual_name + "/content/")) {
|
if (FileUtil::Exists(directory + virtual_name + "/content/")) {
|
||||||
const auto content_path =
|
const auto content_path =
|
||||||
fmt::format("/title/{:08x}/{}/content/", high_id, virtual_name);
|
fmt::format("/title/{:08x}/{}/content/", high_id, virtual_name);
|
||||||
const auto& [name, extdata_id] = LoadTitleData(content_path);
|
const auto& [name, extdata_id, encryption, seed_crypto] =
|
||||||
|
LoadTitleData(content_path);
|
||||||
out.push_back(
|
out.push_back(
|
||||||
{type, id, FileUtil::Exists(citra_path + "content/"),
|
{type, id, FileUtil::Exists(citra_path + "content/"),
|
||||||
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"),
|
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/"),
|
||||||
name, extdata_id});
|
name, extdata_id, encryption, seed_crypto});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != ContentType::Application) {
|
if (type != ContentType::Application) {
|
||||||
|
|||||||
+17
-2
@@ -27,6 +27,18 @@ enum class ContentType {
|
|||||||
Sysdata,
|
Sysdata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption type of an importable content.
|
||||||
|
*/
|
||||||
|
enum class EncryptionType {
|
||||||
|
None,
|
||||||
|
FixedKey,
|
||||||
|
NCCHSecure1,
|
||||||
|
NCCHSecure2,
|
||||||
|
NCCHSecure3,
|
||||||
|
NCCHSecure4,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Struct that specifies an importable content.
|
* Struct that specifies an importable content.
|
||||||
*/
|
*/
|
||||||
@@ -37,6 +49,8 @@ struct ContentSpecifier {
|
|||||||
u64 maximum_size; ///< The maximum size of the content. May be slightly bigger than real size.
|
u64 maximum_size; ///< The maximum size of the content. May be slightly bigger than real size.
|
||||||
std::string name; ///< Optional. The content's preferred display name.
|
std::string name; ///< Optional. The content's preferred display name.
|
||||||
u64 extdata_id; ///< Extdata ID for Applications.
|
u64 extdata_id; ///< Extdata ID for Applications.
|
||||||
|
EncryptionType encryption = EncryptionType::None; ///< Only for NCCHs. Encryption scheme.
|
||||||
|
bool seed_crypto = false; ///< Only for NCCHs. Whether seed crypto is used.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,11 +140,12 @@ private:
|
|||||||
void DeleteSysdata(u64 id) const;
|
void DeleteSysdata(u64 id) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the English short title name and extdata id of a title.
|
* Loads the English short title name, extdata id and encryption of a title.
|
||||||
* @param path Path of the 'content' folder relative to the SDMC root folder.
|
* @param path Path of the 'content' folder relative to the SDMC root folder.
|
||||||
* Required to end with '/'.
|
* Required to end with '/'.
|
||||||
|
* @return {name, extdata_id, encryption, seed_crypto}
|
||||||
*/
|
*/
|
||||||
std::pair<std::string, u64> LoadTitleData(const std::string& path) const;
|
std::tuple<std::string, u64, EncryptionType, bool> LoadTitleData(const std::string& path) const;
|
||||||
|
|
||||||
bool is_good{};
|
bool is_good{};
|
||||||
Config config;
|
Config config;
|
||||||
|
|||||||
@@ -309,6 +309,53 @@ bool NCCHContainer::HasExHeader() {
|
|||||||
return has_exheader;
|
return has_exheader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultStatus NCCHContainer::ReadEncryptionType(EncryptionType& encryption) {
|
||||||
|
ResultStatus result = Load();
|
||||||
|
if (result != ResultStatus::Success)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (!has_header)
|
||||||
|
return ResultStatus::ErrorNotUsed;
|
||||||
|
|
||||||
|
if (!is_encrypted) {
|
||||||
|
encryption = EncryptionType::None;
|
||||||
|
} else if (ncch_header.fixed_key) {
|
||||||
|
encryption = EncryptionType::FixedKey;
|
||||||
|
} else {
|
||||||
|
switch (ncch_header.secondary_key_slot) {
|
||||||
|
case 0:
|
||||||
|
encryption = EncryptionType::NCCHSecure1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
encryption = EncryptionType::NCCHSecure2;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
encryption = EncryptionType::NCCHSecure3;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
encryption = EncryptionType::NCCHSecure4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_FS, "Unknown encryption type {:X}!", ncch_header.secondary_key_slot);
|
||||||
|
return ResultStatus::ErrorNotUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultStatus NCCHContainer::ReadSeedCrypto(bool& used) {
|
||||||
|
ResultStatus result = Load();
|
||||||
|
if (result != ResultStatus::Success)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (!has_header)
|
||||||
|
return ResultStatus::ErrorNotUsed;
|
||||||
|
|
||||||
|
used = ncch_header.seed_crypto;
|
||||||
|
return ResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct RomFSIVFCHeader {
|
struct RomFSIVFCHeader {
|
||||||
u32_le magic;
|
u32_le magic;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/decryptor.h"
|
#include "core/decryptor.h"
|
||||||
|
#include "core/importer.h"
|
||||||
#include "core/result_status.h"
|
#include "core/result_status.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -252,6 +253,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool HasExHeader();
|
bool HasExHeader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets encryption type (which key is used).
|
||||||
|
* @return ResultStatus result of function.
|
||||||
|
*/
|
||||||
|
ResultStatus ReadEncryptionType(EncryptionType& encryption);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether seed crypto is used.
|
||||||
|
* @return ResultStatus result of function.
|
||||||
|
*/
|
||||||
|
ResultStatus ReadSeedCrypto(bool& used);
|
||||||
|
|
||||||
NCCH_Header ncch_header;
|
NCCH_Header ncch_header;
|
||||||
ExHeader_Header exheader_header;
|
ExHeader_Header exheader_header;
|
||||||
ExeFs_Header exefs_header;
|
ExeFs_Header exefs_header;
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ static constexpr std::array<std::pair<Core::ContentType, const char*>, 7> Conten
|
|||||||
{Core::ContentType::Sysdata, QT_TR_NOOP("System Data")},
|
{Core::ContentType::Sysdata, QT_TR_NOOP("System Data")},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
static const std::unordered_map<Core::EncryptionType, const char*> EncryptionTypeMap{{
|
||||||
|
{Core::EncryptionType::None, QT_TR_NOOP("None")},
|
||||||
|
{Core::EncryptionType::FixedKey, QT_TR_NOOP("FixedKey")},
|
||||||
|
{Core::EncryptionType::NCCHSecure1, QT_TR_NOOP("Secure1")},
|
||||||
|
{Core::EncryptionType::NCCHSecure2, QT_TR_NOOP("Secure2")},
|
||||||
|
{Core::EncryptionType::NCCHSecure3, QT_TR_NOOP("Secure3")},
|
||||||
|
{Core::EncryptionType::NCCHSecure4, QT_TR_NOOP("Secure4")},
|
||||||
|
}};
|
||||||
|
|
||||||
QString GetContentName(const Core::ContentSpecifier& specifier) {
|
QString GetContentName(const Core::ContentSpecifier& specifier) {
|
||||||
return specifier.name.empty()
|
return specifier.name.empty()
|
||||||
? QStringLiteral("0x%1").arg(specifier.id, 16, 16, QLatin1Char('0'))
|
? QStringLiteral("0x%1").arg(specifier.id, 16, 16, QLatin1Char('0'))
|
||||||
@@ -84,11 +93,13 @@ ImportDialog::ImportDialog(QWidget* parent, const Core::Config& config)
|
|||||||
RelistContent();
|
RelistContent();
|
||||||
UpdateSizeDisplay();
|
UpdateSizeDisplay();
|
||||||
|
|
||||||
// Set up column widths
|
// Set up column widths.
|
||||||
ui->main->setColumnWidth(0, width() / 8);
|
// These values are tweaked with regard to the default dialog size.
|
||||||
ui->main->setColumnWidth(1, width() / 2);
|
ui->main->setColumnWidth(0, width() * 0.11);
|
||||||
ui->main->setColumnWidth(2, width() / 6);
|
ui->main->setColumnWidth(1, width() * 0.415);
|
||||||
ui->main->setColumnWidth(3, width() / 10);
|
ui->main->setColumnWidth(2, width() * 0.14);
|
||||||
|
ui->main->setColumnWidth(3, width() * 0.17);
|
||||||
|
ui->main->setColumnWidth(4, width() * 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImportDialog::~ImportDialog() = default;
|
ImportDialog::~ImportDialog() = default;
|
||||||
@@ -174,8 +185,20 @@ void ImportDialog::InsertSecondLevelItem(std::size_t row, const Core::ContentSpe
|
|||||||
name = GetContentName(content);
|
name = GetContentName(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString encryption = tr(EncryptionTypeMap.at(content.encryption));
|
||||||
|
if (content.seed_crypto) {
|
||||||
|
encryption.append(tr(" (Seed)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.type != Core::ContentType::Application &&
|
||||||
|
content.type != Core::ContentType::Update && content.type != Core::ContentType::DLC) {
|
||||||
|
|
||||||
|
// Do not display encryption in this case
|
||||||
|
encryption.clear();
|
||||||
|
}
|
||||||
|
|
||||||
auto* item = new QTreeWidgetItem{
|
auto* item = new QTreeWidgetItem{
|
||||||
{QString{}, name, ReadableByteSize(content.maximum_size),
|
{QString{}, name, ReadableByteSize(content.maximum_size), encryption,
|
||||||
content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}};
|
content.already_exists ? QStringLiteral("Yes") : QStringLiteral("No")}};
|
||||||
|
|
||||||
ui->main->invisibleRootItem()->child(row)->addChild(item);
|
ui->main->invisibleRootItem()->child(row)->addChild(item);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>600</width>
|
<width>700</width>
|
||||||
<height>400</height>
|
<height>400</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -56,6 +56,11 @@
|
|||||||
<string>Size</string>
|
<string>Size</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Encryption</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
<column>
|
<column>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Exists</string>
|
<string>Exists</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user