mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-06-02 04:55:28 -06:00
Implement Z3DS compression CLI in new citra_cli static library
This commit is contained in:
parent
267887d7a9
commit
8ffb94b06c
@ -203,6 +203,7 @@ if (ENABLE_QT)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT) # Or any other hypothetical future frontends
|
if (ENABLE_QT) # Or any other hypothetical future frontends
|
||||||
|
add_subdirectory(citra_cli)
|
||||||
add_subdirectory(citra_meta)
|
add_subdirectory(citra_meta)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
12
src/citra_cli/CMakeLists.txt
Normal file
12
src/citra_cli/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
add_library(citra_cli STATIC EXCLUDE_FROM_ALL
|
||||||
|
citra_cli.h
|
||||||
|
citra_cli.cpp
|
||||||
|
compression_cli.h
|
||||||
|
compression_cli.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(citra_cli PRIVATE citra_common citra_core)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(citra_cli PRIVATE getopt)
|
||||||
|
endif()
|
||||||
45
src/citra_cli/citra_cli.cpp
Normal file
45
src/citra_cli/citra_cli.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#undef _UNICODE
|
||||||
|
#include <getopt.h>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "citra_cli/citra_cli.h"
|
||||||
|
#include "citra_cli/compression_cli.h"
|
||||||
|
|
||||||
|
namespace CitraCLI {
|
||||||
|
|
||||||
|
bool CheckForOptions(const char* optstring, int argc, char* argv[]) {
|
||||||
|
const int original_opterr = opterr;
|
||||||
|
opterr = 0; // Temporarily suppress invalid option messages
|
||||||
|
|
||||||
|
bool return_value = false;
|
||||||
|
int option;
|
||||||
|
while ((option = getopt(argc, argv, optstring)) != -1) {
|
||||||
|
for (size_t i = 0; optstring[i] != '\0'; ++i) {
|
||||||
|
if (optstring[i] == ':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == optstring[i]) {
|
||||||
|
return_value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opterr = original_opterr;
|
||||||
|
optind = 1; // Reset getopt so that it can be used again
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ParseCommand(int argc, char* argv[]) {
|
||||||
|
if (CheckForOptions(compression_ops_optstring, argc, argv)) {
|
||||||
|
return ParseCompressionCommand(argc, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CitraCLI
|
||||||
13
src/citra_cli/citra_cli.h
Normal file
13
src/citra_cli/citra_cli.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
namespace CitraCLI {
|
||||||
|
|
||||||
|
constexpr char compression_ops_optstring[] = "c:x:o:";
|
||||||
|
constexpr char cli_capture_optstring[] = "c:x:o:";
|
||||||
|
|
||||||
|
bool CheckForOptions(const char* optstring, int argc, char* argv[]);
|
||||||
|
int ParseCommand(int argc, char* argv[]);
|
||||||
|
|
||||||
|
} // namespace CitraCLI
|
||||||
129
src/citra_cli/compression_cli.cpp
Normal file
129
src/citra_cli/compression_cli.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#undef _UNICODE
|
||||||
|
#include <getopt.h>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "citra_cli/citra_cli.h"
|
||||||
|
#include "common/common_paths.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/zstd_compression.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
|
namespace CitraCLI {
|
||||||
|
|
||||||
|
static std::string strip_path_filename(std::string path) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
fs::path path_path = path;
|
||||||
|
fs::path stripped_path = path_path.remove_filename();
|
||||||
|
return stripped_path.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string build_output_path(std::string source_path, std::string extension,
|
||||||
|
std::string output_dir_path) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
fs::path source_path_path = source_path;
|
||||||
|
std::string recommended_filename =
|
||||||
|
source_path_path.filename().replace_extension(extension).string();
|
||||||
|
return output_dir_path + DIR_SEP + recommended_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool perform_z3ds_operation(bool is_compressing, const std::string& src_file,
|
||||||
|
const std::string& dst_file,
|
||||||
|
const std::array<u8, 4>& underlying_magic, size_t frame_size,
|
||||||
|
std::function<FileUtil::ProgressCallback>&& update_callback,
|
||||||
|
std::unordered_map<std::string, std::vector<u8>> metadata) {
|
||||||
|
if (is_compressing) {
|
||||||
|
return FileUtil::CompressZ3DSFile(src_file, dst_file, underlying_magic, frame_size,
|
||||||
|
std::move(update_callback), metadata);
|
||||||
|
} else { // decompressing
|
||||||
|
return FileUtil::DeCompressZ3DSFile(src_file, dst_file, std::move(update_callback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ParseCompressionCommand(int argc, char* argv[]) {
|
||||||
|
Common::Log::Initialize();
|
||||||
|
Common::Log::Start();
|
||||||
|
|
||||||
|
const std::string common_error_addendum = "\nCheck log for more details.";
|
||||||
|
|
||||||
|
std::optional<std::string> compress_path; // The path of a decompressed file to be compressed
|
||||||
|
std::optional<std::string> decompress_path; // The path of a compressed file to be decompressed
|
||||||
|
std::optional<std::string> output_dir_path; // The directory which will contain processed file
|
||||||
|
|
||||||
|
int option;
|
||||||
|
while ((option = getopt(argc, argv, compression_ops_optstring)) != -1) {
|
||||||
|
switch (option) {
|
||||||
|
case 'c':
|
||||||
|
compress_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
decompress_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
output_dir_path = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_compressing; // True if compressing, false if decompressing
|
||||||
|
std::string source_path;
|
||||||
|
std::string action_description; // String containing a user-friendly verb
|
||||||
|
// describing the performed operation
|
||||||
|
if (compress_path.has_value()) {
|
||||||
|
is_compressing = true;
|
||||||
|
source_path = compress_path.value();
|
||||||
|
action_description = "Compressing";
|
||||||
|
} else if (decompress_path.has_value()) {
|
||||||
|
is_compressing = false;
|
||||||
|
source_path = decompress_path.value();
|
||||||
|
action_description = "Decompressing";
|
||||||
|
} else {
|
||||||
|
std::cout << "Invalid option combination provided. Quitting." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << action_description << " file '" << source_path << "'..." << std::flush;
|
||||||
|
|
||||||
|
if (!output_dir_path.has_value()) {
|
||||||
|
output_dir_path = strip_path_filename(source_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto compress_info = Loader::GetCompressFileInfo(source_path, is_compressing);
|
||||||
|
|
||||||
|
if (!compress_info.has_value()) {
|
||||||
|
std::cout << "fail: Failed to get compress info for file." << common_error_addendum
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string extension; // The extension that the final processed file should have
|
||||||
|
if (is_compressing) {
|
||||||
|
extension = compress_info.value().first.recommended_compressed_extension;
|
||||||
|
} else {
|
||||||
|
extension = compress_info.value().first.recommended_uncompressed_extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output_path = build_output_path(source_path, extension, output_dir_path.value());
|
||||||
|
|
||||||
|
bool success = perform_z3ds_operation(
|
||||||
|
is_compressing, source_path, output_path, compress_info.value().first.underlying_magic,
|
||||||
|
compress_info.value().second, nullptr, compress_info.value().first.default_metadata);
|
||||||
|
if (!success) {
|
||||||
|
FileUtil::Delete(output_path);
|
||||||
|
std::cout << "fail: Failed to perform Z3DS operation." << common_error_addendum
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "success" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CitraCLI
|
||||||
9
src/citra_cli/compression_cli.h
Normal file
9
src/citra_cli/compression_cli.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
namespace CitraCLI {
|
||||||
|
|
||||||
|
int ParseCompressionCommand(int argc, char* argv[]);
|
||||||
|
|
||||||
|
}
|
||||||
@ -68,7 +68,7 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND MINGW)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(citra_meta PRIVATE citra_common fmt)
|
target_link_libraries(citra_meta PRIVATE citra_cli citra_common fmt)
|
||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
target_link_libraries(citra_meta PRIVATE citra_qt)
|
target_link_libraries(citra_meta PRIVATE citra_qt)
|
||||||
|
|||||||
@ -10,6 +10,8 @@ namespace Common {
|
|||||||
|
|
||||||
constexpr char help_string[] =
|
constexpr char help_string[] =
|
||||||
"Usage: {} [options] <file path>\n"
|
"Usage: {} [options] <file path>\n"
|
||||||
|
"-c [path] Z3DS compress a ROM located at the given path\n"
|
||||||
|
" (optionally provide '-o [path]' for output directory)\n"
|
||||||
"-d, --dump-video [path] Dump video recording of emulator playback to the given file path\n"
|
"-d, --dump-video [path] Dump video recording of emulator playback to the given file path\n"
|
||||||
"-f, --fullscreen Start in fullscreen mode\n"
|
"-f, --fullscreen Start in fullscreen mode\n"
|
||||||
"-g, --gdbport [port] Enable gdb stub on the given port\n"
|
"-g, --gdbport [port] Enable gdb stub on the given port\n"
|
||||||
@ -24,6 +26,8 @@ constexpr char help_string[] =
|
|||||||
"the old citra-room executable)\n"
|
"the old citra-room executable)\n"
|
||||||
#endif
|
#endif
|
||||||
"-v, --version Output version information and exit\n"
|
"-v, --version Output version information and exit\n"
|
||||||
"-w, --windowed Start in windowed mode";
|
"-w, --windowed Start in windowed mode\n"
|
||||||
|
"-x [path] Decompress a Z3DS compressed ROM located at the given path\n"
|
||||||
|
" (optionally provide '-o [path]' for output directory)";
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "citra_cli/citra_cli.h"
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
|
||||||
@ -59,6 +60,10 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (CitraCLI::CheckForOptions(CitraCLI::cli_capture_optstring, argc, argv)) {
|
||||||
|
return CitraCLI::ParseCommand(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_ROOM
|
#if ENABLE_ROOM
|
||||||
bool launch_room = false;
|
bool launch_room = false;
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
|
|||||||
@ -3235,64 +3235,6 @@ void GMainWindow::OnDumpVideo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<std::pair<Loader::AppLoader::CompressFileInfo, size_t>> GetCompressFileInfo(
|
|
||||||
const std::string& filepath, bool compress) {
|
|
||||||
Loader::AppLoader::CompressFileInfo compress_info{};
|
|
||||||
compress_info.is_supported = false;
|
|
||||||
size_t frame_size{};
|
|
||||||
auto loader = Loader::GetLoader(filepath);
|
|
||||||
if (loader) {
|
|
||||||
compress_info = loader->GetCompressFileInfo();
|
|
||||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_FRAME_SIZE;
|
|
||||||
} else {
|
|
||||||
bool is_compressed = false;
|
|
||||||
if (Service::AM::CheckCIAToInstall(filepath, is_compressed, compress ? true : false) ==
|
|
||||||
Service::AM::InstallStatus::Success) {
|
|
||||||
compress_info.is_supported = true;
|
|
||||||
compress_info.is_compressed = is_compressed;
|
|
||||||
compress_info.recommended_compressed_extension = "zcia";
|
|
||||||
compress_info.recommended_uncompressed_extension = "cia";
|
|
||||||
compress_info.underlying_magic = std::array<u8, 4>({'C', 'I', 'A', '\0'});
|
|
||||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_CIA_FRAME_SIZE;
|
|
||||||
if (compress) {
|
|
||||||
auto meta_info = Service::AM::GetCIAInfos(filepath);
|
|
||||||
if (meta_info.Succeeded()) {
|
|
||||||
const auto& meta_info_val = meta_info.Unwrap();
|
|
||||||
std::vector<u8> value(sizeof(Service::AM::TitleInfo));
|
|
||||||
memcpy(value.data(), &meta_info_val.first, sizeof(Service::AM::TitleInfo));
|
|
||||||
compress_info.default_metadata.emplace("titleinfo", value);
|
|
||||||
if (meta_info_val.second) {
|
|
||||||
value.resize(sizeof(Loader::SMDH));
|
|
||||||
memcpy(value.data(), meta_info_val.second.get(), sizeof(Loader::SMDH));
|
|
||||||
compress_info.default_metadata.emplace("smdh", value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!compress_info.is_supported) {
|
|
||||||
LOG_ERROR(Frontend,
|
|
||||||
"Error {} file {}, the selected file is not a compatible 3DS ROM format or is "
|
|
||||||
"encrypted.",
|
|
||||||
compress ? "compressing" : "decompressing", filepath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (compress_info.is_compressed && compress) {
|
|
||||||
LOG_ERROR(Frontend, "Error compressing file {}, the selected file is already compressed",
|
|
||||||
filepath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (!compress_info.is_compressed && !compress) {
|
|
||||||
LOG_ERROR(Frontend,
|
|
||||||
"Error decompressing file {}, the selected file is already decompressed",
|
|
||||||
filepath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::pair(compress_info, frame_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GMainWindow::OnCompressFile() {
|
void GMainWindow::OnCompressFile() {
|
||||||
// NOTE: Encrypted files SHOULD NEVER be compressed, otherwise the resulting
|
// NOTE: Encrypted files SHOULD NEVER be compressed, otherwise the resulting
|
||||||
// compressed file will have very poor compression ratios, due to the high
|
// compressed file will have very poor compression ratios, due to the high
|
||||||
@ -3315,7 +3257,7 @@ void GMainWindow::OnCompressFile() {
|
|||||||
bool single_file = filepaths.size() == 1;
|
bool single_file = filepaths.size() == 1;
|
||||||
if (single_file) {
|
if (single_file) {
|
||||||
// If it's a single file, ask the user for the output file.
|
// If it's a single file, ask the user for the output file.
|
||||||
auto compress_info = GetCompressFileInfo(filepaths[0].toStdString(), true);
|
auto compress_info = Loader::GetCompressFileInfo(filepaths[0].toStdString(), true);
|
||||||
if (!compress_info.has_value()) {
|
if (!compress_info.has_value()) {
|
||||||
emit CompressFinished(true, false);
|
emit CompressFinished(true, false);
|
||||||
return;
|
return;
|
||||||
@ -3355,7 +3297,7 @@ void GMainWindow::OnCompressFile() {
|
|||||||
std::string in_path = filepath.toStdString();
|
std::string in_path = filepath.toStdString();
|
||||||
|
|
||||||
// Identify file type
|
// Identify file type
|
||||||
auto compress_info = GetCompressFileInfo(filepath.toStdString(), true);
|
auto compress_info = Loader::GetCompressFileInfo(filepath.toStdString(), true);
|
||||||
if (!compress_info.has_value()) {
|
if (!compress_info.has_value()) {
|
||||||
total_success = false;
|
total_success = false;
|
||||||
continue;
|
continue;
|
||||||
@ -3408,7 +3350,7 @@ void GMainWindow::OnDecompressFile() {
|
|||||||
bool single_file = filepaths.size() == 1;
|
bool single_file = filepaths.size() == 1;
|
||||||
if (single_file) {
|
if (single_file) {
|
||||||
// If it's a single file, ask the user for the output file.
|
// If it's a single file, ask the user for the output file.
|
||||||
auto compress_info = GetCompressFileInfo(filepaths[0].toStdString(), false);
|
auto compress_info = Loader::GetCompressFileInfo(filepaths[0].toStdString(), false);
|
||||||
if (!compress_info.has_value()) {
|
if (!compress_info.has_value()) {
|
||||||
emit CompressFinished(false, false);
|
emit CompressFinished(false, false);
|
||||||
return;
|
return;
|
||||||
@ -3449,7 +3391,7 @@ void GMainWindow::OnDecompressFile() {
|
|||||||
std::string in_path = filepath.toStdString();
|
std::string in_path = filepath.toStdString();
|
||||||
|
|
||||||
// Identify file type
|
// Identify file type
|
||||||
auto compress_info = GetCompressFileInfo(filepath.toStdString(), false);
|
auto compress_info = Loader::GetCompressFileInfo(filepath.toStdString(), false);
|
||||||
if (!compress_info.has_value()) {
|
if (!compress_info.has_value()) {
|
||||||
total_success = false;
|
total_success = false;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -6,8 +6,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "common/zstd_compression.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
|
#include "core/hle/service/am/am.h"
|
||||||
#include "core/loader/3dsx.h"
|
#include "core/loader/3dsx.h"
|
||||||
#include "core/loader/artic.h"
|
#include "core/loader/artic.h"
|
||||||
#include "core/loader/elf.h"
|
#include "core/loader/elf.h"
|
||||||
@ -179,4 +181,62 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
|
|||||||
return GetFileLoader(system, std::move(file), type, filename_filename, filename);
|
return GetFileLoader(system, std::move(file), type, filename_filename, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<Loader::AppLoader::CompressFileInfo, size_t>> GetCompressFileInfo(
|
||||||
|
const std::string& filepath, bool compress) {
|
||||||
|
Loader::AppLoader::CompressFileInfo compress_info{};
|
||||||
|
compress_info.is_supported = false;
|
||||||
|
size_t frame_size{};
|
||||||
|
auto loader = Loader::GetLoader(filepath);
|
||||||
|
if (loader) {
|
||||||
|
compress_info = loader->GetCompressFileInfo();
|
||||||
|
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_FRAME_SIZE;
|
||||||
|
} else {
|
||||||
|
bool is_compressed = false;
|
||||||
|
if (Service::AM::CheckCIAToInstall(filepath, is_compressed, compress ? true : false) ==
|
||||||
|
Service::AM::InstallStatus::Success) {
|
||||||
|
compress_info.is_supported = true;
|
||||||
|
compress_info.is_compressed = is_compressed;
|
||||||
|
compress_info.recommended_compressed_extension = "zcia";
|
||||||
|
compress_info.recommended_uncompressed_extension = "cia";
|
||||||
|
compress_info.underlying_magic = std::array<u8, 4>({'C', 'I', 'A', '\0'});
|
||||||
|
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_CIA_FRAME_SIZE;
|
||||||
|
if (compress) {
|
||||||
|
auto meta_info = Service::AM::GetCIAInfos(filepath);
|
||||||
|
if (meta_info.Succeeded()) {
|
||||||
|
const auto& meta_info_val = meta_info.Unwrap();
|
||||||
|
std::vector<u8> value(sizeof(Service::AM::TitleInfo));
|
||||||
|
memcpy(value.data(), &meta_info_val.first, sizeof(Service::AM::TitleInfo));
|
||||||
|
compress_info.default_metadata.emplace("titleinfo", value);
|
||||||
|
if (meta_info_val.second) {
|
||||||
|
value.resize(sizeof(Loader::SMDH));
|
||||||
|
memcpy(value.data(), meta_info_val.second.get(), sizeof(Loader::SMDH));
|
||||||
|
compress_info.default_metadata.emplace("smdh", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!compress_info.is_supported) {
|
||||||
|
LOG_ERROR(Frontend,
|
||||||
|
"Error {} file {}, the selected file is not a compatible 3DS ROM format or is "
|
||||||
|
"encrypted.",
|
||||||
|
compress ? "compressing" : "decompressing", filepath);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (compress_info.is_compressed && compress) {
|
||||||
|
LOG_ERROR(Frontend, "Error compressing file {}, the selected file is already compressed",
|
||||||
|
filepath);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!compress_info.is_compressed && !compress) {
|
||||||
|
LOG_ERROR(Frontend,
|
||||||
|
"Error decompressing file {}, the selected file is already decompressed",
|
||||||
|
filepath);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::pair(compress_info, frame_size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
@ -322,4 +322,7 @@ protected:
|
|||||||
*/
|
*/
|
||||||
std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
|
std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
|
||||||
|
|
||||||
|
std::optional<std::pair<Loader::AppLoader::CompressFileInfo, size_t>> GetCompressFileInfo(
|
||||||
|
const std::string& filepath, bool compress);
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user