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-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
2016-07-14 12:34:31 +06:00
# include "backends/networking/browser/openurl.h"
2016-06-15 16:38:37 +06:00
# include "common/translation.h"
2016-06-10 16:35:23 +06:00
# include "widgets/edittext.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 ' ,
kOpenUrlCmd = ' OpUr '
2016-06-09 17:00:05 +06:00
} ;
2016-06-16 15:19:13 +06:00
StorageWizardDialog : : StorageWizardDialog ( uint32 storageId ) :
2016-07-05 16:31:52 +06:00
Dialog ( " GlobalOptions_Cloud_ConnectionWizard " ) , _storageId ( storageId ) , _close ( false ) , _stopServerOnClose ( false ) {
2016-06-09 17:00:05 +06:00
_backgroundType = GUI : : ThemeEngine : : kDialogBackgroundPlain ;
Common : : String headline = Common : : String : : format ( _ ( " %s Storage Connection Wizard " ) , CloudMan . listStorages ( ) [ _storageId ] . c_str ( ) ) ;
new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.Headline " , headline ) ;
new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.NavigateLine " , _s ( " Navigate to the following URL: " ) ) ;
2016-07-14 12:34:31 +06:00
new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.URLLine " , getUrl ( ) ) ;
2016-06-09 17:00:05 +06:00
2016-06-16 21:04:10 +06:00
StaticTextWidget * returnLine1 = new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.ReturnLine1 " , _s ( " Obtain the code from the storage, enter it " ) ) ;
StaticTextWidget * returnLine2 = new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.ReturnLine2 " , _s ( " in the following field and press 'Connect': " ) ) ;
2016-06-15 00:47:41 +06:00
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i )
_codeWidget [ i ] = new EditTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.CodeBox " + Common : : String : : format ( " %d " , i + 1 ) , " " , 0 , kCodeBoxCmd ) ;
_messageWidget = new StaticTextWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.MessageLine " , " " ) ;
2016-06-09 17:00:05 +06:00
// Buttons
new ButtonWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.CancelButton " , _ ( " Cancel " ) , 0 , kCloseCmd ) ;
2016-07-14 12:34:31 +06:00
new ButtonWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton " , _ ( " Open URL " ) , 0 , kOpenUrlCmd ) ;
2016-06-15 00:47:41 +06:00
_connectWidget = new ButtonWidget ( this , " GlobalOptions_Cloud_ConnectionWizard.ConnectButton " , _ ( " Connect " ) , 0 , kConnectCmd ) ;
2016-06-16 21:04:10 +06:00
# ifdef USE_SDL_NET
// hide fields and even the button if local webserver is on
returnLine1 - > setLabel ( _s ( " You would be navigated to ScummVM's page " ) ) ;
returnLine2 - > setLabel ( _s ( " when you'd allow it to use your storage. " ) ) ;
for ( uint32 i = 0 ; i < CODE_FIELDS ; + + i )
_codeWidget [ i ] - > setVisible ( false ) ;
_messageWidget - > setVisible ( false ) ;
_connectWidget - > setVisible ( false ) ;
# endif
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 ;
MessageDialog alert ( _ ( " The other Storage is working. Do you want to interrupt it? " ) , _ ( " Yes " ) , _ ( " No " ) ) ;
if ( alert . runModal ( ) = = GUI : : kMessageOK ) {
if ( CloudMan . isDownloading ( ) ) CloudMan . cancelDownload ( ) ;
if ( CloudMan . isSyncing ( ) ) CloudMan . cancelSync ( ) ;
// 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 ( ) ;
} else
doClose = false ;
}
if ( doClose ) {
close ( ) ;
return ;
}
}
2016-06-15 16:38:37 +06:00
# ifdef USE_SDL_NET
2016-07-05 16:31:52 +06:00
_stopServerOnClose = ! LocalServer . isRunning ( ) ;
2016-06-15 16:38:37 +06:00
LocalServer . start ( ) ;
2016-06-16 15:19:13 +06:00
LocalServer . indexPageHandler ( ) . setTarget ( this ) ;
2016-06-15 16:38:37 +06:00
# endif
}
void StorageWizardDialog : : close ( ) {
# ifdef USE_SDL_NET
2016-07-05 16:31:52 +06:00
if ( _stopServerOnClose ) LocalServer . stopOnIdle ( ) ;
2016-06-16 15:19:13 +06:00
LocalServer . indexPageHandler ( ) . setTarget ( nullptr ) ;
2016-06-15 16:38:37 +06:00
# endif
Dialog : : close ( ) ;
}
2016-06-09 17:00:05 +06:00
void StorageWizardDialog : : handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) {
switch ( cmd ) {
2016-06-15 00:47:41 +06:00
case kCodeBoxCmd : {
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 ;
if ( CODE_FIELDS - correctFields = = 1 ) messageTemplate = _ ( " Field %s has a mistake in it. " ) ;
else messageTemplate = _ ( " Fields %s have mistakes in them. " ) ;
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-06-16 07:52:44 +02: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-06-15 13:31:59 +06: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 : {
if ( ! Networking : : Browser : : openUrl ( getUrl ( ) ) ) {
2016-07-14 12:44:05 +06:00
MessageDialog alert ( _ ( " Failed to open URL! \n You should navigate there manually then. " ) ) ;
alert . runModal ( ) ;
2016-07-14 12:34:31 +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 ( ) ;
if ( subcode . size ( ) = = 0 ) continue ;
code + = subcode ;
code . deleteLastChar ( ) ;
}
2016-06-15 13:31:59 +06:00
code . erase ( code . size ( ) - 3 ) ;
2016-06-15 00:47:41 +06:00
CloudMan . connectStorage ( _storageId , code ) ;
2016-06-09 17:00:05 +06:00
setResult ( 1 ) ;
close ( ) ;
break ;
2016-06-15 00:47:41 +06:00
}
2016-06-16 15:19:13 +06:00
# ifdef USE_SDL_NET
case kStorageCodePassedCmd :
CloudMan . connectStorage ( _storageId , LocalServer . indexPageHandler ( ) . code ( ) ) ;
_close = true ;
break ;
# endif
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-14 12:34:31 +06:00
Common : : String StorageWizardDialog : : getUrl ( ) const {
Common : : String url = " https://www.scummvm.org/c/ " ;
switch ( _storageId ) {
case Cloud : : kStorageDropboxId : url + = " db " ; break ;
case Cloud : : kStorageOneDriveId : url + = " od " ; break ;
case Cloud : : kStorageGoogleDriveId : url + = " gd " ; break ;
case Cloud : : kStorageBoxId : url + = " bx " ; break ;
}
# ifdef USE_SDL_NET
url + = " s " ;
# endif
return url ;
}
2016-06-15 13:31:59 +06:00
int StorageWizardDialog : : decodeHashchar ( char c ) {
const char HASHCHARS [ 65 ] = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?! " ;
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-06-15 13:40:15 +06:00
if ( s . size ( ) = = 0 ) return false ; //no last char
2016-06-15 13:31:59 +06:00
int providedChecksum = decodeHashchar ( s . lastChar ( ) ) ;
2016-06-16 12:37:44 +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-06-15 00:47:41 +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