diff --git a/Common/UI/View.h b/Common/UI/View.h index 6f5a8cbfdd..a10333221f 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -576,6 +576,7 @@ public: // Outlines it, to point it out without the keyboard focus (which would turn on keyboard navigation). void SetHighlighted(bool highlighted) { highlighted_ = highlighted; } + bool IsHighlighted() const { return highlighted_; } Event OnClick; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 90a0377514..5e11027ec5 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -33,6 +33,7 @@ #include "Common/Audio/AudioBackend.h" #include "Common/File/AndroidStorage.h" +#include "Common/File/DirListing.h" #include "Common/Data/Text/I18n.h" #include "Common/Data/Text/Parsers.h" #include "Common/Data/Encoding/Utf8.h" @@ -82,6 +83,7 @@ #include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" #include "UI/JitCompareScreen.h" +#include "UI/ShareFiles.h" #include "android/jni/app-android.h" @@ -121,17 +123,132 @@ void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager) { } } +// Zipped, so apps that only take known file types accept it. The zip import recognizes it too. +static void ShareFrameDump(const Path &dumpPath) { + std::string name = dumpPath.GetFilename(); + if (endsWithNoCase(name, ".ppdmp")) { + name.resize(name.size() - strlen(".ppdmp")); + } + if (!ShareFilesAsZip(name, { { dumpPath, "" } })) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Failed")); + } +} + +static bool CopyFileContents(const Path &src, const Path &dest) { + FILE *in = File::OpenCFile(src, "rb"); + if (!in) { + return false; + } + // Content URIs can be written but not created this way, so this works on a document the picker made. + FILE *out = File::OpenCFile(dest, "wb"); + if (!out) { + fclose(in); + return false; + } + bool success = true; + char buffer[65536]; + size_t count; + while ((count = fread(buffer, 1, sizeof(buffer), in)) > 0) { + if (fwrite(buffer, 1, count, out) != count) { + success = false; + break; + } + } + if (ferror(in)) { + success = false; + } + fclose(in); + if (fclose(out) != 0) { + success = false; + } + return success; +} + +static bool CanSaveFileAs() { +#if PPSSPP_PLATFORM(ANDROID) + return System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER); +#else + return false; +#endif +} + +// Where a dump can't be shown in its folder: share it, save it somewhere the user picks, or neither. +class FrameDumpPopupScreen : public UI::PopupScreen { +public: + FrameDumpPopupScreen(const Path &dumpPath) : PopupScreen(dumpPath.GetFilename()), dumpPath_(dumpPath) {} + const char *tag() const override { return "FrameDumpPopup"; } + +protected: + bool ShowButtons() const override { return false; } + void CreatePopupContents(UI::ViewGroup *parent) override { + using namespace UI; + auto di = GetI18NCategory(I18NCat::DIALOG); + auto pa = GetI18NCategory(I18NCat::PAUSE); + if (CanShareFiles()) { + parent->Add(new Choice(di->T("Share"), ImageID("I_SHARE")))->OnClick.Add([this](UI::EventParams &e) { + ShareFrameDump(dumpPath_); + TriggerFinish(DR_OK); + }); + } + if (CanSaveFileAs()) { + parent->Add(new Choice(di->T("Save"), ImageID("I_FILE_SAVE")))->OnClick.Add([this, di](UI::EventParams &e) { + // Stays open until the picker answers, since the answer is dropped once the screen is gone. + System_BrowseForFileSave(GetRequesterToken(), di->T("Save"), dumpPath_.GetFilename(), BrowseFileType::ANY, [this](std::string_view value, int) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + const Path dest(value); + if (CopyFileContents(dumpPath_, dest)) { + // A picked document's URI ends in an ID, so ask for its name. + File::FileInfo info; + std::string name = dumpPath_.GetFilename(); + if (File::GetFileInfo(dest, &info) && !info.name.empty()) { + name = info.name; + } + g_OSD.Show(OSDType::MESSAGE_SUCCESS, name); + } else { + g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Failed")); + } + TriggerFinish(DR_OK); + }); + }); + } + parent->Add(new Choice(pa->T("Continue")))->OnClick.Add([this](UI::EventParams &e) { + TriggerFinish(DR_CANCEL); + }); + } + +private: + Path dumpPath_; +}; + +extern ScreenManager *g_screenManager; + +void OnFrameDumpCreated(const Path &dumpPath, bool openNow) { + NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); + std::function open; + if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { + open = [dumpPath]() { System_ShowFileInFolder(dumpPath); }; + } else if (CanShareFiles() || CanSaveFileAs()) { + open = [dumpPath]() { g_screenManager->push(new FrameDumpPopupScreen(dumpPath)); }; + } + g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f, "framedump"); + if (!open) { + return; + } + if (openNow) { + // The recorder calls back on the emulation thread. + System_RunOnMainThread(open); + } else { + g_OSD.SetClickCallback("framedump", open); + } +} + void SaveFrameDump() { if (!gpu) { return; } gpu->GetRecorder()->RecordNextFrame([](const Path &dumpPath) { - NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); - if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - System_ShowFileInFolder(dumpPath); - } else { - g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f); - } + OnFrameDumpCreated(dumpPath, true); }); } @@ -155,12 +272,18 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { screenManager()->push(new LogConfigScreen()); }); - items->Add(new Choice(dev->T("Debugger")))->OnClick.Add([](UI::EventParams &e) { + if (PSP_CoreParameter().fileType != IdentifiedFileType::PPSSPP_GE_DUMP) { + items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) { + SaveFrameDump(); + }); + } + + items->Add(new Choice(dev->T("Debugger"), ImageID("I_DEBUGGER")))->OnClick.Add([](UI::EventParams &e) { g_Config.bShowImDebugger = !g_Config.bShowImDebugger; }); if (WebServerRunning(WebServerFlags::DEBUGGER)) { - items->Add(new Choice(dev->T("Remote debugger")))->OnClick.Add([](UI::EventParams &e) { + items->Add(new Choice(dev->T("Remote debugger"), ImageID("I_NETWORK")))->OnClick.Add([](UI::EventParams &e) { int port = g_Config.iRemoteISOPort; // Also used for serving a local remote debugger. if (g_Config.bRemoteDebuggerLocal) { // TODO: Need to modify this URL to add /cpu when we upgrade to the latest version of the web debugger. @@ -213,12 +336,6 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs"))); }); - if (PSP_CoreParameter().fileType != IdentifiedFileType::PPSSPP_GE_DUMP) { - items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) { - SaveFrameDump(); - }); - } - // This one is not very useful these days, and only really on desktop. Hide it on other platforms. if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) { items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Add([](UI::EventParams &e) { diff --git a/UI/DevScreens.h b/UI/DevScreens.h index fc4f2fae61..9240e12c04 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -213,3 +213,7 @@ public: void DrawProfile(UIContext &ui); void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager); + +// Tells the user where the dump went. With openNow, shows it in its folder (or shares it, where +// folders can't be shown) right away, otherwise when the message is clicked. Callable from any thread. +void OnFrameDumpCreated(const Path &dumpPath, bool openNow); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 075d1bdfd1..35c0368260 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -771,14 +771,9 @@ void EmuScreen::ProcessVKey(VirtKey virtKey, bool down) { if (!gpu) { return; } + // Not opened right away, which would interrupt the game. gpu->GetRecorder()->RecordNextFrame([](const Path &dumpPath) { - NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); - g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f, "framedump"); - if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - g_OSD.SetClickCallback("framedump", [dumpPath]() { - System_ShowFileInFolder(dumpPath); - }); - } + OnFrameDumpCreated(dumpPath, false); }); } break; diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index c849745382..1780051aca 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -306,22 +306,11 @@ u64 GameInfo::GetGameSavedataSizeInBytes() const { if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY || fileType == IdentifiedFileType::PPSSPP_SAVESTATE) { return 0; } - std::vector saveDataDir = GetSaveDataDirectories(); - u64 totalSize = 0; - u64 filesSizeInDir = 0; - for (const auto& dir : saveDataDir) { - std::vector fileInfo; - File::GetFilesInDir(dir, &fileInfo); - for (auto const &file : fileInfo) { - if (!file.isDirectory) - filesSizeInDir += file.size; + for (const Path &dir : GetSaveDataDirectories()) { + if (!IsInstalledGameData(dir)) { + totalSize += File::ComputeRecursiveDirectorySize(dir); } - if (filesSizeInDir < 0xA00000) { - // HACK: Generally the savedata size in a dir shouldn't be more than 10MB. - totalSize += filesSizeInDir; - } - filesSizeInDir = 0; } return totalSize; } @@ -330,29 +319,57 @@ u64 GameInfo::GetInstallDataSizeInBytes() const { if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY || fileType == IdentifiedFileType::PPSSPP_SAVESTATE) { return 0; } - std::vector saveDataDir = GetSaveDataDirectories(); - u64 totalSize = 0; - u64 filesSizeInDir = 0; - for (const auto& dir : saveDataDir) { - std::vector fileInfo; - File::GetFilesInDir(dir, &fileInfo); - for (auto const &file : fileInfo) { - // TODO: Might want to recurse here? Don't know games that use directories - // for install-data though. - if (!file.isDirectory) - filesSizeInDir += file.size; + for (const Path &dir : GetSaveDataDirectories()) { + if (IsInstalledGameData(dir)) { + totalSize += File::ComputeRecursiveDirectorySize(dir); } - if (filesSizeInDir >= 0xA00000) { - // HACK: Generally the savedata size in a dir shouldn't be more than 10MB. - // This is probably GameInstall data. - totalSize += filesSizeInDir; - } - filesSizeInDir = 0; } return totalSize; } +bool IsInstalledGameData(const Path &saveDir) { + // Saves can have files outside the list too (PS1 memory cards, homebrew settings), but only a few MB. + // Installs are hundreds. + constexpr u64 MIN_INSTALLED_SIZE = 10 * 1024 * 1024; + // Written by the savedata utility, but never in the list. + static const char *const metaFiles[] = { "PARAM.SFO", "ICON0.PNG", "ICON1.PMF", "ICON1.PNG", "PIC1.PNG", "SND0.AT3" }; + + std::vector listed; + std::string sfoData; + ParamSFOData sfo; + if (File::ReadBinaryFileToString(saveDir / "PARAM.SFO", &sfoData) && sfo.ReadSFO((const u8 *)sfoData.data(), sfoData.size())) { + unsigned int size = 0; + const u8 *list = sfo.GetValueData("SAVEDATA_FILE_LIST", &size); + // Entries of 32 bytes (SaveSFOFileListEntry), starting with a 13-byte name. + constexpr unsigned int ENTRY_SIZE = 32; + constexpr unsigned int NAME_SIZE = 13; + for (unsigned int offset = 0; list && offset + ENTRY_SIZE <= size; offset += ENTRY_SIZE) { + const char *name = (const char *)list + offset; + const std::string_view entryName(name, strnlen(name, NAME_SIZE)); + if (!entryName.empty()) { + listed.emplace_back(entryName); + } + } + } + + std::vector files; + File::GetFilesInDir(saveDir, &files); + u64 unlistedSize = 0; + for (const File::FileInfo &file : files) { + if (file.isDirectory) { + unlistedSize += File::ComputeRecursiveDirectorySize(file.fullName); + continue; + } + const auto matches = [&file](std::string_view name) { return equalsNoCase(file.name, name); }; + if (std::any_of(std::begin(metaFiles), std::end(metaFiles), matches) || std::any_of(listed.begin(), listed.end(), matches)) { + continue; + } + unlistedSize += file.size; + } + return unlistedSize >= MIN_INSTALLED_SIZE; +} + bool GameInfo::CreateLoader() { if (!fileLoader) { std::lock_guard guard(loaderLock); diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 9b97f5bb8e..9550731236 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -253,3 +253,7 @@ private: // This one can be global, no good reason not to. extern GameInfoCache *g_gameInfoCache; + +// Whether a folder in PSP/SAVEDATA holds data a game installed (or downloaded) there, rather than a save. +// Games write those files with sceIo rather than the savedata utility, so PARAM.SFO's file list leaves them out. +bool IsInstalledGameData(const Path &saveDir); diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index c3015a6ca9..1e18b7d784 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -61,6 +61,7 @@ #include "UI/MainScreen.h" #include "UI/BackgroundAudio.h" #include "UI/SavedataScreen.h" +#include "UI/ShareFiles.h" #include "UI/MiscViews.h" // This is in an objective-C file. @@ -296,6 +297,10 @@ void GameScreen::dialogFinished(const Screen *dialog, DialogResult result) { if (lookupsStarted_) { StartLookups(); } + if (!strcmp(dialog->tag(), "Savedata")) { + // Saves may have been deleted or imported there. update() recreates the views when the size is in. + info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, g_desiredFlags, &knownFlags_, GameInfoFlags::SAVEDATA_SIZE); + } UITwoPaneBaseDialogScreen::dialogFinished(dialog, result); } @@ -712,6 +717,18 @@ void GameScreen::CreateContextMenu(UI::ViewGroup *parent) { }); } + if (info_->saveDataSize || info_->installDataSize) { + auto sa = GetI18NCategory(I18NCat::SAVEDATA); + parent->Add(new Choice(sa->T("Savedata Manager"), ImageID("I_FILE_SAVE")))->OnClick.Add([this](UI::EventParams &e) { + screenManager()->push(new SavedataScreen(gamePath_)); + }); + } + if (info_->saveDataSize && CanShareFiles()) { + auto sa = GetI18NCategory(I18NCat::SAVEDATA); + parent->Add(new Choice(sa->T("Export all saves for this game"), ImageID("I_SHARE")))->OnClick.Add([this](UI::EventParams &e) { + ExportAllGameSavesForGame(info_.get()); + }); + } if (info_->saveDataSize) { Choice *btnDeleteSaveData = new Choice(ga->T("Delete Save Data"), ImageID("I_TRASHCAN")); parent->Add(btnDeleteSaveData)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 2ece491bc5..a880a7e544 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -25,10 +25,13 @@ #include "Common/Math/curves.h" #include "Common/Data/Text/Parsers.h" #include "Common/System/NativeApp.h" +#include "Common/System/OSD.h" #include "Common/System/Request.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" #include "Common/UI/PopupScreens.h" +#include "Common/UI/Root.h" +#include "Common/UI/ScrollView.h" #include "Common/UI/ViewGroup.h" #include "Common/UI/AsyncImageFileView.h" #include "Common/UI/ScreenManager.h" @@ -37,6 +40,7 @@ #include "UI/GameInfoCache.h" #include "UI/PauseScreen.h" #include "UI/MiscScreens.h" +#include "UI/ShareFiles.h" #include "Common/File/FileUtil.h" #include "Common/TimeUtil.h" @@ -49,6 +53,38 @@ class SavedataButton; +// Adds a folder's files to a zip under nameInZip, the layout the savedata import expects (DIR/PARAM.SFO). +static void AddFolderToZip(const Path &folder, const std::string &nameInZip, std::vector *entries) { + std::vector files; + File::GetFilesInDir(folder, &files); + for (const File::FileInfo &file : files) { + const std::string name = nameInZip + "/" + file.name; + if (file.isDirectory) { + AddFolderToZip(file.fullName, name, entries); + } else { + entries->push_back({ file.fullName, name }); + } + } +} + +static void ExportAsZip(std::string_view zipName, const std::vector &entries) { + if (!ShareFilesAsZip(zipName, entries)) { + auto sa = GetI18NCategory(I18NCat::SAVEDATA); + g_OSD.Show(OSDType::MESSAGE_ERROR, sa->T("Export failed")); + } +} + +void ExportAllGameSavesForGame(GameInfo *info) { + std::vector entries; + for (const Path &dir : info->GetSaveDataDirectories()) { + // Installed data is large and not a save, so it's left out. + if (File::Exists(dir / "PARAM.SFO") && !IsInstalledGameData(dir)) { + AddFolderToZip(dir, dir.GetFilename(), &entries); + } + } + ExportAsZip(info->GetTitle() + " (" + info->id + ")", entries); +} + SavedataView::SavedataView(UIContext &dc, const Path &savePath, IdentifiedFileType type, std::string_view title, std::string_view savedataTitle, std::string_view savedataDetail, std::string_view fileSize, std::string_view mtime, bool showIcon, UI::LayoutParams *layoutParams) : LinearLayout(ORIENT_VERTICAL, layoutParams) { @@ -169,12 +205,44 @@ void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { savedataView_ = content->Add(new SavedataView(dc, ginfo.get(), ginfo->fileType, true)); auto di = GetI18NCategory(I18NCat::DIALOG); + auto sa = GetI18NCategory(I18NCat::SAVEDATA); - LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); - buttonRow->SetSpacing(12); + const bool isState = savePath_.GetFileExtension() == ".ppst"; + // Installed data is large and not a save, so it's left out of exports. + bool canExport = false; + if (CanShareFiles()) { + canExport = isState || !IsInstalledGameData(savePath_); + if (!canExport) { + TextView *installedNote = content->Add(new TextView(sa->T("InstalledDataNotExported", "This is data the game installed, not a save, so it can't be exported."), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(Margins(10, 0, 10, 5)))); + installedNote->SetTextColor(dc.GetTheme().popupStyle.fgColor); + } + } - buttonRow->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f)))->OnClick.Handle(this, &UIScreen::OnBack); - buttonRow->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { + LinearLayout *buttonRow1 = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + LinearLayout *buttonRow2 = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + buttonRow1->SetSpacing(12); + + if (canExport) { + buttonRow1->Add(new Choice(sa->T("Export"), ImageID("I_SHARE"), new LinearLayoutParams(1.0f)))->OnClick.Add([this, isState](UI::EventParams &e) { + std::vector entries; + if (isState) { + // The state and its screenshot, in the root of the zip, which the import takes as savestates. + entries.push_back({ savePath_, "" }); + Path screenshot; + if (savePath_.WithReplacedExtension(".ppst", ".jpg", &screenshot) && File::Exists(screenshot)) { + entries.push_back({ screenshot, "" }); + } + std::string name = savePath_.GetFilename(); + name.resize(name.size() - strlen(".ppst")); + ExportAsZip(name, entries); + } else { + AddFolderToZip(savePath_, savePath_.GetFilename(), &entries); + ExportAsZip(savePath_.GetFilename(), entries); + } + }); + } + + buttonRow1->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { auto di = GetI18NCategory(I18NCat::DIALOG); std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO); @@ -188,13 +256,15 @@ void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { } })); }); + + buttonRow2->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f)))->OnClick.Handle(this, &UIScreen::OnBack); if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - buttonRow->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { + buttonRow2->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { System_ShowFileInFolder(savePath_); }); } - buttonRow->SetRoundCorners(); - parent->Add(buttonRow); + buttonRow1->SetRoundCorners(); + buttonRow2->SetRoundCorners(); } class SortedLinearLayout : public UI::LinearLayoutList { @@ -406,6 +476,11 @@ void SavedataButton::Draw(UIContext &dc) { dc.PopScissor(); dc.RebindTexture(); + if (highlighted_) { + // Square corners. (A zero radius draws no outline at all.) Medium, since a thin line looks like the gaps between the rows. + dc.Draw()->DrawRoundRect(bounds_, ROUND_RECT_RADII[0], 0xFFFFFFFF, RoundRectStyle::OutlineMedium, RoundCorners::None); + dc.Draw()->Flush(); + } } std::string SavedataButton::DescribeText() const { @@ -413,13 +488,48 @@ std::string SavedataButton::DescribeText() const { return ApplySafeSubstitutions(u->T("%1 button"), title_) + "\n" + subtitle_; } -SavedataBrowser::SavedataBrowser(const Path &path, UI::LayoutParams *layoutParams) - : LinearLayout(ORIENT_VERTICAL, layoutParams), path_(path) { +SavedataBrowser::SavedataBrowser(const Path &path, std::string_view highlightPrefix, UI::LayoutParams *layoutParams) + : LinearLayout(ORIENT_VERTICAL, layoutParams), path_(path), highlightPrefix_(highlightPrefix) { Refresh(); } +void SavedataBrowser::ScrollToHighlighted() { + if (!gameList_) { + scrollPending_ = false; + return; + } + SavedataButton *first = nullptr; + for (int i = 0; i < gameList_->GetNumSubviews(); i++) { + SavedataButton *button = static_cast(gameList_->GetViewByIndex(i)); + if (!button->IsHighlighted() || button->GetVisibility() != UI::V_VISIBLE) { + continue; + } + // Scrolling needs the position, so wait until the tab has been shown and laid out (after sorting). + if (button->GetBounds().h <= 0.0f) { + return; + } + if (!first || button->GetBounds().y < first->GetBounds().y) { + first = button; + } + } + scrollPending_ = false; + if (!first) { + return; + } + UI::ScrollView *scrollView = UI::GetEnclosingScrollView(first); + if (scrollView) { + scrollView->ScrollToCenter(first); + } + if (UI::IsFocusMovementEnabled()) { + UI::SetFocusedView(first, UI::FocusFlags::CAUSE_RESTORE); + } +} + void SavedataBrowser::Update() { LinearLayout::Update(); + if (scrollPending_) { + ScrollToHighlighted(); + } if (searchPending_) { searchPending_ = false; @@ -556,7 +666,13 @@ void SavedataBrowser::Refresh() { isSaveData = true; if (isSaveData || isState) { - savedataButtons.push_back(new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT))); + SavedataButton *button = new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT)); + // Save folders and savestates both start with the game ID. + if (!highlightPrefix_.empty() && startsWith(fileInfo[i].name, highlightPrefix_)) { + button->SetHighlighted(true); + scrollPending_ = true; + } + savedataButtons.push_back(button); } } @@ -626,7 +742,7 @@ void SavedataScreen::CreateSavedataTab(UI::ViewGroup *savedata) { sortStrip->SetRoundCorners(); savedata->Add(sortStrip); - dataBrowser_ = savedata->Add(new SavedataBrowser(savedata_dir, new LayoutParams(FILL_PARENT, FILL_PARENT))); + dataBrowser_ = savedata->Add(new SavedataBrowser(savedata_dir, gameID_, new LayoutParams(FILL_PARENT, FILL_PARENT))); dataBrowser_->SetSortOption(sortOption_); if (!searchFilter_.empty()) dataBrowser_->SetSearchFilter(searchFilter_); @@ -652,13 +768,43 @@ void SavedataScreen::CreateSavestateTab(UI::ViewGroup *savestate) { sortStrip->SetRoundCorners(); savestate->Add(sortStrip); - stateBrowser_ = savestate->Add(new SavedataBrowser(savestate_dir)); + stateBrowser_ = savestate->Add(new SavedataBrowser(savestate_dir, gameID_)); stateBrowser_->SetSortOption(sortOption_); if (!searchFilter_.empty()) stateBrowser_->SetSearchFilter(searchFilter_); stateBrowser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); } +bool SavedataScreen::LookUpGame() { + gameID_.clear(); + if (gamePath_.empty()) { + return true; + } + std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); + if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) { + gameLookupPending_ = true; + return false; + } + gameLookupPending_ = false; + // Same rule as GetSaveDataDirectories(), or every folder would match. + if (ginfo->id.size() < 5) { + return true; + } + gameID_ = ginfo->id; + return true; +} + +void SavedataScreen::PreCreateViews() { + LookUpGame(); +} + +void SavedataScreen::update() { + UITabbedBaseDialogScreen::update(); + if (gameLookupPending_ && LookUpGame()) { + RecreateViews(); + } +} + void SavedataScreen::CreateTabs() { using namespace UI; auto sa = GetI18NCategory(I18NCat::SAVEDATA); diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index c0040b0795..f632ac07cc 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -19,6 +19,7 @@ #include #include +#include #include "Common/File/Path.h" @@ -38,7 +39,8 @@ enum class SavedataSortOption { class SavedataBrowser : public UI::LinearLayout { public: - SavedataBrowser(const Path &path, UI::LayoutParams *layoutParams = 0); + // Entries whose names start with highlightPrefix (a game ID) are outlined, and the first one scrolled to. + SavedataBrowser(const Path &path, std::string_view highlightPrefix, UI::LayoutParams *layoutParams = 0); void Update() override; @@ -58,13 +60,17 @@ private: void Refresh(); void SavedataButtonClick(UI::EventParams &e); + void ScrollToHighlighted(); + SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; UI::ViewGroup *gameList_ = nullptr; UI::TextView *noMatchView_ = nullptr; UI::TextView *searchingView_ = nullptr; Path path_; + std::string highlightPrefix_; std::string searchFilter_; bool searchPending_ = false; + bool scrollPending_ = false; }; class SavedataView; @@ -95,6 +101,7 @@ public: void dialogFinished(const Screen *dialog, DialogResult result) override; void sendMessage(UIMessage message, const char *value) override; + void update() override; ViewLayoutMode LayoutMode() const override { return ViewLayoutMode::ApplyInsets; @@ -103,6 +110,7 @@ public: const char *tag() const override { return "Savedata"; } protected: + void PreCreateViews() override; void CreateTabs() override; void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override; @@ -113,7 +121,11 @@ private: void CreateSavedataTab(UI::ViewGroup *savedata); void CreateSavestateTab(UI::ViewGroup *savestate); + // The ID of the game the screen was opened for (its saves are highlighted), once its PARAM.SFO is loaded. + bool LookUpGame(); + std::string gameID_; + bool gameLookupPending_ = false; SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; SavedataBrowser *dataBrowser_ = nullptr; SavedataBrowser *stateBrowser_ = nullptr; @@ -173,3 +185,6 @@ private: UI::TextView *mTime_ = nullptr; UI::TextView *fileSize_ = nullptr; }; + +// Zips a game's saves (without installed data) and shares the zip. +void ExportAllGameSavesForGame(GameInfo *info); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index cd646f9f1e..08ea4b63f9 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1197,6 +1197,10 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } return true; } + case SystemRequestType::BROWSE_FOR_FILE_SAVE: + // The type isn't passed on, the picker keeps the suggested name's extension. + PushCommand("browse_file_save", StringFromFormat("%d:@:%s", requestId, param2.c_str()), requestId); + return true; case SystemRequestType::BROWSE_FOR_FOLDER: PushCommand("browse_folder", StringFromFormat("%d", requestId), requestId); return true; diff --git a/android/src/org/ppsspp/ppsspp/DocumentResultProxyActivity.java b/android/src/org/ppsspp/ppsspp/DocumentResultProxyActivity.java index 7ac345ff13..25d8b41be4 100644 --- a/android/src/org/ppsspp/ppsspp/DocumentResultProxyActivity.java +++ b/android/src/org/ppsspp/ppsspp/DocumentResultProxyActivity.java @@ -53,8 +53,10 @@ public class DocumentResultProxyActivity extends AppCompatActivity { if (uri != null) { Log.i(TAG, "DocumentResultProxy: Selected URI: " + uri); final boolean isTree = pickerIntent != null && Intent.ACTION_OPEN_DOCUMENT_TREE.equals(pickerIntent.getAction()); + // A created document gets written to after the picker is gone. + final boolean isCreate = pickerIntent != null && Intent.ACTION_CREATE_DOCUMENT.equals(pickerIntent.getAction()); try { - if (isTree) { + if (isTree || isCreate) { getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } else { getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 682bddf66d..10a6cecb54 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -1808,6 +1808,30 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList } intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + Intent proxy = new Intent(this, DocumentResultProxyActivity.class); + proxy.putExtra("picker_intent", intent); + proxy.putExtra("request_id", requestId); + startActivity(proxy); + return true; + } catch (Exception e) { + NativeApp.reportException(e, params); + Log.e(TAG, e.toString()); + return false; + } + } else if (command.equals("browse_file_save")) { + try { + // requestId:@:suggested filename + String[] param = params.split(":@:", 2); + int requestId = Integer.parseInt(param[0]); + Log.i(TAG, "browse_file_save request ID: " + requestId); + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + // The generic type keeps the extension of the suggested name. + intent.setType("application/octet-stream"); + if (param.length > 1) { + intent.putExtra(Intent.EXTRA_TITLE, param[1]); + } + Intent proxy = new Intent(this, DocumentResultProxyActivity.class); proxy.putExtra("picker_intent", intent); proxy.putExtra("request_id", requestId); diff --git a/assets/lang/ar_AE.ini b/assets/lang/ar_AE.ini index 7996038b9f..ec97aafaef 100644 --- a/assets/lang/ar_AE.ini +++ b/assets/lang/ar_AE.ini @@ -596,6 +596,7 @@ Search = البحث seconds, 0:off = ثوانٍ، 0 = مُغلق # AI translated Select = حدد Settings = الإعدادات # AI translated +Share = مشاركة # AI translated Shift = شفت Show in folder = أظهر في المجلد Sign In = تسجيل الدخول # AI translated @@ -1472,7 +1473,11 @@ View Feedback = أظهر كل المراجعات [Savedata] Date = التاريخ +Export = تصدير # AI translated +Export all saves for this game = تصدير كل بيانات الحفظ لهذه اللعبة # AI translated +Export failed = فشل التصدير # AI translated Filename = إسم الملف +InstalledDataNotExported = هذه بيانات ثبّتتها اللعبة، وليست بيانات حفظ، لذا لا يمكن تصديرها. # AI translated No screenshot = لا توجد لقطة شاشة # AI translated None yet. Things will appear here after you save. = ليس بعد. الأشياء سوف تظهر هنا بعد الحفظ. Nothing matching '%1' was found = لم يتم العثور على ما يطابق '%1' # AI translated diff --git a/assets/lang/az_AZ.ini b/assets/lang/az_AZ.ini index ac21aeb0c0..19bc8f99e7 100644 --- a/assets/lang/az_AZ.ini +++ b/assets/lang/az_AZ.ini @@ -588,6 +588,7 @@ Search = Axtar seconds, 0:off = saniyə, 0 = bağlı Select = Seç Settings = Quruluşlar +Share = Paylaş # AI translated Shift = Shift # same as English Show in folder = Qovluqda göstər Sign In = Daxil ol # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Bütün geri bildirimləri gör [Savedata] Date = Tarix +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Fayl adı +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = Ekran çəkilişi yoxdur None yet. Things will appear here after you save. = Hələlik heç nə. Qoruduqdan sonra burada nəsnələr oluşacaq. Nothing matching '%1' was found = '%1' ilə uyğun heç nə tapılmadı # AI translated diff --git a/assets/lang/be_BY.ini b/assets/lang/be_BY.ini index 44b011463e..e32f7d368f 100644 --- a/assets/lang/be_BY.ini +++ b/assets/lang/be_BY.ini @@ -588,6 +588,7 @@ Search = Пошук seconds, 0:off = секунд, 0 = выкл. # AI translated Select = Select # same as English Settings = Налады +Share = Падзяліцца # AI translated Shift = Shift # same as English Show in folder = Паказаць у тэчцы Sign In = Увайсці # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Усе водгукі # AI translated [Savedata] Date = Дата +Export = Экспартаваць # AI translated +Export all saves for this game = Экспартаваць усе захаванні гэтай гульні # AI translated +Export failed = Не ўдалося экспартаваць # AI translated Filename = Імя файла +InstalledDataNotExported = Гэта даныя, усталяваныя гульнёй, а не захаванне, таму іх нельга экспартаваць. # AI translated No screenshot = Няма здымка экрана # AI translated None yet. Things will appear here after you save. = Пакуль нічога няма. Тут з'явяцца захаванні, калі вы захаваецеся. # AI translated Nothing matching '%1' was found = Нічога не знойдзена па запыце '%1' # AI translated diff --git a/assets/lang/bg_BG.ini b/assets/lang/bg_BG.ini index b72abe744c..45f593cbf0 100644 --- a/assets/lang/bg_BG.ini +++ b/assets/lang/bg_BG.ini @@ -588,6 +588,7 @@ Search = Търсене seconds, 0:off = секунди, 0 = изкл. # AI translated Select = Изберете Settings = Настройки +Share = Сподели # AI translated Shift = Shift # same as English Show in folder = Покажи в папка Sign In = Вход # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Виж всички отзиви # AI translated [Savedata] Date = Дата # AI translated +Export = Експортирай # AI translated +Export all saves for this game = Експортиране на всички запазени данни за тази игра # AI translated +Export failed = Експортирането не бе успешно # AI translated Filename = Име на файла # AI translated +InstalledDataNotExported = Това са данни, инсталирани от играта, а не запис, затова не могат да бъдат експортирани. # AI translated No screenshot = Няма скрийншот # AI translated None yet. Things will appear here after you save. = Все още няма нищо. Тук ще се появят записи, след като запазите. # AI translated Nothing matching '%1' was found = Не е намерено нищо, съвпадащо с '%1' # AI translated diff --git a/assets/lang/ca_ES.ini b/assets/lang/ca_ES.ini index 715482fb63..54361f0d69 100644 --- a/assets/lang/ca_ES.ini +++ b/assets/lang/ca_ES.ini @@ -588,6 +588,7 @@ Search = Cercar seconds, 0:off = segons, 0 = off Select = Seleccionar Settings = Configuració # AI translated +Share = Comparteix # AI translated Shift = Canviar Show in folder = Mostrar en carpeta Sign In = Inicia la sessió # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Veure tots els comentaris # AI translated [Savedata] Date = Data +Export = Exporta # AI translated +Export all saves for this game = Exporta totes les dades desades d'aquest joc # AI translated +Export failed = No s'ha pogut exportar # AI translated Filename = Nom del fitxer +InstalledDataNotExported = Són dades que ha instal·lat el joc, no una partida desada, així que no es poden exportar. # AI translated No screenshot = Sense captura de pantalla None yet. Things will appear here after you save. = No hi ha dades. Un cop desis partida apareixeran aquí. # AI translated Nothing matching '%1' was found = No s'ha trobat res que coincideixi amb '%1' # AI translated diff --git a/assets/lang/cz_CZ.ini b/assets/lang/cz_CZ.ini index 2f43f5674c..114a6a38c8 100644 --- a/assets/lang/cz_CZ.ini +++ b/assets/lang/cz_CZ.ini @@ -588,6 +588,7 @@ Search = Hledat # AI translated seconds, 0:off = sekundy, 0 = vypnuto Select = Vybrat Settings = Nastavení # AI translated +Share = Sdílet # AI translated Shift = Shift # same as English Show in folder = Zobrazit ve složce Sign In = Přihlásit se # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Zobrazit zpětnou vazbu # AI translated [Savedata] Date = Datum # AI translated +Export = Exportovat # AI translated +Export all saves for this game = Exportovat všechna uložená data této hry # AI translated +Export failed = Export se nezdařil # AI translated Filename = Název souboru # AI translated +InstalledDataNotExported = Toto jsou data nainstalovaná hrou, ne uložená pozice, takže je nelze exportovat. # AI translated No screenshot = Žádný snímek obrazovky # AI translated None yet. Things will appear here after you save. = Zatím žádné. Položky zde budou zobrazeny, až uložíte hru. Nothing matching '%1' was found = Nebylo nalezeno nic odpovídajícího '%1' # AI translated diff --git a/assets/lang/da_DK.ini b/assets/lang/da_DK.ini index d0a9e04c94..c11526c29c 100644 --- a/assets/lang/da_DK.ini +++ b/assets/lang/da_DK.ini @@ -588,6 +588,7 @@ Search = Søg # AI translated seconds, 0:off = sekunder, 0 = off Select = Vælg Settings = Indstillinger # AI translated +Share = Del # AI translated Shift = Shift # same as English Show in folder = Vis i katalog Sign In = Log ind # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Se feedback [Savedata] Date = Dato # AI translated +Export = Eksportér # AI translated +Export all saves for this game = Eksportér alle gemte data for dette spil # AI translated +Export failed = Eksporten mislykkedes # AI translated Filename = Filnavn # AI translated +InstalledDataNotExported = Dette er data, som spillet har installeret, ikke gemte data, så de kan ikke eksporteres. # AI translated No screenshot = Intet skærmdump # AI translated None yet. Things will appear here after you save. = Intet endnu. Ting vises her, når du har gemt. # AI translated Nothing matching '%1' was found = Intet, der matcher '%1', blev fundet # AI translated diff --git a/assets/lang/de_DE.ini b/assets/lang/de_DE.ini index 85080919a8..ed4eaabf00 100644 --- a/assets/lang/de_DE.ini +++ b/assets/lang/de_DE.ini @@ -587,6 +587,7 @@ Search = Suche seconds, 0:off = Sekunden, 0 = Aus Select = Auswählen Settings = Einstellungen +Share = Teilen # AI translated Shift = Umschalt Show in folder = Im Ordner anzeigen Sign In = Anmelden # AI translated @@ -1461,7 +1462,11 @@ View Feedback = Alle Rückmeldungen ansehen [Savedata] Date = Datum +Export = Exportieren # AI translated +Export all saves for this game = Alle Speicherdaten dieses Spiels exportieren # AI translated +Export failed = Export fehlgeschlagen # AI translated Filename = Dateiname +InstalledDataNotExported = Das sind vom Spiel installierte Daten, keine Speicherdaten, daher können sie nicht exportiert werden. # AI translated No screenshot = Kein Bildschirmfoto None yet. Things will appear here after you save. = Noch keine. Dinge werden hier erscheinen, nachdem du gespeichert hast. Nothing matching '%1' was found = Nichts gefunden, das '%1' entspricht # AI translated diff --git a/assets/lang/dr_ID.ini b/assets/lang/dr_ID.ini index 93d4069f62..aa6d112efb 100644 --- a/assets/lang/dr_ID.ini +++ b/assets/lang/dr_ID.ini @@ -588,6 +588,7 @@ Search = Search seconds, 0:off = seconds, 0 = off Select = Pillei Settings = Settings +Share = Share Shift = Angka'i Show in folder = Show in folder Sign In = Sign In @@ -1462,7 +1463,11 @@ View Feedback = View all feedbacks [Savedata] Date = Date +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Filename +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = No screenshot None yet. Things will appear here after you save. = None yet. Things will appear here after you save. Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 88fb41c31d..f3a446bce6 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -612,6 +612,7 @@ Search = Search seconds, 0:off = seconds, 0 = off Select = Select Settings = Settings +Share = Share Shift = Shift Show in folder = Show in folder Sign In = Sign In @@ -1487,7 +1488,11 @@ View Feedback = View all feedbacks [Savedata] Date = Date +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Filename +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = No screenshot None yet. Things will appear here after you save. = None yet. Things will appear here after you save. Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/es_ES.ini b/assets/lang/es_ES.ini index deaff51758..4ea1d58134 100644 --- a/assets/lang/es_ES.ini +++ b/assets/lang/es_ES.ini @@ -589,6 +589,7 @@ Search = Buscar seconds, 0:off = segundos, 0 = no Select = Seleccionar Settings = Ajustes +Share = Compartir # AI translated Shift = Alternar Show in folder = Mostrar carpeta Sign In = Iniciar sesión # AI translated @@ -1463,7 +1464,11 @@ View Feedback = Ver comentarios [Savedata] Date = Fecha +Export = Exportar # AI translated +Export all saves for this game = Exportar todos los datos guardados de este juego # AI translated +Export failed = Error al exportar # AI translated Filename = Nombre de archivo +InstalledDataNotExported = Son datos que instaló el juego, no una partida guardada, así que no se pueden exportar. # AI translated No screenshot = Sin captura de pantalla None yet. Things will appear here after you save. = Ninguna todavía. Las cosas aparecerán aquí después de guardar. Nothing matching '%1' was found = No se encontró nada que coincida con '%1' # AI translated diff --git a/assets/lang/es_LA.ini b/assets/lang/es_LA.ini index 41987fcbe7..0d97ab8463 100644 --- a/assets/lang/es_LA.ini +++ b/assets/lang/es_LA.ini @@ -588,6 +588,7 @@ Search = Buscar seconds, 0:off = segundos, 0 = no # AI translated Select = Seleccionar Settings = Ajustes +Share = Compartir # AI translated Shift = Alternar Show in folder = Mostrar en carpeta Sign In = Iniciar sesión # AI translated @@ -1464,7 +1465,11 @@ View Feedback = Ver comentarios [Savedata] Date = Fecha +Export = Exportar # AI translated +Export all saves for this game = Exportar todas las partidas guardadas de este juego # AI translated +Export failed = Error al exportar # AI translated Filename = Nombre de archivo +InstalledDataNotExported = Son datos que instaló el juego, no una partida guardada, así que no se pueden exportar. # AI translated No screenshot = No hay capturas de pantalla. None yet. Things will appear here after you save. = No hay datos. Una vez que guardes partida aparecerán aquí. Nothing matching '%1' was found = No se encontró nada que coincida con '%1' # AI translated diff --git a/assets/lang/fa_IR.ini b/assets/lang/fa_IR.ini index 764fc4f476..f7c7a171c3 100644 --- a/assets/lang/fa_IR.ini +++ b/assets/lang/fa_IR.ini @@ -588,6 +588,7 @@ Search = جستجو seconds, 0:off = ثانیه، 0 = خاموش # AI translated Select = انتخاب Settings = تنظیمات +Share = اشتراک‌گذاری # AI translated Shift = تغییر Show in folder = نمایش پوشه Sign In = ورود # AI translated @@ -1462,7 +1463,11 @@ View Feedback = مشاهدهٔ همهٔ بازخوردها # AI translated [Savedata] Date = تاریخ +Export = خروجی گرفتن # AI translated +Export all saves for this game = خروجی گرفتن از همهٔ داده‌های ذخیرهٔ این بازی # AI translated +Export failed = خروجی گرفتن ناموفق بود # AI translated Filename = نام فایل +InstalledDataNotExported = این داده‌ای است که بازی نصب کرده، نه دادهٔ ذخیره، بنابراین نمی‌توان از آن خروجی گرفت. # AI translated No screenshot = بدون نماگرفت None yet. Things will appear here after you save. = هنوز چیزی نیست. پس از ذخیره، موارد اینجا نمایش داده می‌شوند. # AI translated Nothing matching '%1' was found = موردی مطابق با '%1' یافت نشد # AI translated diff --git a/assets/lang/fi_FI.ini b/assets/lang/fi_FI.ini index 81395493a1..c7e8d44b83 100644 --- a/assets/lang/fi_FI.ini +++ b/assets/lang/fi_FI.ini @@ -588,6 +588,7 @@ Search = Etsi seconds, 0:off = sekunteissa, 0 = pois Select = Valitse Settings = Asetukset +Share = Jaa # AI translated Shift = Shift # same as English Show in folder = Näytä kansiossa Sign In = Kirjaudu sisään # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Näytä kaikki palautteet [Savedata] Date = Päivämäärä +Export = Vie # AI translated +Export all saves for this game = Vie kaikki tämän pelin tallennukset # AI translated +Export failed = Vienti epäonnistui # AI translated Filename = Tiedostonimi +InstalledDataNotExported = Nämä ovat pelin asentamia tietoja eivätkä tallennus, joten niitä ei voi viedä. # AI translated No screenshot = Ei kuvakaappausta None yet. Things will appear here after you save. = Ei vielä. Tähän ilmestyy asioita tallentamisen jälkeen. Nothing matching '%1' was found = Hakuehtoa '%1' vastaavia kohteita ei löytynyt # AI translated diff --git a/assets/lang/fr_FR.ini b/assets/lang/fr_FR.ini index 0be68e3cf7..218042a899 100644 --- a/assets/lang/fr_FR.ini +++ b/assets/lang/fr_FR.ini @@ -612,6 +612,7 @@ Search = Rechercher seconds, 0:off = secondes, 0 = off Select = Sélectionner Settings = Paramètres # AI translated +Share = Partager # AI translated Shift = Majuscule Show in folder = Montrer dans dossier Sign In = Connexion # AI translated @@ -1489,7 +1490,11 @@ View Feedback = Voir tous les rapports [Savedata] Date = Date # same as English +Export = Exporter # AI translated +Export all saves for this game = Exporter toutes les sauvegardes de ce jeu # AI translated +Export failed = Échec de l'exportation # AI translated Filename = Nom de fichier +InstalledDataNotExported = Ce sont des données installées par le jeu, pas une sauvegarde : elles ne peuvent donc pas être exportées. # AI translated No screenshot = Pas de capture d'écran None yet. Things will appear here after you save. = Rien à afficher pour le moment.\nDes choses apparaîtront ici lorsque vous sauvegarderez. Nothing matching '%1' was found = Aucun résultat pour '%1' # AI translated diff --git a/assets/lang/gl_ES.ini b/assets/lang/gl_ES.ini index c0f399f775..2139e5da5d 100644 --- a/assets/lang/gl_ES.ini +++ b/assets/lang/gl_ES.ini @@ -588,6 +588,7 @@ Search = Buscar # AI translated seconds, 0:off = segundos, 0 = desactivado # AI translated Select = Seleccionar Settings = Axustes # AI translated +Share = Compartir # AI translated Shift = Maiúsculas # AI translated Show in folder = Mostrar no cartafol # AI translated Sign In = Iniciar sesión # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Ver todas as opinións # AI translated [Savedata] Date = Data # AI translated +Export = Exportar # AI translated +Export all saves for this game = Exportar todos os datos de gardado deste xogo # AI translated +Export failed = Non se puido exportar # AI translated Filename = Nome do ficheiro # AI translated +InstalledDataNotExported = Son datos que instalou o xogo, non unha partida gardada, así que non se poden exportar. # AI translated No screenshot = Sen captura # AI translated None yet. Things will appear here after you save. = Aínda nada. Os elementos aparecerán aquí despois de gardar. # AI translated Nothing matching '%1' was found = Non se atopou nada que coincida con '%1' # AI translated diff --git a/assets/lang/gr_EL.ini b/assets/lang/gr_EL.ini index d2670a0f2e..60ac5b9a68 100644 --- a/assets/lang/gr_EL.ini +++ b/assets/lang/gr_EL.ini @@ -588,6 +588,7 @@ Search = Αναζήτηση # AI translated seconds, 0:off = δευτερόλεπτα, 0 = off Select = Επιλογή # AI translated Settings = Ρυθμίσεις # AI translated +Share = Κοινοποίηση # AI translated Shift = Shift # same as English Show in folder = Εμφάνιση σε Φάκελο Sign In = Σύνδεση # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Προβολή σχολίων [Savedata] Date = Ημερομηνία +Export = Εξαγωγή # AI translated +Export all saves for this game = Εξαγωγή όλων των δεδομένων αποθήκευσης αυτού του παιχνιδιού # AI translated +Export failed = Η εξαγωγή απέτυχε # AI translated Filename = Όνομα αρχείου # AI translated +InstalledDataNotExported = Αυτά είναι δεδομένα που εγκατέστησε το παιχνίδι, όχι αποθήκευση, οπότε δεν μπορούν να εξαχθούν. # AI translated No screenshot = Χωρίς στιγμιότυπο οθόνης None yet. Things will appear here after you save. = Τίποτα προς το παρόν. Θα εμφανιστούν στοιχεία αφότου πραγματοποιήσετε αποθήκευση. Nothing matching '%1' was found = Δεν βρέθηκε τίποτα που να ταιριάζει με '%1' # AI translated diff --git a/assets/lang/he_IL.ini b/assets/lang/he_IL.ini index 4441f8f53c..acfdf58bf1 100644 --- a/assets/lang/he_IL.ini +++ b/assets/lang/he_IL.ini @@ -588,6 +588,7 @@ Search = חיפוש # AI translated seconds, 0:off = שניות, 0 = כבוי # AI translated Select = בחר Settings = הגדרות # AI translated +Share = שתף # AI translated Shift = העבר Show in folder = הצג בתיקייה # AI translated Sign In = התחברות # AI translated @@ -1462,7 +1463,11 @@ View Feedback = הצג את כל המשובים # AI translated [Savedata] Date = תאריך # AI translated +Export = ייצוא # AI translated +Export all saves for this game = ייצוא כל נתוני השמירה של משחק זה # AI translated +Export failed = הייצוא נכשל # AI translated Filename = שם קובץ # AI translated +InstalledDataNotExported = אלה נתונים שהמשחק התקין, לא שמירה, ולכן לא ניתן לייצא אותם. # AI translated No screenshot = אין צילום מסך # AI translated None yet. Things will appear here after you save. = אין עדיין. פריטים יופיעו כאן לאחר שתשמור. # AI translated Nothing matching '%1' was found = לא נמצא דבר התואם ל-'%1' # AI translated diff --git a/assets/lang/hr_HR.ini b/assets/lang/hr_HR.ini index ee514d36c2..f537a393ad 100644 --- a/assets/lang/hr_HR.ini +++ b/assets/lang/hr_HR.ini @@ -588,6 +588,7 @@ Search = Pretraži # AI translated seconds, 0:off = sekundi, 0 = isključeno Select = Odaberi # AI translated Settings = Postavke # AI translated +Share = Dijeli # AI translated Shift = Shift # same as English Show in folder = Prikaži u mapi Sign In = Prijava # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Provjeri sve povratne informacije [Savedata] Date = Datum +Export = Izvezi # AI translated +Export all saves for this game = Izvezi sve savedate ove igre # AI translated +Export failed = Izvoz nije uspio # AI translated Filename = Ime datoteke +InstalledDataNotExported = Ovo su podaci koje je igra instalirala, a ne spremljena igra, pa se ne mogu izvesti. # AI translated No screenshot = Nema snimke zaslona None yet. Things will appear here after you save. = Još ništa. Stvari će se ovdje pojaviti nakon što nešto spremiš. Nothing matching '%1' was found = Ništa što odgovara '%1' nije pronađeno # AI translated diff --git a/assets/lang/hu_HU.ini b/assets/lang/hu_HU.ini index 7b4589860a..6c0927a3de 100644 --- a/assets/lang/hu_HU.ini +++ b/assets/lang/hu_HU.ini @@ -588,6 +588,7 @@ Search = Keresés seconds, 0:off = másodpercig, 0 = ki Select = Kiválaszt Settings = Beállítások +Share = Megosztás # AI translated Shift = Shift # same as English Show in folder = Mutatás mappában Sign In = Bejelentkezés # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Az összes visszajelzés megtekintése… [Savedata] Date = Dátum +Export = Exportálás # AI translated +Export all saves for this game = A játék összes mentésének exportálása # AI translated +Export failed = Az exportálás sikertelen # AI translated Filename = Fájlnév +InstalledDataNotExported = Ezek a játék által telepített adatok, nem mentés, ezért nem exportálhatók. # AI translated No screenshot = Nincs kép None yet. Things will appear here after you save. = Itt jelennek majd meg a mentések. Még nincs semmi. Nothing matching '%1' was found = Nincs a(z) '%1' keresésnek megfelelő találat # AI translated diff --git a/assets/lang/id_ID.ini b/assets/lang/id_ID.ini index f390dad47f..76e29ef357 100644 --- a/assets/lang/id_ID.ini +++ b/assets/lang/id_ID.ini @@ -588,6 +588,7 @@ Search = Cari seconds, 0:off = detik, 0 = mati Select = Pilih Settings = Pengaturan +Share = Bagikan # AI translated Shift = Geser Show in folder = Tampilkan di folder # AI translated Sign In = Masuk # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Lihat semua umpan balik [Savedata] Date = Tanggal +Export = Ekspor # AI translated +Export all saves for this game = Ekspor semua data simpanan game ini # AI translated +Export failed = Gagal mengekspor # AI translated Filename = Nama berkas +InstalledDataNotExported = Ini adalah data yang dipasang oleh game, bukan data simpanan, jadi tidak dapat diekspor. # AI translated No screenshot = Tidak ada tangkapan layar None yet. Things will appear here after you save. = Belum ada. Sesuatu akan muncul di sini setelah anda menyimpan. Nothing matching '%1' was found = Tidak ada yang cocok dengan '%1' # AI translated diff --git a/assets/lang/it_IT.ini b/assets/lang/it_IT.ini index f90d5922ac..044685a1fa 100644 --- a/assets/lang/it_IT.ini +++ b/assets/lang/it_IT.ini @@ -588,6 +588,7 @@ Search = Cerca seconds, 0:off = secondi, 0 = spento Select = Seleziona Settings = Impostazioni +Share = Condividi # AI translated Shift = Maiuscolo Show in folder = Mostra nella Cartella Sign In = Accedi # AI translated @@ -1465,7 +1466,11 @@ View Feedback = Visualizza tutti i Feedback [Savedata] Date = Data +Export = Esporta # AI translated +Export all saves for this game = Esporta tutti i salvataggi di questo gioco # AI translated +Export failed = Esportazione non riuscita # AI translated Filename = Nome del file +InstalledDataNotExported = Questi sono dati installati dal gioco, non un salvataggio, quindi non possono essere esportati. # AI translated No screenshot = Nessuno screenshot None yet. Things will appear here after you save. = Ancora niente. Si inizierà a vedere qualcosa dopo il salvataggio. Nothing matching '%1' was found = Nessun risultato per '%1' # AI translated diff --git a/assets/lang/ja_JP.ini b/assets/lang/ja_JP.ini index af60342ce8..1e4910a4c8 100644 --- a/assets/lang/ja_JP.ini +++ b/assets/lang/ja_JP.ini @@ -588,6 +588,7 @@ Search = 検索 seconds, 0:off = 秒, 0 = オフ Select = 選択 Settings = 設定 +Share = 共有 # AI translated Shift = シフト Show in folder = フォルダを表示する Sign In = サインイン # AI translated @@ -1463,7 +1464,11 @@ View Feedback = 全てのフィードバックを見る [Savedata] Date = 日付 +Export = エクスポート # AI translated +Export all saves for this game = このゲームのセーブデータをすべてエクスポート # AI translated +Export failed = エクスポートに失敗しました # AI translated Filename = ファイル名 +InstalledDataNotExported = これはゲームがインストールしたデータで、セーブデータではないため、エクスポートできません。 # AI translated No screenshot = スクリーンショットなし None yet. Things will appear here after you save. = まだありません。セーブ後に項目が表示されます。 Nothing matching '%1' was found = '%1' に一致するものは見つかりませんでした # AI translated diff --git a/assets/lang/jv_ID.ini b/assets/lang/jv_ID.ini index a01a35736b..b1d15daf16 100644 --- a/assets/lang/jv_ID.ini +++ b/assets/lang/jv_ID.ini @@ -588,6 +588,7 @@ Search = Search seconds, 0:off = detik, 0 = off Select = Peleh Settings = Settings +Share = Sebar # AI translated Shift = Shift Show in folder = Tampilno Nang Folder Sign In = Sign In @@ -1462,7 +1463,11 @@ View Feedback = View all feedbacks [Savedata] Date = Date +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Filename +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = Ora ana tangkapan layar None yet. Things will appear here after you save. = None yet. Things will appear here after you save. Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/km_KH.ini b/assets/lang/km_KH.ini index 4dcec79088..68e356f6cd 100644 --- a/assets/lang/km_KH.ini +++ b/assets/lang/km_KH.ini @@ -588,6 +588,7 @@ Search =ស្វែងរក seconds, 0:off =វិនាទី 0 = បិទ Select =ជ្រើសរើស Settings =ការកំណត់ +Share = ចែករំលែក # AI translated Shift =ប្ដូរ Show in folder =បង្ហាញក្នុងថតឯកសារ Sign In = Sign In @@ -1463,7 +1464,11 @@ View Feedback =មើលមតិកែលម្អទាំងអស់។ [Savedata] Date =កាលបរិច្ឆេទ +Export = នាំចេញ # AI translated +Export all saves for this game = Export all saves for this game +Export failed = ការនាំចេញបានបរាជ័យ # AI translated Filename =ឈ្មោះឯកសារ +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot =គ្មានរូបថតអេក្រង់ទេ។ None yet. Things will appear here after you save. =មិនទាន់មានទេ។ អ្វីៗនឹងបង្ហាញនៅទីនេះ បន្ទាប់ពីអ្នករក្សាទុក។ Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index d5f5cfa0b9..4ebf6a899e 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -588,6 +588,7 @@ Search = 검색 seconds, 0:off = 초, 0 = 끔 Select = 선택 Settings = 설정 +Share = 공유 # AI translated Shift = 전환 Show in folder = 폴더에 표기 Sign In = 로그인 # AI translated @@ -1462,7 +1463,11 @@ View Feedback = 모든 피드백 보기 [Savedata] Date = 날짜 +Export = 내보내기 # AI translated +Export all saves for this game = 이 게임의 모든 저장 데이터 내보내기 # AI translated +Export failed = 내보내기 실패 # AI translated Filename = 파일이름 +InstalledDataNotExported = 이 데이터는 게임이 설치한 데이터이며 저장 데이터가 아니므로 내보낼 수 없습니다. # AI translated No screenshot = 스크린샷 없음 None yet. Things will appear here after you save. = 아직 없습니다. 저장하면 여기에 항목이 표시됩니다. Nothing matching '%1' was found = '%1'와(과) 일치하는 항목이 없습니다 # AI translated diff --git a/assets/lang/ku_SO.ini b/assets/lang/ku_SO.ini index bc0f6727bc..9f8b146895 100644 --- a/assets/lang/ku_SO.ini +++ b/assets/lang/ku_SO.ini @@ -602,6 +602,7 @@ Search = گەڕان seconds, 0:off = چرکەکان، 0 = کوژاوە Select = دەستنیشان کردن Settings = سێتینگەکان +Share = بڵاو بکەرەوە # AI translated Shift = Shift Show in folder = نیشاندانەوە لە فۆڵدەر Sign In = Sign In @@ -1476,7 +1477,11 @@ View Feedback = View all feedbacks [Savedata] Date = Date +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Filename +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = No screenshot None yet. Things will appear here after you save. = None yet. Things will appear here after you save. Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/lo_LA.ini b/assets/lang/lo_LA.ini index 3ba7ecb517..b5356a204c 100644 --- a/assets/lang/lo_LA.ini +++ b/assets/lang/lo_LA.ini @@ -588,6 +588,7 @@ Search = Search seconds, 0:off = ວິນາທີ, 0 = ປິດ Select = ເລືອກ Settings = Settings +Share = ແບ່ງປັນ # AI translated Shift = ປ່ຽນ Show in folder = ສະແດງໃນໂຟນເດີ້ Sign In = Sign In @@ -1462,7 +1463,11 @@ View Feedback = ເບິ່ງການລາຍງານ [Savedata] Date = Date +Export = Export +Export all saves for this game = Export all saves for this game +Export failed = Export failed Filename = Filename +InstalledDataNotExported = This is data the game installed, not a save, so it can't be exported. No screenshot = ບໍ່ມີການຈັບພາບໜ້າຈໍ None yet. Things will appear here after you save. = ຍັງບໍ່ມີຂໍ້ມູນເທື່ອ. ຈະມີກໍຕໍ່ເມື່ອເຈົ້າໄດ້ກົດບັນທຶກເອົາໄວ້. Nothing matching '%1' was found = Nothing matching '%1' was found diff --git a/assets/lang/lt-LT.ini b/assets/lang/lt-LT.ini index 887f2c094b..485202db13 100644 --- a/assets/lang/lt-LT.ini +++ b/assets/lang/lt-LT.ini @@ -588,6 +588,7 @@ Search = Ieškoti # AI translated seconds, 0:off = sekundžių, 0 = išjungta # AI translated Select = Pasirinkti Settings = Nustatymai # AI translated +Share = Bendrinti # AI translated Shift = Shift # same as English Show in folder = Rodyti aplanke Sign In = Prisijungti # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Peržiūrėti visus atsiliepimus # AI translated [Savedata] Date = Data # AI translated +Export = Eksportuoti # AI translated +Export all saves for this game = Eksportuoti visus šio žaidimo išsaugotus duomenis # AI translated +Export failed = Eksportuoti nepavyko # AI translated Filename = Failo pavadinimas # AI translated +InstalledDataNotExported = Tai žaidimo įdiegti duomenys, o ne išsaugojimas, todėl jų negalima eksportuoti. # AI translated No screenshot = Nėra ekrano nuotraukos # AI translated None yet. Things will appear here after you save. = Kol kas nieko nėra. Įrašai atsiras čia, kai išsaugosite. # AI translated Nothing matching '%1' was found = Nerasta nieko, kas atitiktų '%1' # AI translated diff --git a/assets/lang/ms_MY.ini b/assets/lang/ms_MY.ini index 9a781010d7..c1bfd14a02 100644 --- a/assets/lang/ms_MY.ini +++ b/assets/lang/ms_MY.ini @@ -588,6 +588,7 @@ Search = Carian seconds, 0:off = saat, 0 = tutup Select = Pilih Settings = Tetapan +Share = Kongsi # AI translated Shift = Shift # AI translated Show in folder = Tunjukkan dalam folder Sign In = Log masuk # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Lihat semua maklum balas [Savedata] Date = Tarikh +Export = Eksport # AI translated +Export all saves for this game = Eksport semua data simpanan permainan ini # AI translated +Export failed = Gagal mengeksport # AI translated Filename = Nama fail +InstalledDataNotExported = Ini ialah data yang dipasang oleh permainan, bukan data simpanan, jadi ia tidak boleh dieksport. # AI translated No screenshot = Tiada tangkapan skrin None yet. Things will appear here after you save. = Belum ada lagi. Item akan muncul di sini selepas anda menyimpan. Nothing matching '%1' was found = Tiada padanan untuk '%1' ditemui # AI translated diff --git a/assets/lang/nl_NL.ini b/assets/lang/nl_NL.ini index 029fbde54d..50013850cc 100644 --- a/assets/lang/nl_NL.ini +++ b/assets/lang/nl_NL.ini @@ -588,6 +588,7 @@ Search = Zoeken # AI translated seconds, 0:off = seconden, 0 = uit # AI translated Select = Selecteren Settings = Instellingen # AI translated +Share = Delen # AI translated Shift = Shift # same as English Show in folder = Weergeven in map Sign In = Aanmelden # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Alle feedback bekijken [Savedata] Date = Datum # AI translated +Export = Exporteren # AI translated +Export all saves for this game = Alle savedata van deze game exporteren # AI translated +Export failed = Exporteren mislukt # AI translated Filename = Bestandsnaam # AI translated +InstalledDataNotExported = Dit zijn gegevens die de game heeft geïnstalleerd, geen savedata, dus ze kunnen niet worden geëxporteerd. # AI translated No screenshot = Geen screenshot None yet. Things will appear here after you save. = Nog niets. Er zullen hier dingen verschijnen zodra u iets opslaat. Nothing matching '%1' was found = Niets gevonden dat overeenkomt met '%1' # AI translated diff --git a/assets/lang/nn_NO.ini b/assets/lang/nn_NO.ini index 9957f93131..3fa2849c91 100644 --- a/assets/lang/nn_NO.ini +++ b/assets/lang/nn_NO.ini @@ -612,6 +612,7 @@ Search = Søk seconds, 0:off = sekund, 0 = av Select = Vel Settings = Innstillingar +Share = Del # AI translated Shift = Skift Show in folder = Vis i mappe Sign In = Logg inn # AI translated @@ -1487,7 +1488,11 @@ View Feedback = Vis alle tilbakemeldingar [Savedata] Date = Dato +Export = Eksporter # AI translated +Export all saves for this game = Eksporter alle lagringsdata for dette spelet # AI translated +Export failed = Eksporten mislukkast # AI translated Filename = Filnamn +InstalledDataNotExported = Dette er data som spelet har installert, ikkje lagringsdata, så dei kan ikkje eksporterast. # AI translated No screenshot = Ingen skjermbilete None yet. Things will appear here after you save. = Ingenting enno. Ting dukkar opp her etter at du har lagra. Nothing matching '%1' was found = Fann ingenting som samsvarar med '%1' # AI translated diff --git a/assets/lang/no_NO.ini b/assets/lang/no_NO.ini index 8b6957d75f..fc59ca2cbf 100644 --- a/assets/lang/no_NO.ini +++ b/assets/lang/no_NO.ini @@ -612,6 +612,7 @@ Search = Søk seconds, 0:off = sekunder, 0 = av Select = Velg Settings = Innstillinger +Share = Del # AI translated Shift = Skift Show in folder = Vis i mappe Sign In = Logg inn # AI translated @@ -1487,7 +1488,11 @@ View Feedback = Vis alle tilbakemeldinger [Savedata] Date = Dato +Export = Eksporter # AI translated +Export all saves for this game = Eksporter alle lagringsdata for dette spillet # AI translated +Export failed = Eksporten mislyktes # AI translated Filename = Filnavn +InstalledDataNotExported = Dette er data som spillet har installert, ikke lagringsdata, så de kan ikke eksporteres. # AI translated No screenshot = Ingen skjermklipp None yet. Things will appear here after you save. = Ingenting ennå. Ting vises her etter at du har lagret. Nothing matching '%1' was found = Fant ingenting som samsvarer med '%1' # AI translated diff --git a/assets/lang/pl_PL.ini b/assets/lang/pl_PL.ini index 8db11b797e..8774b0fe25 100644 --- a/assets/lang/pl_PL.ini +++ b/assets/lang/pl_PL.ini @@ -588,6 +588,7 @@ Search = Szukaj seconds, 0:off = sekundy, 0 = wył. Select = Wybierz Settings = Ustawienia +Share = Udostępnij # AI translated Shift = Shift # same as English Show in folder = Pokaż w folderze Sign In = Zaloguj się # AI translated @@ -1466,7 +1467,11 @@ View Feedback = Zobacz opinie [Savedata] Date = Data # AI translated +Export = Eksportuj # AI translated +Export all saves for this game = Eksportuj wszystkie zapisy tej gry # AI translated +Export failed = Eksport nie powiódł się # AI translated Filename = Nazwa pliku +InstalledDataNotExported = To dane zainstalowane przez grę, a nie zapis, więc nie można ich wyeksportować. # AI translated No screenshot = Brak obrazka None yet. Things will appear here after you save. = Na razie nic tu nie ma. Wpisy pojawią się, gdy zapiszesz grę. Nothing matching '%1' was found = Nie znaleziono niczego pasującego do '%1' # AI translated diff --git a/assets/lang/pt_BR.ini b/assets/lang/pt_BR.ini index 3538f85486..a60c680241 100644 --- a/assets/lang/pt_BR.ini +++ b/assets/lang/pt_BR.ini @@ -613,6 +613,7 @@ Search = Busca seconds, 0:off = segundos, 0 = desligado Select = Selecionar Settings = Configurações +Share = Compartilhar # AI translated Shift = Shift # same as English Show in folder = Mostrar na pasta Sign In = Entrar # AI translated @@ -1490,7 +1491,11 @@ View Feedback = Visualizar todos os feedbacks [Savedata] Date = Data +Export = Exportar # AI translated +Export all saves for this game = Exportar todos os dados salvos deste jogo # AI translated +Export failed = Falha ao exportar # AI translated Filename = Nome do arquivo +InstalledDataNotExported = Estes são dados instalados pelo jogo, não dados salvos, então não podem ser exportados. # AI translated No screenshot = Sem screenshot None yet. Things will appear here after you save. = Nada ainda. As coisas aparecerão aqui após você salvar. Nothing matching '%1' was found = Nada correspondente a '%1' foi encontrado # AI translated diff --git a/assets/lang/pt_PT.ini b/assets/lang/pt_PT.ini index 59928e7e90..6c698a3ff8 100644 --- a/assets/lang/pt_PT.ini +++ b/assets/lang/pt_PT.ini @@ -612,6 +612,7 @@ Search = Pesquisar seconds, 0:off = segundos, 0 = desativado Select = Selecionar Settings = Definições +Share = Partilhar # AI translated Shift = Shift # same as English Show in folder = Mostrar na pasta Sign In = Iniciar sessão # AI translated @@ -1486,7 +1487,11 @@ View Feedback = Ver todos os feedbacks [Savedata] Date = Data +Export = Exportar # AI translated +Export all saves for this game = Exportar todos os dados salvos deste jogo # AI translated +Export failed = Falha ao exportar # AI translated Filename = Nome do ficheiro +InstalledDataNotExported = Estes são dados instalados pelo jogo, não dados salvos, por isso não podem ser exportados. # AI translated No screenshot = Sem Captura de tela None yet. Things will appear here after you save. = Nada ainda. As coisas vão aparecer aqui após salvares. Nothing matching '%1' was found = Não foi encontrado nada correspondente a '%1' # AI translated diff --git a/assets/lang/ro_RO.ini b/assets/lang/ro_RO.ini index c64da7abed..c157b7b03b 100644 --- a/assets/lang/ro_RO.ini +++ b/assets/lang/ro_RO.ini @@ -589,6 +589,7 @@ Search = Căutare # AI translated seconds, 0:off = secunde, 0 = off Select = Selectează Settings = Setări # AI translated +Share = Distribuie # AI translated Shift = Shift # same as English Show in folder = Arată în folder Sign In = Conectare # AI translated @@ -1463,7 +1464,11 @@ View Feedback = Vezi tot feedbackul # AI translated [Savedata] Date = Dată # AI translated +Export = Exportă # AI translated +Export all saves for this game = Exportă toate salvările acestui joc # AI translated +Export failed = Exportul a eșuat # AI translated Filename = Nume fișier # AI translated +InstalledDataNotExported = Acestea sunt date instalate de joc, nu o salvare, deci nu pot fi exportate. # AI translated No screenshot = Fără captură de ecran # AI translated None yet. Things will appear here after you save. = Nimic încă. Elementele vor apărea aici după ce salvați. # AI translated Nothing matching '%1' was found = Nu s-a găsit nimic care să corespundă cu '%1' # AI translated diff --git a/assets/lang/ru_RU.ini b/assets/lang/ru_RU.ini index 430f89e85f..fc9d53eb66 100644 --- a/assets/lang/ru_RU.ini +++ b/assets/lang/ru_RU.ini @@ -588,6 +588,7 @@ Search = Поиск seconds, 0:off = секунд, 0 = выкл. Select = Выбрать Settings = Настройки +Share = Поделиться # AI translated Shift = Shift # AI translated Show in folder = Показать в папке Sign In = Вход # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Показать все отзывы [Savedata] Date = Дата +Export = Экспорт # AI translated +Export all saves for this game = Экспортировать все сохранения этой игры # AI translated +Export failed = Не удалось экспортировать # AI translated Filename = Имя файла +InstalledDataNotExported = Это данные, установленные игрой, а не сохранение, поэтому их нельзя экспортировать. # AI translated No screenshot = Нет скриншота None yet. Things will appear here after you save. = Здесь ничего нет. Появится после сохранения. Nothing matching '%1' was found = Ничего не найдено по запросу '%1' # AI translated diff --git a/assets/lang/sv_SE.ini b/assets/lang/sv_SE.ini index 74e0ce303e..841469d50e 100644 --- a/assets/lang/sv_SE.ini +++ b/assets/lang/sv_SE.ini @@ -589,6 +589,7 @@ Search = Sök seconds, 0:off = sekunder, 0 = av Select = Välj Settings = Inställningar +Share = Dela # AI translated Shift = Skift Show in folder = Visa i mapp Sign In = Logga in # AI translated @@ -1463,7 +1464,11 @@ View Feedback = Visa all feedback [Savedata] Date = Datum +Export = Exportera # AI translated +Export all saves for this game = Exportera all sparad data för det här spelet # AI translated +Export failed = Exporten misslyckades # AI translated Filename = Filnamn +InstalledDataNotExported = Det här är data som spelet har installerat, inte sparad data, så den kan inte exporteras. # AI translated No screenshot = Ingen skärmdump None yet. Things will appear here after you save. = Inget ännu. Innehåll visas här när du har sparat. Nothing matching '%1' was found = Inget som matchar '%1' hittades # AI translated diff --git a/assets/lang/tg_PH.ini b/assets/lang/tg_PH.ini index fa08229342..9c9fb5025d 100644 --- a/assets/lang/tg_PH.ini +++ b/assets/lang/tg_PH.ini @@ -589,6 +589,7 @@ Search = Hanapin seconds, 0:off = segundo, 0 = Nakapatay Select = Piliin Settings = Mga Setting +Share = Ibahagi # AI translated Shift = Shift # same as English Show in folder = Ipakita sa folder Sign In = Mag-sign in # AI translated @@ -1463,7 +1464,11 @@ View Feedback = Tingnan ang Feedback [Savedata] Date = Petsa +Export = I-export # AI translated +Export all saves for this game = I-export ang lahat ng savedata ng larong ito # AI translated +Export failed = Nabigo ang pag-export # AI translated Filename = Pangalan ng file +InstalledDataNotExported = Ito ay data na in-install ng laro, hindi save, kaya hindi ito maaaring i-export. # AI translated No screenshot = Walang Screenshot None yet. Things will appear here after you save. = Wala pa. Makikita mo dito kapag may na i-savestate ka na Nothing matching '%1' was found = Walang nahanap na tugma sa '%1' # AI translated diff --git a/assets/lang/th_TH.ini b/assets/lang/th_TH.ini index ce61537619..882bd042fb 100644 --- a/assets/lang/th_TH.ini +++ b/assets/lang/th_TH.ini @@ -594,6 +594,7 @@ Search = ค้นหา seconds, 0:off = วินาที, 0 = ปิด Select = เลือก Settings = การตั้งค่า +Share = แชร์ # AI translated Shift = Shift # AI translated Show in folder = แสดงในโฟลเดอร์ Sign In = ลงชื่อเข้าใช้ # AI translated @@ -1472,7 +1473,11 @@ View Feedback = ดูความคิดเห็น [Savedata] Date = ตามวันที่ +Export = ส่งออก # AI translated +Export all saves for this game = ส่งออกเซฟทั้งหมดของเกมนี้ # AI translated +Export failed = ส่งออกไม่สำเร็จ # AI translated Filename = ตามชื่อ +InstalledDataNotExported = นี่คือข้อมูลที่เกมติดตั้งไว้ ไม่ใช่เซฟ จึงส่งออกไม่ได้ # AI translated No screenshot = ไม่มีภาพประกอบ None yet. Things will appear here after you save. = ยังไม่มีข้อมูล จะมีก็ต่อเมื่อหลังจากที่คุณได้กดบันทึกเอาไว้ Nothing matching '%1' was found = ไม่พบรายการที่ตรงกับ '%1' # AI translated diff --git a/assets/lang/tr_TR.ini b/assets/lang/tr_TR.ini index 804b0fe6f2..d62cd243ca 100644 --- a/assets/lang/tr_TR.ini +++ b/assets/lang/tr_TR.ini @@ -590,6 +590,7 @@ Search = Ara seconds, 0:off = saniye, 0 = kapalı Select = Seç Settings = Ayarlar +Share = Paylaş # AI translated Shift = Shift # same as English Show in folder = Klasörde göster Sign In = Oturum aç # AI translated @@ -1463,7 +1464,11 @@ View Feedback = Tüm geri bildirimleri göster [Savedata] Date = Tarih +Export = Dışa aktar # AI translated +Export all saves for this game = Bu oyunun tüm kayıtlarını dışa aktar # AI translated +Export failed = Dışa aktarma başarısız # AI translated Filename = Dosya Adı +InstalledDataNotExported = Bu, oyunun yüklediği veridir, kayıt değildir, bu yüzden dışa aktarılamaz. # AI translated No screenshot = Ekran Görüntüsü Yok None yet. Things will appear here after you save. = Henüz bir şey yok. Kayıt aldığında kayıtların burada görünecek. Nothing matching '%1' was found = '%1' ile eşleşen bir şey bulunamadı # AI translated diff --git a/assets/lang/uk_UA.ini b/assets/lang/uk_UA.ini index 2a62b11ea3..a2822d7958 100644 --- a/assets/lang/uk_UA.ini +++ b/assets/lang/uk_UA.ini @@ -588,6 +588,7 @@ Search = Пошук seconds, 0:off = секунд, 0 = вимк. Select = Вибрати Settings = Параметри +Share = Поділитися # AI translated Shift = Shift # same as English Show in folder = Показати в теці Sign In = Вхід # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Переглянути всі відгуки [Savedata] Date = Дата +Export = Експорт # AI translated +Export all saves for this game = Експортувати всі збереження цієї гри # AI translated +Export failed = Не вдалося експортувати # AI translated Filename = Ім'я файлу +InstalledDataNotExported = Це дані, встановлені грою, а не збереження, тому їх не можна експортувати. # AI translated No screenshot = Немає скріншоту None yet. Things will appear here after you save. = Тут поки що нічого немає. З'явиться після вашого збереження. Nothing matching '%1' was found = Нічого не знайдено за запитом '%1' # AI translated diff --git a/assets/lang/vi_VN.ini b/assets/lang/vi_VN.ini index 2b60c7e9e4..45c25280c7 100644 --- a/assets/lang/vi_VN.ini +++ b/assets/lang/vi_VN.ini @@ -588,6 +588,7 @@ Search = Tìm kiếm # AI translated seconds, 0:off = giây, 0 = tắt Select = Lựa chọn Settings = Cài đặt # AI translated +Share = Chia sẻ # AI translated Shift = Shift # same as English Show in folder = Hiện trong thư mục Sign In = Đăng nhập # AI translated @@ -1462,7 +1463,11 @@ View Feedback = Xem tất cả phản hồi [Savedata] Date = Ngày # AI translated +Export = Xuất # AI translated +Export all saves for this game = Xuất tất cả dữ liệu lưu của trò chơi này # AI translated +Export failed = Xuất thất bại # AI translated Filename = Tên file +InstalledDataNotExported = Đây là dữ liệu do trò chơi cài đặt, không phải dữ liệu lưu, nên không thể xuất. # AI translated No screenshot = Không có screenshot None yet. Things will appear here after you save. = Không có gì ở đây. Mọi thứ sẽ xuất hiện ở đây sau khi bạn lưu. Nothing matching '%1' was found = Không tìm thấy kết quả nào khớp với '%1' # AI translated diff --git a/assets/lang/zh_CN.ini b/assets/lang/zh_CN.ini index a7b295b1b6..60e58e7757 100644 --- a/assets/lang/zh_CN.ini +++ b/assets/lang/zh_CN.ini @@ -588,6 +588,7 @@ Search = 搜索 seconds, 0:off = 秒,0=禁用 Select = 选择 Settings = 设置 +Share = 分享 # AI translated Shift = 翻页 Show in folder = 打开文件夹 Sign In = 登录 # AI translated @@ -1463,7 +1464,11 @@ View Feedback = 查看所有反馈 [Savedata] Date = 日期 +Export = 导出 # AI translated +Export all saves for this game = 导出此游戏的所有存档 # AI translated +Export failed = 导出失败 # AI translated Filename = 文件名 +InstalledDataNotExported = 这是游戏安装的数据,不是存档,因此无法导出。 # AI translated No screenshot = 没有截图 None yet. Things will appear here after you save. = 暂无内容,游戏保存数据之后再回来吧。 Nothing matching '%1' was found = 未找到与“%1”匹配的内容 # AI translated diff --git a/assets/lang/zh_TW.ini b/assets/lang/zh_TW.ini index 1d741aeda2..03a32d3c06 100644 --- a/assets/lang/zh_TW.ini +++ b/assets/lang/zh_TW.ini @@ -588,6 +588,7 @@ Search = 搜尋 seconds, 0:off = 秒,0 = 關閉 Select = 選取 Settings = 設定 +Share = 分享 # AI translated Shift = 移位 Show in folder = 在資料夾中顯示 Sign In = 登入 # AI translated @@ -1462,7 +1463,11 @@ View Feedback = 檢視所有意見反應 [Savedata] Date = 日期 +Export = 匯出 # AI translated +Export all saves for this game = 匯出此遊戲的所有儲存資料 # AI translated +Export failed = 匯出失敗 # AI translated Filename = 檔案名稱 +InstalledDataNotExported = 這是遊戲安裝的資料,不是儲存資料,因此無法匯出。 # AI translated No screenshot = 沒有螢幕截圖 None yet. Things will appear here after you save. = 尚無,將在存檔後出現新項目 Nothing matching '%1' was found = 找不到與「%1」相符的內容 # AI translated