ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

storagewizarddialog.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "gui/storagewizarddialog.h"
00024 #include "gui/gui-manager.h"
00025 #include "gui/message.h"
00026 #include "gui/widget.h"
00027 #include "gui/widgets/edittext.h"
00028 #include "gui/widgets/scrollcontainer.h"
00029 #include "backends/cloud/cloudmanager.h"
00030 #ifdef USE_SDL_NET
00031 #include "backends/networking/sdl_net/localwebserver.h"
00032 #endif
00033 #include "common/translation.h"
00034 
00035 namespace GUI {
00036 
00037 enum {
00038     kConnectCmd = 'Cnnt',
00039     kCodeBoxCmd = 'CdBx',
00040     kOpenUrlCmd = 'OpUr',
00041     kPasteCodeCmd = 'PsCd',
00042     kStorageWizardContainerReflowCmd = 'SWCr'
00043 };
00044 
00045 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
00046     Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false) {
00047 #ifdef USE_SDL_NET
00048     _stopServerOnClose = false;
00049 #endif
00050     _backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
00051 
00052     ScrollContainerWidget *container = new ScrollContainerWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Container", kStorageWizardContainerReflowCmd);
00053     container->setTarget(this);
00054 
00055     Common::String headline = Common::String::format(_("%s Storage Connection Wizard"), CloudMan.listStorages()[_storageId].c_str());
00056     _headlineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.Headline", headline);
00057 
00058     _navigateLineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.NavigateLine", _("Navigate to the following URL:"));
00059     _urlLineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.URLLine", getUrl());
00060 
00061     _returnLine1 = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine1", _("Obtain the code from the storage, enter it"));
00062     _returnLine2 = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine2", _("in the following field and press 'Connect':"));
00063     for (uint32 i = 0; i < CODE_FIELDS; ++i)
00064         _codeWidget[i] = new EditTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.CodeBox" + Common::String::format("%d", i+1), "", 0, kCodeBoxCmd);
00065     _messageWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.MessageLine", "");
00066 
00067     // Buttons
00068     _cancelWidget = new ButtonWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.CancelButton", _("Cancel"), 0, kCloseCmd);
00069     _openUrlWidget = new ButtonWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
00070     _pasteCodeWidget = new ButtonWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.PasteCodeButton", _("Paste"), _("Pastes clipboard contents into fields"), kPasteCodeCmd);
00071     _connectWidget = new ButtonWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.ConnectButton", _("Connect"), 0, kConnectCmd);
00072 
00073     // Initialy the code is empty, so disable the connect button
00074     _connectWidget->setEnabled(false);
00075 
00076     if (Cloud::CloudManager::couldUseLocalServer()) {
00077         // hide fields and even the button if local webserver is on
00078         _returnLine1->setLabel(_("You will be directed to ScummVM's page where"));
00079         _returnLine2->setLabel(_("you should allow it to access your storage."));
00080     }
00081 
00082     _picture = new GraphicsWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.Picture");
00083 #ifndef DISABLE_FANCY_THEMES
00084     if (g_gui.theme()->supportsImages()) {
00085         _picture->useThemeTransparency(true);
00086         switch (_storageId) {
00087         case Cloud::kStorageDropboxId:
00088             _picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDropboxLogo));
00089             break;
00090         case Cloud::kStorageOneDriveId:
00091             _picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageOneDriveLogo));
00092             break;
00093         case Cloud::kStorageGoogleDriveId:
00094             _picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageGoogleDriveLogo));
00095             break;
00096         case Cloud::kStorageBoxId:
00097             _picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageBoxLogo));
00098             break;
00099         }
00100     }
00101 #endif
00102 
00103     containerWidgetsReflow();
00104 }
00105 
00106 void StorageWizardDialog::open() {
00107     Dialog::open();
00108 
00109     if (CloudMan.isWorking()) {
00110         bool doClose = true;
00111 
00112         MessageDialog alert(_("Another Storage is active. Do you want to interrupt it?"), _("Yes"), _("No"));
00113         if (alert.runModal() == GUI::kMessageOK) {
00114             if (CloudMan.isDownloading())
00115                 CloudMan.cancelDownload();
00116             if (CloudMan.isSyncing())
00117                 CloudMan.cancelSync();
00118 
00119             // I believe it still would return `true` here, but just in case
00120             if (CloudMan.isWorking()) {
00121                 MessageDialog alert2(_("Wait until current Storage finishes up and try again."));
00122                 alert2.runModal();
00123             } else {
00124                 doClose = false;
00125             }
00126         }
00127 
00128         if (doClose) {
00129             close();
00130             return;
00131         }
00132     }
00133 
00134 #ifdef USE_SDL_NET
00135     if (Cloud::CloudManager::couldUseLocalServer()) {
00136         _stopServerOnClose = !LocalServer.isRunning();
00137         LocalServer.start(true); // using "minimal mode" (no "/files", "/download", etc available)
00138         LocalServer.indexPageHandler().setTarget(this);
00139     }
00140 #endif
00141 }
00142 
00143 void StorageWizardDialog::close() {
00144 #ifdef USE_SDL_NET
00145     if (Cloud::CloudManager::couldUseLocalServer()) {
00146         if (_stopServerOnClose)
00147             LocalServer.stopOnIdle();
00148         LocalServer.indexPageHandler().setTarget(nullptr);
00149     }
00150 #endif
00151     Dialog::close();
00152 }
00153 
00154 void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00155     switch (cmd) {
00156     case kCodeBoxCmd: {
00157         Common::String code, message;
00158         uint32 correctFields = 0;
00159         for (uint32 i = 0; i < CODE_FIELDS; ++i) {
00160             Common::String subcode = _codeWidget[i]->getEditString();
00161             if (subcode.size() == 0) {
00162                 ++correctFields;
00163                 continue;
00164             }
00165             bool correct = correctChecksum(subcode);
00166             if (correct) {
00167                 code += subcode;
00168                 code.deleteLastChar();
00169                 ++correctFields;
00170             } else {
00171                 if (i == correctFields) { //first incorrect field
00172                     message += Common::String::format("#%d", i + 1);
00173                 } else {
00174                     message += Common::String::format(", #%d", i + 1);
00175                 }
00176             }
00177         }
00178 
00179         if (message.size() > 0) {
00180             Common::String messageTemplate;
00181             if (CODE_FIELDS - correctFields == 1)
00182                 messageTemplate = _("Field %s has a mistake in it.");
00183             else
00184                 messageTemplate = _("Fields %s have mistakes in them.");
00185             message = Common::String::format(messageTemplate.c_str(), message.c_str());
00186         }
00187 
00188         bool ok = false;
00189         if (correctFields == CODE_FIELDS && code.size() > 0) {
00190             //the last 3 chars must be an encoded crc16
00191             if (code.size() > 3) {
00192                 uint32 size = code.size();
00193                 uint32 gotcrc = decodeHashchar(code[size - 3]) | (decodeHashchar(code[size - 2]) << 6) | (decodeHashchar(code[size - 1]) << 12);
00194                 code.erase(size - 3);
00195                 uint32 crc = crc16(code);
00196                 ok = (crc == gotcrc);
00197             }
00198             if (ok)
00199                 message = _("All OK!");
00200             else
00201                 message = _("Invalid code");
00202         }
00203         _connectWidget->setEnabled(ok);
00204         _messageWidget->setLabel(message);
00205         break;
00206     }
00207     case kOpenUrlCmd: {
00208         if (!g_system->openUrl(getUrl())) {
00209             MessageDialog alert(_("Failed to open URL!\nPlease navigate to this page manually."));
00210             alert.runModal();
00211         }
00212         break;
00213     }
00214     case kPasteCodeCmd: {
00215         if (g_system->hasTextInClipboard()) {
00216             Common::String message = g_system->getTextFromClipboard();
00217             for (uint32 i = 0; i < CODE_FIELDS; ++i) {
00218                 if (message.empty()) break;
00219                 Common::String subcode = "";
00220                 for (uint32 j = 0; j < message.size(); ++j) {
00221                     if (message[j] == ' ') {
00222                         message.erase(0, j+1);
00223                         break;
00224                     }
00225                     subcode += message[j];
00226                     if (j+1 == message.size()) {
00227                         message = "";
00228                         break;
00229                     }
00230                 }
00231                 _codeWidget[i]->setEditString(subcode);
00232             }
00233             handleCommand(sender, kCodeBoxCmd, data);
00234             g_gui.scheduleTopDialogRedraw();
00235         }
00236         break;
00237     }
00238     case kConnectCmd: {
00239         Common::String code;
00240         for (uint32 i = 0; i < CODE_FIELDS; ++i) {
00241             Common::String subcode = _codeWidget[i]->getEditString();
00242             if (subcode.size() == 0)
00243                 continue;
00244             code += subcode;
00245             code.deleteLastChar();
00246         }
00247         if (code.size() > 3) {
00248             code.erase(code.size() - 3);
00249             CloudMan.connectStorage(_storageId, code);
00250             setResult(1);
00251             close();
00252         }
00253         break;
00254     }
00255 #ifdef USE_SDL_NET
00256     case kStorageCodePassedCmd:
00257         CloudMan.connectStorage(_storageId, LocalServer.indexPageHandler().code());
00258         _close = true;
00259         break;
00260 #endif
00261     case kStorageWizardContainerReflowCmd:
00262         containerWidgetsReflow();
00263         break;
00264     default:
00265         Dialog::handleCommand(sender, cmd, data);
00266     }
00267 }
00268 
00269 void StorageWizardDialog::handleTickle() {
00270     if (_close) {
00271         setResult(1);
00272         close();
00273     }
00274 
00275     Dialog::handleTickle();
00276 }
00277 
00278 void StorageWizardDialog::containerWidgetsReflow() {
00279     // contents
00280     if (_headlineWidget) _headlineWidget->setVisible(true);
00281     if (_navigateLineWidget) _navigateLineWidget->setVisible(true);
00282     if (_urlLineWidget) _urlLineWidget->setVisible(true);
00283     if (_returnLine1) _returnLine1->setVisible(true);
00284     if (_returnLine2) _returnLine2->setVisible(true);
00285 
00286     bool showFields = (!Cloud::CloudManager::couldUseLocalServer());
00287     for (uint32 i = 0; i < CODE_FIELDS; ++i)
00288         _codeWidget[i]->setVisible(showFields);
00289     _messageWidget->setVisible(showFields);
00290 
00291     // left column / first bottom row
00292     if (_picture) {
00293         _picture->setVisible(g_system->getOverlayWidth() > 320);
00294     }
00295     if (_openUrlWidget) {
00296         bool visible = g_system->hasFeature(OSystem::kFeatureOpenUrl);
00297         _openUrlWidget->setVisible(visible);
00298     }
00299     if (_pasteCodeWidget) {
00300         bool visible = showFields && g_system->hasFeature(OSystem::kFeatureClipboardSupport);
00301         _pasteCodeWidget->setVisible(visible);
00302     }
00303 
00304     // bottom row
00305     if (_cancelWidget) _cancelWidget->setVisible(true);
00306     if (_connectWidget) {
00307         _connectWidget->setVisible(showFields);
00308     }
00309 }
00310 
00311 Common::String StorageWizardDialog::getUrl() const {
00312     Common::String url = "https://www.scummvm.org/c/";
00313     switch (_storageId) {
00314     case Cloud::kStorageDropboxId:
00315         url += "db";
00316         break;
00317     case Cloud::kStorageOneDriveId:
00318         url += "od";
00319         break;
00320     case Cloud::kStorageGoogleDriveId:
00321         url += "gd";
00322         break;
00323     case Cloud::kStorageBoxId:
00324         url += "bx";
00325         break;
00326     }
00327 
00328     if (Cloud::CloudManager::couldUseLocalServer())
00329         url += "s";
00330 
00331     return url;
00332 }
00333 
00334 int StorageWizardDialog::decodeHashchar(char c) {
00335     const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";
00336     for (uint32 i = 0; i < 64; ++i)
00337         if (c == HASHCHARS[i])
00338             return i;
00339     return -1;
00340 }
00341 
00342 bool StorageWizardDialog::correctChecksum(Common::String s) {
00343     if (s.size() == 0)
00344         return false; //no last char
00345     int providedChecksum = decodeHashchar(s.lastChar());
00346     int calculatedChecksum = 0x2A; //any initial value would do, but it must equal to the one used on the page where these checksums were generated
00347     for (uint32 i = 0; i < s.size()-1; ++i) {
00348         calculatedChecksum = calculatedChecksum ^ s[i];
00349     }
00350     return providedChecksum == (calculatedChecksum % 64);
00351 }
00352 
00353 uint32 StorageWizardDialog::crc16(Common::String s) { //"CRC16_CCITT_FALSE"
00354     uint32 crc = 0xFFFF, x;
00355     for (uint32 i = 0; i < s.size(); ++i) {
00356         x = ((crc >> 8) ^ s[i]) & 0xFF;
00357         x ^= x >> 4;
00358         crc = ((crc << 8) ^ (x << 12) ^ (x << 5) ^ x) & 0xFFFF;
00359     }
00360     return crc;
00361 }
00362 
00363 } // End of namespace GUI


Generated on Sat Aug 17 2019 05:00:46 for ResidualVM by doxygen 1.7.1
curved edge   curved edge