diff options
Diffstat (limited to 'src/yuzu/main.cpp')
-rw-r--r-- | src/yuzu/main.cpp | 210 |
1 files changed, 132 insertions, 78 deletions
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 33c9fd0af..4e435c7e2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2010,8 +2010,16 @@ bool GMainWindow::OnShutdownBegin() { emit EmulationStopping(); + int shutdown_time = 1000; + + if (system->DebuggerEnabled()) { + shutdown_time = 0; + } else if (system->GetExitLocked()) { + shutdown_time = 5000; + } + shutdown_timer.setSingleShot(true); - shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); + shutdown_timer.start(shutdown_time); connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); @@ -2267,40 +2275,62 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); } -static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { - std::size_t out = 0; - - for (const auto& subdir : dir->GetSubdirectories()) { - out += 1 + CalculateRomFSEntrySize(subdir, full); - } - - return out + (full ? dir->GetFiles().size() : 0); -} - -static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src, - const FileSys::VirtualDir& dest, std::size_t block_size, bool full) { +static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog, + const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, + bool full) { if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) return false; if (dialog.wasCanceled()) return false; + std::vector<u8> buffer(CopyBufferSize); + auto last_timestamp = std::chrono::steady_clock::now(); + + const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file, + const FileSys::VirtualFile& dest_file) { + if (src_file == nullptr || dest_file == nullptr) { + return false; + } + if (!dest_file->Resize(src_file->GetSize())) { + return false; + } + + for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) { + if (dialog.wasCanceled()) { + dest_file->Resize(0); + return false; + } + + using namespace std::literals::chrono_literals; + const auto new_timestamp = std::chrono::steady_clock::now(); + + if ((new_timestamp - last_timestamp) > 33ms) { + last_timestamp = new_timestamp; + dialog.setValue( + static_cast<int>(std::min(read_size, total_size) * 100 / total_size)); + QCoreApplication::processEvents(); + } + + const auto read = src_file->Read(buffer.data(), buffer.size(), i); + dest_file->Write(buffer.data(), read, i); + + read_size += read; + } + + return true; + }; + if (full) { for (const auto& file : src->GetFiles()) { const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); - if (!FileSys::VfsRawCopy(file, out, block_size)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) + if (!QtRawCopy(file, out)) return false; } } for (const auto& dir : src->GetSubdirectories()) { const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!RomFSRawCopy(dialog, dir, out, block_size, full)) - return false; - dialog.setValue(dialog.value() + 1); - if (dialog.wasCanceled()) + if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full)) return false; } @@ -2573,50 +2603,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - FileSys::VirtualFile base_romfs; - if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { - failed(); - return; - } + FileSys::VirtualFile packed_update_raw{}; + loader->ReadUpdateRaw(packed_update_raw); const auto& installed = system->GetContentProvider(); - const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); - if (!romfs_title_id) { + u64 title_id{}; + u8 raw_type{}; + if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) { failed(); return; } - const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program - : FileSys::ContentRecordType::Data; - const auto base_nca = installed.GetEntry(*romfs_title_id, type); + const auto type = static_cast<FileSys::ContentRecordType>(raw_type); + const auto base_nca = installed.GetEntry(title_id, type); if (!base_nca) { failed(); return; } + const FileSys::NCA update_nca{packed_update_raw, nullptr}; + if (type != FileSys::ContentRecordType::Program || + update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS || + update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) { + packed_update_raw = {}; + } + + const auto base_romfs = base_nca->GetRomFS(); + if (!base_romfs) { + failed(); + return; + } + const auto dump_dir = target == DumpRomFSTarget::Normal ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; - const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); + const auto romfs_dir = fmt::format("{:016X}/romfs", title_id); const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); - FileSys::VirtualFile romfs; - - if (*romfs_title_id == program_id) { - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed}; - romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false); - } else { - romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS(); - } - - const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); - if (extracted == nullptr) { - failed(); - return; - } + const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; + auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); @@ -2640,11 +2668,16 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } + const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); + if (extracted == nullptr) { + failed(); + return; + } + const auto full = res == selections.constFirst(); - const auto entry_size = CalculateRomFSEntrySize(extracted, full); - // The minimum required space is the size of the extracted RomFS + 1 GiB - const auto minimum_free_space = extracted->GetSize() + 0x40000000; + // The expected required space is the size of the RomFS + 1 GiB + const auto minimum_free_space = romfs->GetSize() + 0x40000000; if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) { QMessageBox::warning(this, tr("RomFS Extraction Failed!"), @@ -2655,12 +2688,15 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, - static_cast<s32>(entry_size), this); + QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + size_t read_size = 0; - if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { + if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) { progress.close(); QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), tr("The operation completed successfully.")); @@ -3261,7 +3297,7 @@ void GMainWindow::OnPauseContinueGame() { } void GMainWindow::OnStopGame() { - if (system->GetExitLock() && !ConfirmForceLockedExit()) { + if (system->GetExitLocked() && !ConfirmForceLockedExit()) { return; } @@ -4350,28 +4386,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() { return mii_nca->GetRomFS().get() != nullptr; } -std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, - u64 program_id) { - const auto dlc_entries = - installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); - std::vector<FileSys::ContentProviderEntry> dlc_match; - dlc_match.reserve(dlc_entries.size()); - std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), - [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { - return FileSys::GetBaseTitleID(entry.title_id) == program_id && - installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; - }); - - std::vector<u64> romfs_tids; - romfs_tids.push_back(program_id); - for (const auto& entry : dlc_match) { - romfs_tids.push_back(entry.title_id); - } - - if (romfs_tids.size() > 1) { - QStringList list{QStringLiteral("Base")}; - for (std::size_t i = 1; i < romfs_tids.size(); ++i) { - list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); +bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, + u64* selected_title_id, u8* selected_content_record_type) { + using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; + boost::container::flat_map<u64, ContentInfo> available_title_ids; + + const auto RetrieveEntries = [&](FileSys::TitleType title_type, + FileSys::ContentRecordType record_type) { + const auto entries = installed.ListEntriesFilter(title_type, record_type); + for (const auto& entry : entries) { + if (FileSys::GetBaseTitleID(entry.title_id) == program_id && + installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { + available_title_ids[entry.title_id] = {title_type, record_type}; + } + } + }; + + RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); + RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + + if (available_title_ids.empty()) { + return false; + } + + size_t title_index = 0; + + if (available_title_ids.size() > 1) { + QStringList list; + for (auto& [title_id, content_info] : available_title_ids) { + const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); + if (content_info.first == FileSys::TitleType::Application) { + list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); + } else { + list.push_back( + QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); + } } bool ok; @@ -4379,13 +4428,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv this, tr("Select RomFS Dump Target"), tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); if (!ok) { - return {}; + return false; } - return romfs_tids[list.indexOf(res)]; + title_index = list.indexOf(res); } - return program_id; + const auto selected_info = available_title_ids.nth(title_index); + *selected_title_id = selected_info->first; + *selected_content_record_type = static_cast<u8>(selected_info->second.second); + return true; } bool GMainWindow::ConfirmClose() { @@ -4515,6 +4567,8 @@ void GMainWindow::RequestGameExit() { auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); bool has_signalled = false; + system->SetExitRequested(true); + if (applet_oe != nullptr) { applet_oe->GetMessageQueue()->RequestExit(); has_signalled = true; |