diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp index 242fab411..5f842f1a4 100644 --- a/src/citra_qt/citra_qt.cpp +++ b/src/citra_qt/citra_qt.cpp @@ -336,6 +336,46 @@ GMainWindow::GMainWindow(Core::System& system_) game_path = args[i]; 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; + } + i++; + for (; i < args.size(); i++){ + 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; + } + i++; + for (; i < args.size(); i++){ + 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__ @@ -3281,6 +3321,72 @@ 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; + QStringList filepaths = compress_paths; + if (compress_paths.isEmpty()) { + return; + } + + bool single_file = filepaths.size() == 1; + + // Set the output directory based on the starting file + QFileInfo startFileInfo(filepaths[0]); + out_path = startFileInfo.absolutePath(); + if (out_path.isEmpty()) { + return; + } + + 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); + out_filepath = out_path + 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() { QStringList filepaths = QFileDialog::getOpenFileNames( @@ -3374,6 +3480,58 @@ void GMainWindow::OnDecompressFile() { }); } +void GMainWindow::OnDecompressFileCLI() { + QStringList filepaths = decompress_paths; + QString out_path; + + if (filepaths.isEmpty()) { + return; + } + + bool single_file = filepaths.size() == 1; + QFileInfo startFileInfo(filepaths[0]); + out_path = startFileInfo.absolutePath(); + + 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); + + out_filepath = out_path + 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 void GMainWindow::OnOpenFFmpeg() { auto filename = diff --git a/src/citra_qt/citra_qt.h b/src/citra_qt/citra_qt.h index cdf9eaff6..593c9c569 100644 --- a/src/citra_qt/citra_qt.h +++ b/src/citra_qt/citra_qt.h @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef __unix__ #include #endif @@ -290,7 +291,9 @@ private slots: void OnCaptureScreenshot(); void OnDumpVideo(); void OnCompressFile(); + void OnCompressFileCLI(); void OnDecompressFile(); + void OnDecompressFileCLI(); #ifdef _WIN32 void OnOpenFFmpeg(); #endif @@ -397,6 +400,12 @@ private: // Video dumping bool video_dumping_on_start = false; QString video_dumping_path; + + // Compress/Decompress Paths and Future + QStringList compress_paths; + QStringList decompress_paths; + QFuture compression_future; + // Whether game shutdown is delayed due to video dumping bool game_shutdown_delayed = false; // Whether game was paused due to stopping video dumping @@ -404,7 +413,6 @@ private: QString gl_renderer; std::vector physical_devices; - // Debugger panes ProfilerWidget* profilerWidget; #if MICROPROFILE_ENABLED