2016-06-09 17:00:05 +06:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* 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 ; either version 2
* of the License , or ( at your option ) any later version .
*
* 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 for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
# include "gui/storagewizarddialog.h"
# include "gui/gui-manager.h"
2016-07-14 12:44:05 +06:00
# include "gui/message.h"
# include "gui/widget.h"
2016-07-25 18:27:13 +06:00
# include "gui/widgets/edittext.h"
# include "gui/widgets/scrollcontainer.h"
2016-06-09 17:00:05 +06:00
# include "backends/cloud/cloudmanager.h"
2016-06-15 16:38:37 +06:00
# ifdef USE_SDL_NET
# include "backends/networking/sdl_net/localwebserver.h"
# endif
# include "common/translation.h"
2016-06-09 17:00:05 +06:00
namespace GUI {
enum {
2016-06-10 16:35:23 +06:00
kConnectCmd = ' Cnnt ' ,
2016-07-14 12:34:31 +06:00
kCodeBoxCmd = ' CdBx ' ,
2016-07-25 16:37:44 +06:00
kOpenUrlCmd = ' OpUr ' ,
2016-07-25 18:27:13 +06:00
kPasteCodeCmd = ' PsCd ' ,
2016-07-26 11:38:53 +06:00
kStorageWizardContainerReflowCmd = ' SWCr '
2016-06-09 17:00:05 +06:00
} ;
2016-06-16 15:19:13 +06:00
StorageWizardDialog : : StorageWizardDialog ( uint32 storageId ) :
2016-07-25 13:13:47 +06:00
Dialog ( " GlobalOptions_Cloud_ConnectionWizard " ) , _storageId ( storageId ) , _close ( false ) {
# ifdef USE_SDL_NET
_stopServerOnClose = false ;
# endif
2016-06-09 17:00:05 +06:00
_backgroundType = GUI : : ThemeEngine : : kDialogBackgroundPlain ;
2016-07-25 18:27:13 +06:00
ScrollContainerWidget * container = new ScrollContainerWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.Container " , kStorageWizardContainerReflowCmd ) ;
container - > setTarget ( this ) ;
2016-06-09 17:00:05 +06:00
Common : : String headline = Common : : String : : format ( _ ( " %s Storage Connection Wizard " ) , CloudMan . listStorages ( ) [ _storageId ] . c_str ( ) ) ;
2016-07-25 18:27:13 +06:00
_headlineWidget = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.Headline " , headline ) ;
2016-07-21 11:44:36 +06:00
2016-09-04 19:53:10 +02:00
_navigateLineWidget = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.NavigateLine " , _ ( " Navigate to the following URL: " ) ) ;
2016-07-25 18:27:13 +06:00
_urlLineWidget = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.URLLine " , getUrl ( ) ) ;
2016-06-09 17:00:05 +06:00
2016-09-04 19:53:10 +02:00
_returnLine1 = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine1 " , _ ( " Obtain the code from the storage, enter it " ) ) ;
_returnLine2 = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine2 " , _ ( " in the following field and press 'Connect': " ) ) ;
2016-06-15 00:47:41 +06:00
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i )
2016-07-25 18:27:13 +06:00
_codeWidget [ i ] = new EditTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.CodeBox " + Common : : String : : format ( " %d " , i + 1 ) , " " , 0 , kCodeBoxCmd ) ;
_messageWidget = new StaticTextWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.MessageLine " , " " ) ;
2016-06-09 17:00:05 +06:00
// Buttons
2016-07-25 18:27:13 +06:00
_cancelWidget = new ButtonWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.CancelButton " , _ ( " Cancel " ) , 0 , kCloseCmd ) ;
_openUrlWidget = new ButtonWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.OpenUrlButton " , _ ( " Open URL " ) , 0 , kOpenUrlCmd ) ;
_pasteCodeWidget = new ButtonWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.PasteCodeButton " , _ ( " Paste " ) , _ ( " Pastes clipboard contents into fields " ) , kPasteCodeCmd ) ;
_connectWidget = new ButtonWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.ConnectButton " , _ ( " Connect " ) , 0 , kConnectCmd ) ;
2016-10-09 15:02:02 +02:00
2016-08-30 21:29:53 +01:00
// Initialy the code is empty, so disable the connect button
_connectWidget - > setEnabled ( false ) ;
2016-06-16 21:04:10 +06:00
2016-07-21 12:06:00 +06:00
if ( Cloud : : CloudManager : : couldUseLocalServer ( ) ) {
2016-07-20 16:36:44 +06:00
// hide fields and even the button if local webserver is on
2016-09-04 23:24:30 +01:00
_returnLine1 - > setLabel ( _ ( " You will be directed to ScummVM's page where " ) ) ;
_returnLine2 - > setLabel ( _ ( " you should allow it to access your storage. " ) ) ;
2016-07-20 16:36:44 +06:00
}
2016-10-09 15:02:02 +02:00
2016-07-25 18:27:13 +06:00
_picture = new GraphicsWidget ( container , " GlobalOptions_Cloud_ConnectionWizard_Container.Picture " ) ;
2016-07-25 16:37:44 +06:00
# ifndef DISABLE_FANCY_THEMES
2016-07-25 18:27:13 +06:00
if ( g_gui . theme ( ) - > supportsImages ( ) ) {
_picture - > useThemeTransparency ( true ) ;
2016-07-25 19:25:49 +06:00
switch ( _storageId ) {
case Cloud : : kStorageDropboxId :
_picture - > setGfx ( g_gui . theme ( ) - > getImageSurface ( ThemeEngine : : kImageDropboxLogo ) ) ;
break ;
case Cloud : : kStorageOneDriveId :
_picture - > setGfx ( g_gui . theme ( ) - > getImageSurface ( ThemeEngine : : kImageOneDriveLogo ) ) ;
break ;
case Cloud : : kStorageGoogleDriveId :
_picture - > setGfx ( g_gui . theme ( ) - > getImageSurface ( ThemeEngine : : kImageGoogleDriveLogo ) ) ;
break ;
case Cloud : : kStorageBoxId :
_picture - > setGfx ( g_gui . theme ( ) - > getImageSurface ( ThemeEngine : : kImageBoxLogo ) ) ;
break ;
}
2016-07-25 16:37:44 +06:00
}
# endif
2016-07-25 18:27:13 +06:00
containerWidgetsReflow ( ) ;
2016-06-09 17:00:05 +06:00
}
2016-06-15 16:38:37 +06:00
void StorageWizardDialog : : open ( ) {
Dialog : : open ( ) ;
2016-07-20 15:00:44 +06:00
if ( CloudMan . isWorking ( ) ) {
bool doClose = true ;
2016-09-04 23:24:30 +01:00
MessageDialog alert ( _ ( " Another Storage is active. Do you want to interrupt it? " ) , _ ( " Yes " ) , _ ( " No " ) ) ;
2016-07-20 15:00:44 +06:00
if ( alert . runModal ( ) = = GUI : : kMessageOK ) {
2016-07-22 15:48:46 +03:00
if ( CloudMan . isDownloading ( ) )
CloudMan . cancelDownload ( ) ;
if ( CloudMan . isSyncing ( ) )
CloudMan . cancelSync ( ) ;
2016-07-20 15:00:44 +06:00
// I believe it still would return `true` here, but just in case
if ( CloudMan . isWorking ( ) ) {
MessageDialog alert2 ( _ ( " Wait until current Storage finishes up and try again. " ) ) ;
alert2 . runModal ( ) ;
2016-07-22 15:48:46 +03:00
} else {
2016-07-20 15:00:44 +06:00
doClose = false ;
2016-07-22 15:48:46 +03:00
}
2016-07-20 15:00:44 +06:00
}
if ( doClose ) {
close ( ) ;
return ;
}
}
2016-07-24 11:13:20 +06:00
# ifdef USE_SDL_NET
2016-07-21 12:06:00 +06:00
if ( Cloud : : CloudManager : : couldUseLocalServer ( ) ) {
2016-07-20 16:36:44 +06:00
_stopServerOnClose = ! LocalServer . isRunning ( ) ;
2016-08-01 12:54:54 +06:00
LocalServer . start ( true ) ; // using "minimal mode" (no "/files", "/download", etc available)
2016-07-20 16:36:44 +06:00
LocalServer . indexPageHandler ( ) . setTarget ( this ) ;
}
2016-07-24 11:13:20 +06:00
# endif
2016-06-15 16:38:37 +06:00
}
void StorageWizardDialog : : close ( ) {
2016-07-24 11:13:20 +06:00
# ifdef USE_SDL_NET
2016-07-21 12:06:00 +06:00
if ( Cloud : : CloudManager : : couldUseLocalServer ( ) ) {
2016-07-22 15:48:46 +03:00
if ( _stopServerOnClose )
LocalServer . stopOnIdle ( ) ;
2016-07-20 16:36:44 +06:00
LocalServer . indexPageHandler ( ) . setTarget ( nullptr ) ;
}
2016-07-24 11:13:20 +06:00
# endif
2016-06-15 16:38:37 +06:00
Dialog : : close ( ) ;
}
2016-06-09 17:00:05 +06:00
void StorageWizardDialog : : handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) {
switch ( cmd ) {
2016-07-21 11:44:36 +06:00
case kCodeBoxCmd : {
2016-06-15 00:47:41 +06:00
Common : : String code , message ;
2016-06-18 14:34:43 +06:00
uint32 correctFields = 0 ;
2016-06-15 00:47:41 +06:00
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i ) {
Common : : String subcode = _codeWidget [ i ] - > getEditString ( ) ;
if ( subcode . size ( ) = = 0 ) {
+ + correctFields ;
continue ;
}
2016-06-16 07:52:44 +02:00
bool correct = correctChecksum ( subcode ) ;
2016-06-15 00:47:41 +06:00
if ( correct ) {
code + = subcode ;
code . deleteLastChar ( ) ;
+ + correctFields ;
} else {
if ( i = = correctFields ) { //first incorrect field
message + = Common : : String : : format ( " #%d " , i + 1 ) ;
} else {
message + = Common : : String : : format ( " , #%d " , i + 1 ) ;
}
}
}
2016-06-15 13:31:59 +06:00
2016-06-15 00:47:41 +06:00
if ( message . size ( ) > 0 ) {
Common : : String messageTemplate ;
2016-07-22 15:48:46 +03:00
if ( CODE_FIELDS - correctFields = = 1 )
messageTemplate = _ ( " Field %s has a mistake in it. " ) ;
else
messageTemplate = _ ( " Fields %s have mistakes in them. " ) ;
2016-06-15 00:47:41 +06:00
message = Common : : String : : format ( messageTemplate . c_str ( ) , message . c_str ( ) ) ;
}
2016-06-15 13:31:59 +06:00
bool ok = false ;
2016-06-15 00:47:41 +06:00
if ( correctFields = = CODE_FIELDS & & code . size ( ) > 0 ) {
2016-06-16 07:52:44 +02:00
//the last 3 chars must be an encoded crc16
2016-06-15 13:31:59 +06:00
if ( code . size ( ) > 3 ) {
uint32 size = code . size ( ) ;
2016-07-25 16:37:44 +06:00
uint32 gotcrc = decodeHashchar ( code [ size - 3 ] ) | ( decodeHashchar ( code [ size - 2 ] ) < < 6 ) | ( decodeHashchar ( code [ size - 1 ] ) < < 12 ) ;
2016-06-15 13:31:59 +06:00
code . erase ( size - 3 ) ;
uint32 crc = crc16 ( code ) ;
ok = ( crc = = gotcrc ) ;
2016-06-16 07:52:44 +02:00
}
2016-07-22 15:48:46 +03:00
if ( ok )
message = _ ( " All OK! " ) ;
else
message = _ ( " Invalid code " ) ;
2016-06-15 00:47:41 +06:00
}
2016-06-16 07:52:44 +02:00
_connectWidget - > setEnabled ( ok ) ;
2016-06-15 00:47:41 +06:00
_messageWidget - > setLabel ( message ) ;
2016-06-10 16:35:23 +06:00
break ;
2016-06-15 00:47:41 +06:00
}
2016-07-14 12:34:31 +06:00
case kOpenUrlCmd : {
2016-09-09 23:51:40 +01:00
if ( ! g_system - > openUrl ( getUrl ( ) ) ) {
2016-09-04 23:24:30 +01:00
MessageDialog alert ( _ ( " Failed to open URL! \n Please navigate to this page manually. " ) ) ;
2016-07-14 12:44:05 +06:00
alert . runModal ( ) ;
2016-07-14 12:34:31 +06:00
}
break ;
}
2016-07-25 16:37:44 +06:00
case kPasteCodeCmd : {
2016-07-26 12:21:15 +06:00
if ( g_system - > hasTextInClipboard ( ) ) {
Common : : String message = g_system - > getTextFromClipboard ( ) ;
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i ) {
if ( message . empty ( ) ) break ;
Common : : String subcode = " " ;
for ( uint32 j = 0 ; j < message . size ( ) ; + + j ) {
if ( message [ j ] = = ' ' ) {
message . erase ( 0 , j + 1 ) ;
break ;
}
subcode + = message [ j ] ;
if ( j + 1 = = message . size ( ) ) {
message = " " ;
break ;
2016-07-25 16:37:44 +06:00
}
}
2016-07-26 12:21:15 +06:00
_codeWidget [ i ] - > setEditString ( subcode ) ;
2016-07-25 16:37:44 +06:00
}
2016-07-26 12:21:15 +06:00
handleCommand ( sender , kCodeBoxCmd , data ) ;
draw ( ) ;
2016-07-25 16:37:44 +06:00
}
break ;
}
2016-06-15 00:47:41 +06:00
case kConnectCmd : {
Common : : String code ;
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i ) {
Common : : String subcode = _codeWidget [ i ] - > getEditString ( ) ;
2016-07-22 15:48:46 +03:00
if ( subcode . size ( ) = = 0 )
continue ;
2016-06-15 00:47:41 +06:00
code + = subcode ;
code . deleteLastChar ( ) ;
}
2016-08-30 21:22:59 +01:00
if ( code . size ( ) > 3 ) {
code . erase ( code . size ( ) - 3 ) ;
CloudMan . connectStorage ( _storageId , code ) ;
setResult ( 1 ) ;
close ( ) ;
}
2016-06-09 17:00:05 +06:00
break ;
2016-06-15 00:47:41 +06:00
}
2016-07-25 13:13:47 +06:00
# ifdef USE_SDL_NET
2016-06-16 15:19:13 +06:00
case kStorageCodePassedCmd :
CloudMan . connectStorage ( _storageId , LocalServer . indexPageHandler ( ) . code ( ) ) ;
2016-07-21 11:44:36 +06:00
_close = true ;
2016-06-16 15:19:13 +06:00
break ;
2016-07-25 13:13:47 +06:00
# endif
2016-07-25 18:27:13 +06:00
case kStorageWizardContainerReflowCmd :
containerWidgetsReflow ( ) ;
break ;
2016-06-09 17:00:05 +06:00
default :
Dialog : : handleCommand ( sender , cmd , data ) ;
}
}
2016-06-16 15:19:13 +06:00
void StorageWizardDialog : : handleTickle ( ) {
if ( _close ) {
setResult ( 1 ) ;
close ( ) ;
}
Dialog : : handleTickle ( ) ;
}
2016-07-25 18:27:13 +06:00
void StorageWizardDialog : : containerWidgetsReflow ( ) {
// contents
if ( _headlineWidget ) _headlineWidget - > setVisible ( true ) ;
if ( _navigateLineWidget ) _navigateLineWidget - > setVisible ( true ) ;
if ( _urlLineWidget ) _urlLineWidget - > setVisible ( true ) ;
if ( _returnLine1 ) _returnLine1 - > setVisible ( true ) ;
if ( _returnLine2 ) _returnLine2 - > setVisible ( true ) ;
bool showFields = ( ! Cloud : : CloudManager : : couldUseLocalServer ( ) ) ;
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i )
_codeWidget [ i ] - > setVisible ( showFields ) ;
_messageWidget - > setVisible ( showFields ) ;
// left column / first bottom row
if ( _picture ) {
_picture - > setVisible ( g_system - > getOverlayWidth ( ) > 320 ) ;
}
2016-09-10 07:46:52 +02:00
if ( _openUrlWidget ) {
bool visible = g_system - > hasFeature ( OSystem : : kFeatureOpenUrl ) ;
_openUrlWidget - > setVisible ( visible ) ;
}
2016-07-25 18:27:13 +06:00
if ( _pasteCodeWidget ) {
2016-07-26 12:21:15 +06:00
bool visible = showFields & & g_system - > hasFeature ( OSystem : : kFeatureClipboardSupport ) ;
2016-07-25 18:27:13 +06:00
_pasteCodeWidget - > setVisible ( visible ) ;
}
// bottom row
if ( _cancelWidget ) _cancelWidget - > setVisible ( true ) ;
if ( _connectWidget ) {
_connectWidget - > setVisible ( showFields ) ;
}
}
2016-07-14 12:34:31 +06:00
Common : : String StorageWizardDialog : : getUrl ( ) const {
Common : : String url = " https://www.scummvm.org/c/ " ;
switch ( _storageId ) {
2016-07-22 15:48:46 +03:00
case Cloud : : kStorageDropboxId :
url + = " db " ;
break ;
case Cloud : : kStorageOneDriveId :
url + = " od " ;
break ;
case Cloud : : kStorageGoogleDriveId :
url + = " gd " ;
break ;
case Cloud : : kStorageBoxId :
url + = " bx " ;
break ;
2016-07-14 12:34:31 +06:00
}
2016-07-21 11:44:36 +06:00
2016-07-22 15:48:46 +03:00
if ( Cloud : : CloudManager : : couldUseLocalServer ( ) )
url + = " s " ;
2016-07-14 12:34:31 +06:00
return url ;
}
2016-06-15 13:31:59 +06:00
int StorageWizardDialog : : decodeHashchar ( char c ) {
2016-07-21 11:44:36 +06:00
const char HASHCHARS [ 65 ] = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?! " ;
2016-06-15 13:31:59 +06:00
for ( uint32 i = 0 ; i < 64 ; + + i )
if ( c = = HASHCHARS [ i ] )
return i ;
return - 1 ;
2016-06-15 00:47:41 +06:00
}
bool StorageWizardDialog : : correctChecksum ( Common : : String s ) {
2016-07-22 15:48:46 +03:00
if ( s . size ( ) = = 0 )
return false ; //no last char
2016-06-15 13:31:59 +06:00
int providedChecksum = decodeHashchar ( s . lastChar ( ) ) ;
2016-07-21 11:44:36 +06:00
int calculatedChecksum = 0x2A ; //any initial value would do, but it must equal to the one used on the page where these checksums were generated
2016-06-15 00:47:41 +06:00
for ( uint32 i = 0 ; i < s . size ( ) - 1 ; + + i ) {
2016-06-15 13:31:59 +06:00
calculatedChecksum = calculatedChecksum ^ s [ i ] ;
2016-07-21 11:44:36 +06:00
}
2016-06-15 13:31:59 +06:00
return providedChecksum = = ( calculatedChecksum % 64 ) ;
2016-06-15 00:47:41 +06:00
}
2016-06-15 13:31:59 +06:00
uint32 StorageWizardDialog : : crc16 ( Common : : String s ) { //"CRC16_CCITT_FALSE"
uint32 crc = 0xFFFF , x ;
for ( uint32 i = 0 ; i < s . size ( ) ; + + i ) {
x = ( ( crc > > 8 ) ^ s [ i ] ) & 0xFF ;
x ^ = x > > 4 ;
2016-06-16 07:52:44 +02:00
crc = ( ( crc < < 8 ) ^ ( x < < 12 ) ^ ( x < < 5 ) ^ x ) & 0xFFFF ;
2016-06-15 13:31:59 +06:00
}
return crc ;
2016-06-15 00:47:41 +06:00
}
2016-06-09 17:00:05 +06:00
} // End of namespace GUI