mirror of
https://github.com/Atmosphere-NX/hac2l.git
synced 2025-06-29 22:42:40 +02:00
hac2l: add appfs target for fs-of-apps (subdir on pc, xci secure part, pfs0)
This commit is contained in:
parent
952c488d28
commit
1c41ca7fc6
114
source/hactool_application_list.hpp
Normal file
114
source/hactool_application_list.hpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::hactool {
|
||||||
|
|
||||||
|
template<typename UserData>
|
||||||
|
class ApplicationContentTreeEntry : public util::IntrusiveRedBlackTreeBaseNode<ApplicationContentTreeEntry<UserData>> {
|
||||||
|
private:
|
||||||
|
ncm::ApplicationId m_id;
|
||||||
|
u32 m_version;
|
||||||
|
u8 m_id_offset;
|
||||||
|
ncm::ContentType m_type;
|
||||||
|
UserData m_data;
|
||||||
|
public:
|
||||||
|
ApplicationContentTreeEntry(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) : m_id(id), m_version(v), m_id_offset(o), m_type(t), m_data() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
ncm::ApplicationId GetId() const {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetVersion() const {
|
||||||
|
return m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GetIdOffset() const {
|
||||||
|
return m_id_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncm::ContentType GetType() const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserData &GetData() const { return m_data; }
|
||||||
|
|
||||||
|
UserData &GetData() { return m_data; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ApplicationContentTreeEntryCompare {
|
||||||
|
static ALWAYS_INLINE int Compare(const ApplicationContentTreeEntry<T> &a, const ApplicationContentTreeEntry<T> &b) {
|
||||||
|
const auto a_i = a.GetId();
|
||||||
|
const auto a_v = a.GetVersion();
|
||||||
|
const auto a_o = a.GetIdOffset();
|
||||||
|
const auto a_t = a.GetType();
|
||||||
|
const auto b_i = b.GetId();
|
||||||
|
const auto b_v = b.GetVersion();
|
||||||
|
const auto b_o = b.GetIdOffset();
|
||||||
|
const auto b_t = b.GetType();
|
||||||
|
if (std::tie(a_i, a_v, a_o, a_t) < std::tie(b_i, b_v, b_o, b_t)) {
|
||||||
|
return -1;
|
||||||
|
} else if (std::tie(a_i, a_v, a_o, a_t) > std::tie(b_i, b_v, b_o, b_t)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using ApplicationContentTree = typename util::IntrusiveRedBlackTreeBaseTraits<ApplicationContentTreeEntry<T>>::TreeType<ApplicationContentTreeEntryCompare<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ApplicationContentsHolder {
|
||||||
|
NON_COPYABLE(ApplicationContentsHolder);
|
||||||
|
NON_MOVEABLE(ApplicationContentsHolder);
|
||||||
|
private:
|
||||||
|
ApplicationContentTree<T> m_tree;
|
||||||
|
public:
|
||||||
|
ApplicationContentsHolder() : m_tree() { /* ... */ }
|
||||||
|
|
||||||
|
~ApplicationContentsHolder() {
|
||||||
|
while (!m_tree.empty()) {
|
||||||
|
auto it = m_tree.begin();
|
||||||
|
while (it != m_tree.end()) {
|
||||||
|
auto *entry = std::addressof(*it);
|
||||||
|
it = m_tree.erase(it);
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationContentTreeEntry<T> *Insert(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) {
|
||||||
|
auto *entry = new ApplicationContentTreeEntry<T>(id, v, o, t);
|
||||||
|
m_tree.insert(*entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto begin() const { return m_tree.begin(); }
|
||||||
|
auto end() const { return m_tree.end(); }
|
||||||
|
|
||||||
|
auto Find(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) {
|
||||||
|
ApplicationContentTreeEntry<T> dummy(id, v, o, t);
|
||||||
|
return m_tree.find(dummy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -63,6 +63,14 @@ namespace ams::hactool {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PathView::HasPrefix(util::string_view prefix) const {
|
||||||
|
return m_path.compare(0, prefix.length(), prefix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PathView::HasSuffix(util::string_view suffix) const {
|
||||||
|
return m_path.compare(m_path.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
Result OpenFileStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path) {
|
Result OpenFileStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path) {
|
||||||
/* Open the file storage. */
|
/* Open the file storage. */
|
||||||
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
|
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
|
||||||
@ -81,6 +89,30 @@ namespace ams::hactool {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result OpenSubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path) {
|
||||||
|
/* Get the fs path. */
|
||||||
|
ams::fs::Path fs_path;
|
||||||
|
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
|
||||||
|
R_TRY(fs_path.SetShallowBuffer(path));
|
||||||
|
|
||||||
|
/* Verify that we can open the directory on the base filesystem. */
|
||||||
|
{
|
||||||
|
std::unique_ptr<fs::fsa::IDirectory> sub_dir;
|
||||||
|
R_TRY(fs->OpenDirectory(std::addressof(sub_dir), fs_path, fs::OpenDirectoryMode_Directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the subdirectory filesystem. */
|
||||||
|
auto subdir_fs = fssystem::AllocateShared<fssystem::SubDirectoryFileSystem>(fs);
|
||||||
|
R_UNLESS(subdir_fs != nullptr, fs::ResultAllocationMemoryFailedAllocateShared());
|
||||||
|
|
||||||
|
/* Initialize the subdirectory filesystem. */
|
||||||
|
R_TRY(subdir_fs->Initialize(fs_path));
|
||||||
|
|
||||||
|
/* Set the output. */
|
||||||
|
*out = std::move(subdir_fs);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result PrintDirectory(std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *prefix, const char *path) {
|
Result PrintDirectory(std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *prefix, const char *path) {
|
||||||
/* Get the fs path. */
|
/* Get the fs path. */
|
||||||
ams::fs::Path fs_path;
|
ams::fs::Path fs_path;
|
||||||
|
@ -18,8 +18,19 @@
|
|||||||
|
|
||||||
namespace ams::hactool {
|
namespace ams::hactool {
|
||||||
|
|
||||||
|
class PathView {
|
||||||
|
private:
|
||||||
|
util::string_view m_path;
|
||||||
|
public:
|
||||||
|
PathView(util::string_view p) : m_path(p) { /* ...*/ }
|
||||||
|
bool HasPrefix(util::string_view prefix) const;
|
||||||
|
bool HasSuffix(util::string_view suffix) const;
|
||||||
|
};
|
||||||
|
|
||||||
Result OpenFileStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path);
|
Result OpenFileStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path);
|
||||||
|
|
||||||
|
Result OpenSubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *path);
|
||||||
|
|
||||||
Result PrintDirectory(std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *prefix, const char *path);
|
Result PrintDirectory(std::shared_ptr<fs::fsa::IFileSystem> &fs, const char *prefix, const char *path);
|
||||||
|
|
||||||
Result ExtractDirectory(std::shared_ptr<fs::fsa::IFileSystem> &dst_fs, std::shared_ptr<fs::fsa::IFileSystem> &src_fs, const char *prefix, const char *dst_path, const char *src_path);
|
Result ExtractDirectory(std::shared_ptr<fs::fsa::IFileSystem> &dst_fs, std::shared_ptr<fs::fsa::IFileSystem> &src_fs, const char *prefix, const char *dst_path, const char *src_path);
|
||||||
|
@ -110,6 +110,8 @@ namespace ams::hactool {
|
|||||||
options.file_type = FileType::Nca;
|
options.file_type = FileType::Nca;
|
||||||
} else if (std::strcmp(arg, "xci") == 0) {
|
} else if (std::strcmp(arg, "xci") == 0) {
|
||||||
options.file_type = FileType::Xci;
|
options.file_type = FileType::Xci;
|
||||||
|
} else if (std::strcmp(arg, "appfs") == 0) {
|
||||||
|
options.file_type = FileType::AppFs;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ namespace ams::hactool {
|
|||||||
Kip,
|
Kip,
|
||||||
Ini,
|
Ini,
|
||||||
Npdm,
|
Npdm,
|
||||||
|
AppFs,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
@ -41,6 +42,9 @@ namespace ams::hactool {
|
|||||||
bool dev = false;
|
bool dev = false;
|
||||||
bool enable_hash = false;
|
bool enable_hash = false;
|
||||||
bool disable_key_warns = false;
|
bool disable_key_warns = false;
|
||||||
|
int preferred_app_index = -1;
|
||||||
|
int preferred_program_index = -1;
|
||||||
|
int preferred_version = -1;
|
||||||
const char *key_file_path = nullptr;
|
const char *key_file_path = nullptr;
|
||||||
const char *titlekey_path = nullptr;
|
const char *titlekey_path = nullptr;
|
||||||
const char *consolekey_path = nullptr;
|
const char *consolekey_path = nullptr;
|
||||||
|
234
source/hactool_processor.app_fs.cpp
Normal file
234
source/hactool_processor.app_fs.cpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "hactool_processor.hpp"
|
||||||
|
#include "hactool_fs_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::hactool {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const s32 MetaFileSystemPartitionIndex = 0;
|
||||||
|
|
||||||
|
constexpr const char MetaNcaFileNameExtension[] = ".cnmt.nca";
|
||||||
|
constexpr const char NcaFileNameExtension[] = ".nca";
|
||||||
|
|
||||||
|
constexpr const char ContentMetaFileNameExtension[] = ".cnmt";
|
||||||
|
|
||||||
|
Result ReadContentMetaFile(std::unique_ptr<u8[]> *out, size_t *out_size, std::shared_ptr<fs::fsa::IFileSystem> &fs) {
|
||||||
|
bool found = false;
|
||||||
|
R_RETURN(fssystem::IterateDirectoryRecursively(fs.get(),
|
||||||
|
[&] (const fs::Path &, const fs::DirectoryEntry &) -> Result { R_SUCCEED(); },
|
||||||
|
[&] (const fs::Path &, const fs::DirectoryEntry &) -> Result { R_SUCCEED(); },
|
||||||
|
[&] (const fs::Path &path, const fs::DirectoryEntry &entry) -> Result {
|
||||||
|
/* If we already found the content meta, finish. */
|
||||||
|
R_SUCCEED_IF(found);
|
||||||
|
|
||||||
|
/* If the path isn't a meta nca, finish. */
|
||||||
|
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(ContentMetaFileNameExtension));
|
||||||
|
|
||||||
|
/* Open the file storage. */
|
||||||
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
|
R_TRY(OpenFileStorage(std::addressof(storage), fs, path.GetString()));
|
||||||
|
|
||||||
|
/* Get the meta file size. */
|
||||||
|
s64 size;
|
||||||
|
R_TRY(storage->GetSize(std::addressof(size)));
|
||||||
|
|
||||||
|
/* Allocate buffer. */
|
||||||
|
auto data = std::make_unique<u8[]>(static_cast<size_t>(size));
|
||||||
|
R_UNLESS(data != nullptr, fs::ResultAllocationMemoryFailedMakeUnique());
|
||||||
|
|
||||||
|
/* Read the meta into the buffer. */
|
||||||
|
R_TRY(storage->Read(0, data.get(), size));
|
||||||
|
|
||||||
|
/* Return the output buffer. */
|
||||||
|
*out = std::move(data);
|
||||||
|
*out_size = static_cast<size_t>(size);
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
R_THROW(ncm::ResultContentMetaNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Processor::ProcessAsApplicationFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs, ProcessAsApplicationFileSystemCtx *ctx) {
|
||||||
|
/* Ensure we have a context. */
|
||||||
|
ProcessAsApplicationFileSystemCtx local_ctx{};
|
||||||
|
if (ctx == nullptr) {
|
||||||
|
ctx = std::addressof(local_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the fs. */
|
||||||
|
ctx->fs = std::move(fs);
|
||||||
|
|
||||||
|
/* Iterate all files in the filesystem. */
|
||||||
|
{
|
||||||
|
/* Iterate, printing the contents of the directory. */
|
||||||
|
const auto iter_result = fssystem::IterateDirectoryRecursively(ctx->fs.get(),
|
||||||
|
[&] (const fs::Path &, const fs::DirectoryEntry &) -> Result { R_SUCCEED(); },
|
||||||
|
[&] (const fs::Path &, const fs::DirectoryEntry &) -> Result { R_SUCCEED(); },
|
||||||
|
[&] (const fs::Path &path, const fs::DirectoryEntry &entry) -> Result {
|
||||||
|
/* If the path isn't a meta nca, finish. */
|
||||||
|
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(MetaNcaFileNameExtension));
|
||||||
|
|
||||||
|
/* Try opening the meta. */
|
||||||
|
std::shared_ptr<fs::IStorage> meta_nca_storage;
|
||||||
|
if (const auto res = OpenFileStorage(std::addressof(meta_nca_storage), ctx->fs, path.GetString()); R_FAILED(res)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to open meta nca (%s): 2%03d-%04d\n", path.GetString(), res.GetModule(), res.GetDescription());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessAsNcaContext meta_nca_ctx = {};
|
||||||
|
if (const auto res = this->ProcessAsNca(std::move(meta_nca_storage), std::addressof(meta_nca_ctx)); R_FAILED(res)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to process meta nca (%s): 2%03d-%04d\n", path.GetString(), res.GetModule(), res.GetDescription());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We only care about meta ncas. */
|
||||||
|
if (meta_nca_ctx.reader->GetContentType() != fssystem::NcaHeader::ContentType::Meta) {
|
||||||
|
fprintf(stderr, "[Warning]: Expected %s to be Meta, was %s\n", path.GetString(), fs::impl::IdString().ToString(meta_nca_ctx.reader->GetContentType()));
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clarification: we only care about meta ncas which are mountable. */
|
||||||
|
if (!meta_nca_ctx.is_mounted[MetaFileSystemPartitionIndex]) {
|
||||||
|
fprintf(stderr, "[Warning]: Expected to mount meta nca partition for %s, but didn't.\n", path.GetString());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the content meta file. */
|
||||||
|
std::unique_ptr<u8[]> meta_data;
|
||||||
|
size_t meta_size;
|
||||||
|
if (const auto res = ReadContentMetaFile(std::addressof(meta_data), std::addressof(meta_size), meta_nca_ctx.file_systems[MetaFileSystemPartitionIndex]); R_FAILED(res)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to read cnmt from %s: 2%03d-%04d\n", path.GetString(), res.GetModule(), res.GetDescription());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the cnmt. */
|
||||||
|
const auto meta_reader = ncm::PackagedContentMetaReader(meta_data.get(), meta_size);
|
||||||
|
const auto * const meta_header = meta_reader.GetHeader();
|
||||||
|
|
||||||
|
/* We only care about applications/patches. */
|
||||||
|
R_SUCCEED_IF(meta_header->type != ncm::ContentMetaType::Application && meta_header->type != ncm::ContentMetaType::Patch);
|
||||||
|
|
||||||
|
/* Get the key. */
|
||||||
|
const auto app_id = meta_reader.GetApplicationId();
|
||||||
|
AMS_ABORT_UNLESS(app_id.has_value());
|
||||||
|
|
||||||
|
/* Get the version. */
|
||||||
|
const auto version = meta_header->version;
|
||||||
|
|
||||||
|
/* Add all the content metas. */
|
||||||
|
for (size_t i = 0; i < meta_reader.GetContentCount(); ++i) {
|
||||||
|
const auto &info = *meta_reader.GetContentInfo(i);
|
||||||
|
|
||||||
|
/* Check that the type isn't a delta. */
|
||||||
|
if (info.GetType() == ncm::ContentType::DeltaFragment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we don't already have an info for the content. */
|
||||||
|
if (auto existing = ctx->apps.Find(*app_id, version, info.GetIdOffset(), info.GetType()); existing != ctx->apps.end()) {
|
||||||
|
fprintf(stderr, "[Warning]: Ignoring duplicate entry { %016" PRIX64 ", %" PRIu32 ", %d, %d }\n", app_id->value, version, static_cast<int>(info.GetIdOffset()), static_cast<int>(info.GetType()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to open the storage for the specified file. */
|
||||||
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
|
{
|
||||||
|
const auto cid_str = ncm::GetContentIdString(info.GetId());
|
||||||
|
char file_name[ncm::ContentIdStringLength + 0x10];
|
||||||
|
util::TSNPrintf(file_name, sizeof(file_name), "%s%s", cid_str.data, NcaFileNameExtension);
|
||||||
|
|
||||||
|
const auto res = [&] () -> Result {
|
||||||
|
ams::fs::Path fs_path;
|
||||||
|
R_TRY(fs_path.Initialize(path));
|
||||||
|
R_TRY(fs_path.RemoveChild());
|
||||||
|
R_TRY(fs_path.AppendChild(file_name));
|
||||||
|
|
||||||
|
R_RETURN(OpenFileStorage(std::addressof(storage), ctx->fs, fs_path.GetString()));
|
||||||
|
}();
|
||||||
|
if (R_FAILED(res)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to open NCA (type %d) specified by %s: 2%03d-%04d\n", static_cast<int>(info.GetType()), path.GetString(), res.GetModule(), res.GetDescription());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the new version for the content. */
|
||||||
|
auto *entry = ctx->apps.Insert(*app_id, version, info.GetIdOffset(), info.GetType());
|
||||||
|
entry->GetData().storage = std::move(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (R_FAILED(iter_result)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to parse application filesystem: 2%03d-%04d\n", iter_result.GetModule(), iter_result.GetDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Recursive processing? */
|
||||||
|
|
||||||
|
/* Print. */
|
||||||
|
if (ctx == std::addressof(local_ctx)) {
|
||||||
|
this->PrintAsApplicationFileSystem(*ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save. */
|
||||||
|
if (ctx == std::addressof(local_ctx)) {
|
||||||
|
this->SaveAsApplicationFileSystem(*ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Processor::PrintAsApplicationFileSystem(ProcessAsApplicationFileSystemCtx &ctx) {
|
||||||
|
auto _ = this->PrintHeader("Application File System");
|
||||||
|
|
||||||
|
{
|
||||||
|
s32 app_idx = -1;
|
||||||
|
ncm::ApplicationId cur_app_id{};
|
||||||
|
const char *field_name = "Programs";
|
||||||
|
for (const auto &entry : ctx.apps) {
|
||||||
|
if (entry.GetType() != ncm::ContentType::Program) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app_idx == -1 || cur_app_id != entry.GetId()) {
|
||||||
|
++app_idx;
|
||||||
|
cur_app_id = entry.GetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
|
||||||
|
field_name = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Processor::SaveAsApplicationFileSystem(ProcessAsApplicationFileSystemCtx &ctx) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "hactool_options.hpp"
|
#include "hactool_options.hpp"
|
||||||
|
#include "hactool_application_list.hpp"
|
||||||
|
|
||||||
namespace ams::hactool {
|
namespace ams::hactool {
|
||||||
|
|
||||||
@ -74,6 +75,16 @@ namespace ams::hactool {
|
|||||||
ProcessAsNpdmContext npdm_ctx;
|
ProcessAsNpdmContext npdm_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ProcessAsApplicationFileSystemCtx {
|
||||||
|
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
||||||
|
|
||||||
|
struct ApplicationEntryData {
|
||||||
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
ApplicationContentsHolder<ApplicationEntryData> apps;
|
||||||
|
};
|
||||||
|
|
||||||
struct ProcessAsXciContext {
|
struct ProcessAsXciContext {
|
||||||
std::shared_ptr<fs::IStorage> storage;
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
|
|
||||||
@ -102,6 +113,8 @@ namespace ams::hactool {
|
|||||||
PartitionData logo_partition;
|
PartitionData logo_partition;
|
||||||
PartitionData normal_partition;
|
PartitionData normal_partition;
|
||||||
PartitionData secure_partition;
|
PartitionData secure_partition;
|
||||||
|
|
||||||
|
ProcessAsApplicationFileSystemCtx app_ctx;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
Options m_options;
|
Options m_options;
|
||||||
@ -193,16 +206,19 @@ namespace ams::hactool {
|
|||||||
Result ProcessAsNca(std::shared_ptr<fs::IStorage> storage, ProcessAsNcaContext *ctx = nullptr);
|
Result ProcessAsNca(std::shared_ptr<fs::IStorage> storage, ProcessAsNcaContext *ctx = nullptr);
|
||||||
Result ProcessAsNpdm(std::shared_ptr<fs::IStorage> storage, ProcessAsNpdmContext *ctx = nullptr);
|
Result ProcessAsNpdm(std::shared_ptr<fs::IStorage> storage, ProcessAsNpdmContext *ctx = nullptr);
|
||||||
Result ProcessAsXci(std::shared_ptr<fs::IStorage> storage, ProcessAsXciContext *ctx = nullptr);
|
Result ProcessAsXci(std::shared_ptr<fs::IStorage> storage, ProcessAsXciContext *ctx = nullptr);
|
||||||
|
Result ProcessAsApplicationFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs, ProcessAsApplicationFileSystemCtx *ctx = nullptr);
|
||||||
|
|
||||||
/* Printing. */
|
/* Printing. */
|
||||||
void PrintAsNca(ProcessAsNcaContext &ctx);
|
void PrintAsNca(ProcessAsNcaContext &ctx);
|
||||||
void PrintAsNpdm(ProcessAsNpdmContext &ctx);
|
void PrintAsNpdm(ProcessAsNpdmContext &ctx);
|
||||||
void PrintAsXci(ProcessAsXciContext &ctx);
|
void PrintAsXci(ProcessAsXciContext &ctx);
|
||||||
|
void PrintAsApplicationFileSystem(ProcessAsApplicationFileSystemCtx &ctx);
|
||||||
|
|
||||||
/* Saving. */
|
/* Saving. */
|
||||||
void SaveAsNca(ProcessAsNcaContext &ctx);
|
void SaveAsNca(ProcessAsNcaContext &ctx);
|
||||||
void SaveAsNpdm(ProcessAsNpdmContext &ctx);
|
void SaveAsNpdm(ProcessAsNpdmContext &ctx);
|
||||||
void SaveAsXci(ProcessAsXciContext &ctx);
|
void SaveAsXci(ProcessAsXciContext &ctx);
|
||||||
|
void SaveAsApplicationFileSystem(ProcessAsApplicationFileSystemCtx &ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void Processor::PrintLineImpl(const char *fmt, ...) const {
|
inline void Processor::PrintLineImpl(const char *fmt, ...) const {
|
||||||
|
@ -33,24 +33,34 @@ namespace ams::hactool {
|
|||||||
/* Setup our internal keys. */
|
/* Setup our internal keys. */
|
||||||
this->PresetInternalKeys();
|
this->PresetInternalKeys();
|
||||||
|
|
||||||
/* Open the file storage. */
|
if (m_options.file_type == FileType::AppFs) {
|
||||||
std::shared_ptr<fs::IStorage> input = nullptr;
|
/* Open the filesystem. */
|
||||||
if (m_options.in_file_path != nullptr) {
|
std::shared_ptr<fs::fsa::IFileSystem> input = nullptr;
|
||||||
R_TRY(OpenFileStorage(std::addressof(input), m_local_fs, m_options.in_file_path));
|
if (m_options.in_file_path != nullptr) {
|
||||||
}
|
R_TRY(OpenSubDirectoryFileSystem(std::addressof(input), m_local_fs, m_options.in_file_path));
|
||||||
|
}
|
||||||
|
|
||||||
/* Process for the specific file type. */
|
R_TRY(this->ProcessAsApplicationFileSystem(std::move(input)));
|
||||||
switch (m_options.file_type) {
|
} else {
|
||||||
case FileType::Nca:
|
/* Open the file storage. */
|
||||||
R_TRY(this->ProcessAsNca(std::move(input)));
|
std::shared_ptr<fs::IStorage> input = nullptr;
|
||||||
break;
|
if (m_options.in_file_path != nullptr) {
|
||||||
case FileType::Npdm:
|
R_TRY(OpenFileStorage(std::addressof(input), m_local_fs, m_options.in_file_path));
|
||||||
R_TRY(this->ProcessAsNpdm(std::move(input)));
|
}
|
||||||
break;
|
|
||||||
case FileType::Xci:
|
/* Process for the specific file type. */
|
||||||
R_TRY(this->ProcessAsXci(std::move(input)));
|
switch (m_options.file_type) {
|
||||||
break;
|
case FileType::Nca:
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
R_TRY(this->ProcessAsNca(std::move(input)));
|
||||||
|
break;
|
||||||
|
case FileType::Npdm:
|
||||||
|
R_TRY(this->ProcessAsNpdm(std::move(input)));
|
||||||
|
break;
|
||||||
|
case FileType::Xci:
|
||||||
|
R_TRY(this->ProcessAsXci(std::move(input)));
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
@ -207,7 +207,12 @@ namespace ams::hactool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Recursive processing? */
|
/* If we have applications, process them. */
|
||||||
|
if (ctx->secure_partition.fs != nullptr) {
|
||||||
|
if (const auto process_app_res = this->ProcessAsApplicationFileSystem(ctx->secure_partition.fs, std::addressof(ctx->app_ctx)); R_FAILED(process_app_res)) {
|
||||||
|
fprintf(stderr, "[Warning]: Failed to process game card's applications: 2%03d-%04d\n", process_app_res.GetModule(), process_app_res.GetDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Print. */
|
/* Print. */
|
||||||
if (ctx == std::addressof(local_ctx)) {
|
if (ctx == std::addressof(local_ctx)) {
|
||||||
@ -353,6 +358,25 @@ namespace ams::hactool {
|
|||||||
PrintGamecardPartition("Update Partition", "update:", ctx.update_partition);
|
PrintGamecardPartition("Update Partition", "update:", ctx.update_partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.secure_partition.fs != nullptr) {
|
||||||
|
s32 app_idx = -1;
|
||||||
|
ncm::ApplicationId cur_app_id{};
|
||||||
|
const char *field_name = "Programs";
|
||||||
|
for (const auto &entry : ctx.app_ctx.apps) {
|
||||||
|
if (entry.GetType() != ncm::ContentType::Program) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app_idx == -1 || cur_app_id != entry.GetId()) {
|
||||||
|
++app_idx;
|
||||||
|
cur_app_id = entry.GetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
|
||||||
|
field_name = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AMS_UNUSED(ctx);
|
AMS_UNUSED(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user