mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-26 03:38:39 +01:00
Merge pull request #21277 from hrydgard/even-more-stuff
Fix a rare savedata crash, handle savedata files better in the game browser
This commit is contained in:
6 files changed
+123
-93
No files matched your search
@@ -88,7 +88,10 @@ void VulkanBarrierBatch::TransitionColorImageAuto(
|
||||
srcStageMask_ |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
default:
|
||||
_assert_msg_(false, "Unexpected oldLayout: %s", VulkanImageLayoutToString(*imageLayout));
|
||||
_dbg_assert_msg_(false, "Unexpected oldLayout: %s", VulkanImageLayoutToString(*imageLayout));
|
||||
// Sync hard.
|
||||
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStageMask_ = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -115,7 +118,10 @@ void VulkanBarrierBatch::TransitionColorImageAuto(
|
||||
dstStageMask_ |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
default:
|
||||
_assert_msg_(false, "Unexpected newLayout: %s", VulkanImageLayoutToString(newImageLayout));
|
||||
_dbg_assert_msg_(false, "Unexpected newLayout: %s", VulkanImageLayoutToString(newImageLayout));
|
||||
// Sync hard.
|
||||
dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStageMask_ = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -167,7 +173,10 @@ void VulkanBarrierBatch::TransitionDepthStencilImageAuto(
|
||||
srcStageMask_ |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
default:
|
||||
_assert_msg_(false, "Unexpected oldLayout: %s", VulkanImageLayoutToString(*imageLayout));
|
||||
_dbg_assert_msg_(false, "Unexpected oldLayout: %s", VulkanImageLayoutToString(*imageLayout));
|
||||
// Sync hard.
|
||||
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStageMask_ = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -189,7 +198,10 @@ void VulkanBarrierBatch::TransitionDepthStencilImageAuto(
|
||||
dstStageMask_ |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
default:
|
||||
_assert_msg_(false, "Unexpected newLayout: %s", VulkanImageLayoutToString(newImageLayout));
|
||||
_dbg_assert_msg_(false, "Unexpected newLayout: %s", VulkanImageLayoutToString(newImageLayout));
|
||||
// Sync hard.
|
||||
dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStageMask_ = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,11 @@ inline size_t truncate_cpy(char(&out)[Count], std::string_view src) {
|
||||
return truncate_cpy(out, Count, src);
|
||||
}
|
||||
|
||||
template<size_t Count>
|
||||
inline std::string_view StringViewFromFixedSizeField(const char(&field)[Count]) {
|
||||
return std::string_view(field, strnlen(field, Count));
|
||||
}
|
||||
|
||||
inline std::string join(std::string_view a, std::string_view b) {
|
||||
std::string result;
|
||||
result.reserve(a.size() + b.size());
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace
|
||||
return result == dataSize;
|
||||
}
|
||||
|
||||
PSPFileInfo FileFromListing(const std::vector<PSPFileInfo> &listing, const std::string &filename) {
|
||||
static PSPFileInfo FileFromListing(const std::vector<PSPFileInfo> &listing, std::string_view filename) {
|
||||
for (const PSPFileInfo &sub : listing) {
|
||||
if (sub.name == filename)
|
||||
return sub;
|
||||
@@ -266,31 +266,22 @@ std::string SavedataParam::GetSaveFilePath(const SceUtilitySavedataParam *param,
|
||||
return GetSaveFilePath(param, GetSaveDir(param, saveId));
|
||||
}
|
||||
|
||||
inline static std::string FixedToString(const char *str, size_t n)
|
||||
{
|
||||
if (!str) {
|
||||
return std::string();
|
||||
} else {
|
||||
return std::string(str, strnlen(str, n));
|
||||
}
|
||||
}
|
||||
|
||||
std::string SavedataParam::GetGameName(const SceUtilitySavedataParam *param) const
|
||||
{
|
||||
return FixedToString(param->gameName, ARRAY_SIZE(param->gameName));
|
||||
return std::string(StringViewFromFixedSizeField(param->gameName));
|
||||
}
|
||||
|
||||
std::string SavedataParam::GetSaveName(const SceUtilitySavedataParam *param) const
|
||||
{
|
||||
const std::string saveName = FixedToString(param->saveName, ARRAY_SIZE(param->saveName));
|
||||
const std::string_view saveName = StringViewFromFixedSizeField(param->saveName);
|
||||
if (saveName == "<>")
|
||||
return "";
|
||||
return saveName;
|
||||
return std::string(saveName);
|
||||
}
|
||||
|
||||
std::string SavedataParam::GetFileName(const SceUtilitySavedataParam *param) const
|
||||
{
|
||||
return FixedToString(param->fileName, ARRAY_SIZE(param->fileName));
|
||||
return std::string(StringViewFromFixedSizeField(param->fileName));
|
||||
}
|
||||
|
||||
std::string SavedataParam::GetKey(const SceUtilitySavedataParam *param) const
|
||||
@@ -507,9 +498,9 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
|
||||
// Update values. NOTE! #18430 made this conditional on !subWrite, but this is not correct, as it causes #18687.
|
||||
// So now we do a hacky trick and just check for a valid title before we proceed with updating the sfoFile.
|
||||
if (strnlen(param->sfoParam.title, sizeof(param->sfoParam.title)) > 0) {
|
||||
sfoFile->SetValue("TITLE", param->sfoParam.title, 128);
|
||||
sfoFile->SetValue("SAVEDATA_TITLE", param->sfoParam.savedataTitle, 128);
|
||||
sfoFile->SetValue("SAVEDATA_DETAIL", param->sfoParam.detail, 1024);
|
||||
sfoFile->SetValue("TITLE", StringViewFromFixedSizeField(param->sfoParam.title), 128);
|
||||
sfoFile->SetValue("SAVEDATA_TITLE", StringViewFromFixedSizeField(param->sfoParam.savedataTitle), 128);
|
||||
sfoFile->SetValue("SAVEDATA_DETAIL", StringViewFromFixedSizeField(param->sfoParam.detail), 1024);
|
||||
sfoFile->SetValue("PARENTAL_LEVEL", param->sfoParam.parentalLevel, 4);
|
||||
sfoFile->SetValue("CATEGORY", "MS", 4);
|
||||
sfoFile->SetValue("SAVEDATA_DIRECTORY", GetSaveDir(param, saveDirName), 64);
|
||||
@@ -598,28 +589,24 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
|
||||
}
|
||||
|
||||
// SAVE ICON0
|
||||
if (param->icon0FileData.buf.IsValid())
|
||||
{
|
||||
if (param->icon0FileData.buf.IsValid()) {
|
||||
std::string icon0path = dirPath + "/" + ICON0_FILENAME;
|
||||
WritePSPFile(icon0path, param->icon0FileData.buf, param->icon0FileData.size);
|
||||
}
|
||||
// SAVE ICON1
|
||||
if (param->icon1FileData.buf.IsValid())
|
||||
{
|
||||
if (param->icon1FileData.buf.IsValid()) {
|
||||
std::string icon1path = dirPath + "/" + ICON1_FILENAME;
|
||||
if (param->icon1FileData.size > 0) {
|
||||
WritePSPFile(icon1path, param->icon1FileData.buf, param->icon1FileData.size);
|
||||
}
|
||||
}
|
||||
// SAVE PIC1
|
||||
if (param->pic1FileData.buf.IsValid())
|
||||
{
|
||||
if (param->pic1FileData.buf.IsValid()) {
|
||||
std::string pic1path = dirPath + "/" + PIC1_FILENAME;
|
||||
WritePSPFile(pic1path, param->pic1FileData.buf, param->pic1FileData.size);
|
||||
}
|
||||
// Save SND
|
||||
if (param->snd0FileData.buf.IsValid())
|
||||
{
|
||||
if (param->snd0FileData.buf.IsValid()) {
|
||||
std::string snd0path = dirPath + "/" + SND0_FILENAME;
|
||||
if (param->snd0FileData.size > 0) {
|
||||
WritePSPFile(snd0path, param->snd0FileData.buf, param->snd0FileData.size);
|
||||
@@ -1426,12 +1413,12 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) {
|
||||
s64 writeBytes = 0;
|
||||
for (int i = 0; i < param->sizeInfo->numNormalEntries; ++i) {
|
||||
const auto &entry = param->sizeInfo->normalEntries[i];
|
||||
overwriteBytes += FileFromListing(listing, entry.name).size;
|
||||
overwriteBytes += FileFromListing(listing, StringViewFromFixedSizeField(entry.name)).size;
|
||||
writeBytes += entry.size;
|
||||
}
|
||||
for (int i = 0; i < param->sizeInfo->numSecureEntries; ++i) {
|
||||
const auto &entry = param->sizeInfo->secureEntries[i];
|
||||
overwriteBytes += FileFromListing(listing, entry.name).size;
|
||||
overwriteBytes += FileFromListing(listing, StringViewFromFixedSizeField(entry.name)).size;
|
||||
writeBytes += entry.size + 0x10;
|
||||
}
|
||||
|
||||
@@ -1592,7 +1579,7 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string thisSaveName = FixedToString(saveNameListData[i], ARRAY_SIZE(saveNameListData[i]));
|
||||
const std::string thisSaveName(StringViewFromFixedSizeField(saveNameListData[i]));
|
||||
|
||||
const std::string folderName = gameName + thisSaveName;
|
||||
|
||||
|
||||
+23
-4
@@ -85,12 +85,21 @@ static void LaunchFile(ScreenManager *screenManager, Screen *currentScreen, cons
|
||||
// Check if we already know that this game isn't playable.
|
||||
auto info = g_gameInfoCache->GetInfo(nullptr, path, GameInfoFlags::FILE_TYPE);
|
||||
|
||||
if (info->fileType == IdentifiedFileType::PSP_UMD_VIDEO_ISO) {
|
||||
switch (info->fileType) {
|
||||
case IdentifiedFileType::PSP_UMD_VIDEO_ISO:
|
||||
// We show info about it.
|
||||
screenManager->push(new GameScreen(path, false));
|
||||
return;
|
||||
case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY:
|
||||
{
|
||||
// Show the savedata popup, why not?
|
||||
std::string title = SanitizeString(info->GetTitle(), StringRestriction::NoLineBreaksOrSpecials, 0, 200);
|
||||
screenManager->push(new SavedataPopupScreen(Path(), path, title));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentScreen) {
|
||||
screenManager->cancelScreensAbove(currentScreen);
|
||||
}
|
||||
@@ -398,9 +407,14 @@ void GameButton::Draw(UIContext &dc) {
|
||||
}
|
||||
|
||||
if (gridStyle_ && g_Config.bShowIDOnGameIcon && ginfo->fileType != IdentifiedFileType::PSP_ELF && !ginfo->id_version.empty()) {
|
||||
std::string_view idStr = ginfo->id_version;
|
||||
if (ginfo->fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) {
|
||||
auto ga = GetI18NCategory(I18NCat::GAME);
|
||||
idStr = ga->T("SaveData");
|
||||
}
|
||||
dc.SetFontScale(0.5f * g_Config.fGameGridScale, 0.5f * g_Config.fGameGridScale);
|
||||
dc.DrawText(ginfo->id_version, bounds_.x + 5, y + 1, 0xFF000000, ALIGN_TOPLEFT);
|
||||
dc.DrawText(ginfo->id_version, bounds_.x + 4, y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT);
|
||||
dc.DrawText(idStr, bounds_.x + 5, y + 1, 0xFF000000, ALIGN_TOPLEFT);
|
||||
dc.DrawText(idStr, bounds_.x + 4, y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT);
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
@@ -1728,6 +1742,11 @@ void MainScreen::dialogFinished(const Screen *dialog, DialogResult result) {
|
||||
} else if (tag == "Upload") {
|
||||
// Files may have been uploaded.
|
||||
RecreateViews();
|
||||
} else if (tag == "SavedataPopup") {
|
||||
// We must have come from the file browser tab.
|
||||
if (gameBrowsers_.size() >= 2) {
|
||||
gameBrowsers_[1]->RequestRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+45
-57
@@ -132,75 +132,63 @@ SavedataView::SavedataView(UIContext &dc, GameInfo *ginfo, IdentifiedFileType ty
|
||||
}
|
||||
}
|
||||
|
||||
class SavedataPopupScreen : public UI::PopupScreen {
|
||||
public:
|
||||
SavedataPopupScreen(Path gamePath, Path savePath, std::string_view title) : PopupScreen(StripSpaces(title)), savePath_(savePath), gamePath_(gamePath) { }
|
||||
SavedataPopupScreen::SavedataPopupScreen(Path gamePath, Path savePath, std::string_view title) : PopupScreen(StripSpaces(title)), savePath_(savePath), gamePath_(gamePath) {}
|
||||
|
||||
const char *tag() const override { return "SavedataPopup"; }
|
||||
void update() override {
|
||||
PopupScreen::update();
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::SIZE);
|
||||
if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) {
|
||||
// Hm, this is no good. But hopefully the previous screen loaded it.
|
||||
return;
|
||||
}
|
||||
if (savedataView_) {
|
||||
savedataView_->UpdateGame(ginfo.get());
|
||||
}
|
||||
void SavedataPopupScreen::update() {
|
||||
PopupScreen::update();
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::SIZE);
|
||||
if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) {
|
||||
// Hm, this is no good. But hopefully the previous screen loaded it.
|
||||
return;
|
||||
}
|
||||
if (savedataView_) {
|
||||
savedataView_->UpdateGame(ginfo.get());
|
||||
}
|
||||
}
|
||||
|
||||
void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
using namespace UI;
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::SIZE);
|
||||
if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) {
|
||||
// This is OK, handled in Update. Though most likely, the previous screen loaded it.
|
||||
}
|
||||
|
||||
void CreatePopupContents(UI::ViewGroup *parent) override {
|
||||
using namespace UI;
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f, UI::Margins(0, 3)));
|
||||
parent->Add(contentScroll);
|
||||
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(screenManager()->getDrawContext(), savePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::SIZE);
|
||||
if (!ginfo->Ready(GameInfoFlags::PARAM_SFO)) {
|
||||
// This is OK, handled in Update. Though most likely, the previous screen loaded it.
|
||||
}
|
||||
// TODO: If the game info wasn't already loaded, we'll get a bogus fileType here.
|
||||
savedataView_ = contentScroll->Add(new SavedataView(dc, ginfo.get(), ginfo->fileType, true));
|
||||
|
||||
ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f, UI::Margins(0, 3)));
|
||||
parent->Add(contentScroll);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
|
||||
// TODO: If the game info wasn't already loaded, we'll get a bogus fileType here.
|
||||
savedataView_ = contentScroll->Add(new SavedataView(dc, ginfo.get(), ginfo->fileType, true));
|
||||
LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
buttonRow->SetSpacing(0);
|
||||
Margins buttonMargins(5, 5, 5, 13); // not sure why we need more at the bottom to make it look right
|
||||
|
||||
buttonRow->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
buttonRow->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO);
|
||||
|
||||
LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
buttonRow->SetSpacing(0);
|
||||
Margins buttonMargins(5, 5, 5, 13); // not sure why we need more at the bottom to make it look right
|
||||
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
|
||||
|
||||
buttonRow->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||
buttonRow->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
std::shared_ptr<GameInfo> ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO);
|
||||
|
||||
const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN);
|
||||
|
||||
std::string_view confirmMessage = di->T("Are you sure you want to delete the file?");
|
||||
screenManager()->push(new MessagePopupScreen(di->T("Delete"), confirmMessage, trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"), [=](bool result) {
|
||||
if (result) {
|
||||
ginfo->Delete();
|
||||
TriggerFinish(DR_NO);
|
||||
}
|
||||
}));
|
||||
std::string_view confirmMessage = di->T("Are you sure you want to delete the file?");
|
||||
screenManager()->push(new MessagePopupScreen(di->T("Delete"), confirmMessage, trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"), [=](bool result) {
|
||||
if (result) {
|
||||
ginfo->Delete();
|
||||
TriggerFinish(DR_NO);
|
||||
}
|
||||
}));
|
||||
});
|
||||
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
|
||||
buttonRow->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) {
|
||||
System_ShowFileInFolder(savePath_);
|
||||
});
|
||||
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
|
||||
buttonRow->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) {
|
||||
System_ShowFileInFolder(savePath_);
|
||||
});
|
||||
}
|
||||
parent->Add(buttonRow);
|
||||
}
|
||||
|
||||
protected:
|
||||
UI::Size PopupWidth() const override { return 600; }
|
||||
|
||||
private:
|
||||
SavedataView *savedataView_ = nullptr;
|
||||
Path savePath_;
|
||||
Path gamePath_;
|
||||
};
|
||||
parent->Add(buttonRow);
|
||||
}
|
||||
|
||||
class SortedLinearLayout : public UI::LinearLayoutList {
|
||||
public:
|
||||
|
||||
@@ -67,6 +67,25 @@ private:
|
||||
bool searchPending_ = false;
|
||||
};
|
||||
|
||||
class SavedataView;
|
||||
|
||||
class SavedataPopupScreen : public UI::PopupScreen {
|
||||
public:
|
||||
SavedataPopupScreen(Path gamePath, Path savePath, std::string_view title);
|
||||
|
||||
const char *tag() const override { return "SavedataPopup"; }
|
||||
void update() override;
|
||||
void CreatePopupContents(UI::ViewGroup *parent) override;
|
||||
|
||||
protected:
|
||||
UI::Size PopupWidth() const override { return 600; }
|
||||
|
||||
private:
|
||||
SavedataView *savedataView_ = nullptr;
|
||||
Path savePath_;
|
||||
Path gamePath_;
|
||||
};
|
||||
|
||||
class SavedataScreen : public UITabbedBaseDialogScreen {
|
||||
public:
|
||||
// gamePath can be empty, in that case this screen will show all savedata in the save directory.
|
||||
|
||||
Reference in new issue
Block a user