2021-06-05 14:59:38 +00:00
// Copyright (c) 2013- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
# include "ppsspp_config.h"
# include "android/jni/app-android.h"
# include "Common/Log.h"
# include "Common/UI/UI.h"
# include "Common/UI/View.h"
# include "Common/UI/ViewGroup.h"
# include "Common/StringUtils.h"
# include "Common/System/System.h"
2023-03-22 11:26:14 +00:00
# include "Common/System/Request.h"
2021-06-05 14:59:38 +00:00
# include "Common/System/NativeApp.h"
# include "Common/System/Display.h"
2023-07-12 09:29:24 +00:00
# include "Common/System/OSD.h"
2021-07-25 13:33:11 +00:00
# include "Common/Data/Text/I18n.h"
2021-07-26 20:11:07 +00:00
# include "Common/Data/Text/Parsers.h"
2021-07-25 13:33:11 +00:00
# include "Common/File/AndroidStorage.h"
# include "Common/File/FileUtil.h"
# include "Common/File/Path.h"
2021-07-24 22:16:30 +00:00
# include "Common/File/DiskFree.h"
2021-06-05 14:59:38 +00:00
2021-07-29 21:03:43 +00:00
# include "Common/Thread/ThreadManager.h"
2021-07-25 13:33:11 +00:00
# include "Core/Config.h"
2021-09-20 14:20:58 +00:00
# include "Core/Reporting.h"
# include "Core/System.h"
# include "Core/Util/GameManager.h"
2024-01-22 13:00:46 +00:00
# include "Core/Util/MemStick.h"
2021-06-05 14:59:38 +00:00
# include "UI/MemStickScreen.h"
# include "UI/MainScreen.h"
# include "UI/MiscScreens.h"
2023-07-15 10:22:00 +00:00
# include "UI/OnScreenDisplay.h"
2021-06-05 14:59:38 +00:00
2021-07-24 22:16:30 +00:00
static std : : string FormatSpaceString ( int64_t space ) {
if ( space > = 0 ) {
2021-07-26 20:11:07 +00:00
char buffer [ 50 ] ;
NiceSizeFormat ( space , buffer , sizeof ( buffer ) ) ;
return buffer ;
2021-07-24 22:16:30 +00:00
} else {
return " N/A " ;
}
}
MemStickScreen : : MemStickScreen ( bool initialSetup )
: initialSetup_ ( initialSetup ) {
2021-09-19 13:54:01 +00:00
# if PPSSPP_PLATFORM(ANDROID)
// Let's only offer the browse-for-folder choice on Android 10 or later.
// Earlier versions often don't really have working folder browsers.
storageBrowserWorking_ = System_GetPropertyInt ( SYSPROP_SYSTEMVERSION ) > = 29 ;
# else
// For testing UI only
storageBrowserWorking_ = true ;
# endif
if ( initialSetup_ ) {
// Preselect current choice.
if ( System_GetPropertyBool ( SYSPROP_ANDROID_SCOPED_STORAGE ) ) {
choice_ = CHOICE_BROWSE_FOLDER ;
} else {
2024-07-14 12:42:59 +00:00
WARN_LOG_REPORT ( Log : : System , " Scoped storage not enabled - shouldn't be in MemStickScreen at initial setup " ) ;
2021-09-19 13:54:01 +00:00
choice_ = CHOICE_STORAGE_ROOT ;
// Shouldn't really be here in initial setup.
}
} else {
// Detect the current choice, so it's preselected in the UI.
2023-05-01 20:09:22 +00:00
# if PPSSPP_PLATFORM(UWP)
if ( g_Config . memStickDirectory = = g_Config . internalDataDirectory ) {
2023-04-28 19:22:17 +00:00
# else
2021-09-19 13:54:01 +00:00
if ( g_Config . memStickDirectory = = Path ( g_extFilesDir ) ) {
2023-04-28 19:22:17 +00:00
# endif
2021-09-19 13:54:01 +00:00
choice_ = CHOICE_PRIVATE_DIRECTORY ;
} else if ( g_Config . memStickDirectory = = Path ( g_externalDir ) ) {
choice_ = CHOICE_STORAGE_ROOT ;
} else if ( storageBrowserWorking_ ) {
choice_ = CHOICE_BROWSE_FOLDER ;
} else {
choice_ = CHOICE_SET_MANUAL ;
}
}
2021-07-25 13:33:11 +00:00
}
2021-06-05 14:59:38 +00:00
2021-09-19 16:32:10 +00:00
static void AddExplanation ( UI : : ViewGroup * viewGroup , MemStickScreen : : Choice choice , UI : : View * extraView = nullptr ) {
2023-04-05 22:34:50 +00:00
auto iz = GetI18NCategory ( I18NCat : : MEMSTICK ) ;
2021-09-19 16:32:10 +00:00
using namespace UI ;
int flags = FLAG_WRAP_TEXT ;
UI : : ViewGroup * holder = new UI : : LinearLayout ( ORIENT_VERTICAL ) ;
UI : : ViewGroup * indentHolder = new UI : : LinearLayout ( ORIENT_HORIZONTAL ) ;
indentHolder - > Add ( new Spacer ( 20.0 ) ) ;
indentHolder - > Add ( holder ) ;
viewGroup - > Add ( indentHolder ) ;
if ( extraView ) {
holder - > Add ( extraView ) ;
}
switch ( choice ) {
case MemStickScreen : : CHOICE_STORAGE_ROOT :
// Old school choice
2021-09-20 06:34:58 +00:00
holder - > Add ( new TextView ( iz - > T ( " DataWillStay " , " Data will stay even if you uninstall PPSSPP " ) , flags , false ) ) - > SetBullet ( true ) ;
holder - > Add ( new TextView ( iz - > T ( " DataCanBeShared " , " Data can be shared between PPSSPP regular/Gold " ) , flags , false ) ) - > SetBullet ( true ) ;
2021-09-19 16:32:10 +00:00
holder - > Add ( new TextView ( iz - > T ( " EasyUSBAccess " , " Easy USB access " ) , flags , false ) ) - > SetBullet ( true ) ;
break ;
case MemStickScreen : : CHOICE_BROWSE_FOLDER :
2021-09-20 06:34:58 +00:00
holder - > Add ( new TextView ( iz - > T ( " DataWillStay " , " Data will stay even if you uninstall PPSSPP " ) , flags , false ) ) - > SetBullet ( true ) ;
holder - > Add ( new TextView ( iz - > T ( " DataCanBeShared " , " Data can be shared between PPSSPP regular/Gold " ) , flags , false ) ) - > SetBullet ( true ) ;
2023-04-28 19:22:17 +00:00
# if !PPSSPP_PLATFORM(UWP)
2021-09-19 16:32:10 +00:00
holder - > Add ( new TextView ( iz - > T ( " EasyUSBAccess " , " Easy USB access " ) , flags , false ) ) - > SetBullet ( true ) ;
2023-04-28 19:22:17 +00:00
# endif
2021-09-19 16:32:10 +00:00
break ;
case MemStickScreen : : CHOICE_PRIVATE_DIRECTORY :
// Consider https://www.compart.com/en/unicode/U+26A0 (unicode warning sign?)? or a graphic?
holder - > Add ( new TextView ( iz - > T ( " DataWillBeLostOnUninstall " , " Warning! Data will be lost when you uninstall PPSSPP! " ) , flags , false ) ) - > SetBullet ( true ) ;
holder - > Add ( new TextView ( iz - > T ( " DataCannotBeShared " , " Data CANNOT be shared between PPSSPP regular/Gold! " ) , flags , false ) ) - > SetBullet ( true ) ;
2023-04-28 19:22:17 +00:00
# if !PPSSPP_PLATFORM(UWP)
2021-09-19 16:32:10 +00:00
# if GOLD
holder - > Add ( new TextView ( iz - > T ( " USBAccessThroughGold " , " USB access through Android/data/org.ppsspp.ppssppgold/files " ) , flags , false ) ) - > SetBullet ( true ) ;
# else
holder - > Add ( new TextView ( iz - > T ( " USBAccessThrough " , " USB access through Android/data/org.ppsspp.ppsspp/files " ) , flags , false ) ) - > SetBullet ( true ) ;
2023-04-28 19:22:17 +00:00
# endif
2021-09-19 16:32:10 +00:00
# endif
break ;
case MemStickScreen : : CHOICE_SET_MANUAL :
default :
holder - > Add ( new TextView ( iz - > T ( " EasyUSBAccess " , " Easy USB access " ) , flags , false ) ) - > SetBullet ( true ) ;
// What more?
// Should we have a special text here? It'll popup a text window for editing.
break ;
}
}
2021-06-05 14:59:38 +00:00
void MemStickScreen : : CreateViews ( ) {
using namespace UI ;
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2024-09-26 19:09:23 +00:00
auto ms = GetI18NCategory ( I18NCat : : MEMSTICK ) ;
2021-06-05 14:59:38 +00:00
2021-09-19 18:24:28 +00:00
Margins actionMenuMargins ( 15 , 0 , 15 , 0 ) ;
2021-06-05 14:59:38 +00:00
2021-07-24 22:16:30 +00:00
root_ = new LinearLayout ( ORIENT_HORIZONTAL ) ;
2021-06-05 14:59:38 +00:00
2021-07-25 13:33:11 +00:00
Spacer * spacerColumn = new Spacer ( new LinearLayoutParams ( 20.0 , FILL_PARENT , 0.0f ) ) ;
2021-09-19 18:24:28 +00:00
ScrollView * mainColumnScroll = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( 1.0 ) ) ;
2021-08-05 19:53:08 +00:00
2021-09-19 18:24:28 +00:00
ViewGroup * mainColumn = new LinearLayoutList ( ORIENT_VERTICAL ) ;
mainColumnScroll - > Add ( mainColumn ) ;
2021-07-25 13:33:11 +00:00
2021-09-19 18:24:28 +00:00
root_ - > Add ( spacerColumn ) ;
root_ - > Add ( mainColumnScroll ) ;
2021-08-05 19:53:08 +00:00
2021-07-25 13:33:11 +00:00
if ( initialSetup_ ) {
2021-09-19 18:24:28 +00:00
mainColumn - > Add ( new Spacer ( new LinearLayoutParams ( FILL_PARENT , 12.0f , 0.0f ) ) ) ;
2024-09-26 19:09:23 +00:00
mainColumn - > Add ( new TextView ( ms - > T ( " Welcome to PPSSPP! " ) , ALIGN_LEFT , false ) ) ;
2021-07-25 13:33:11 +00:00
}
2021-09-19 18:24:28 +00:00
mainColumn - > Add ( new Spacer ( new LinearLayoutParams ( FILL_PARENT , 18.0f , 0.0f ) ) ) ;
2024-09-26 19:09:23 +00:00
mainColumn - > Add ( new TextView ( ms - > T ( " MemoryStickDescription " , " Choose where to keep PSP data (Memory Stick) " ) , ALIGN_LEFT , false ) ) ;
2021-09-19 18:24:28 +00:00
mainColumn - > Add ( new Spacer ( new LinearLayoutParams ( FILL_PARENT , 18.0f , 0.0f ) ) ) ;
2021-09-18 21:40:43 +00:00
2021-09-19 18:24:28 +00:00
ViewGroup * subColumns = new LinearLayoutList ( ORIENT_HORIZONTAL ) ;
mainColumn - > Add ( subColumns ) ;
ViewGroup * leftColumn = new LinearLayoutList ( ORIENT_VERTICAL , new LinearLayoutParams ( 1.0 ) ) ;
subColumns - > Add ( leftColumn ) ;
ViewGroup * rightColumnItems = new LinearLayout ( ORIENT_VERTICAL , new LinearLayoutParams ( 220 , FILL_PARENT , actionMenuMargins ) ) ;
subColumns - > Add ( rightColumnItems ) ;
2021-07-25 13:33:11 +00:00
2021-08-04 21:21:28 +00:00
// For legacy Android systems, so you can switch back to the old ways if you move to SD or something.
2021-09-19 16:32:10 +00:00
// Trying to avoid needing a scroll view, so only showing the explanation for one option at a time.
2023-08-14 05:27:51 +00:00
# if !PPSSPP_PLATFORM(UWP)
2021-08-04 21:21:28 +00:00
if ( ! System_GetPropertyBool ( SYSPROP_ANDROID_SCOPED_STORAGE ) ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new RadioButton ( & choice_ , CHOICE_STORAGE_ROOT , ms - > T ( " Use PSP folder at root of storage " ) ) ) - > OnClick . Handle ( this , & MemStickScreen : : OnChoiceClick ) ;
2021-09-19 16:32:10 +00:00
if ( choice_ = = CHOICE_STORAGE_ROOT ) {
AddExplanation ( leftColumn , ( MemStickScreen : : Choice ) choice_ ) ;
}
2021-09-19 13:54:01 +00:00
}
2023-08-14 05:27:51 +00:00
# endif
2021-09-19 13:54:01 +00:00
if ( storageBrowserWorking_ ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new RadioButton ( & choice_ , CHOICE_BROWSE_FOLDER , ms - > T ( " Create or Choose a PSP folder " ) ) ) - > OnClick . Handle ( this , & MemStickScreen : : OnChoiceClick ) ;
2021-09-19 16:32:10 +00:00
2021-09-19 13:54:01 +00:00
// TODO: Show current folder here if we have one set.
} else {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new RadioButton ( & choice_ , CHOICE_SET_MANUAL , ms - > T ( " Manually specify PSP folder " ) ) ) - > OnClick . Handle ( this , & MemStickScreen : : OnChoiceClick ) ;
leftColumn - > Add ( new TextView ( ms - > T ( " DataWillStay " , " Data will stay even if you uninstall PPSSPP. " ) ) ) - > SetBullet ( true ) ;
leftColumn - > Add ( new TextView ( ms - > T ( " DataCanBeShared " , " Data can be shared between PPSSPP regular/Gold. " ) ) ) - > SetBullet ( true ) ;
2021-09-19 13:54:01 +00:00
// TODO: Show current folder here if we have one set.
}
2024-09-26 19:09:23 +00:00
errorNoticeView_ = leftColumn - > Add ( new NoticeView ( NoticeLevel : : WARN , ms - > T ( " Cancelled - try again " ) , " " ) ) ;
2023-07-15 10:22:00 +00:00
errorNoticeView_ - > SetVisibility ( UI : : V_GONE ) ;
2021-09-19 16:32:10 +00:00
if ( choice_ = = CHOICE_BROWSE_FOLDER | | choice_ = = CHOICE_SET_MANUAL ) {
UI : : View * extraView = nullptr ;
if ( ! g_Config . memStickDirectory . empty ( ) ) {
2024-09-26 19:09:23 +00:00
extraView = new TextView ( StringFromFormat ( " %s: %s " , ms - > T_cstr ( " Current " ) , g_Config . memStickDirectory . ToVisualString ( ) . c_str ( ) ) , ALIGN_LEFT , false ) ;
2021-09-19 16:32:10 +00:00
}
AddExplanation ( leftColumn , ( MemStickScreen : : Choice ) choice_ , extraView ) ;
2021-08-04 21:21:28 +00:00
}
2023-05-01 20:09:22 +00:00
2024-09-26 19:09:23 +00:00
std : : string privateString ( ms - > T ( " Use App Private Data " ) ) ;
2023-05-01 20:09:22 +00:00
2021-09-19 16:32:10 +00:00
if ( initialSetup_ ) {
2024-09-26 19:09:23 +00:00
privateString = StringFromFormat ( " %s (%s) " , ms - > T_cstr ( " Skip for now " ) , privateString . c_str ( ) ) ;
2021-09-19 16:32:10 +00:00
}
2022-09-30 09:31:32 +00:00
leftColumn - > Add ( new RadioButton ( & choice_ , CHOICE_PRIVATE_DIRECTORY , privateString ) ) - > OnClick . Handle ( this , & MemStickScreen : : OnChoiceClick ) ;
2021-09-19 16:32:10 +00:00
if ( choice_ = = CHOICE_PRIVATE_DIRECTORY ) {
AddExplanation ( leftColumn , ( MemStickScreen : : Choice ) choice_ ) ;
}
2021-09-10 21:42:21 +00:00
2021-09-19 13:54:01 +00:00
leftColumn - > Add ( new Spacer ( new LinearLayoutParams ( FILL_PARENT , 12.0f , 0.0f ) ) ) ;
2024-10-10 09:55:07 +00:00
std : : string_view confirmButtonText ;
2021-09-19 13:54:01 +00:00
ImageID confirmButtonImage = ImageID : : invalid ( ) ;
switch ( choice_ ) {
case CHOICE_BROWSE_FOLDER :
2021-09-19 18:24:28 +00:00
confirmButtonText = di - > T ( " OK " ) ;
2021-09-19 13:54:01 +00:00
confirmButtonImage = ImageID ( " I_FOLDER_OPEN " ) ;
break ;
case CHOICE_PRIVATE_DIRECTORY :
2021-09-19 17:10:55 +00:00
if ( initialSetup_ ) {
confirmButtonText = di - > T ( " Skip " ) ;
confirmButtonImage = ImageID ( " I_WARNING " ) ;
} else {
2021-09-20 14:13:37 +00:00
confirmButtonText = di - > T ( " OK " ) ;
2021-09-19 17:10:55 +00:00
}
2021-09-19 13:54:01 +00:00
break ;
2021-09-19 16:32:10 +00:00
case CHOICE_STORAGE_ROOT :
2021-09-19 13:54:01 +00:00
case CHOICE_SET_MANUAL :
default :
2021-09-20 14:13:37 +00:00
confirmButtonText = di - > T ( " OK " ) ;
2021-09-19 13:54:01 +00:00
break ;
}
2021-09-19 16:45:15 +00:00
rightColumnItems - > Add ( new UI : : Choice ( confirmButtonText , confirmButtonImage ) ) - > OnClick . Handle < MemStickScreen > ( this , & MemStickScreen : : OnConfirmClick ) ;
2021-09-19 13:54:01 +00:00
rightColumnItems - > Add ( new Spacer ( new LinearLayoutParams ( FILL_PARENT , 12.0f , 0.0f ) ) ) ;
2021-06-05 14:59:38 +00:00
2021-07-25 13:33:11 +00:00
if ( ! initialSetup_ ) {
2021-09-19 16:32:10 +00:00
rightColumnItems - > Add ( new UI : : Choice ( di - > T ( " Back " ) ) ) - > OnClick . Handle < UIScreen > ( this , & UIScreen : : OnBack ) ;
2021-06-05 14:59:38 +00:00
}
2021-10-05 09:30:50 +00:00
if ( System_GetPropertyInt ( SYSPROP_DEVICE_TYPE ) ! = DEVICE_TYPE_TV ) {
2024-09-26 19:09:23 +00:00
rightColumnItems - > Add ( new UI : : Choice ( ms - > T ( " WhatsThis " , " What's this? " ) ) ) - > OnClick . Handle < MemStickScreen > ( this , & MemStickScreen : : OnHelp ) ;
2021-10-05 09:30:50 +00:00
}
2021-06-05 14:59:38 +00:00
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " MemStickScreen: initialSetup=%d " , ( int ) initialSetup_ ) ;
2021-07-25 13:33:11 +00:00
}
2021-06-05 14:59:38 +00:00
2021-09-15 20:46:52 +00:00
UI : : EventReturn MemStickScreen : : OnHelp ( UI : : EventParams & params ) {
2023-01-28 23:25:00 +00:00
// I'm letting the old redirect handle this one, as the target is within /docs on the website,
// and that structure may change a bit.
2023-03-21 09:42:23 +00:00
System_LaunchUrl ( LaunchUrlType : : BROWSER_URL , " https://www.ppsspp.org/guide_storage.html " ) ;
2021-09-15 20:46:52 +00:00
return UI : : EVENT_DONE ;
}
2021-09-19 13:54:01 +00:00
UI : : EventReturn MemStickScreen : : OnChoiceClick ( UI : : EventParams & params ) {
// Change the confirm button to match the choice,
// and change the text that we show.
RecreateViews ( ) ;
return UI : : EVENT_DONE ;
}
UI : : EventReturn MemStickScreen : : OnConfirmClick ( UI : : EventParams & params ) {
switch ( choice_ ) {
case CHOICE_SET_MANUAL :
return SetFolderManually ( params ) ;
case CHOICE_STORAGE_ROOT :
return UseStorageRoot ( params ) ;
case CHOICE_PRIVATE_DIRECTORY :
return UseInternalStorage ( params ) ;
case CHOICE_BROWSE_FOLDER :
return Browse ( params ) ;
}
return UI : : EVENT_DONE ;
}
UI : : EventReturn MemStickScreen : : SetFolderManually ( UI : : EventParams & params ) {
2024-09-09 11:58:29 +00:00
// The old way, from before scoped storage. Write in the full path.
2023-06-21 22:47:00 +00:00
# if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
2023-04-05 22:34:50 +00:00
auto sy = GetI18NCategory ( I18NCat : : SYSTEM ) ;
2024-09-26 10:04:29 +00:00
System_InputBoxGetString ( GetRequesterToken ( ) , sy - > T ( " Memory Stick Folder " ) , g_Config . memStickDirectory . ToString ( ) , false , [ & ] ( const std : : string & value , int ) {
2023-04-05 22:34:50 +00:00
auto sy = GetI18NCategory ( I18NCat : : SYSTEM ) ;
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2021-09-10 21:59:54 +00:00
2023-03-25 17:51:06 +00:00
std : : string newPath = value ;
2024-10-10 09:55:07 +00:00
size_t pos = newPath . find_last_not_of ( ' / ' ) ;
2023-03-25 17:51:06 +00:00
// Gotta have at least something but a /, and also needs to start with a /.
2024-10-10 09:55:07 +00:00
if ( newPath . empty ( ) | | pos = = std : : string : : npos | | newPath [ 0 ] ! = ' / ' ) {
2023-03-25 17:51:06 +00:00
settingInfo_ - > Show ( sy - > T ( " ChangingMemstickPathInvalid " , " That path couldn't be used to save Memory Stick files. " ) , nullptr ) ;
return ;
}
if ( pos ! = newPath . size ( ) - 1 ) {
newPath = newPath . substr ( 0 , pos + 1 ) ;
}
if ( newPath . empty ( ) ) {
// Reuse below message instead of adding yet another string.
System_Toast ( sy - > T ( " Path does not exist! " ) ) ;
return ;
}
Path pendingMemStickFolder ( newPath ) ;
if ( ! File : : Exists ( pendingMemStickFolder ) ) {
// Try to fix the path string, apparently some users got used to leaving out the /.
if ( newPath [ 0 ] ! = ' / ' ) {
newPath = " / " + newPath ;
}
pendingMemStickFolder = Path ( newPath ) ;
}
if ( ! File : : Exists ( pendingMemStickFolder ) & & pendingMemStickFolder . Type ( ) = = PathType : : NATIVE ) {
// Still no path? Try to automatically fix the case.
std : : string oldNewPath = newPath ;
FixPathCase ( Path ( " " ) , newPath , FixPathCaseBehavior : : FPC_FILE_MUST_EXIST ) ;
if ( oldNewPath ! = newPath ) {
2024-07-14 12:42:59 +00:00
NOTICE_LOG ( Log : : IO , " Fixed path case: %s -> %s " , oldNewPath . c_str ( ) , newPath . c_str ( ) ) ;
2023-03-25 17:51:06 +00:00
pendingMemStickFolder = Path ( newPath ) ;
} else {
2024-07-14 12:42:59 +00:00
NOTICE_LOG ( Log : : IO , " Failed to fix case of path %s (result: %s) " , newPath . c_str ( ) , oldNewPath . c_str ( ) ) ;
2023-03-25 17:51:06 +00:00
}
}
if ( pendingMemStickFolder = = g_Config . memStickDirectory ) {
// Same directory as before - all good. Nothing to do.
TriggerFinish ( DialogResult : : DR_OK ) ;
return ;
}
if ( ! File : : Exists ( pendingMemStickFolder ) ) {
System_Toast ( sy - > T ( " Path does not exist! " ) ) ;
return ;
}
screenManager ( ) - > push ( new ConfirmMemstickMoveScreen ( pendingMemStickFolder , false ) ) ;
2021-09-10 21:59:54 +00:00
} ) ;
2021-09-11 02:01:56 +00:00
# endif
2021-09-10 21:59:54 +00:00
return UI : : EVENT_DONE ;
}
2021-09-19 13:54:01 +00:00
UI : : EventReturn MemStickScreen : : UseInternalStorage ( UI : : EventParams & params ) {
2023-05-01 20:09:22 +00:00
# if PPSSPP_PLATFORM(UWP)
Path pendingMemStickFolder = g_Config . internalDataDirectory ;
2023-04-28 19:22:17 +00:00
# else
2021-08-04 21:21:28 +00:00
Path pendingMemStickFolder = Path ( g_extFilesDir ) ;
2023-04-28 19:22:17 +00:00
# endif
2021-08-04 21:21:28 +00:00
if ( initialSetup_ ) {
// There's not gonna be any files here in this case since it's a fresh install.
// Let's just accept it and move on. No need to move files either.
if ( SwitchMemstickFolderTo ( pendingMemStickFolder ) ) {
TriggerFinish ( DialogResult : : DR_OK ) ;
} else {
// This can't really happen?? Not worth making an error message.
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : System , " Could not switch memstick path in setup (internal) " ) ;
2021-08-04 21:21:28 +00:00
}
2022-04-17 21:02:17 +00:00
// Don't have a confirmation dialog that would otherwise do it for us, need to just switch directly to the main screen.
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
2021-09-10 20:52:49 +00:00
} else if ( pendingMemStickFolder ! = g_Config . memStickDirectory ) {
2021-08-04 21:21:28 +00:00
// Always ask for confirmation when called from the UI. Likely there's already some data.
screenManager ( ) - > push ( new ConfirmMemstickMoveScreen ( pendingMemStickFolder , false ) ) ;
2021-09-10 20:52:49 +00:00
} else {
// User chose the same directory it's already in. Let's just bail.
TriggerFinish ( DialogResult : : DR_OK ) ;
2021-08-04 21:21:28 +00:00
}
return UI : : EVENT_DONE ;
}
2021-09-19 13:54:01 +00:00
UI : : EventReturn MemStickScreen : : UseStorageRoot ( UI : : EventParams & params ) {
2021-08-04 21:21:28 +00:00
Path pendingMemStickFolder = Path ( g_externalDir ) ;
2021-07-24 22:16:30 +00:00
if ( initialSetup_ ) {
// There's not gonna be any files here in this case since it's a fresh install.
// Let's just accept it and move on. No need to move files either.
2021-08-04 21:21:28 +00:00
if ( SwitchMemstickFolderTo ( pendingMemStickFolder ) ) {
2021-07-26 20:15:34 +00:00
TriggerFinish ( DialogResult : : DR_OK ) ;
} else {
// This can't really happen?? Not worth making an error message.
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : System , " Could not switch memstick path in setup " ) ;
2021-07-26 20:15:34 +00:00
}
2021-09-10 20:52:49 +00:00
} else if ( pendingMemStickFolder ! = g_Config . memStickDirectory ) {
2021-07-24 22:16:30 +00:00
// Always ask for confirmation when called from the UI. Likely there's already some data.
2021-08-04 21:21:28 +00:00
screenManager ( ) - > push ( new ConfirmMemstickMoveScreen ( pendingMemStickFolder , false ) ) ;
2021-09-10 20:52:49 +00:00
} else {
// User chose the same directory it's already in. Let's just bail.
TriggerFinish ( DialogResult : : DR_OK ) ;
2021-07-24 22:16:30 +00:00
}
2021-07-25 13:33:11 +00:00
return UI : : EVENT_DONE ;
2021-06-05 14:59:38 +00:00
}
2021-09-19 13:54:01 +00:00
UI : : EventReturn MemStickScreen : : Browse ( UI : : EventParams & params ) {
2023-04-05 22:34:50 +00:00
auto mm = GetI18NCategory ( I18NCat : : MAINMENU ) ;
2024-01-25 11:47:37 +00:00
System_BrowseForFolder ( GetRequesterToken ( ) , mm - > T ( " Choose folder " ) , g_Config . memStickDirectory , [ = ] ( const std : : string & value , int ) {
2023-07-12 09:29:24 +00:00
Path pendingMemStickFolder = Path ( value ) ;
2024-09-26 19:09:23 +00:00
INFO_LOG ( Log : : System , " Got folder: '%s' (old: %s) " , pendingMemStickFolder . c_str ( ) , g_Config . memStickDirectory . c_str ( ) ) ;
2023-03-22 15:09:33 +00:00
// Browse finished. Let's pop up the confirmation dialog.
2023-07-12 09:29:24 +00:00
if ( ! pendingMemStickFolder . empty ( ) & & pendingMemStickFolder = = g_Config . memStickDirectory & & File : : IsDirectory ( pendingMemStickFolder ) ) {
2024-09-26 19:09:23 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2023-07-12 09:29:24 +00:00
// Not sure how this could happen, but let's go with it.
2024-09-26 19:09:23 +00:00
g_OSD . Show ( OSDType : : MESSAGE_SUCCESS , di - > T ( " Done! " ) ) ;
2023-07-12 09:29:24 +00:00
done_ = true ;
2023-03-22 15:09:33 +00:00
return ;
2021-06-05 14:59:38 +00:00
}
2023-07-15 10:22:00 +00:00
errorNoticeView_ - > SetVisibility ( UI : : V_GONE ) ;
2024-09-26 19:09:23 +00:00
2023-03-22 15:09:33 +00:00
screenManager ( ) - > push ( new ConfirmMemstickMoveScreen ( pendingMemStickFolder , initialSetup_ ) ) ;
2023-07-15 10:22:00 +00:00
} , [ = ] ( ) {
errorNoticeView_ - > SetVisibility ( UI : : V_VISIBLE ) ;
2023-03-22 15:09:33 +00:00
} ) ;
return UI : : EVENT_DONE ;
2021-06-05 14:59:38 +00:00
}
2021-07-24 22:16:30 +00:00
void MemStickScreen : : dialogFinished ( const Screen * dialog , DialogResult result ) {
if ( result = = DialogResult : : DR_OK ) {
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " Confirmation screen done - moving on. " ) ;
2021-07-24 22:16:30 +00:00
// There's a screen manager bug if we call TriggerFinish directly.
// Can't be bothered right now, so we pick this up in update().
done_ = true ;
}
// otherwise, we just keep going.
}
void MemStickScreen : : update ( ) {
UIDialogScreenWithBackground : : update ( ) ;
if ( done_ ) {
TriggerFinish ( DialogResult : : DR_OK ) ;
done_ = false ;
}
}
2024-10-10 09:55:07 +00:00
ConfirmMemstickMoveScreen : : ConfirmMemstickMoveScreen ( const Path & newMemstickFolder , bool initialSetup )
2024-04-11 12:50:42 +00:00
: newMemstickFolder_ ( newMemstickFolder ) , initialSetup_ ( initialSetup ) , progressReporter_ ( ) {
2021-07-24 22:16:30 +00:00
existingFilesInNewFolder_ = FolderSeemsToBeUsed ( newMemstickFolder ) ;
if ( initialSetup_ ) {
moveData_ = false ;
}
}
2021-07-29 21:03:43 +00:00
ConfirmMemstickMoveScreen : : ~ ConfirmMemstickMoveScreen ( ) {
if ( moveDataTask_ ) {
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " Move Data task still running, blocking on it " ) ;
2021-07-29 21:03:43 +00:00
moveDataTask_ - > BlockUntilReady ( ) ;
delete moveDataTask_ ;
}
2024-10-11 09:55:27 +00:00
if ( oldSpaceTask_ ) {
oldSpaceTask_ - > BlockUntilReady ( ) ;
delete oldSpaceTask_ ;
}
if ( newSpaceTask_ ) {
newSpaceTask_ - > BlockUntilReady ( ) ;
delete newSpaceTask_ ;
}
2021-07-29 21:03:43 +00:00
}
2021-07-24 22:16:30 +00:00
void ConfirmMemstickMoveScreen : : CreateViews ( ) {
using namespace UI ;
2023-04-05 22:34:50 +00:00
auto sy = GetI18NCategory ( I18NCat : : SYSTEM ) ;
2024-09-26 19:09:23 +00:00
auto ms = GetI18NCategory ( I18NCat : : MEMSTICK ) ;
2021-06-05 14:59:38 +00:00
2021-07-24 22:16:30 +00:00
root_ = new LinearLayout ( ORIENT_HORIZONTAL ) ;
2021-06-05 14:59:38 +00:00
2024-10-11 09:55:27 +00:00
Path & oldMemstickFolder = g_Config . memStickDirectory ;
2021-07-24 22:16:30 +00:00
Spacer * spacerColumn = new Spacer ( new LinearLayoutParams ( 20.0 , FILL_PARENT , 0.0f ) ) ;
ViewGroup * leftColumn = new LinearLayout ( ORIENT_VERTICAL , new LinearLayoutParams ( 1.0 ) ) ;
ViewGroup * rightColumn = new LinearLayout ( ORIENT_VERTICAL , new LinearLayoutParams ( 1.0 ) ) ;
root_ - > Add ( spacerColumn ) ;
root_ - > Add ( leftColumn ) ;
root_ - > Add ( rightColumn ) ;
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new TextView ( ms - > T ( " Selected PSP Data Folder " ) , ALIGN_LEFT , false ) ) ;
2021-08-05 19:53:08 +00:00
if ( ! initialSetup_ ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new NoticeView ( NoticeLevel : : WARN , ms - > T ( " PPSSPP will restart after the change " ) , " " ) ) ;
2021-08-05 19:53:08 +00:00
}
2023-05-04 08:03:14 +00:00
leftColumn - > Add ( new TextView ( newMemstickFolder_ . ToVisualString ( ) , ALIGN_LEFT , false ) ) ;
2024-10-11 09:55:27 +00:00
newFreeSpaceView_ = leftColumn - > Add ( new TextView ( ms - > T ( " Free space " ) , ALIGN_LEFT , false ) ) ;
newSpaceTask_ = Promise < SpaceResult * > : : Spawn ( & g_threadManager , [ & ] ( ) - > SpaceResult * {
int64_t freeSpaceNew ;
free_disk_space ( newMemstickFolder_ , freeSpaceNew ) ;
return new SpaceResult { freeSpaceNew } ;
} , TaskType : : IO_BLOCKING , TaskPriority : : HIGH ) ;
2021-07-24 22:16:30 +00:00
if ( existingFilesInNewFolder_ ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new NoticeView ( NoticeLevel : : SUCCESS , ms - > T ( " Already contains PSP data " ) , " " ) ) ;
2021-08-05 19:53:08 +00:00
if ( ! moveData_ ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new NoticeView ( NoticeLevel : : INFO , ms - > T ( " No data will be changed " ) , " " ) ) ;
2021-08-05 19:53:08 +00:00
}
2021-07-24 22:16:30 +00:00
}
if ( ! error_ . empty ( ) ) {
leftColumn - > Add ( new TextView ( error_ , ALIGN_LEFT , false ) ) ;
}
if ( ! oldMemstickFolder . empty ( ) ) {
2024-10-11 09:55:27 +00:00
oldSpaceTask_ = Promise < SpaceResult * > : : Spawn ( & g_threadManager , [ & ] ( ) - > SpaceResult * {
int64_t freeSpaceOld ;
free_disk_space ( oldMemstickFolder , freeSpaceOld ) ;
return new SpaceResult { freeSpaceOld } ;
} , TaskType : : IO_BLOCKING , TaskPriority : : HIGH ) ;
2023-04-28 19:22:17 +00:00
2024-09-26 19:09:23 +00:00
rightColumn - > Add ( new TextView ( std : : string ( ms - > T ( " Current " ) ) + " : " , ALIGN_LEFT , false ) ) ;
2023-05-04 08:03:14 +00:00
rightColumn - > Add ( new TextView ( oldMemstickFolder . ToVisualString ( ) , ALIGN_LEFT , false ) ) ;
2024-10-11 09:55:27 +00:00
oldFreeSpaceView_ = rightColumn - > Add ( new TextView ( ms - > T ( " Free space " ) , ALIGN_LEFT , false ) ) ;
2021-07-24 22:16:30 +00:00
}
2021-07-29 21:03:43 +00:00
if ( moveDataTask_ ) {
2024-01-22 20:52:39 +00:00
progressView_ = leftColumn - > Add ( new TextView ( progressReporter_ . Format ( ) ) ) ;
2021-07-29 21:03:43 +00:00
} else {
progressView_ = nullptr ;
2021-07-24 22:16:30 +00:00
}
2021-07-29 21:03:43 +00:00
if ( ! moveDataTask_ ) {
if ( ! initialSetup_ ) {
2024-09-26 19:09:23 +00:00
leftColumn - > Add ( new CheckBox ( & moveData_ , ms - > T ( " Move Data " ) ) ) - > OnClick . Handle ( this , & ConfirmMemstickMoveScreen : : OnMoveDataClick ) ;
2021-07-29 21:03:43 +00:00
}
2023-12-20 09:35:02 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2021-07-29 21:03:43 +00:00
leftColumn - > Add ( new Choice ( di - > T ( " OK " ) ) ) - > OnClick . Handle ( this , & ConfirmMemstickMoveScreen : : OnConfirm ) ;
leftColumn - > Add ( new Choice ( di - > T ( " Back " ) ) ) - > OnClick . Handle < UIScreen > ( this , & UIScreen : : OnBack ) ;
}
2021-07-24 22:16:30 +00:00
}
2021-08-05 19:53:08 +00:00
UI : : EventReturn ConfirmMemstickMoveScreen : : OnMoveDataClick ( UI : : EventParams & params ) {
RecreateViews ( ) ;
return UI : : EVENT_DONE ;
}
2021-07-29 21:03:43 +00:00
void ConfirmMemstickMoveScreen : : update ( ) {
UIDialogScreenWithBackground : : update ( ) ;
2024-09-26 19:09:23 +00:00
auto ms = GetI18NCategory ( I18NCat : : MEMSTICK ) ;
2021-07-29 21:03:43 +00:00
if ( moveDataTask_ ) {
if ( progressView_ ) {
2024-01-22 20:52:39 +00:00
progressView_ - > SetText ( progressReporter_ . Format ( ) ) ;
2021-07-29 21:03:43 +00:00
}
2021-09-18 13:10:31 +00:00
MoveResult * result = moveDataTask_ - > Poll ( ) ;
2021-07-29 21:03:43 +00:00
if ( result ) {
2021-09-18 13:10:31 +00:00
if ( result - > success ) {
2024-09-26 19:09:23 +00:00
progressReporter_ . SetProgress ( ms - > T ( " Done! " ) ) ;
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " Move data task finished successfully! " ) ;
2021-07-29 21:03:43 +00:00
// Succeeded!
FinishFolderMove ( ) ;
} else {
2024-09-26 19:09:23 +00:00
progressReporter_ . SetProgress ( ms - > T ( " Failed to move some files! " ) ) ;
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " Move data task failed! " ) ;
2021-07-29 21:03:43 +00:00
// What do we do here? We might be in the middle of a move... Bad.
RecreateViews ( ) ;
}
delete moveDataTask_ ;
moveDataTask_ = nullptr ;
}
}
2024-10-11 09:55:27 +00:00
if ( newSpaceTask_ & & newFreeSpaceView_ ) {
SpaceResult * result = newSpaceTask_ - > Poll ( ) ;
if ( result ) {
newFreeSpaceView_ - > SetText ( std : : string ( ms - > T ( " Free space " ) ) + " : " + FormatSpaceString ( result - > bytesFree ) ) ;
delete newSpaceTask_ ;
newSpaceTask_ = nullptr ;
}
}
if ( oldSpaceTask_ & & oldFreeSpaceView_ ) {
SpaceResult * result = oldSpaceTask_ - > Poll ( ) ;
if ( result ) {
oldFreeSpaceView_ - > SetText ( std : : string ( ms - > T ( " Free space " ) ) + " : " + FormatSpaceString ( result - > bytesFree ) ) ;
delete oldSpaceTask_ ;
oldSpaceTask_ = nullptr ;
}
}
2021-07-29 21:03:43 +00:00
}
2021-07-24 22:16:30 +00:00
UI : : EventReturn ConfirmMemstickMoveScreen : : OnConfirm ( UI : : EventParams & params ) {
// Transfer all the files in /PSP from the original directory.
// Should probably be done on a background thread so we can show some UI.
// So we probably need another screen for this with a progress bar..
// If the directory itself is called PSP, don't go below.
if ( moveData_ ) {
2024-01-22 20:52:39 +00:00
progressReporter_ . SetProgress ( T ( I18NCat : : MEMSTICK , " Starting move... " ) ) ;
2021-07-29 21:03:43 +00:00
2021-11-20 21:40:10 +00:00
moveDataTask_ = Promise < MoveResult * > : : Spawn ( & g_threadManager , [ & ] ( ) - > MoveResult * {
2021-07-29 21:03:43 +00:00
Path moveSrc = g_Config . memStickDirectory ;
Path moveDest = newMemstickFolder_ ;
2024-01-22 13:00:46 +00:00
return MoveDirectoryContentsSafe ( moveSrc , moveDest , progressReporter_ ) ;
2023-01-15 15:55:07 +00:00
} , TaskType : : IO_BLOCKING , TaskPriority : : HIGH ) ;
2021-07-29 21:03:43 +00:00
RecreateViews ( ) ;
} else {
FinishFolderMove ( ) ;
2021-07-25 13:33:11 +00:00
}
2021-06-05 14:59:38 +00:00
2021-07-29 21:03:43 +00:00
return UI : : EVENT_DONE ;
}
void ConfirmMemstickMoveScreen : : FinishFolderMove ( ) {
2023-05-01 20:09:22 +00:00
auto ms = GetI18NCategory ( I18NCat : : MEMSTICK ) ;
2021-07-29 21:03:43 +00:00
2024-01-22 14:04:21 +00:00
Path oldMemstickFolder = g_Config . memStickDirectory ;
2021-07-24 22:16:30 +00:00
// Successful so far, switch the memstick folder.
2021-07-26 20:15:34 +00:00
if ( ! SwitchMemstickFolderTo ( newMemstickFolder_ ) ) {
// TODO: More precise errors.
2023-05-01 20:09:22 +00:00
error_ = ms - > T ( " That folder doesn't work as a memstick folder. " ) ;
2021-07-29 21:03:43 +00:00
return ;
2021-07-26 20:15:34 +00:00
}
2021-07-24 22:16:30 +00:00
// If the chosen folder already had a config, reload it!
g_Config . Load ( ) ;
2024-01-22 14:04:21 +00:00
// If the current browser directory is the old memstick folder, drop it.
if ( g_Config . currentDirectory = = oldMemstickFolder ) {
g_Config . currentDirectory = g_Config . defaultCurrentDirectory ;
}
2023-08-18 13:51:16 +00:00
PostLoadConfig ( ) ;
2021-07-24 22:16:30 +00:00
2021-08-05 19:53:08 +00:00
if ( ! initialSetup_ ) {
// We restart the app here, to get the new settings.
2023-03-22 21:17:53 +00:00
System_RestartApp ( " " ) ;
2021-07-25 13:33:11 +00:00
} else {
2021-09-18 14:06:11 +00:00
// This is initial setup, we now switch to the main screen, if we were successful
// (which we better have been...)
if ( g_Config . Save ( " MemstickPathChanged " ) ) {
// TriggerFinish(DialogResult::DR_OK);
screenManager ( ) - > switchScreen ( new MainScreen ( ) ) ;
} else {
2023-05-01 20:09:22 +00:00
error_ = ms - > T ( " Failed to save config " ) ;
2021-09-18 14:06:11 +00:00
RecreateViews ( ) ;
}
2021-06-05 14:59:38 +00:00
}
}