2016-07-03 17:24:33 +00:00
// Copyright (c) 2014- 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/.
2020-03-10 06:01:55 +00:00
# include "ppsspp_config.h"
2017-02-27 19:51:36 +00:00
# include <algorithm>
# include <thread>
2017-12-20 09:22:15 +00:00
# include <mutex>
2017-02-27 19:51:36 +00:00
2020-03-10 06:01:55 +00:00
# if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
# include "Common/CommonWindows.h"
# include <netfw.h>
# endif
2020-03-10 02:51:25 +00:00
// TODO: For text align flags, probably shouldn't be in gfx_es2/...
2020-10-04 21:24:14 +00:00
# include "Common/Render/DrawBuffer.h"
2020-10-04 18:48:47 +00:00
# include "Common/Net/HTTPClient.h"
# include "Common/Net/Resolve.h"
# include "Common/Net/URL.h"
2023-01-03 22:29:22 +00:00
# include "Common/Thread/ThreadUtil.h"
2023-03-22 11:26:14 +00:00
# include "Common/System/Request.h"
2020-09-29 10:19:22 +00:00
2020-10-03 22:25:21 +00:00
# include "Common/File/PathBrowser.h"
2020-10-04 07:29:36 +00:00
# include "Common/Data/Format/JSONReader.h"
2020-10-01 11:05:04 +00:00
# include "Common/Data/Text/I18n.h"
2016-07-03 17:24:33 +00:00
# include "Common/Common.h"
2022-01-30 23:49:02 +00:00
# include "Common/Log.h"
2020-09-29 10:19:22 +00:00
# include "Common/StringUtils.h"
2022-01-30 23:49:02 +00:00
# include "Common/TimeUtil.h"
2016-07-03 17:24:33 +00:00
# include "Core/Config.h"
2022-12-27 23:08:57 +00:00
# include "Core/System.h"
2018-04-21 20:51:18 +00:00
# include "Core/WebServer.h"
2016-07-03 17:24:33 +00:00
# include "UI/RemoteISOScreen.h"
2024-06-21 12:22:41 +00:00
# include "UI/OnScreenDisplay.h"
2016-07-03 17:24:33 +00:00
using namespace UI ;
2016-07-04 00:38:29 +00:00
static const char * REPORT_HOSTNAME = " report.ppsspp.org " ;
static const int REPORT_PORT = 80 ;
2016-07-04 14:32:49 +00:00
static bool scanCancelled = false ;
2017-03-22 07:00:52 +00:00
static bool scanAborted = false ;
2016-07-04 14:32:49 +00:00
2020-03-10 06:01:55 +00:00
enum class ServerAllowStatus {
NO ,
YES ,
UNKNOWN ,
} ;
static ServerAllowStatus IsServerAllowed ( int port ) {
# if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
INetFwMgr * fwMgr = nullptr ;
HRESULT hr = CoCreateInstance ( __uuidof ( NetFwMgr ) , nullptr , CLSCTX_INPROC_SERVER , __uuidof ( INetFwMgr ) , ( void * * ) & fwMgr ) ;
if ( FAILED ( hr ) ) {
return ServerAllowStatus : : UNKNOWN ;
}
std : : wstring app ;
size_t sz ;
do {
app . resize ( app . size ( ) + MAX_PATH ) ;
// On failure, this will return the same value as passed in, but success will always be one lower.
sz = GetModuleFileName ( nullptr , & app [ 0 ] , ( DWORD ) app . size ( ) ) ;
} while ( sz > = app . size ( ) ) ;
VARIANT allowedV , restrictedV ;
VariantInit ( & allowedV ) ;
VariantInit ( & restrictedV ) ;
hr = fwMgr - > IsPortAllowed ( & app [ 0 ] , NET_FW_IP_VERSION_ANY , port , nullptr , NET_FW_IP_PROTOCOL_TCP , & allowedV , & restrictedV ) ;
fwMgr - > Release ( ) ;
if ( FAILED ( hr ) ) {
return ServerAllowStatus : : UNKNOWN ;
}
bool allowed = allowedV . vt = = VT_BOOL & & allowedV . boolVal ! = VARIANT_FALSE ;
bool restricted = restrictedV . vt = = VT_BOOL & & restrictedV . boolVal ! = VARIANT_FALSE ;
if ( ! allowed | | restricted ) {
return ServerAllowStatus : : NO ;
}
return ServerAllowStatus : : YES ;
# else
return ServerAllowStatus : : UNKNOWN ;
# endif
}
2023-12-28 14:34:40 +00:00
std : : string RemoteSubdir ( ) {
2018-12-22 06:41:02 +00:00
if ( g_Config . bRemoteISOManual ) {
2019-10-06 15:50:35 +00:00
return g_Config . sRemoteISOSubdir ;
2018-12-22 06:41:02 +00:00
}
return " / " ;
}
2020-03-10 02:51:25 +00:00
bool RemoteISOConnectScreen : : FindServer ( std : : string & resultHost , int & resultPort ) {
2016-07-04 00:38:29 +00:00
http : : Client http ;
Buffer result ;
int code = 500 ;
2020-03-10 02:51:25 +00:00
bool hadTimeouts = false ;
2016-07-04 00:38:29 +00:00
2018-12-22 06:41:02 +00:00
std : : string subdir = RemoteSubdir ( ) ;
2023-04-05 22:34:50 +00:00
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
2020-03-10 02:51:25 +00:00
auto SetStatus = [ & ] ( const std : : string & key , const std : : string & host , int port ) {
std : : string formatted = ReplaceAll ( ri - > T ( key ) , " [URL] " , StringFromFormat ( " http://%s:%d/ " , host . c_str ( ) , port ) ) ;
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
statusMessage_ = formatted ;
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : System , " Remote: %s " , formatted . c_str ( ) ) ;
2020-03-10 02:51:25 +00:00
} ;
2021-05-01 06:12:42 +00:00
http . SetUserAgent ( StringFromFormat ( " PPSSPP/%s " , PPSSPP_GIT_VERSION ) ) ;
2017-03-22 07:00:52 +00:00
auto TryServer = [ & ] ( const std : : string & host , int port ) {
2020-03-10 02:51:25 +00:00
SetStatus ( " Resolving [URL]... " , host , port ) ;
if ( ! http . Resolve ( host . c_str ( ) , port ) ) {
SetStatus ( " Could not resolve [URL] " , host , port ) ;
return false ;
}
SetStatus ( " Connecting to [URL]... " , host , port ) ;
2017-03-22 07:00:52 +00:00
// Don't wait as long for a connect - we need a good connection for smooth streaming anyway.
// This way if it's down, we'll find the right one faster.
2020-03-10 02:51:25 +00:00
if ( ! http . Connect ( 1 , 10.0 , & scanCancelled ) ) {
hadTimeouts = true ;
SetStatus ( " Could not connect to [URL] " , host , port ) ;
return false ;
}
2018-12-22 06:41:02 +00:00
2020-03-10 02:51:25 +00:00
SetStatus ( " Loading game list from [URL]... " , host , port ) ;
2023-07-18 13:13:44 +00:00
net : : RequestProgress progress ( & scanCancelled ) ;
2021-08-22 15:17:26 +00:00
code = http . GET ( http : : RequestParams ( subdir . c_str ( ) ) , & result , & progress ) ;
2020-03-10 02:51:25 +00:00
http . Disconnect ( ) ;
2018-12-22 06:41:02 +00:00
2020-03-10 02:51:25 +00:00
if ( code ! = 200 ) {
if ( code < 0 ) {
hadTimeouts = true ;
2018-12-22 06:41:02 +00:00
}
2020-03-10 02:51:25 +00:00
SetStatus ( " Game list failed from [URL] " , host , port ) ;
return false ;
}
2018-12-22 06:41:02 +00:00
2020-03-10 02:51:25 +00:00
// Make sure this isn't just the debugger. If so, move on.
std : : string listing ;
std : : vector < std : : string > items ;
result . TakeAll ( & listing ) ;
SplitString ( listing , ' \n ' , items ) ;
bool supported = false ;
for ( const std : : string & item : items ) {
2023-12-29 20:34:24 +00:00
if ( item . empty ( ) )
2020-03-10 02:51:25 +00:00
continue ;
2023-12-29 20:34:24 +00:00
if ( ! RemoteISOFileSupported ( item ) ) {
if ( item . back ( ) ! = ' / ' ) {
// We accept lists of just directories - we kinda have to.
continue ;
}
2018-12-22 06:41:02 +00:00
}
2020-03-10 02:51:25 +00:00
supported = true ;
break ;
}
if ( supported ) {
resultHost = host ;
resultPort = port ;
SetStatus ( " Connected to [URL] " , host , port ) ;
2024-07-14 12:42:59 +00:00
NOTICE_LOG ( Log : : System , " RemoteISO found: %s : %d " , host . c_str ( ) , port ) ;
2020-03-10 02:51:25 +00:00
return true ;
2017-03-22 07:00:52 +00:00
}
return false ;
} ;
2017-02-14 02:18:14 +00:00
// Try last server first, if it is set
2022-09-30 09:31:32 +00:00
if ( g_Config . iLastRemoteISOPort & & ! g_Config . sLastRemoteISOServer . empty ( ) ) {
2017-03-22 07:00:52 +00:00
if ( TryServer ( g_Config . sLastRemoteISOServer . c_str ( ) , g_Config . iLastRemoteISOPort ) ) {
return true ;
}
2017-02-14 02:18:14 +00:00
}
2017-03-22 07:06:02 +00:00
// Don't scan if in manual mode.
if ( g_Config . bRemoteISOManual | | scanCancelled ) {
2017-03-06 04:26:53 +00:00
return false ;
}
2016-07-04 00:38:29 +00:00
2017-03-06 04:26:53 +00:00
// Start by requesting a list of recent local ips for this network.
2020-03-10 02:51:25 +00:00
SetStatus ( " Looking for peers... " , " " , 0 ) ;
2017-03-06 04:26:53 +00:00
if ( http . Resolve ( REPORT_HOSTNAME , REPORT_PORT ) ) {
2017-03-22 07:00:52 +00:00
if ( http . Connect ( 2 , 20.0 , & scanCancelled ) ) {
2023-07-18 13:13:44 +00:00
net : : RequestProgress progress ( & scanCancelled ) ;
2021-08-22 15:17:26 +00:00
code = http . GET ( http : : RequestParams ( " /match/list " ) , & result , & progress ) ;
2017-03-06 04:26:53 +00:00
http . Disconnect ( ) ;
2017-02-22 01:45:58 +00:00
}
2017-03-06 04:26:53 +00:00
}
if ( code ! = 200 | | scanCancelled ) {
2020-03-10 02:51:25 +00:00
if ( ! scanCancelled ) {
SetStatus ( " Could not load peers, retrying soon... " , " " , 0 ) ;
}
2017-03-06 04:26:53 +00:00
return false ;
}
2016-07-04 00:38:29 +00:00
2017-03-06 04:26:53 +00:00
std : : string json ;
result . TakeAll ( & json ) ;
2016-07-04 00:38:29 +00:00
2018-08-12 22:05:00 +00:00
using namespace json ;
2017-03-06 04:26:53 +00:00
JsonReader reader ( json . c_str ( ) , json . size ( ) ) ;
if ( ! reader . ok ( ) ) {
2020-03-10 02:51:25 +00:00
SetStatus ( " Could not load peers, retrying soon... " , " " , 0 ) ;
2017-03-06 04:26:53 +00:00
return false ;
}
2016-07-04 00:38:29 +00:00
2018-04-15 18:24:10 +00:00
const JsonValue entries = reader . rootArray ( ) ;
if ( entries . getTag ( ) ! = JSON_ARRAY ) {
2020-03-10 02:51:25 +00:00
SetStatus ( " Could not load peers, retrying soon... " , " " , 0 ) ;
2017-03-06 04:26:53 +00:00
return false ;
}
2016-07-04 00:38:29 +00:00
2018-04-15 18:24:10 +00:00
for ( const auto pentry : entries ) {
JsonGet entry = pentry - > value ;
if ( scanCancelled )
return false ;
2024-01-16 12:14:57 +00:00
const char * host = entry . getStringOr ( " ip " , " " ) ;
2018-04-15 18:24:10 +00:00
int port = entry . getInt ( " p " , 0 ) ;
2017-03-06 04:26:53 +00:00
2017-03-22 07:00:52 +00:00
if ( TryServer ( host , port ) ) {
2017-03-06 04:26:53 +00:00
return true ;
2016-07-04 00:38:29 +00:00
}
}
2017-03-06 04:26:53 +00:00
2020-03-10 02:51:25 +00:00
// None of the local IPs were reachable. We'll retry again.
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
if ( hadTimeouts ) {
statusMessage_ = ri - > T ( " RemoteISOScanningTimeout " , " Scanning... check your desktop's firewall settings " ) ;
} else {
statusMessage_ = ri - > T ( " RemoteISOScanning " , " Scanning... click Share Games on your desktop " ) ;
}
2016-07-04 03:48:27 +00:00
return false ;
}
2021-05-09 17:06:02 +00:00
static bool LoadGameList ( const Path & url , std : : vector < Path > & games ) {
2024-05-03 15:54:57 +00:00
PathBrowser browser ;
browser . SetPath ( url ) ;
2021-04-25 18:38:22 +00:00
std : : vector < File : : FileInfo > files ;
2022-12-27 23:08:57 +00:00
browser . SetUserAgent ( StringFromFormat ( " PPSSPP/%s " , PPSSPP_GIT_VERSION ) ) ;
2024-05-03 15:54:57 +00:00
browser . SetRootAlias ( " ms: " , GetSysDirectory ( DIRECTORY_MEMSTICK_ROOT ) ) ;
2023-12-29 12:21:55 +00:00
browser . GetListing ( files , " iso:cso:chd:pbp:elf:prx:ppdmp: " , & scanCancelled ) ;
2019-10-06 16:31:06 +00:00
if ( scanCancelled ) {
return false ;
}
for ( auto & file : files ) {
2021-05-17 01:04:03 +00:00
if ( file . isDirectory | | RemoteISOFileSupported ( file . name ) ) {
2019-10-06 18:49:35 +00:00
games . push_back ( file . fullName ) ;
}
2019-10-06 16:31:06 +00:00
}
2019-10-06 15:50:35 +00:00
2016-07-04 03:48:27 +00:00
return ! games . empty ( ) ;
2016-07-04 00:38:29 +00:00
}
2024-06-21 12:22:41 +00:00
RemoteISOScreen : : RemoteISOScreen ( const Path & filename ) : TabbedUIDialogScreenWithGameBackground ( filename ) { }
void RemoteISOScreen : : CreateTabs ( ) {
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
UI : : LinearLayout * connect = AddTab ( " Connect " , ri - > T ( " Connect " ) ) ;
connect - > SetSpacing ( 5.0f ) ;
CreateConnectTab ( connect ) ;
UI : : LinearLayout * settings = AddTab ( " Settings " , ri - > T ( " Settings " ) ) ;
CreateSettingsTab ( settings ) ;
}
2016-07-03 17:43:35 +00:00
2017-03-15 05:01:18 +00:00
void RemoteISOScreen : : update ( ) {
2024-06-21 12:22:41 +00:00
TabbedUIDialogScreenWithGameBackground : : update ( ) ;
2016-07-03 17:43:35 +00:00
2020-03-10 06:01:55 +00:00
if ( ! WebServerStopped ( WebServerFlags : : DISCS ) ) {
auto result = IsServerAllowed ( g_Config . iRemoteISOPort ) ;
if ( result = = ServerAllowStatus : : NO ) {
firewallWarning_ - > SetVisibility ( V_VISIBLE ) ;
} else if ( result = = ServerAllowStatus : : YES ) {
firewallWarning_ - > SetVisibility ( V_GONE ) ;
}
}
2018-04-21 20:51:18 +00:00
bool nowRunning = ! WebServerStopped ( WebServerFlags : : DISCS ) ;
2016-07-03 18:41:27 +00:00
if ( serverStopping_ & & ! nowRunning ) {
serverStopping_ = false ;
2016-07-03 17:43:35 +00:00
}
2016-07-03 18:41:27 +00:00
2016-07-03 17:43:35 +00:00
if ( serverRunning_ ! = nowRunning ) {
RecreateViews ( ) ;
}
serverRunning_ = nowRunning ;
2016-07-03 17:24:33 +00:00
}
2024-06-21 12:22:41 +00:00
void RemoteISOScreen : : CreateConnectTab ( UI : : ViewGroup * tab ) {
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
2016-07-03 17:24:33 +00:00
Margins actionMenuMargins ( 0 , 20 , 15 , 0 ) ;
Margins contentMargins ( 0 , 20 , 5 , 5 ) ;
2024-06-21 12:22:41 +00:00
2017-03-22 07:03:45 +00:00
ViewGroup * leftColumn = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( FILL_PARENT , FILL_PARENT , 0.4f , contentMargins ) ) ;
2016-07-03 17:24:33 +00:00
LinearLayout * leftColumnItems = new LinearLayout ( ORIENT_VERTICAL , new LayoutParams ( WRAP_CONTENT , FILL_PARENT ) ) ;
ViewGroup * rightColumn = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( 300 , FILL_PARENT , actionMenuMargins ) ) ;
LinearLayout * rightColumnItems = new LinearLayout ( ORIENT_VERTICAL ) ;
2024-06-21 12:22:41 +00:00
if ( serverRunning_ ) {
leftColumnItems - > Add ( new NoticeView ( NoticeLevel : : SUCCESS , ri - > T ( " Currently sharing " ) , " " , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
} else {
leftColumnItems - > Add ( new NoticeView ( NoticeLevel : : INFO , ri - > T ( " Not currently sharing " ) , " " , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
}
2023-12-29 07:07:23 +00:00
if ( ( RemoteISOShareType ) g_Config . iRemoteISOShareType = = RemoteISOShareType : : RECENT ) {
leftColumnItems - > Add ( new TextView ( ri - > T ( " RemoteISODesc " , " Games in your recent list will be shared " ) , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
} else {
2024-06-21 12:22:41 +00:00
leftColumnItems - > Add ( new TextView ( std : : string ( ri - > T ( " Share Games (Server) " ) ) + " : " + Path ( g_Config . sRemoteISOSharedDir ) . ToVisualString ( ) , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
2023-12-29 07:07:23 +00:00
}
2017-08-16 21:39:30 +00:00
leftColumnItems - > Add ( new TextView ( ri - > T ( " RemoteISOWifi " , " Note: Connect both devices to the same wifi " ) , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
2020-03-10 06:01:55 +00:00
firewallWarning_ = leftColumnItems - > Add ( new TextView ( ri - > T ( " RemoteISOWinFirewall " , " WARNING: Windows Firewall is blocking sharing " ) , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
firewallWarning_ - > SetTextColor ( 0xFF0000FF ) ;
firewallWarning_ - > SetVisibility ( V_GONE ) ;
2016-07-03 17:24:33 +00:00
rightColumnItems - > SetSpacing ( 0.0f ) ;
2017-08-16 21:39:30 +00:00
Choice * browseChoice = new Choice ( ri - > T ( " Browse Games " ) ) ;
2016-07-04 03:46:06 +00:00
rightColumnItems - > Add ( browseChoice ) - > OnClick . Handle ( this , & RemoteISOScreen : : HandleBrowse ) ;
2018-04-21 20:51:18 +00:00
if ( WebServerStopping ( WebServerFlags : : DISCS ) ) {
2017-08-16 21:39:30 +00:00
rightColumnItems - > Add ( new Choice ( ri - > T ( " Stopping.. " ) ) ) - > SetDisabledPtr ( & serverStopping_ ) ;
2016-07-04 03:46:06 +00:00
browseChoice - > SetEnabled ( false ) ;
2018-04-21 20:51:18 +00:00
} else if ( ! WebServerStopped ( WebServerFlags : : DISCS ) ) {
2017-08-16 21:39:30 +00:00
rightColumnItems - > Add ( new Choice ( ri - > T ( " Stop Sharing " ) ) ) - > OnClick . Handle ( this , & RemoteISOScreen : : HandleStopServer ) ;
2016-07-04 03:46:06 +00:00
browseChoice - > SetEnabled ( false ) ;
2016-07-03 17:24:33 +00:00
} else {
2017-08-16 21:39:30 +00:00
rightColumnItems - > Add ( new Choice ( ri - > T ( " Share Games (Server) " ) ) ) - > OnClick . Handle ( this , & RemoteISOScreen : : HandleStartServer ) ;
2016-07-04 03:46:06 +00:00
browseChoice - > SetEnabled ( true ) ;
2016-07-03 17:24:33 +00:00
}
2024-06-21 12:22:41 +00:00
LinearLayout * beforeBack = new LinearLayout ( ORIENT_HORIZONTAL , new LayoutParams ( FILL_PARENT , FILL_PARENT ) ) ;
2017-03-22 07:03:45 +00:00
beforeBack - > Add ( leftColumn ) ;
beforeBack - > Add ( rightColumn ) ;
2016-07-03 17:24:33 +00:00
leftColumn - > Add ( leftColumnItems ) ;
rightColumn - > Add ( rightColumnItems ) ;
2024-06-21 12:22:41 +00:00
tab - > Add ( beforeBack ) ;
}
void RemoteISOScreen : : CreateSettingsTab ( UI : : ViewGroup * remoteisoSettings ) {
serverRunning_ = ! WebServerStopped ( WebServerFlags : : DISCS ) ;
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
remoteisoSettings - > Add ( new ItemHeader ( ri - > T ( " Remote disc streaming " ) ) ) ;
remoteisoSettings - > Add ( new CheckBox ( & g_Config . bRemoteShareOnStartup , ri - > T ( " Share on PPSSPP startup " ) ) ) ;
remoteisoSettings - > Add ( new CheckBox ( & g_Config . bRemoteISOManual , ri - > T ( " Manual Mode Client " , " Manually configure client " ) ) ) ;
remoteisoSettings - > Add ( new CheckBox ( & g_Config . bRemoteTab , ri - > T ( " Show Remote tab on main screen " ) ) ) ;
if ( System_GetPropertyBool ( SYSPROP_HAS_FOLDER_BROWSER ) ) {
static const char * shareTypes [ ] = { " Recent files " , " Choose directory " } ;
remoteisoSettings - > Add ( new PopupMultiChoice ( & g_Config . iRemoteISOShareType , ri - > T ( " Files to share " ) , shareTypes , 0 , ARRAY_SIZE ( shareTypes ) , I18NCat : : REMOTEISO , screenManager ( ) ) ) ;
FolderChooserChoice * folderChooser = remoteisoSettings - > Add ( new FolderChooserChoice ( GetRequesterToken ( ) , & g_Config . sRemoteISOSharedDir , ri - > T ( " Files to share " ) ) ) ;
folderChooser - > SetEnabledFunc ( [ = ] ( ) {
return g_Config . iRemoteISOShareType = = ( int ) RemoteISOShareType : : LOCAL_FOLDER ;
} ) ;
} else {
// Can't pick a folder, only allow sharing recent stuff.
g_Config . iRemoteISOShareType = ( int ) RemoteISOShareType : : RECENT ;
}
UI : : Choice * remoteServer = new PopupTextInputChoice ( GetRequesterToken ( ) , & g_Config . sLastRemoteISOServer , ri - > T ( " Remote Server " ) , " " , 255 , screenManager ( ) ) ;
remoteisoSettings - > Add ( remoteServer ) ;
remoteServer - > SetEnabledPtr ( & g_Config . bRemoteISOManual ) ;
PopupSliderChoice * remotePort = remoteisoSettings - > Add ( new PopupSliderChoice ( & g_Config . iLastRemoteISOPort , 0 , 65535 , 0 , ri - > T ( " Remote Port " ) , 100 , screenManager ( ) ) ) ;
remotePort - > SetEnabledPtr ( & g_Config . bRemoteISOManual ) ;
UI : : Choice * remoteSubdir ;
{
PopupTextInputChoice * remoteSubdirInput = new PopupTextInputChoice ( GetRequesterToken ( ) , & g_Config . sRemoteISOSubdir , ri - > T ( " Remote Subdirectory " ) , " " , 255 , screenManager ( ) ) ;
remoteSubdirInput - > OnChange . Handle ( this , & RemoteISOScreen : : OnChangeRemoteISOSubdir ) ;
remoteSubdir = remoteSubdirInput ;
}
remoteisoSettings - > Add ( remoteSubdir ) ;
remoteSubdir - > SetEnabledPtr ( & g_Config . bRemoteISOManual ) ;
PopupSliderChoice * portChoice = new PopupSliderChoice ( & g_Config . iRemoteISOPort , 0 , 65535 , 0 , ri - > T ( " Local Server Port " , " Local Server Port " ) , 100 , screenManager ( ) ) ;
remoteisoSettings - > Add ( portChoice ) ;
portChoice - > SetDisabledPtr ( & serverRunning_ ) ;
}
static void CleanupRemoteISOSubdir ( ) {
// Replace spaces and force forward slashes.
// TODO: Maybe we should uri escape this after?
ReplaceAll ( g_Config . sRemoteISOSubdir , " " , " %20 " ) ;
ReplaceAll ( g_Config . sRemoteISOSubdir , " \\ " , " / " ) ;
// Make sure it begins with /.
if ( g_Config . sRemoteISOSubdir . empty ( ) | | g_Config . sRemoteISOSubdir [ 0 ] ! = ' / ' )
g_Config . sRemoteISOSubdir = " / " + g_Config . sRemoteISOSubdir ;
}
UI : : EventReturn RemoteISOScreen : : OnChangeRemoteISOSubdir ( UI : : EventParams & e ) {
CleanupRemoteISOSubdir ( ) ;
return UI : : EVENT_DONE ;
2016-07-03 17:24:33 +00:00
}
UI : : EventReturn RemoteISOScreen : : HandleStartServer ( UI : : EventParams & e ) {
2018-04-21 20:51:18 +00:00
if ( ! StartWebServer ( WebServerFlags : : DISCS ) ) {
2016-07-03 17:24:33 +00:00
return EVENT_SKIPPED ;
}
return EVENT_DONE ;
}
UI : : EventReturn RemoteISOScreen : : HandleStopServer ( UI : : EventParams & e ) {
2018-04-21 20:51:18 +00:00
if ( ! StopWebServer ( WebServerFlags : : DISCS ) ) {
2016-07-03 17:24:33 +00:00
return EVENT_SKIPPED ;
}
2016-07-03 18:41:27 +00:00
serverStopping_ = true ;
RecreateViews ( ) ;
2016-07-03 17:24:33 +00:00
return EVENT_DONE ;
}
2016-07-04 00:38:29 +00:00
UI : : EventReturn RemoteISOScreen : : HandleBrowse ( UI : : EventParams & e ) {
screenManager ( ) - > push ( new RemoteISOConnectScreen ( ) ) ;
return EVENT_DONE ;
}
2020-03-10 02:51:25 +00:00
RemoteISOConnectScreen : : RemoteISOConnectScreen ( ) {
2016-07-04 14:32:49 +00:00
scanCancelled = false ;
2017-03-22 07:00:52 +00:00
scanAborted = false ;
2016-07-04 00:38:29 +00:00
scanThread_ = new std : : thread ( [ ] ( RemoteISOConnectScreen * thiz ) {
2023-01-03 22:29:22 +00:00
SetCurrentThreadName ( " RemoteISOScan " ) ;
2016-07-04 00:38:29 +00:00
thiz - > ExecuteScan ( ) ;
} , this ) ;
}
RemoteISOConnectScreen : : ~ RemoteISOConnectScreen ( ) {
2016-07-04 14:32:49 +00:00
int maxWait = 5000 ;
scanCancelled = true ;
2016-07-04 03:48:27 +00:00
while ( GetStatus ( ) = = ScanStatus : : SCANNING | | GetStatus ( ) = = ScanStatus : : LOADING ) {
2024-11-21 14:25:02 +00:00
sleep_ms ( 1 , " remote-iso-scan " ) ;
2016-07-04 14:32:49 +00:00
if ( - - maxWait < 0 ) {
// If it does ever wake up, it may crash... but better than hanging?
2017-03-22 07:00:52 +00:00
scanAborted = true ;
2016-07-04 14:32:49 +00:00
break ;
}
2016-07-04 00:38:29 +00:00
}
2019-09-28 17:48:07 +00:00
if ( scanThread_ - > joinable ( ) )
scanThread_ - > join ( ) ;
2016-07-04 00:38:29 +00:00
delete scanThread_ ;
}
void RemoteISOConnectScreen : : CreateViews ( ) {
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
2016-07-04 00:38:29 +00:00
Margins actionMenuMargins ( 0 , 20 , 15 , 0 ) ;
Margins contentMargins ( 0 , 20 , 5 , 5 ) ;
ViewGroup * leftColumn = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( WRAP_CONTENT , FILL_PARENT , 0.4f , contentMargins ) ) ;
LinearLayout * leftColumnItems = new LinearLayout ( ORIENT_VERTICAL , new LayoutParams ( WRAP_CONTENT , FILL_PARENT ) ) ;
ViewGroup * rightColumn = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( 300 , FILL_PARENT , actionMenuMargins ) ) ;
LinearLayout * rightColumnItems = new LinearLayout ( ORIENT_VERTICAL ) ;
2020-03-10 02:51:25 +00:00
statusView_ = leftColumnItems - > Add ( new TextView ( ri - > T ( " RemoteISOScanning " , " Scanning... click Share Games on your desktop " ) , FLAG_WRAP_TEXT , false , new LinearLayoutParams ( Margins ( 12 , 5 , 0 , 5 ) ) ) ) ;
2016-07-04 00:38:29 +00:00
rightColumnItems - > SetSpacing ( 0.0f ) ;
2017-08-14 10:08:03 +00:00
rightColumnItems - > Add ( new Choice ( di - > T ( " Cancel " ) , " " , false , new AnchorLayoutParams ( 150 , WRAP_CONTENT , 10 , NONE , NONE , 10 ) ) ) - > OnClick . Handle < UIScreen > ( this , & UIScreen : : OnBack ) ;
2016-07-04 00:38:29 +00:00
root_ = new LinearLayout ( ORIENT_HORIZONTAL , new LinearLayoutParams ( FILL_PARENT , FILL_PARENT , 1.0f ) ) ;
root_ - > Add ( leftColumn ) ;
root_ - > Add ( rightColumn ) ;
leftColumn - > Add ( leftColumnItems ) ;
rightColumn - > Add ( rightColumnItems ) ;
}
2017-03-15 05:01:18 +00:00
void RemoteISOConnectScreen : : update ( ) {
2023-04-05 22:34:50 +00:00
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
2016-07-04 03:48:27 +00:00
2023-01-01 20:33:11 +00:00
UIDialogScreenWithBackground : : update ( ) ;
2016-07-04 00:38:29 +00:00
2016-07-04 03:48:27 +00:00
ScanStatus s = GetStatus ( ) ;
switch ( s ) {
case ScanStatus : : SCANNING :
case ScanStatus : : LOADING :
break ;
case ScanStatus : : FOUND :
2017-08-16 21:39:30 +00:00
statusView_ - > SetText ( ri - > T ( " RemoteISOLoading " , " Connected - loading game list " ) ) ;
2016-07-04 03:48:27 +00:00
status_ = ScanStatus : : LOADING ;
// Let's reuse scanThread_.
2019-09-28 17:48:07 +00:00
if ( scanThread_ - > joinable ( ) )
scanThread_ - > join ( ) ;
2016-07-04 03:48:27 +00:00
delete scanThread_ ;
2020-03-10 02:51:25 +00:00
statusMessage_ . clear ( ) ;
2016-07-04 03:48:27 +00:00
scanThread_ = new std : : thread ( [ ] ( RemoteISOConnectScreen * thiz ) {
thiz - > ExecuteLoad ( ) ;
} , this ) ;
break ;
case ScanStatus : : FAILED :
2020-09-24 21:52:03 +00:00
nextRetry_ = time_now_d ( ) + 15.0 ;
2016-07-04 03:48:27 +00:00
status_ = ScanStatus : : RETRY_SCAN ;
break ;
case ScanStatus : : RETRY_SCAN :
2020-09-24 21:52:03 +00:00
if ( nextRetry_ < time_now_d ( ) ) {
2016-07-04 03:48:27 +00:00
status_ = ScanStatus : : SCANNING ;
2016-07-04 00:38:29 +00:00
nextRetry_ = 0.0 ;
2019-09-28 17:48:07 +00:00
if ( scanThread_ - > joinable ( ) )
scanThread_ - > join ( ) ;
2016-07-04 00:38:29 +00:00
delete scanThread_ ;
2020-03-10 02:51:25 +00:00
statusMessage_ . clear ( ) ;
2016-07-04 00:38:29 +00:00
scanThread_ = new std : : thread ( [ ] ( RemoteISOConnectScreen * thiz ) {
thiz - > ExecuteScan ( ) ;
} , this ) ;
}
2016-07-04 03:48:27 +00:00
break ;
case ScanStatus : : LOADED :
2017-03-20 00:43:03 +00:00
TriggerFinish ( DR_OK ) ;
2019-10-06 19:40:00 +00:00
screenManager ( ) - > push ( new RemoteISOBrowseScreen ( url_ , games_ ) ) ;
2016-07-04 03:48:27 +00:00
break ;
2016-07-04 00:38:29 +00:00
}
2020-03-10 02:51:25 +00:00
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
if ( ! statusMessage_ . empty ( ) ) {
statusView_ - > SetText ( statusMessage_ ) ;
}
2016-07-04 00:38:29 +00:00
}
void RemoteISOConnectScreen : : ExecuteScan ( ) {
2016-07-04 03:48:27 +00:00
FindServer ( host_ , port_ ) ;
2017-03-22 07:00:52 +00:00
if ( scanAborted ) {
2016-07-04 14:32:49 +00:00
return ;
}
2016-07-04 00:38:29 +00:00
2017-02-27 20:57:46 +00:00
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
2016-07-04 03:48:27 +00:00
status_ = host_ . empty ( ) ? ScanStatus : : FAILED : ScanStatus : : FOUND ;
2016-07-04 00:38:29 +00:00
}
2016-07-04 03:48:27 +00:00
ScanStatus RemoteISOConnectScreen : : GetStatus ( ) {
2017-02-27 20:57:46 +00:00
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
2016-07-04 03:48:27 +00:00
return status_ ;
2016-07-04 00:38:29 +00:00
}
2023-12-28 14:34:40 +00:00
std : : string FormatRemoteISOUrl ( const char * host , int port , const char * subdir ) {
return StringFromFormat ( " http://%s:%d%s " , host , port , subdir ) ;
}
2016-07-04 03:48:27 +00:00
void RemoteISOConnectScreen : : ExecuteLoad ( ) {
2019-10-06 19:40:00 +00:00
std : : string subdir = RemoteSubdir ( ) ;
2023-12-28 14:34:40 +00:00
url_ = FormatRemoteISOUrl ( host_ . c_str ( ) , port_ , subdir . c_str ( ) ) ;
2021-05-09 17:06:02 +00:00
bool result = LoadGameList ( Path ( url_ ) , games_ ) ;
2017-03-22 07:00:52 +00:00
if ( scanAborted ) {
2016-07-04 14:32:49 +00:00
return ;
}
2016-07-04 03:48:27 +00:00
2019-10-06 19:40:00 +00:00
if ( result & & ! games_ . empty ( ) & & ! g_Config . bRemoteISOManual ) {
g_Config . sLastRemoteISOServer = host_ ;
g_Config . iLastRemoteISOPort = port_ ;
}
2017-02-27 20:57:46 +00:00
std : : lock_guard < std : : mutex > guard ( statusLock_ ) ;
2016-07-04 03:48:27 +00:00
status_ = result ? ScanStatus : : LOADED : ScanStatus : : FAILED ;
2016-07-04 00:38:29 +00:00
}
void RemoteISOBrowseScreen : : CreateViews ( ) {
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
auto ri = GetI18NCategory ( I18NCat : : REMOTEISO ) ;
2016-07-04 03:48:27 +00:00
2022-12-08 15:04:20 +00:00
bool vertical = UseVerticalLayout ( ) ;
2016-07-04 03:48:27 +00:00
TabHolder * leftColumn = new TabHolder ( ORIENT_HORIZONTAL , 64 , new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT ) ) ;
tabHolder_ = leftColumn ;
tabHolder_ - > SetTag ( " RemoteGames " ) ;
gameBrowsers_ . clear ( ) ;
leftColumn - > SetClip ( true ) ;
ScrollView * scrollRecentGames = new ScrollView ( ORIENT_VERTICAL , new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT ) ) ;
scrollRecentGames - > SetTag ( " RemoteGamesTab " ) ;
2024-01-18 10:55:39 +00:00
GameBrowser * tabRemoteGames = new GameBrowser ( GetRequesterToken ( ) ,
2023-12-28 14:34:40 +00:00
Path ( url_ ) , BrowseFlags : : NAVIGATE , & g_Config . bGridView1 , screenManager ( ) , " " , " " ,
2016-07-04 03:48:27 +00:00
new LinearLayoutParams ( FILL_PARENT , FILL_PARENT ) ) ;
2023-12-28 14:34:40 +00:00
tabRemoteGames - > SetHomePath ( Path ( url_ ) ) ;
2016-07-04 03:48:27 +00:00
scrollRecentGames - > Add ( tabRemoteGames ) ;
gameBrowsers_ . push_back ( tabRemoteGames ) ;
2017-08-16 21:39:30 +00:00
leftColumn - > AddTab ( ri - > T ( " Remote Server " ) , scrollRecentGames ) ;
2016-07-04 03:48:27 +00:00
tabRemoteGames - > OnChoice . Handle < MainScreen > ( this , & MainScreen : : OnGameSelectedInstant ) ;
tabRemoteGames - > OnHoldChoice . Handle < MainScreen > ( this , & MainScreen : : OnGameSelected ) ;
tabRemoteGames - > OnHighlight . Handle < MainScreen > ( this , & MainScreen : : OnGameHighlight ) ;
ViewGroup * rightColumn = new ScrollView ( ORIENT_VERTICAL ) ;
LinearLayout * rightColumnItems = new LinearLayout ( ORIENT_VERTICAL , new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT ) ) ;
rightColumnItems - > SetSpacing ( 0.0f ) ;
rightColumn - > Add ( rightColumnItems ) ;
rightColumnItems - > Add ( new Choice ( di - > T ( " Back " ) , " " , false , new AnchorLayoutParams ( 150 , WRAP_CONTENT , 10 , NONE , NONE , 10 ) ) ) - > OnClick . Handle < UIScreen > ( this , & UIScreen : : OnBack ) ;
if ( vertical ) {
root_ = new LinearLayout ( ORIENT_VERTICAL ) ;
rightColumn - > ReplaceLayoutParams ( new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT ) ) ;
leftColumn - > ReplaceLayoutParams ( new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT , 1.0 ) ) ;
root_ - > Add ( rightColumn ) ;
root_ - > Add ( leftColumn ) ;
} else {
2023-12-20 09:35:02 +00:00
Margins actionMenuMargins ( 0 , 10 , 10 , 0 ) ;
2016-07-04 03:48:27 +00:00
root_ = new LinearLayout ( ORIENT_HORIZONTAL ) ;
leftColumn - > ReplaceLayoutParams ( new LinearLayoutParams ( FILL_PARENT , WRAP_CONTENT , 1.0 ) ) ;
rightColumn - > ReplaceLayoutParams ( new LinearLayoutParams ( 300 , FILL_PARENT , actionMenuMargins ) ) ;
root_ - > Add ( leftColumn ) ;
root_ - > Add ( rightColumn ) ;
}
root_ - > SetDefaultFocusView ( tabHolder_ ) ;
upgradeBar_ = 0 ;
2016-07-04 00:38:29 +00:00
}