From 87bcdb7b88d70842bb9edf1b681560de9385877b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Jul 2021 15:33:11 +0200 Subject: [PATCH] Work on the new experience --- Core/Config.cpp | 11 ++- Core/Config.h | 2 +- UI/GameSettingsScreen.cpp | 2 +- UI/MemStickScreen.cpp | 164 ++++++++++++++++++++++---------------- UI/MemStickScreen.h | 16 +++- UI/NativeApp.cpp | 44 +++++----- 6 files changed, 141 insertions(+), 98 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index d97e2509cd..7c35cd89de 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1412,12 +1412,14 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { INFO_LOG(LOADER, "Config loaded: '%s'", iniFilename_.c_str()); } -void Config::Save(const char *saveReason) { +bool Config::Save(const char *saveReason) { if (!IsFirstInstance()) { // TODO: Should we allow saving config if started from a different directory? // How do we tell? WARN_LOG(LOADER, "Not saving config - secondary instances don't."); - return; + + // Don't want to retry or something. + return true; } if (jitForcedOff) { @@ -1488,7 +1490,7 @@ void Config::Save(const char *saveReason) { if (!iniFile.Save(iniFilename_)) { ERROR_LOG(LOADER, "Error saving config (%s)- can't write ini '%s'", saveReason, iniFilename_.c_str()); System_SendMessage("toast", "Failed to save settings!\nCheck permissions, or try to restart the device."); - return; + return false; } INFO_LOG(LOADER, "Config saved (%s): '%s'", saveReason, iniFilename_.c_str()); @@ -1501,7 +1503,7 @@ void Config::Save(const char *saveReason) { KeyMap::SaveToIni(controllerIniFile); if (!controllerIniFile.Save(controllerIniFilename_)) { ERROR_LOG(LOADER, "Error saving config - can't write ini '%s'", controllerIniFilename_.c_str()); - return; + return false; } INFO_LOG(LOADER, "Controller config saved: %s", controllerIniFilename_.c_str()); } @@ -1512,6 +1514,7 @@ void Config::Save(const char *saveReason) { // force JIT off again just in case Config::Save() is called without exiting PPSSPP g_Config.iCpuCore = (int)CPUCore::INTERPRETER; } + return true; } // Use for debugging the version check without messing with the server diff --git a/Core/Config.h b/Core/Config.h index a07a6182d8..f5dcd9ad44 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -490,7 +490,7 @@ public: std::string dismissedVersion; void Load(const char *iniFileName = nullptr, const char *controllerIniFilename = nullptr); - void Save(const char *saveReason); + bool Save(const char *saveReason); void Reload(); void RestoreDefaults(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 996638b294..71006d005b 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1097,7 +1097,7 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) { - screenManager()->push(new MemStickScreen()); + screenManager()->push(new MemStickScreen(false)); return UI::EVENT_DONE; } diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index 6759457f39..beee406b99 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -25,23 +25,26 @@ #include "Common/UI/ViewGroup.h" #include "Common/StringUtils.h" -#include "Common/File/FileUtil.h" -#include "Common/File/Path.h" #include "Common/System/System.h" #include "Common/System/NativeApp.h" -#include "Common/Data/Text/I18n.h" #include "Common/System/Display.h" +#include "Common/Data/Text/I18n.h" + +#include "Common/File/AndroidStorage.h" +#include "Common/File/FileUtil.h" +#include "Common/File/Path.h" #include "Core/Util/GameManager.h" #include "Core/System.h" +#include "Core/Config.h" #include "UI/MemStickScreen.h" #include "UI/MainScreen.h" #include "UI/MiscScreens.h" -#include "Core/Config.h" - -MemStickScreen::~MemStickScreen() { } +MemStickScreen::~MemStickScreen() { + pendingMemStickFolder_ = g_Config.memStickDirectory; +} void MemStickScreen::CreateViews() { using namespace UI; @@ -51,45 +54,68 @@ void MemStickScreen::CreateViews() { Margins actionMenuMargins(15, 15, 15, 0); - root_ = new AnchorLayout(); + root_ = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); - ViewGroup *columns = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); - root_->Add(columns); - - ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0)); + Spacer *spacerColumn = new Spacer(new LinearLayoutParams(20.0, FILL_PARENT, 0.0f)); + ViewGroup *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); - columns->Add(leftColumn); - columns->Add(rightColumnItems); + root_->Add(spacerColumn); + root_->Add(leftColumn); + root_->Add(rightColumnItems); - Path path = g_Config.memStickDirectory; - int64_t freeSpaceAtMemStick = -1; -#if PPSSPP_PLATFORM(ANDROID) - if (Android_IsContentUri(path.ToString())) { - freeSpaceAtMemStick = Android_GetFreeSpaceByContentUri(path.ToString()); - } else { - freeSpaceAtMemStick = Android_GetFreeSpaceByFilePath(path.ToString()); + if (initialSetup_) { + leftColumn->Add(new TextView(iz->T("Welcome to PPSSPP!"), ALIGN_LEFT, false)); + leftColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f))); + } + + leftColumn->Add(new TextView(iz->T("MemoryStickDescription", "Choose PSP data storage (Memory Stick)"), ALIGN_LEFT, false)); + +#if !PPSSPP_PLATFORM(WINDOWS) + if (!pendingMemStickFolder_.empty()) { + int64_t freeSpaceAtMemStick = -1; + if (Android_IsContentUri(pendingMemStickFolder_.ToString())) { + freeSpaceAtMemStick = Android_GetFreeSpaceByContentUri(pendingMemStickFolder_.ToString()); + } else { + freeSpaceAtMemStick = Android_GetFreeSpaceByFilePath(pendingMemStickFolder_.ToString()); + } + + leftColumn->Add(new TextView(pendingMemStickFolder_.ToVisualString(), ALIGN_LEFT, false)); + std::string freeSpaceText = "Free space: N/A"; + if (freeSpaceAtMemStick >= 0) { + freeSpaceText = StringFromFormat("free space: %lld MB", freeSpaceAtMemStick / (1024 * 1024)); + leftColumn->Add(new TextView(freeSpaceText, ALIGN_LEFT, false)); + } + } + + if (!g_Config.memStickDirectory.empty()) { + TextView *view = leftColumn->Add(new TextView(g_Config.memStickDirectory.ToVisualString(), ALIGN_LEFT, false)); + view->SetShadow(true); } #endif - int leftSide = 100; - settingInfo_ = new SettingInfoMessage(ALIGN_CENTER | FLAG_WRAP_TEXT, new AnchorLayoutParams(dp_xres - leftSide - 40.0f, WRAP_CONTENT, leftSide, dp_yres - 80.0f - 40.0f, NONE, NONE)); - settingInfo_->SetBottomCutoff(dp_yres - 200.0f); + leftColumn->Add(new Choice(iz->T("Create or Choose a PSP folder")))->OnClick.Handle(this, &MemStickScreen::OnBrowse); + leftColumn->Add(new TextView(iz->T("ChooseFolderDesc", "* Data will stay even if you uninstall PPSSPP.\n* Data can be shared with PPSSPP Gold\n* Easy USB access"), ALIGN_LEFT, false)); - root_->Add(settingInfo_); + leftColumn->Add(new Choice(iz->T("Use App Private Directory")))->OnClick.Handle(this, &MemStickScreen::OnUseInternalStorage); + leftColumn->Add(new TextView(iz->T("InternalStorageDesc", "* Warning! Data will be deleted if you uninstall PPSSPP!\n* Data cannot be shared with PPSSPP Gold\n* USB access through Android/data/org.ppsspp.ppsspp/files"), ALIGN_LEFT, false)); - leftColumn->Add(new TextView(iz->T("Memory Stick Storage"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); - leftColumn->Add(new TextView(iz->T("MemoryStickDescription", "Choose where your PSP memory stick data (savegames, etc) is stored"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 50, NONE, NONE))); - leftColumn->Add(new TextView(g_Config.memStickDirectory.ToVisualString(), ALIGN_LEFT, false, new AnchorLayoutParams(10, 140, NONE, NONE))); + leftColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f))); - std::string freeSpaceText = "Free space: N/A"; - if (freeSpaceAtMemStick >= 0) { - freeSpaceText = StringFromFormat("free space: %lld MB", freeSpaceAtMemStick / (1024 * 1024)); + Choice *confirmButton = rightColumnItems->Add(new Choice(iz->T("Confirm"))); + confirmButton->OnClick.Handle(this, &MemStickScreen::OnConfirm); + confirmButton->SetEnabled(!pendingMemStickFolder_.empty()); + + if (!initialSetup_) { + rightColumnItems->Add(new CheckBox(&moveData_, iz->T("Move Data"))); + rightColumnItems->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnOK); } - leftColumn->Add(new TextView(freeSpaceText, ALIGN_LEFT, false, new AnchorLayoutParams(10, 240, NONE, NONE))); + INFO_LOG(SYSTEM, "MemStickScreen: initialSetup=%d", (int)initialSetup_); +} - rightColumnItems->Add(new Choice(iz->T("Browse")))->OnClick.Handle(this, &MemStickScreen::OnBrowse); - rightColumnItems->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnOK); +UI::EventReturn MemStickScreen::OnUseInternalStorage(UI::EventParams ¶ms) { + pendingMemStickFolder_ = Path(g_extFilesDir); + return UI::EVENT_DONE; } UI::EventReturn MemStickScreen::OnBrowse(UI::EventParams ¶ms) { @@ -108,48 +134,52 @@ void MemStickScreen::sendMessage(const char *message, const char *value) { filename = value; INFO_LOG(SYSTEM, "Got folder: '%s'", filename.c_str()); pendingMemStickFolder_ = Path(filename); - CallbackMemStickFolder(true); + RecreateViews(); } } } -void MemStickScreen::CallbackMemStickFolder(bool yes) { +UI::EventReturn MemStickScreen::OnConfirm(UI::EventParams ¶ms) { auto sy = GetI18NCategory("System"); - if (yes) { - Path memStickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt"; - Path testWriteFile = pendingMemStickFolder_ / ".write_verify_file"; + Path memStickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt"; + Path testWriteFile = pendingMemStickFolder_ / ".write_verify_file"; - // Doesn't already exist, create. - // Should this ever happen? - if (pendingMemStickFolder_.Type() == PathType::NATIVE) { - if (!File::Exists(pendingMemStickFolder_)) { - File::CreateFullPath(pendingMemStickFolder_); - } - if (!File::WriteDataToFile(true, "1", 1, testWriteFile)) { - settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); - return; - } - File::Delete(testWriteFile); - } else { - // TODO: Do the same but with scoped storage? Not really necessary, right? If it came from a browse - // for folder, we can assume it exists, barring wacky race conditions like the user being connected - // by USB and deleting it. + // Doesn't already exist, create. + // Should this ever happen? + if (pendingMemStickFolder_.Type() == PathType::NATIVE) { + if (!File::Exists(pendingMemStickFolder_)) { + File::CreateFullPath(pendingMemStickFolder_); } - - // This doesn't need the storage API - this path is accessible the normal way. - std::string str = pendingMemStickFolder_.ToString(); - if (!File::WriteDataToFile(true, str.c_str(), (unsigned int)str.size(), memStickDirFile)) { - ERROR_LOG(SYSTEM, "Failed to write memstick path '%s' to '%s'", pendingMemStickFolder_.c_str(), memStickDirFile.c_str()); - // Not sure what to do if this file. + if (!File::WriteDataToFile(true, "1", 1, testWriteFile)) { + // settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr); + return UI::EVENT_DONE; } - - // Save so the settings, at least, are transferred. - g_Config.memStickDirectory = pendingMemStickFolder_; - g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); - g_Config.UpdateIniLocation(); - - g_Config.Save("MemstickPathChanged"); - screenManager()->RecreateAllViews(); + File::Delete(testWriteFile); + } else { + // TODO: Do the same but with scoped storage? Not really necessary, right? If it came from a browse + // for folder, we can assume it exists and is writable, barring wacky race conditions like the user + // being connected by USB and deleting it. } + + // This doesn't need the storage API - this path is accessible the normal way. + std::string str = pendingMemStickFolder_.ToString(); + if (!File::WriteDataToFile(true, str.c_str(), (unsigned int)str.size(), memStickDirFile)) { + ERROR_LOG(SYSTEM, "Failed to write memstick path '%s' to '%s'", pendingMemStickFolder_.c_str(), memStickDirFile.c_str()); + // Not sure what to do if this file. + } + + // Save so the settings, at least, are transferred. + g_Config.memStickDirectory = pendingMemStickFolder_; + g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); + g_Config.UpdateIniLocation(); + + if (g_Config.Save("MemstickPathChanged")) { + TriggerFinish(DialogResult::DR_OK); + } else { + error_ = sy->T("Failed to save config"); + RecreateViews(); + } + + return UI::EVENT_DONE; } diff --git a/UI/MemStickScreen.h b/UI/MemStickScreen.h index b0c4f55dee..7f7ccaafa2 100644 --- a/UI/MemStickScreen.h +++ b/UI/MemStickScreen.h @@ -30,19 +30,27 @@ // Currently only useful for Android. class MemStickScreen : public UIDialogScreenWithBackground { public: - MemStickScreen() {} + MemStickScreen(bool initialSetup) + : initialSetup_(initialSetup) {} virtual ~MemStickScreen(); std::string tag() const override { return "game"; } + void CreateViews() override; protected: - void CreateViews() override; - void CallbackMemStickFolder(bool yes); void sendMessage(const char *message, const char *value) override; private: // Event handlers UI::EventReturn OnBrowse(UI::EventParams &e); + UI::EventReturn OnConfirm(UI::EventParams ¶ms); + UI::EventReturn OnUseInternalStorage(UI::EventParams ¶ms); + Path pendingMemStickFolder_; - SettingInfoMessage *settingInfo_; + SettingInfoMessage *settingInfo_ = nullptr; + + std::string error_; + + bool initialSetup_; + bool moveData_ = true; }; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 5fb7f3a8de..8925f0485c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -108,6 +108,7 @@ #include "UI/GPUDriverTestScreen.h" #include "UI/HostTypes.h" #include "UI/MiscScreens.h" +#include "UI/MemStickScreen.h" #include "UI/OnScreenDisplay.h" #include "UI/RemoteISOScreen.h" #include "UI/TiltEventProcessor.h" @@ -338,8 +339,6 @@ static void PostLoadConfig() { i18nrepo.LoadIni(g_Config.sLanguageIni); else i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); - - g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count); } static bool CreateDirectoriesAndroid() { @@ -436,7 +435,7 @@ static void ClearFailedGPUBackends() { // We've successfully started graphics without crashing, hurray. // In case they update drivers and have totally different problems much later, clear the failed list. g_Config.sFailedGPUBackends.clear(); - if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { + if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS) || System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { File::Delete(GetSysDirectory(DIRECTORY_APP_CACHE) / "FailedGraphicsBackends.txt"); } else { g_Config.Save("clearFailedGPUBackends"); @@ -449,6 +448,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch ShaderTranslationInit(); InitFastMath(cpu_info.bNEON); + g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count); + SetupAudioFormats(); g_Discord.SetPresenceMenu(); @@ -504,22 +505,22 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // is no longer the plain root of external storage, but it's an app specific directory // on external storage (g_extFilesDir). if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { - g_Config.defaultCurrentDirectory = Path(g_extFilesDir); + // There's no sensible default directory. Let the user browse for files. + g_Config.defaultCurrentDirectory.clear(); } else { - // Maybe there should be an option to use internal memory instead, but I think - // that for most people, using external memory (SDCard/USB Storage) makes the - // most sense. + g_Config.memStickDirectory = Path(external_dir); g_Config.defaultCurrentDirectory = Path(external_dir); + CreateDirectoriesAndroid(); } // Might also add an option to move it to internal / non-visible storage, but there's // little point, really. - g_Config.memStickDirectory = Path(external_dir); g_Config.flash0Directory = Path(external_dir) / "flash0"; Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt"; if (File::Exists(memstickDirFile)) { + INFO_LOG(SYSTEM, "Reading '%s' to find memstick dir.", memstickDirFile.c_str()); std::string memstickDir; if (File::ReadFileToString(true, memstickDirFile, memstickDir)) { Path memstickPath(memstickDir); @@ -529,15 +530,14 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } else { ERROR_LOG(SYSTEM, "Couldn't read directory '%s' specified by memstick_dir.txt.", memstickDir.c_str()); if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) { - // TODO: Gotta resolve this somehow... - // I think we wanna pop up the memstick dir chooser before any other screen in this case. - // For now we just choose a default. - g_Config.memStickDirectory = g_Config.defaultCurrentDirectory; + // Ask the user to configure a memstick directory. + INFO_LOG(SYSTEM, "Asking the user."); + g_Config.memStickDirectory.clear(); } } } } else { - INFO_LOG(SYSTEM, "No memstick directory file found. Using '%s'", memstickDirFile.c_str()); + INFO_LOG(SYSTEM, "No memstick directory file found (tried to open '%s')", memstickDirFile.c_str()); } #elif PPSSPP_PLATFORM(IOS) @@ -572,8 +572,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch DiskCachingFileLoaderCache::SetCacheDir(g_Config.appCacheDirectory); } - if (!LogManager::GetInstance()) + if (!LogManager::GetInstance()) { LogManager::Init(&g_Config.bEnableLogging); + } #if !PPSSPP_PLATFORM(WINDOWS) g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); @@ -585,11 +586,6 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch LogManager *logman = LogManager::GetInstance(); -#if PPSSPP_PLATFORM(ANDROID) - // On early versions of Android we don't need to ask permission. - CreateDirectoriesAndroid(); -#endif - const char *fileToLog = 0; Path stateToLoad; @@ -785,8 +781,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch }); } + INFO_LOG(SYSTEM, "ScreenManager!"); screenManager = new ScreenManager(); - if (gotoGameSettings) { + if (g_Config.memStickDirectory.empty()) { + INFO_LOG(SYSTEM, "No memstick directory! Asking for one to be configured."); + screenManager->switchScreen(new MainScreen()); + screenManager->push(new MemStickScreen(true)); + } else if (gotoGameSettings) { screenManager->switchScreen(new LogoScreen(true)); } else if (gotoTouchScreenTest) { screenManager->switchScreen(new MainScreen()); @@ -832,7 +833,6 @@ static UI::Style MakeStyle(uint32_t fg, uint32_t bg) { UI::Style s; s.background = UI::Drawable(bg); s.fgColor = fg; - return s; } @@ -1477,6 +1477,8 @@ void NativeShutdown() { logger = nullptr; } + g_threadManager.Teardown(); + // Previously we did exit() here on Android but that makes it hard to do things like restart on backend change. // I think we handle most globals correctly or correct-enough now. }