mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-06-04 23:05:00 -06:00
Merge 5a3b28356b into 06a535f50e
This commit is contained in:
commit
b922d9dc04
@ -10,20 +10,24 @@ namespace Common {
|
|||||||
|
|
||||||
constexpr char help_string[] =
|
constexpr char help_string[] =
|
||||||
"Usage: {} [options] <file path>\n"
|
"Usage: {} [options] <file path>\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"
|
||||||
"-h, --help Display this help and exit\n"
|
"-h, --help Display this help and exit\n"
|
||||||
"-i, --install [path] Install a CIA file at the given path\n"
|
"-i, --install [path] Install a CIA file at the given path\n"
|
||||||
"-p, --movie-play [path] Play a TAS movie located at the given path\n"
|
"-c, --compress [path]... -o [path] Compress the cci/3ds/cxi/app/3dsx/cia files at the given path\n"
|
||||||
"-r, --movie-record [path] Record a TAS movie to the given file path\n"
|
" If \'-o [path]\' isnt used, it compresses in place\n"
|
||||||
|
"-x, --decompress [path]... -o [path] Decompress zcci/zcxi/z3dsx/zcia files at the given path\n"
|
||||||
|
" If \'-o [path]\' isnt used, it decompresses in place\n"
|
||||||
|
"-p, --movie-play [path] Play a TAS movie located at the given path\n"
|
||||||
|
"-r, --movie-record [path] Record a TAS movie to the given file path\n"
|
||||||
"-a, --movie-record-author [author] Set the author for the recorded TAS movie (to be used "
|
"-a, --movie-record-author [author] Set the author for the recorded TAS movie (to be used "
|
||||||
"alongside --movie-record)\n"
|
"alongside --movie-record)\n"
|
||||||
#ifdef ENABLE_ROOM
|
#ifdef ENABLE_ROOM
|
||||||
" --room Utilize dedicated multiplayer room functionality (equivalent to "
|
" --room Utilize dedicated multiplayer room functionality (equivalent to "
|
||||||
"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";
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@ -87,6 +87,7 @@
|
|||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/logging/filter.h"
|
||||||
#include "common/memory_detect.h"
|
#include "common/memory_detect.h"
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
@ -247,7 +248,12 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (args[i] == QStringLiteral("--help") || args[i] == QStringLiteral("-h")) {
|
if (args[i] == QStringLiteral("--help") || args[i] == QStringLiteral("-h")) {
|
||||||
ShowCommandOutput("Help", fmt::format(Common::help_string, args[0].toStdString()));
|
UISettings::values.show_console = true;
|
||||||
|
Debugger::ToggleConsole();
|
||||||
|
Common::Log::Filter filter;
|
||||||
|
filter.ParseFilterString("*:Error");
|
||||||
|
QFileInfo azaharFile(args[0]);
|
||||||
|
QTextStream(stdout) << QString::fromStdString(fmt::format(Common::help_string, azaharFile.fileName().toStdString()));
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,6 +343,88 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||||||
game_path = args[i];
|
game_path = args[i];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compress files in place
|
||||||
|
if (args[i] == QStringLiteral("--compress") || args[i] == QStringLiteral("-c")) {
|
||||||
|
if (i >= args.size() - 1 || args[i + 1].startsWith(QChar::fromLatin1('-'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UISettings::values.show_console = true;
|
||||||
|
Debugger::ToggleConsole();
|
||||||
|
Common::Log::Filter filter;
|
||||||
|
filter.ParseFilterString("*:Error");
|
||||||
|
Common::Log::SetGlobalFilter(filter);
|
||||||
|
i++;
|
||||||
|
for (; i < args.size(); i++){
|
||||||
|
if (args[i] == QStringLiteral("-o") || args[i] == QStringLiteral("--output")){
|
||||||
|
i++;
|
||||||
|
if (i < args.size()){
|
||||||
|
QFileInfo outputPath(args[i]);
|
||||||
|
if (outputPath.isDir()){
|
||||||
|
cli_out_path = args[i];
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: " << args[i] << " is not a directory!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: No directory specified";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QFileInfo currPath(args[i]);
|
||||||
|
if (currPath.isFile()){
|
||||||
|
compress_paths.append(args[i]);
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: " << args[i] << " is not a file!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GMainWindow::OnCompressFileCLI();
|
||||||
|
compression_future.waitForFinished();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress files in place
|
||||||
|
if (args[i] == QStringLiteral("--decompress") || args[i] == QStringLiteral("-x")) {
|
||||||
|
if (i >= args.size() - 1 || args[i + 1].startsWith(QChar::fromLatin1('-'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UISettings::values.show_console = true;
|
||||||
|
Debugger::ToggleConsole();
|
||||||
|
Common::Log::Filter filter;
|
||||||
|
filter.ParseFilterString("*:Error");
|
||||||
|
Common::Log::SetGlobalFilter(filter);
|
||||||
|
i++;
|
||||||
|
for (; i < args.size(); i++){
|
||||||
|
if (args[i] == QStringLiteral("-o") || args[i] == QStringLiteral("--output")){
|
||||||
|
i++;
|
||||||
|
if (i < args.size()){
|
||||||
|
QFileInfo outputPath(args[i]);
|
||||||
|
if (outputPath.isDir()){
|
||||||
|
cli_out_path = args[i];
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: " << args[i] << " is not a directory!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: No directory specified";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QFileInfo currPath(args[i]);
|
||||||
|
if (currPath.isFile()){
|
||||||
|
decompress_paths.append(args[i]);
|
||||||
|
} else {
|
||||||
|
QTextStream(stderr) << "Error: " << args[i] << " is not a file!\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GMainWindow::OnDecompressFileCLI();
|
||||||
|
compression_future.waitForFinished();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
@ -3281,6 +3369,71 @@ void GMainWindow::OnCompressFile() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnCompressFileCLI() {
|
||||||
|
// NOTE: Encrypted files SHOULD NEVER be compressed, otherwise the resulting
|
||||||
|
// compressed file will have very poor compression ratios, due to the high
|
||||||
|
// entropy caused by encryption. This may cause confusion to the user as they
|
||||||
|
// will see the files do not compress well and blame the emulator.
|
||||||
|
//
|
||||||
|
// This is enforced using the loaders as they already return an error on encryption.
|
||||||
|
|
||||||
|
QString out_path = cli_out_path;
|
||||||
|
QStringList filepaths = compress_paths;
|
||||||
|
if (compress_paths.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool single_file = filepaths.size() == 1;
|
||||||
|
|
||||||
|
compression_future = QtConcurrent::run([&, filepaths, out_path] {
|
||||||
|
bool single_file = filepaths.size() == 1;
|
||||||
|
QString out_filepath;
|
||||||
|
bool total_success = true;
|
||||||
|
|
||||||
|
for (const QString& filepath : filepaths) {
|
||||||
|
QFileInfo filepathInfo(filepath);
|
||||||
|
QTextStream(stdout) << "Compressing \"" << filepathInfo.fileName() << "\"...\n";
|
||||||
|
std::string in_path = filepath.toStdString();
|
||||||
|
|
||||||
|
// Identify file type
|
||||||
|
auto compress_info = GetCompressFileInfo(filepath.toStdString(), true);
|
||||||
|
if (!compress_info.has_value()) {
|
||||||
|
total_success = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fileinfo(filepath);
|
||||||
|
if (out_path.isEmpty()){
|
||||||
|
out_filepath = fileinfo.absolutePath();
|
||||||
|
} else {
|
||||||
|
out_filepath = out_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_filepath = out_filepath + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||||
|
QStringLiteral(".") +
|
||||||
|
QString::fromStdString(
|
||||||
|
compress_info.value().first.recommended_compressed_extension);
|
||||||
|
|
||||||
|
std::string out_path = out_filepath.toStdString();
|
||||||
|
emit UpdateProgress(0, 0);
|
||||||
|
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||||
|
emit UpdateProgress(written, total);
|
||||||
|
};
|
||||||
|
bool success = FileUtil::CompressZ3DSFile(in_path, out_path,
|
||||||
|
compress_info.value().first.underlying_magic,
|
||||||
|
compress_info.value().second, progress,
|
||||||
|
compress_info.value().first.default_metadata);
|
||||||
|
if (!success) {
|
||||||
|
total_success = false;
|
||||||
|
FileUtil::Delete(out_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit CompressFinished(true, total_success);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GMainWindow::OnDecompressFile() {
|
void GMainWindow::OnDecompressFile() {
|
||||||
|
|
||||||
QStringList filepaths = QFileDialog::getOpenFileNames(
|
QStringList filepaths = QFileDialog::getOpenFileNames(
|
||||||
@ -3374,6 +3527,61 @@ void GMainWindow::OnDecompressFile() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnDecompressFileCLI() {
|
||||||
|
QStringList filepaths = decompress_paths;
|
||||||
|
QString out_path = cli_out_path;
|
||||||
|
|
||||||
|
if (filepaths.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool single_file = filepaths.size() == 1;
|
||||||
|
|
||||||
|
compression_future = QtConcurrent::run([&, filepaths, out_path] {
|
||||||
|
bool single_file = filepaths.size() == 1;
|
||||||
|
QString out_filepath;
|
||||||
|
bool total_success = true;
|
||||||
|
|
||||||
|
for (const QString& filepath : filepaths) {
|
||||||
|
QFileInfo filepathInfo(filepath);
|
||||||
|
QTextStream(stdout) << "Decompressing \"" << filepathInfo.fileName() << "\"...\n";
|
||||||
|
std::string in_path = filepath.toStdString();
|
||||||
|
|
||||||
|
// Identify file type
|
||||||
|
auto compress_info = GetCompressFileInfo(filepath.toStdString(), false);
|
||||||
|
if (!compress_info.has_value()) {
|
||||||
|
total_success = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fileinfo(filepath);
|
||||||
|
|
||||||
|
if (out_path.isEmpty()){
|
||||||
|
out_filepath = fileinfo.absolutePath();
|
||||||
|
} else {
|
||||||
|
out_filepath = out_path;
|
||||||
|
}
|
||||||
|
out_filepath = out_filepath + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||||
|
QStringLiteral(".") +
|
||||||
|
QString::fromStdString(
|
||||||
|
compress_info.value().first.recommended_uncompressed_extension);
|
||||||
|
std::string out_path = out_filepath.toStdString();
|
||||||
|
emit UpdateProgress(0, 0);
|
||||||
|
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||||
|
emit UpdateProgress(written, total);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(PabloMK7): What should we do with the metadata?
|
||||||
|
bool success = FileUtil::DeCompressZ3DSFile(in_path, out_path, progress);
|
||||||
|
if (!success) {
|
||||||
|
total_success = false;
|
||||||
|
FileUtil::Delete(out_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit CompressFinished(false, total_success);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void GMainWindow::OnOpenFFmpeg() {
|
void GMainWindow::OnOpenFFmpeg() {
|
||||||
auto filename =
|
auto filename =
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <qfuture.h>
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
#include <QDBusObjectPath>
|
#include <QDBusObjectPath>
|
||||||
#endif
|
#endif
|
||||||
@ -290,7 +291,9 @@ private slots:
|
|||||||
void OnCaptureScreenshot();
|
void OnCaptureScreenshot();
|
||||||
void OnDumpVideo();
|
void OnDumpVideo();
|
||||||
void OnCompressFile();
|
void OnCompressFile();
|
||||||
|
void OnCompressFileCLI();
|
||||||
void OnDecompressFile();
|
void OnDecompressFile();
|
||||||
|
void OnDecompressFileCLI();
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void OnOpenFFmpeg();
|
void OnOpenFFmpeg();
|
||||||
#endif
|
#endif
|
||||||
@ -397,6 +400,13 @@ private:
|
|||||||
// Video dumping
|
// Video dumping
|
||||||
bool video_dumping_on_start = false;
|
bool video_dumping_on_start = false;
|
||||||
QString video_dumping_path;
|
QString video_dumping_path;
|
||||||
|
|
||||||
|
// Compress/Decompress Paths and Future
|
||||||
|
QStringList compress_paths;
|
||||||
|
QStringList decompress_paths;
|
||||||
|
QString cli_out_path;
|
||||||
|
QFuture<void> compression_future;
|
||||||
|
|
||||||
// Whether game shutdown is delayed due to video dumping
|
// Whether game shutdown is delayed due to video dumping
|
||||||
bool game_shutdown_delayed = false;
|
bool game_shutdown_delayed = false;
|
||||||
// Whether game was paused due to stopping video dumping
|
// Whether game was paused due to stopping video dumping
|
||||||
@ -404,7 +414,6 @@ private:
|
|||||||
|
|
||||||
QString gl_renderer;
|
QString gl_renderer;
|
||||||
std::vector<QString> physical_devices;
|
std::vector<QString> physical_devices;
|
||||||
|
|
||||||
// Debugger panes
|
// Debugger panes
|
||||||
ProfilerWidget* profilerWidget;
|
ProfilerWidget* profilerWidget;
|
||||||
#if MICROPROFILE_ENABLED
|
#if MICROPROFILE_ENABLED
|
||||||
|
|||||||
@ -25,10 +25,13 @@ void ToggleConsole() {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FILE* temp;
|
FILE* temp;
|
||||||
if (UISettings::values.show_console) {
|
if (UISettings::values.show_console) {
|
||||||
BOOL alloc_console_res = AllocConsole();
|
|
||||||
DWORD last_error = 0;
|
DWORD last_error = 0;
|
||||||
|
BOOL alloc_console_res = AttachConsole(ATTACH_PARENT_PROCESS);
|
||||||
if (!alloc_console_res) {
|
if (!alloc_console_res) {
|
||||||
last_error = GetLastError();
|
alloc_console_res = AllocConsole();
|
||||||
|
if (!alloc_console_res) {
|
||||||
|
last_error = GetLastError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the windows debugger already opened a console, calling AllocConsole again
|
// If the windows debugger already opened a console, calling AllocConsole again
|
||||||
// will cause ERROR_ACCESS_DENIED. If that's the case assume a console is open.
|
// will cause ERROR_ACCESS_DENIED. If that's the case assume a console is open.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user