[Scummvm-git-logs] scummvm master -> bfbfbd3e1a8397cc3b1c9eaff6c7754abe3ddc3d

sev- sev at scummvm.org
Tue Aug 30 14:00:41 CEST 2016


This automated email contains information about 438 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
8429c40362 CLOUD: Add SimpleJSON library as Common::JSON
79e6368b42 CLOUD: Add SimpleJSON library (module.mk hotfix)
a7fb8c72ab CLOUD: SimpleJSON refactor
2ac2816d68 CLOUD: Refactor SimpleJSON
e4e2ec390d CLOUD: Remove wcsncasecmp() usage from SimpleJSON
52240c68c7 CONFIGURE: Added detection for SDL_net
7446ffd73b CLOUD: Integrate CloudThread into OSystem
ca2eeb2214 CLOUD: Add Cloud::Manager and Cloud::Storage
6c83930126 CONFIGURE: Added libcurl detection
fade746f37 CLOUD: Add USE_CLOUD feature
14785b12d3 CONFIGURE: Fix cloud support detection
b272bba751 CLOUD: Do minor fixes
1b89e25580 CLOUD: Add first Request
6ad983bb72 CLOUD: Fix GCC static cloudThread compile error
8b585d631b CLOUD: Add CurlRequest
01abba4f1d CLOUD: Add ConnectionManager and NetworkReadStream
9c22b7cc64 CLOUD: Rewrite NetworkReadStream
03217cd5c3 CLOUD: Add CurlJsonRequest
5df8c51402 CLOUD: Fix CurlJsonRequest
e743a65636 CLOUD: Add Dropbox into CloudManager's configs
0a947618fb CLOUD: Make ConnectionManager singleton
f913675c43 CLOUD: Add ConnectionManager hotfix
17eb5f9143 CLOUD: Add complex callbacks
ca456a7241 CLOUD: Add object-oriented Callbacks
e1109c0c32 CLOUD: Add CallbackBridge
9e531e3ce7 CLOUD: Polish Callbacks
b570499164 CLOUD: Add Callback typedefs
c4b1fdc727 CLOUD: Fix Request destructor
41e65db7d0 CLOUD: Add Storage saving mechanism
a439bd4c33 CLOUD: Make StorageInfo useful
735db74b90 CLOUD: Add DropboxStorage::listDirectory sketch
e53e3d188b CLOUD: Add DropboxListDirectoryRequest
3582f6165c CLOUD: Change ISO8601 to use strchr
826a2a921c CLOUD: Add DownloadRequest stub
caaa4c5a5d CLOUD: Make DownloadRequest write to local file
5f4bbe6e9e CLOUD: Add OneDrive Storage stub
5e346ea6fb CLOUD: Add OneDrive refresh_token support
d014b5bf38 CLOUD: Fix MemoryReadWriteStream
ae8e7f39f5 CLOUD: Make download() create necessary directories
17fc40a944 CLOUD: Add AbstractFSNode::create() backends stubs
eda575a660 CLOUD: Add Requests id
62ccf1f902 CLOUD: Add RequestInfo struct
f4547f44df CLOUD: Add RequestIdPair struct
a7b28605a0 CLOUD: Change Request::handle()
b246c17850 CLOUD: Fix CurlJsonRequest to use JsonCallback
8f6bcdf55d CLOUD: Add OneDriveTokenRefresher
24007c029b CLOUD: Add OneDriveStorage::download()
83b349a033 CLOUD: Make OneDriveStorage::download() work fine
98150beb38 CLOUD: Refactor ConnectionManager/Requests system
baf37b9330 CLOUD: Remove DOOM picture
1348befe29 CLOUD: Add access to CurlRequest's Stream
dcbaeb57c4 CLOUD: Fix OneDriveTokenRefresher
6c01edc57a CLOUD: Simplify OneDriveTokenRefresher
cdf30e9d58 CLOUD: Fix posix backend compilation
0ef1cda172 CLOUD: Add FolderDownloadRequest
bc77383545 CONFIGURE: Added configure switches for enabling/desabling cloud and net libs
1dfa73b8f8 CLOUD: Fix ConnectionManager singleton warning
8aa87815a6 CLOUD: Fix "global destructor" warning
14d60e62f8 CLOUD: Fix format string warnings
827c7e43da CLOUD: Add OneDriveListDirectoryRequest
b74d7a6861 CLOUD: Fix Requests destructors
6f35fc4272 CLOUD: Add OneDriveStorage::downloadFolder()
f4dfaed19d Replace 0 constant with nullptr in getCurrentStorage()
81c34adaef Fix comment formatting
86d5b1b2e1 Remove some unnecessary blank lines
1747f8ec42 CLOUD: Add "device_id" into configuration file
c235aa29f9 CLOUD: Add SavesSyncRequest sketch
701b07adfb COMMON: Fix JSON to understand integers correctly
d917592099 CLOUD: Add DropboxUploadRequest
b9e3730ccd CLOUD: Add UploadStatus struct
a19fc52c32 CLOUD: Make DropboxUploadRequest use "/upload" too
aa987e5c52 CLOUD: Add ListDirectoryStatus struct
cc4512e50b COMMON: Add SaveFileManager::openRawFile()
af37ecca34 CLOUD: Make SavesSyncRequest work
001b417f33 CLOUD: Fix OneDriveTokenRefresher
eb63b50b7f CLOUD: Refactor Request
b39f46788a CLOUD: Add OneDriveUploadRequest
3638c8348d CLOUD: Make SavesSyncRequest work with OneDrive
13351a730d CLOUD: Add OneDrive::info()
0d0033fb6a CLOUD: Make syncSaves() common for all Storages
ca85d4482a CLOUD: Use uint64 in StorageInfo
675e7a6ed1 CLOUD: Move download methods into Storage
4e7dec5500 CLOUD: Add DropboxCreateDirectoryRequest
8cdde307f7 CLOUD: Add OneDriveCreateDirectoryRequest
06163cb8b9 CLOUD: Fix SavesSyncRequest to create saves folder
4b3a8be0b9 CLOUD: Shorten Cloud API
a66322408f CLOUD: Implement Storage's isWorking()
1f974a7a2a CLOUD: Fix ConnectionManager's destructor
b3bf532211 CLOUD: Make CloudManager singleton
00d8d23236 CLOUD: Add mutexes in Storage
2a9cf65eeb GUI: Add mode to skip drawing of button for PicButton
0281b1eba8 GUI: Added support for PNG images
762671ccd8 GUI: Added possibility to specify several state images for PicButtonWidget
53a42ececf GUI: Added new alphabitmap image type
f0c52096f3 GUI: Implemented possibility to use alphabitmaps in GraphicsWidget
4474ccf814 GUI: Implemented alphabitmap autoscale
ec7312ac13 GUI: Implemented more modes to autoscale
38114eb760 GUI: Plug NinePatch bitmaps into parser
75f9b099dc GUI: Added possibility to specify scale mode for AlphaBitmaps
94bc75ae46 GUI: Implemented centering of dialog background
1359255ea8 GUI: Added empty dialog background
c6e04845cc GUI: Switched GUI to draw on TransparentSurface
30f7556bee GUI: Added support for alphabitmaps in picbuttons
8c7a8116be GUI: Implemented test point method to GuiObject
6524a8d103 GUI: Added transparency to PicWidgets
ea80e24481 GUI: Added animation classes
a686049c46 GUI: Fix widget clipping
b02b16ab98 CLOUD: Document ConnectionManager's onDeleteCallback
da3b7bd8d9 CLOUD: Add GoogleDriveStorage
1c823b6c1d CLOUD: Fix SavesSyncRequest
7ff1f91808 GUI: Add copyRectToOSD()
9d186929e1 CLOUD: Add CloudIcon
2a15b8b280 GUI: Add clearOSD() method
bcb2a5bd8d CLOUD: Fix CloudIcon to use clearOSD()
b32c2be78d CLOUD: Fix ConnectionManager a little
1b9987ddc9 GUI: Add getOSDFormat() and make OSD 32 bpp
4874fafb15 CLOUD: Fix CloudIcon
de84701aea GUI: Separate OSD message alpha from OSD surface
135f7d09a8 CLOUD: Make CloudIcon pulsate, fade in and fade out
cec93e2c03 CLOUD: Make CloudIcon switch ConnMan's timer off
45e83d06c2 CLOUD: Fix CloudIcon
602d17f5fd CLOUD: Document CloudIcon::draw() more precisely
1bcbab7ad2 CLOUD: Add new cloudicon.png
e98b0b12ad CLOUD: Extend Storage & SavesSyncRequest
5a695040d8 CLOUD: Fix SavesSyncRequest to return right files
91f75efa99 GUI: Fix copyRectOnOSD()
69aed03c4f CLOUD: Add new CloudManager shortcuts
8d09e1a6b3 CLOUD: Update cloudicon.png
8de2862eaa CLOUD: Update syncSaves() to return SavesSyncRequest *
e7763700e2 CLOUD: Make Save/Load dialog start saves sync
e9721976aa GUI: Add SaveLoadCloudSyncProgressDialog
3db80154d6 CLOUD: Fix SaveLoadCloudSyncProgressDialog crash
0ce7be17d3 CLOUD: Make ProgressDialog display downloading progress
9eb4aad7fd CLOUD: Make DefaultSaveFileManager ignore syncing files
6c5a8f34ea CLOUD: Add SaveLoadCloudSyncProgress enum
f1a56eaf36 GUI: Show "locked" saves during sync
67789c3c16 GUI: Update SaveLoadCloudSyncProgressDialog
b614869de8 CLOUD: Minor fixes
f5cb5be393 GUI: Add SaveLoadCloudSyncProgress in ScummModern theme
f24a89e080 GUI: Fix SaveLoadCloudSyncProgressDialog
bb207ae513 CLOUD: Add GoogleDriveResolveIdRequest
b4b6ee0186 CLOUD: Add GoogleDriveCreateDirectory
d1d71afb07 CLOUD: Add GoogleDriveListDirectoryRequest
505d3764cb CLOUD: Fix GoogleDriveStorage to work with root folder
74a3eba8d6 CLOUD: Fix ConnectionManager
c968f0143c CLOUD: Make GoogleDriveResolveIdRequest case-insensitive
bf71ba9a1c CLOUD: Update GoogleDriveCreateDirectoryRequest
ab1d160ec8 ALL: Add MetaEngine::simpleSaveNames()
62a640ff44 GUI: Disable "Run in background" for "difficult" engines
f6e69b6276 CLOUD: Fix DefaultSaveFileManager again
7e6a89c141 CLOUD: Update GoogleDriveStorage and StorageFile
60add0df1b CLOUD: Update Requests to use StorageFile::id()
f0d61084da CLOUD: Update downloading in Storages
e273e3d6e8 CLOUD: Add GoogleDrive download-related requests
b29497effe CLOUD: Add GoogleDriveUploadRequest
1479d12652 CLOUD: Minor GoogleDriveUploadRequest fix
870e96eb9c CLOUD: Update CloudManager and Storage
90ae7b7337 GUI: Add Options dialog Cloud tab
4ff1ed5fe9 GUI: Add Cloud tab information labels
af9930482e CLOUD: Update CloudManager
9b15ec9989 CLOUD: Update CloudManager
e1e48968b4 GUI: Replace Cloud tab's StorageBrowser with PopUp
beb168a3a5 GUI: Add Cloud tab StorageWizardDialog
e6242b0be8 GUI: Add Refresh button in Options Cloud tab
c99b24c16d COMMON: Add String::asUint64()
6a93e8dd09 CLOUD: Add ConnMan::urlEncode()
3e6503743c CLOUD: Add Request::date()
9ee2eb4e60 GUI: Add EditText in StorageWizardDialog
a651a983dd GUI: Add warning message for game's savepath
a966de42a9 CLOUD: Fix compilation error in savesyncrequest.h
bad2ec3ef4 CLOUD: Fix initialization of NetworkReadStream
2a2beaebc5 Fix DropboxStorage::codeFlowComplete()
dbafbf2569 CLOUD: Fix getAccessToken()
c068b74f30 CLOUD: Force handling of all StorageIDs values in CloudManager::getStorageConfigName()
1403cf006c CLOUD: Make enum StorageIDs' name singular
98788a5e7d CLOUD: Remove unnecessary blank lines in switch statement
fc3e7dec1a CLOUD: Introduce kStoragePrefix in CloudManager
fd6be018a3 CLOUD: Fix include in CloudManager
bfc5cab9e8 CLOUD: Fix end of namespace comment in CloudManager
219e565c32 CLOUD: Introduce CloudConfigHelper
ca0b58513d CLOUD: Add CloudConfigHelper to module.mk
0aea8db7e1 CLOUD: Make Storage::savesSync() restart
8a84263d2b CLOUD: Do saves sync on Storage connect
a8eebbe851 CLOUD: Get rid of CloudConfigHelper, use kCloudDomain where approriate
3db4915b66 CLOUD: Add checks in StorageWizardDialog
c1ffb09fb0 CLOUD: Fix configuration handling in CloudManager
a83e91e1ca CLOUD: Update StorageWizardDialog's code check
f571f3dd28 CLOUD: Add comments for StorageWizardDialog methods
e2b3a9366e CONFIGURE: Add --with-sdlnet-prefix option
9f7bea156a CLOUD: Init SDL_Net
0af97e59bc CLOUD: Add LocalWebserver
6126435b64 CLOUD: Add Networking::Client
99c51380fd CLOUD: Add ClientState::BAD_REQUEST
13c54f6685 CLOUD: Add GetClientHandler
6e1b667dd6 CLOUD: Minor Client fix
892c1bf84c CLOUD: Add HTTP response codes in GetClientHandler
434b740f4d CLOUD: Remove a couple of unnecessary whitespaces
ceb86a0dd8 CLOUD: Clarify calculatedChecksum's initial value
3946f23d17 CLOUD: Prepare code for path handlers
733d998e6a CLOUD: Minor Client fix
5ac3adbd4f CLOUD: Add IndexPageHandler
0def9c50a7 CLOUD: Fix Client
43071c0972 CLOUD: Update LocalWebserver
5176eaba81 CLOUD: Add wwwroot
c2c2ba908f GUI: Hide StorageWizardDialog fields if server present
a56c7a3bd6 CLOUD: Update IndexPageHandler to search wwwroot.zip
5e70f64e10 CLOUD: Embed cloud icons as byte arrays
6ac69729d5 CLOUD: Add some mutexes in LocalWebserver
1addefad7e CLOUD: Use correct redirect_uris
b908b286b9 CLOUD: Fix "signed/unsigned integers" warning
04cef8928c CLOUD: Fix "type qualifiers ignored" warning
39eb76f8c2 CLOUD: Fix "zero-length format string" warning
caa53d9f6c CLOUD: Fix "-Wconversion-null"
8484273f36 CLOUD: Fix "-Wcast-qual"
81106b0444 CLOUD: Update local server's style.css
aee713141b CLOUD: Make OutSaveFile start saves sync
65e87c6c70 CLOUD: Update save's timestamp on rewrite
2d3cfffa84 KYRA: Fix openSaveForWriting() to return OutSaveFile
4c381dafa3 CLOUD: Delete the incomplete file (when downloading)
cff183536b CLOUD: Fix crash on exiting ScummVM while ConnMan is active
fa3ea83165 CLOUD: Fix some warnings
f3a392359b CLOUD: Fix finishSuccess() warning
acce1c89ab CLOUD: Fix saves sync
97c0bbd238 GUI: Add DownloadDialog sketch
72b82bd2aa GUI: Add RemoteBrowserDialog
e388accda3 GUI: Fix "Go up"
4aa8e23ea2 GUI: Make RemoteBrowser show "Loading..."
73bb2e20af GUI: Clean up in RemoteBrowser
51a7232c73 GUI: Fix RemoteBrowser Request handling
6faf2c2617 GUI: Add RemoteBrowser parent directories remembering
a37c639986 CLOUD: Make Google Drive sort files list
8972f28bc1 GUI: Add RemoteBrowser file list sorting
c32d6fa047 GUI: Add error message in RemoteBrowser
d776b53971 GUI: Use RemoteBrowser's result in DownloadDialog
dc0a956172 CLOUD: Add CloudManager::downloadFolder()
10250af251 CLOUD: Fix CloudManager's methods
71a326493b GUI: Initiate download in DownloadDialog
b8ee9d4e7d CLOUD: Add FolderDownload-related methods in Storage
ddb1a6ccb6 GUI: Upgrade DownloadDialog
458bfcec79 GUI: Fix DownloadDialog path creation
052d8bf0ae GUI: Forbid using download directory in "Add Game"
659dcd9702 GUI: Fix SaveLoadDialog
1cfdb96616 CLOUD: Fix FolderDownloadRequest
a5765a339e GUI: Update DownloadDialog
2284333b1c GUI: Add lowres support for DownloadDialog's button
ad069f442c GUI: Add "Run server" button in Cloud tab
81c85b6651 CLOUD: Fix SaveLoadDialogs to check USE_CLOUD
211d9ed5f6 GUI: Fix Options' Cloud tab reflowing
3da38ca60b CLOUD: Replace USE_CLOUD with USE_LIBCURL
e601c39802 CLOUD: Make "Run server" button active
eae57728d1 CLOUD: Resolve local machine's IP
c409d29f66 CLOUD: Add "/files" handler
e47b6c04b3 CLOUD: Make "/files" list directories
627bda9d82 COMMON: Add replace(String, String, String)
48e3fff6bc CLOUD: Refactor LocalWebserver
ab4361a76b CLOUD: Make "/create" work
8a9d126152 CLOUD: Move "/create" to separate Handler
b40bfaa046 CLOUD: Add "/download" handler
89a1a54982 CLOUD: Update GetClientHandler's buffer
4d88f51de9 CLOUD: Add query parameters URL encoding
79b39bf0d0 CLOUD: Add Reader sketch
bb67b81d04 CLOUD: Update Reader to support pausing
589b4cd457 CLOUD: Use "multipart/form-data" in upload form
f91bb39192 CLOUD: Use Reader in Client
c82ed40fdd CLOUD: Move method/path/etc info in Reader
83957c9666 CLOUD: Determine file's name in POST
80cc3469e7 CLOUD: Add Reader::readOneByteInStream()
bca05b2720 CLOUD: Save files fields into temp files
b69cc3effb CLOUD: Update Reader to delete temp files
f3ee9e3272 CLOUD: Fix minor Reader-related bugs
f0fc18d2ee CLOUD: Add UploadFileHandler
eaa5fb1759 CLOUD: Put "/upload" "path" parameter in the URL
a424ebbc28 CLOUD: Fix quotes encoding
e4bb7c4e75 CLOUD: Add UploadFileClientHandler
abae5c4371 CLOUD: Cleanup in UploadFileHandler
7fcdcc10cb CLOUD: Cleanup in UploadFileClientHandler
d1b5a64020 CLOUD: Cleanup in Reader and Client
34dd84f429 CLOUD: More cleanup in Client
f1830645d0 CLOUD: Cleanup in LocalWebserver
36b0069e95 CLOUD: Cleanup in Handlers
30430b379f CLOUD: Fix Client's buffer
35b2471290 CLOUD: Fix '\' encoding back
12518ed0bf CLOUD: Fix gradient on LocalWebserver's pages
3064b44b92 CLOUD: Switch to "multiple" files uploading
d5753a4847 CLOUD: Add auto-detect for downloaded game
ee3ce47606 GUI: Use Container in the Cloud tab
9975307caf GUI: Fix Container's visibility issue
f01402f4d8 GUI: Remove unnecessary DownloadDialog's flag
2f5138f795 GUI: Minor Container fixes
29e6020574 CLOUD: Update "/files" hardcoded response template
389c669a47 CLOUD: Add "directory" form for webserver "/upload"
5c60cd14c2 CLOUD: Add LocalWebserver::resolveAddress()
d795c77ef5 GUI: Fix DownloadDialog detection
03f33be54c GUI: Fix Options Cloud tab widgets visibility
b37b392fa0 CLOUD: Add BoxStorage sketch
0a43dad629 CLOUD: Redirect to "/files" from "/"
85f1ce8ece CLOUD: Add BoxTokenRefresher and BoxStorage::info()
eb269e137f CLOUD: Add BoxListDirectoryByIdRequest
e0a6b2135d CLOUD: Add BoxListDirectoryRequest
2b3caf1efa CLOUD: Add IdStorage
d943d7c3a8 CLOUD: Add IdCreateDirectoryRequest
34ee1d29d5 CLOUD: Fix Storage::streamFile()
19ae61dffc CLOUD: Add IdDownloadRequest and IdStreamFileRequest
16ed625dfe CLOUD: Remove BoxStorage::streamFileById debug() call
db72fa34d6 CLOUD: Update NetworkReadStream and CurlRequest
d96cdacb38 CLOUD: Add BoxUploadRequest
5cbb3e8705 CLOUD: Add Storage::uploadStreamSupported()
0b5bd18d85 CLOUD: Update GoogleDriveStorage
b4e9e35e07 CLOUD: Cleanup in Storages
1a53dccf51 CLOUD: Update DownloadRequest
e25338ec24 CLOUD: Update CurlJsonRequest
dfd68306de CLOUD: Upgrade FolderDownloadRequest::getProgress()
990dee3c4f CLOUD: Add Networking::Browser::openUrl() sketch
2ae438327b GUI: Add "Open URL" button in StorageWizardDialog
33ca8d485c GUI: Fix StorageWizardDialog
d8a43cf290 CLOUD: Add openUrl() for POSIX
7951a2ea16 CLOUD: Rename _files to _pendingFiles in FolderDownloadRequest
cdf8ab7949 GUI: Change 'OK' to 'Hide' on close button of DownloadDialog
d863dad055 CLOUD: Fix FolderDownloadRequest::getProgress()
ca33c0a0a8 CLOUD: Fix FolderDownloadRequest
0ca7917093 CLOUD: Update FolderDownloadRequest
479c76bbd2 CLOUD: Fix IdDownloadRequest
85adefdb86 CLOUD: Update FolderDownloadRequest::getProgress()
c431ae6d84 CLOUD: Calculate FolderDownload download speed
1b56f59add GUI: Update DownloadDialog
626d85ea49 CLOUD: Fix module.mk for openurl-default.o
06ccfd4b9a CLOUD: Add icons in "/files" list
40dfb0b4f1 GUI: Fix Cloud-related dialogs a little
ded8cdf0a0 CLOUD: Add openurl-android.cpp
6c0f491c4f CLOUD: Add "Index of" label in server's "/files"
75fbecf616 GUI: Add Ctrl+V handling in EditableWidget
4a0a5af52e TESTBED: Add first Cloud tests
721ee9527e TESTBED: Fix CloudTests to ask users whether to wait
6d227a437a TESTBED: Add CloudTests::testUploading()
7a34abe39e TESTBED: Add CloudTests::testDownloading()
4e27251356 TESTBED: Add CloudTests::testFolderDownloading()
04888cf454 TESTBED: Add CloudTests::testSavesSync()
18fc113aa9 CLOUD: Add URL opening for OS X
dcf9041926 TESTBED: Fix CloudTests
b616cc3d57 CLOUD: Fix indentation in openurl-osx.cpp
830c7b578c TESTBED: Add Webserver test suite
817d831255 TESTBED: Add more Webserver tests
737dc91e64 TESTBED: Add openUrl test in MiscTests
8de753b68a GUI: Add Cloud-related dialogs in classic theme's stx
5163fb4e02 CLOUD: Add ListAjaxHandler
09ae2f7593 CLOUD: Add "/filesAJAX" sketch
6442dad710 CLOUD: Add "breadcrumbs" in "/filesAJAX"
cd6d45ecf8 CLOUD: Minor "/filesAJAX" fix
e6caa482e1 CLOUD: Add messages in "/filesAJAX"
da229dd84c CLOUD: Add "ajax" parameter for "/create" and "/upload"
36b381e411 CLOUD: Make "/create" support AJAX
55568d757c CLOUD: Move Dropbox to API v2
d0c54cdd64 CLOUD: Fix DropboxCreateDirectoryRequest
0c1c274abd CLOUD: Fix OneDriveUploadRequest
a13e03e988 CLOUD: Add Networking::Connection::isLimited()
8e9d106658 CLOUD: Fix makefile
a11b004b6b GUI: Show warning in DownloadDialog
6753c18d6f CLOUD: Add default SavesSync callbacks
ab0f2d1a03 CLOUD: Fix OneDriveUploadRequest
1d78d20fcf CLOUD: Fix Dropbox and Google Drive UploadRequests
63311bac26 GUI: Add error callback in Options' Cloud tab
e833c8f65c CLOUD: Add OSD warning when can't start LocalWebserver
0200694dd0 CLOUD: Fix backends/module.mk
f743b31963 CLOUD: Fix CloudManager::connectStorage() memory leak
b1264df120 CLOUD: Check whether Storage is working when replacing it
39865e6e6c CLOUD: Add port override for LocalWebserver
85f4c69fc9 CLOUD: Update StorageWizardDialog
52503a2713 GUI: Add "Clear port" button in Cloud tab
c7fe842f9a GUI: Fix texts clipping
a449ddce15 CLOUD: Fix Cppcheck warnings
b180c73675 CLOUD: Do some refactoring/cleanup
01161ae7dd CLOUD: Do some refactoring/cleanup in Networking
438ba985a4 JANITORIAL: Remove spaces at the end of the line
772d8ee42b CLOUD: Fix `redirect_uri` selection code
9254df2d96 CLOUD: Fix code formatting
efebb5b90d CLOUD: Remove DropboxStorage::remove()
8c62993769 CLOUD: Remove remove() from BoxStorage
bd8f2ed825 CLOUD: Fix some TODOs in CloudManager
5f9beb76cd CLOUD: Fix HTTP response code TODOs
a30d0d1994 CLOUD: Update DownloadRequest's TODO
758f46ddf0 CLOUD: Fix FolderDownloadRequest TODO
409dd27e76 GUI: Regenerate themes
a65682a828 GUI: Fix warnings
e114d1a697 GUI: Fix format warning
876b8616af CLOUD: Fix format warning
7c9912e3d8 CLOUD: Fix IndexPageHandler warning
43c940c985 CLOUD: Updated BoxListDirectoryByIdRequest
cccfe7c247 CLOUD: Update BoxListDirectoryByIdRequest
d57fca4665 CLOUD: JANITORIAL: Fix code formatting
eb268cd14f CLOUD: Fix warning
53aa0c46f1 GUI: JANITORIAL: Fix code formatting
f95073f008 CLOUD: JANITORIAL: More whitespace fixes
f3959e1401 CLOUD: Upload ListDirectory Requests
d57e0c89b5 CLOUD: #define all OAuth2/API-related URLs
15c6772ff7 ALL: Fix debug, warning and error usage
c9b819b577 GUI: Make Options dialog stop LocalServer on close
86f7b75dd7 GUI: Fix SDL_Net-related errors
b8fae56c67 TESTBED: Fix a few Cloud warnings
b665fc933d ALL: Make simpleSaveNames() a MetaEngineFeature
c74ba4652d GUI: Add "Paste" button in StorageWizardDialog
f39b6ed4ac GUI: Add Container in StorageWizardDialog
091ff83ed6 GUI: Add Storage providers logos
527ab4cdf6 GUI: Fix StorageWizardDialog warning
b9bba9bd4b ALL: Move Clipboard support to OSystem
6dd10f3a68 CLOUD: Add KEY/SECRET override code
0b97aff866 CLOUD: Minor TODO fix
46dda5fce0 CLOUD: Update NetworkReadStream
2c34864a06 CLOUD: Fix UploadFileClientHandler
9d96d40b3d CLOUD: Add JSON-related checks in BoxStorage
166d1121e5 CLOUD: Update TokenRefreshers
a2e0199727 CLOUD: Update BoxUploadRequest
6be736b5ed CLOUD: Update Dropbox Requests
364c74df93 CLOUD: Update DropboxStorage
b3aa9f663f CLOUD: Update DropboxUploadRequest
a381e06fda CLOUD: Update GoogleDriveStorage
d34b9b91ad CLOUD: Update GoogleDriveUploadRequest
fc8e29d583 CLOUD: Update OneDrive
d5aca1f4fa CLOUD: Update OneDriveUploadRequest
bb529e6fd0 CLOUD: Update SavesSyncRequest
37859a9203 CLOUD: Fix Requests
9665719b66 GUI: Set tooltip of local webserver button according to server state
b68bd78b44 CLOUD: Remove unused removePathHandler(), make addPathHandler() private
64b361b335 CLOUD: Move determineMimeType to ResourceHandler
0558ba423c CLOUD: Fix warnings
4f0c071e53 CLOUD: Add custom User-Agent
712410496e CLOUD: Fix UploadFileClientHandler
a1de322c18 CLOUD: Use overriden handle() instead of ClientHandlerCallback in page handlers
126fe9c845 CLOUD: Add "minimal mode" in LocalWebserver
dd9e5a95dc CLOUD: Mark places where path handling is needed
acfa1d1f10 CLOUD: Handle paths in marked places
faf849012c CLOUD: Add GUI for "rootpath" selection
8a1cca896e CLOUD: Update handlers
dc02a789b6 CLOUD: Use forbidden combinations
97cf2be7ef CLOUD: Update LocalWebserver
02a997e468 CLOUD: Remove unused includes
ea360ef8f2 TITANIC: Fix OutSaveFile usage
368f664c81 COMMON: Fix WriteStream::pos() once again
bfbfbd3e1a Merge pull request #788 from Tkachov/cloud


Commit: 8429c40362b1e4d4010f808740b79baae69c97ed
    https://github.com/scummvm/scummvm/commit/8429c40362b1e4d4010f808740b79baae69c97ed
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Add SimpleJSON library as Common::JSON

This commit also adds CloudThread class, which work() method is called
every second by TimerManager.

Right now it prints JSON examples on the console, so that's why it's
introduced with SimpleJSON library.

Changed paths:
  A backends/cloud/cloudthread.cpp
  A backends/cloud/cloudthread.h
  A common/json.cpp
  A common/json.h
    base/main.cpp



diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp
new file mode 100644
index 0000000..ac405e0
--- /dev/null
+++ b/backends/cloud/cloudthread.cpp
@@ -0,0 +1,199 @@
+#include "cloudthread.h"
+#include "../../common/debug.h"
+#include "../../common/json.h"
+
+void example1();
+void example2();
+void example3();
+
+void cloudThread(void *thread) {
+	CloudThread *cloudThread = (CloudThread *)thread;
+	cloudThread->work();
+};
+
+void CloudThread::work() {
+	if(firstTime) {		
+		firstTime = false;
+
+		example1();
+		example2();
+		example3();
+	} else {		
+	}
+}
+
+/// SimpleJSON examples:
+
+using Common::JSON;
+using Common::JSONValue;
+using Common::JSONArray;
+using Common::JSONObject;
+
+// Just some sample JSON text, feel free to change but could break demo
+const char* EXAMPLE = "\
+{ \
+	\"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \
+	\"bool_name\" : true, \
+	\"bool_second\" : FaLsE, \
+	\"null_name\" : nULl, \
+	\"negative\" : -34.276, \
+	\"sub_object\" : { \
+						\"foo\" : \"abc\", \
+						 \"bar\" : 1.35e2, \
+						 \"blah\" : { \"a\" : \"A\", \"b\" : \"B\", \"c\" : \"C\" } \
+					}, \
+	\"array_letters\" : [ \"a\", \"b\", \"c\", [ 1, 2, 3  ]  ] \
+}    ";
+
+// Example 1
+void example1()
+{
+	// Parse example data
+	JSONValue *value = JSON::Parse(EXAMPLE);
+
+	// Did it go wrong?
+	if (value == NULL)
+	{
+		debug("Example code failed to parse, did you change it?\r\n");
+	}
+	else
+	{
+		// Retrieve the main object
+		JSONObject root;
+		if (value->IsObject() == false)
+		{
+			debug("The root element is not an object, did you change the example?\r\n");
+		}
+		else
+		{
+			root = value->AsObject();
+
+			// Retrieving a string
+			if (root.find("string_name") != root.end() && root["string_name"]->IsString())
+			{
+				debug("string_name:\r\n");
+				debug("------------\r\n");
+				debug(root["string_name"]->AsString().c_str());
+				debug("\r\n\r\n");
+			}
+
+			// Retrieving a boolean
+			if (root.find("bool_second") != root.end() && root["bool_second"]->IsBool())
+			{
+				debug("bool_second:\r\n");
+				debug("------------\r\n");
+				debug(root["bool_second"]->AsBool() ? "it's true!" : "it's false!");
+				debug("\r\n\r\n");
+			}
+
+			// Retrieving an array
+			if (root.find("array_letters") != root.end() && root["array_letters"]->IsArray())
+			{
+				JSONArray array = root["array_letters"]->AsArray();
+				debug("array_letters:\r\n");
+				debug("--------------\r\n");
+				for (unsigned int i = 0; i < array.size(); i++)
+				{
+					//wstringstream output;
+					debug("[%d] => %s\r\n", i, array[i]->Stringify().c_str());
+				}
+				debug("\r\n");
+			}
+
+			// Retrieving nested object
+			if (root.find("sub_object") != root.end() && root["sub_object"]->IsObject())
+			{
+				debug("sub_object:\r\n");
+				debug("-----------\r\n");
+				debug(root["sub_object"]->Stringify().c_str());
+				debug("\r\n\r\n");
+			}
+		}
+
+		delete value;
+	}
+}
+
+// Example 3 : compact vs. prettyprint
+void example2()
+{
+	const char* EXAMPLE3 =
+		"{\
+	 \"SelectedTab\":\"Math\",\
+	 	\"Widgets\":[\
+			{\"WidgetPosition\":[0,369,800,582],\"WidgetIndex\":1,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,453,283,489],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F2.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":2,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,494,283,530],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F3.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":3,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,536,283,572],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F4.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":4,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[287,417,400,439],\"IsWidgetVisible\":-1,\"Caption\":\"\",\"EnableCaption\":0,\"Name\":\"F1.Equation\",\"CaptionPosition\":1,\"ControlWidth\":113,\"ControlHeight\":22,\"Font\":0,\"AlignText\":0,\"EnableBorder\":0,\"CaptionOnly\":0,\"Value\":\"FFT(C1)\",\"WidgetIndex\":9,\"WidgetType\":\"WidgetStaticText.1\"},\
+			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":0,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.MeasureOpGui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Measurement To Graph\",\"Value\":\"Amplitude\",\"WidgetIndex\":17,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
+			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":-1,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"FFT\",\"WidgetIndex\":25,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
+			{\"WidgetPosition\":[191,452,230,487],\"IsWidgetVisible\":-1,\"Caption\":\"F2\",\"EnableCaption\":0,\"Name\":\"F2.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"Zoom\",\"WidgetIndex\":26,\"WidgetType\":\"WidgetProcessorCombobox.1\"}\
+		]\
+	 }";
+
+	// Parse example data
+	JSONValue *value = JSON::Parse(EXAMPLE3);
+	if (value)
+	{
+		debug("-----------\r\n");
+		debug(value->Stringify().c_str());
+		debug("\r\n");
+		debug("-----------\r\n");
+		debug(value->Stringify(true).c_str());
+		debug("\r\n");
+		debug("-----------\r\n");
+	}
+
+	// Clean up
+	delete value;
+}
+
+// Example 4 : List keys in an object.
+void example3()
+{
+	// Parse the example.
+	JSONValue *main_object = JSON::Parse(EXAMPLE);
+	if (main_object == NULL)
+	{
+		debug("Example code failed to parse, did you change it?\r\n");
+	}
+	else if (!main_object->IsObject())
+	{
+		debug("Example code is not an object, did you change it?\r\n");
+		delete main_object;
+	}
+	else
+	{
+		// Print the main object.
+		debug("Main object:\r\n");
+		debug(main_object->Stringify(true).c_str());
+		debug("-----------\r\n");
+
+		// Fetch the keys and print them out.
+		Common::Array<Common::String> keys = main_object->ObjectKeys();
+
+		Common::Array<Common::String>::iterator iter = keys.begin();
+		while (iter != keys.end())
+		{
+			debug("Key: ");
+			debug((*iter).c_str());
+			debug("\r\n");
+
+			// Get the key's value.
+			JSONValue *key_value = main_object->Child((*iter).c_str());
+			if (key_value)
+			{
+				debug("Value: ");
+				debug(key_value->Stringify().c_str());
+				debug("\r\n");
+				debug("-----------\r\n");
+			}
+
+			// Next key.
+			iter++;
+		}
+
+		delete main_object;
+	}
+}
diff --git a/backends/cloud/cloudthread.h b/backends/cloud/cloudthread.h
new file mode 100644
index 0000000..dcab42f
--- /dev/null
+++ b/backends/cloud/cloudthread.h
@@ -0,0 +1,35 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_CLOUDTHREAD_H
+#define BACKENDS_CLOUD_CLOUDTHREAD_H
+
+void cloudThread(void *thread); //this one is passed to TimerManager in main()
+
+class CloudThread {
+	bool firstTime;
+public:
+	CloudThread(): firstTime(true) {};
+	void work();
+};
+
+#endif
diff --git a/base/main.cpp b/base/main.cpp
index 1667106..593179d 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -66,6 +66,7 @@
 #endif
 
 #include "backends/keymapper/keymapper.h"
+#include "backends/cloud/cloudthread.h"
 
 #if defined(_WIN32_WCE)
 #include "backends/platform/wince/CELauncherDialog.h"
@@ -475,6 +476,11 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 		dlg.runModal();
 	}
 #endif
+	
+	CloudThread thread;
+	Common::TimerManager *manager = system.getTimerManager();
+	if (!manager->installTimerProc(cloudThread, 1000000, &thread, "Cloud Thread"))
+		warning("Failed to create cloud thread");
 
 	// Unless a game was specified, show the launcher dialog
 	if (0 == ConfMan.getActiveDomain())
diff --git a/common/json.cpp b/common/json.cpp
new file mode 100644
index 0000000..f84ad70
--- /dev/null
+++ b/common/json.cpp
@@ -0,0 +1,1064 @@
+/* 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.
+*
+*/
+
+/*
+ * Files JSON.cpp and JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json
+ *
+ * Copyright (C) 2010 Mike Anchor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "JSON.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef __MINGW32__
+#define wcsncasecmp wcsnicmp
+#endif
+
+// Macros to free an array/object
+#define FREE_ARRAY(x) { JSONArray::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete *iter; } }
+#define FREE_OBJECT(x) { JSONObject::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete (*iter)._value; } }
+
+namespace Common {
+
+/**
+* Blocks off the public constructor
+*
+* @access private
+*
+*/
+JSON::JSON() {}
+
+/**
+* Parses a complete JSON encoded string (UNICODE input version)
+*
+* @access public
+*
+* @param char* data The JSON text
+*
+* @return JSONValue* Returns a JSON Value representing the root, or NULL on error
+*/
+JSONValue* JSON::Parse(const char* data) {
+	// Skip any preceding whitespace, end of data = no JSON = fail
+	if (!SkipWhitespace(&data))
+		return NULL;
+
+	// We need the start of a value here now...
+	JSONValue* value = JSONValue::Parse(&data);
+	if (value == NULL)
+		return NULL;
+
+	// Can be white space now and should be at the end of the string then...
+	if (SkipWhitespace(&data)) {
+		delete value;
+		return NULL;
+	}
+
+	// We're now at the end of the string
+	return value;
+}
+
+/**
+* Turns the passed in JSONValue into a JSON encode string
+*
+* @access public
+*
+* @param JSONValue* value The root value
+*
+* @return String Returns a JSON encoded string representation of the given value
+*/
+String JSON::Stringify(const JSONValue* value) {
+	if (value != NULL)
+		return value->Stringify();
+	else
+		return "";
+}
+
+/**
+* Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return bool Returns true if there is more data, or false if the end of the text was reached
+*/
+bool JSON::SkipWhitespace(const char** data) {
+	while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
+		(*data)++;
+
+	return **data != 0;
+}
+
+/**
+* Extracts a JSON String as defined by the spec - "<some chars>"
+* Any escaped characters are swapped out for their unescaped values
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+* @param String& str Reference to a String to receive the extracted string
+*
+* @return bool Returns true on success, false on failure
+*/
+bool JSON::ExtractString(const char** data, String& str) {
+	str = "";
+
+	while (**data != 0) {
+		// Save the char so we can change it if need be
+		char next_char = **data;
+
+		// Escaping something?
+		if (next_char == '\\') {
+			// Move over the escape char
+			(*data)++;
+
+			// Deal with the escaped char
+			switch (**data) {
+				case '"': next_char = '"';
+					break;
+				case '\\': next_char = '\\';
+					break;
+				case '/': next_char = '/';
+					break;
+				case 'b': next_char = '\b';
+					break;
+				case 'f': next_char = '\f';
+					break;
+				case 'n': next_char = '\n';
+					break;
+				case 'r': next_char = '\r';
+					break;
+				case 't': next_char = '\t';
+					break;
+				case 'u': {
+					// We need 5 chars (4 hex + the 'u') or its not valid
+					if (!simplejson_wcsnlen(*data, 5))
+						return false;
+
+					// Deal with the chars
+					next_char = 0;
+					for (int i = 0; i < 4; i++) {
+						// Do it first to move off the 'u' and leave us on the
+						// final hex digit as we move on by one later on
+						(*data)++;
+
+						next_char <<= 4;
+
+						// Parse the hex digit
+						if (**data >= '0' && **data <= '9')
+							next_char |= (**data - '0');
+						else if (**data >= 'A' && **data <= 'F')
+							next_char |= (10 + (**data - 'A'));
+						else if (**data >= 'a' && **data <= 'f')
+							next_char |= (10 + (**data - 'a'));
+						else {
+							// Invalid hex digit = invalid JSON
+							return false;
+						}
+					}
+					break;
+				}
+
+					// By the spec, only the above cases are allowed
+				default:
+					return false;
+			}
+		}
+
+		// End of the string?
+		else if (next_char == '"') {
+			(*data)++;
+			//str.reserve(); // Remove unused capacity //TODO
+			return true;
+		}
+
+		// Disallowed char?
+		else if (next_char < ' ' && next_char != '\t') {
+			// SPEC Violation: Allow tabs due to real world cases
+			return false;
+		}
+
+		// Add the next char
+		str += next_char;
+
+		// Move on
+		(*data)++;
+	}
+
+	// If we're here, the string ended incorrectly
+	return false;
+}
+
+/**
+* Parses some text as though it is an integer
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return double Returns the double value of the number found
+*/
+double JSON::ParseInt(const char** data) {
+	double integer = 0;
+	while (**data != 0 && **data >= '0' && **data <= '9')
+		integer = integer * 10 + (*(*data)++ - '0');
+
+	return integer;
+}
+
+/**
+* Parses some text as though it is a decimal
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return double Returns the double value of the decimal found
+*/
+double JSON::ParseDecimal(const char** data) {
+	double decimal = 0.0;
+	double factor = 0.1;
+	while (**data != 0 && **data >= '0' && **data <= '9') {
+		int digit = (*(*data)++ - '0');
+		decimal = decimal + digit * factor;
+		factor *= 0.1;
+	}
+	return decimal;
+}
+
+/**
+* Parses a JSON encoded value to a JSONValue object
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the data
+*
+* @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error
+*/
+JSONValue* JSONValue::Parse(const char** data) {
+	// Is it a string?
+	if (**data == '"') {
+		String str;
+		if (!JSON::ExtractString(&(++(*data)), str))
+			return NULL;
+		else
+			return new JSONValue(str);
+	}
+
+	// Is it a boolean?
+	else if ((simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, "true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && wcsncasecmp(*data, "false", 5) == 0)) {
+		bool value = wcsncasecmp(*data, "true", 4) == 0;
+		(*data) += value ? 4 : 5;
+		return new JSONValue(value);
+	}
+
+	// Is it a null?
+	else if (simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, "null", 4) == 0) {
+		(*data) += 4;
+		return new JSONValue();
+	}
+
+	// Is it a number?
+	else if (**data == '-' || (**data >= '0' && **data <= '9')) {
+		// Negative?
+		bool neg = **data == '-';
+		if (neg) (*data)++;
+
+		double number = 0.0;
+
+		// Parse the whole part of the number - only if it wasn't 0
+		if (**data == '0')
+			(*data)++;
+		else if (**data >= '1' && **data <= '9')
+			number = JSON::ParseInt(data);
+		else
+			return NULL;
+
+		// Could be a decimal now...
+		if (**data == '.') {
+			(*data)++;
+
+			// Not get any digits?
+			if (!(**data >= '0' && **data <= '9'))
+				return NULL;
+
+			// Find the decimal and sort the decimal place out
+			// Use ParseDecimal as ParseInt won't work with decimals less than 0.1
+			// thanks to Javier Abadia for the report & fix
+			double decimal = JSON::ParseDecimal(data);
+
+			// Save the number
+			number += decimal;
+		}
+
+		// Could be an exponent now...
+		if (**data == 'E' || **data == 'e') {
+			(*data)++;
+
+			// Check signage of expo
+			bool neg_expo = false;
+			if (**data == '-' || **data == '+') {
+				neg_expo = **data == '-';
+				(*data)++;
+			}
+
+			// Not get any digits?
+			if (!(**data >= '0' && **data <= '9'))
+				return NULL;
+
+			// Sort the expo out
+			double expo = JSON::ParseInt(data);
+			for (double i = 0.0; i < expo; i++)
+				number = neg_expo ? (number / 10.0) : (number * 10.0);
+		}
+
+		// Was it neg?
+		if (neg) number *= -1;
+
+		return new JSONValue(number);
+	}
+
+	// An object?
+	else if (**data == '{') {
+		JSONObject object;
+
+		(*data)++;
+
+		while (**data != 0) {
+			// Whitespace at the start?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// Special case - empty object
+			if (object.size() == 0 && **data == '}') {
+				(*data)++;
+				return new JSONValue(object);
+			}
+
+			// We want a string now...
+			String name;
+			if (!JSON::ExtractString(&(++(*data)), name)) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// More whitespace?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// Need a : now
+			if (*((*data)++) != ':') {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// More whitespace?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// The value is here
+			JSONValue* value = Parse(data);
+			if (value == NULL) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// Add the name:value
+			if (object.find(name) != object.end())
+				delete object[name];
+			object[name] = value;
+
+			// More whitespace?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			// End of object?
+			if (**data == '}') {
+				(*data)++;
+				return new JSONValue(object);
+			}
+
+			// Want a , now
+			if (**data != ',') {
+				FREE_OBJECT(object);
+				return NULL;
+			}
+
+			(*data)++;
+		}
+
+		// Only here if we ran out of data
+		FREE_OBJECT(object);
+		return NULL;
+	}
+
+	// An array?
+	else if (**data == '[') {
+		JSONArray array;
+
+		(*data)++;
+
+		while (**data != 0) {
+			// Whitespace at the start?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_ARRAY(array);
+				return NULL;
+			}
+
+			// Special case - empty array
+			if (array.size() == 0 && **data == ']') {
+				(*data)++;
+				return new JSONValue(array);
+			}
+
+			// Get the value
+			JSONValue* value = Parse(data);
+			if (value == NULL) {
+				FREE_ARRAY(array);
+				return NULL;
+			}
+
+			// Add the value
+			array.push_back(value);
+
+			// More whitespace?
+			if (!JSON::SkipWhitespace(data)) {
+				FREE_ARRAY(array);
+				return NULL;
+			}
+
+			// End of array?
+			if (**data == ']') {
+				(*data)++;
+				return new JSONValue(array);
+			}
+
+			// Want a , now
+			if (**data != ',') {
+				FREE_ARRAY(array);
+				return NULL;
+			}
+
+			(*data)++;
+		}
+
+		// Only here if we ran out of data
+		FREE_ARRAY(array);
+		return NULL;
+	}
+
+	// Ran out of possibilites, it's bad!
+	else {
+		return NULL;
+	}
+}
+
+/**
+* Basic constructor for creating a JSON Value of type NULL
+*
+* @access public
+*/
+JSONValue::JSONValue(/*NULL*/) {
+	type = JSONType_Null;
+}
+
+/**
+* Basic constructor for creating a JSON Value of type String
+*
+* @access public
+*
+* @param char* m_char_value The string to use as the value
+*/
+JSONValue::JSONValue(const char* m_char_value) {
+	type = JSONType_String;
+	string_value = new String(String(m_char_value));
+}
+
+/**
+* Basic constructor for creating a JSON Value of type String
+*
+* @access public
+*
+* @param String m_string_value The string to use as the value
+*/
+JSONValue::JSONValue(const String& m_string_value) {
+	type = JSONType_String;
+	string_value = new String(m_string_value);
+}
+
+/**
+* Basic constructor for creating a JSON Value of type Bool
+*
+* @access public
+*
+* @param bool m_bool_value The bool to use as the value
+*/
+JSONValue::JSONValue(bool m_bool_value) {
+	type = JSONType_Bool;
+	bool_value = m_bool_value;
+}
+
+/**
+* Basic constructor for creating a JSON Value of type Number
+*
+* @access public
+*
+* @param double m_number_value The number to use as the value
+*/
+JSONValue::JSONValue(double m_number_value) {
+	type = JSONType_Number;
+	number_value = m_number_value;
+}
+
+/**
+* Basic constructor for creating a JSON Value of type Array
+*
+* @access public
+*
+* @param JSONArray m_array_value The JSONArray to use as the value
+*/
+JSONValue::JSONValue(const JSONArray& m_array_value) {
+	type = JSONType_Array;
+	array_value = new JSONArray(m_array_value);
+}
+
+/**
+* Basic constructor for creating a JSON Value of type Object
+*
+* @access public
+*
+* @param JSONObject m_object_value The JSONObject to use as the value
+*/
+JSONValue::JSONValue(const JSONObject& m_object_value) {
+	type = JSONType_Object;
+	object_value = new JSONObject(m_object_value);
+}
+
+/**
+* Copy constructor to perform a deep copy of array / object values
+*
+* @access public
+*
+* @param JSONValue m_source The source JSONValue that is being copied
+*/
+JSONValue::JSONValue(const JSONValue& m_source) {
+	type = m_source.type;
+
+	switch (type) {
+		case JSONType_String:
+			string_value = new String(*m_source.string_value);
+			break;
+
+		case JSONType_Bool:
+			bool_value = m_source.bool_value;
+			break;
+
+		case JSONType_Number:
+			number_value = m_source.number_value;
+			break;
+
+		case JSONType_Array: {
+			JSONArray source_array = *m_source.array_value;
+			JSONArray::iterator iter;
+			array_value = new JSONArray();
+			for (iter = source_array.begin(); iter != source_array.end(); iter++)
+				array_value->push_back(new JSONValue(**iter));
+			break;
+		}
+
+		case JSONType_Object: {
+			JSONObject source_object = *m_source.object_value;
+			object_value = new JSONObject();
+			JSONObject::iterator iter;
+			for (iter = source_object.begin(); iter != source_object.end(); iter++) {
+				String name = (*iter)._key;
+				(*object_value)[name] = new JSONValue(*((*iter)._value));
+			}
+			break;
+		}
+
+		case JSONType_Null:
+			// Nothing to do.
+			break;
+	}
+}
+
+/**
+* The destructor for the JSON Value object
+* Handles deleting the objects in the array or the object value
+*
+* @access public
+*/
+JSONValue::~JSONValue() {
+	if (type == JSONType_Array) {
+		JSONArray::iterator iter;
+		for (iter = array_value->begin(); iter != array_value->end(); iter++)
+			delete *iter;
+		delete array_value;
+	}
+	else if (type == JSONType_Object) {
+		JSONObject::iterator iter;
+		for (iter = object_value->begin(); iter != object_value->end(); iter++) {
+			delete (*iter)._value;
+		}
+		delete object_value;
+	}
+	else if (type == JSONType_String) {
+		delete string_value;
+	}
+}
+
+/**
+* Checks if the value is a NULL
+*
+* @access public
+*
+* @return bool Returns true if it is a NULL value, false otherwise
+*/
+bool JSONValue::IsNull() const {
+	return type == JSONType_Null;
+}
+
+/**
+* Checks if the value is a String
+*
+* @access public
+*
+* @return bool Returns true if it is a String value, false otherwise
+*/
+bool JSONValue::IsString() const {
+	return type == JSONType_String;
+}
+
+/**
+* Checks if the value is a Bool
+*
+* @access public
+*
+* @return bool Returns true if it is a Bool value, false otherwise
+*/
+bool JSONValue::IsBool() const {
+	return type == JSONType_Bool;
+}
+
+/**
+* Checks if the value is a Number
+*
+* @access public
+*
+* @return bool Returns true if it is a Number value, false otherwise
+*/
+bool JSONValue::IsNumber() const {
+	return type == JSONType_Number;
+}
+
+/**
+* Checks if the value is an Array
+*
+* @access public
+*
+* @return bool Returns true if it is an Array value, false otherwise
+*/
+bool JSONValue::IsArray() const {
+	return type == JSONType_Array;
+}
+
+/**
+* Checks if the value is an Object
+*
+* @access public
+*
+* @return bool Returns true if it is an Object value, false otherwise
+*/
+bool JSONValue::IsObject() const {
+	return type == JSONType_Object;
+}
+
+/**
+* Retrieves the String value of this JSONValue
+* Use IsString() before using this method.
+*
+* @access public
+*
+* @return String Returns the string value
+*/
+const String& JSONValue::AsString() const {
+	return (*string_value);
+}
+
+/**
+* Retrieves the Bool value of this JSONValue
+* Use IsBool() before using this method.
+*
+* @access public
+*
+* @return bool Returns the bool value
+*/
+bool JSONValue::AsBool() const {
+	return bool_value;
+}
+
+/**
+* Retrieves the Number value of this JSONValue
+* Use IsNumber() before using this method.
+*
+* @access public
+*
+* @return double Returns the number value
+*/
+double JSONValue::AsNumber() const {
+	return number_value;
+}
+
+/**
+* Retrieves the Array value of this JSONValue
+* Use IsArray() before using this method.
+*
+* @access public
+*
+* @return JSONArray Returns the array value
+*/
+const JSONArray& JSONValue::AsArray() const {
+	return (*array_value);
+}
+
+/**
+* Retrieves the Object value of this JSONValue
+* Use IsObject() before using this method.
+*
+* @access public
+*
+* @return JSONObject Returns the object value
+*/
+const JSONObject& JSONValue::AsObject() const {
+	return (*object_value);
+}
+
+/**
+* Retrieves the number of children of this JSONValue.
+* This number will be 0 or the actual number of children
+* if IsArray() or IsObject().
+*
+* @access public
+*
+* @return The number of children.
+*/
+std::size_t JSONValue::CountChildren() const {
+	switch (type) {
+		case JSONType_Array:
+			return array_value->size();
+		case JSONType_Object:
+			return object_value->size();
+		default:
+			return 0;
+	}
+}
+
+/**
+* Checks if this JSONValue has a child at the given index.
+* Use IsArray() before using this method.
+*
+* @access public
+*
+* @return bool Returns true if the array has a value at the given index.
+*/
+bool JSONValue::HasChild(std::size_t index) const {
+	if (type == JSONType_Array) {
+		return index < array_value->size();
+	}
+	else {
+		return false;
+	}
+}
+
+/**
+* Retrieves the child of this JSONValue at the given index.
+* Use IsArray() before using this method.
+*
+* @access public
+*
+* @return JSONValue* Returns JSONValue at the given index or NULL
+*                    if it doesn't exist.
+*/
+JSONValue* JSONValue::Child(std::size_t index) {
+	if (index < array_value->size()) {
+		return (*array_value)[index];
+	}
+	else {
+		return NULL;
+	}
+}
+
+/**
+* Checks if this JSONValue has a child at the given key.
+* Use IsObject() before using this method.
+*
+* @access public
+*
+* @return bool Returns true if the object has a value at the given key.
+*/
+bool JSONValue::HasChild(const char* name) const {
+	if (type == JSONType_Object) {
+		return object_value->find(name) != object_value->end();
+	}
+	else {
+		return false;
+	}
+}
+
+/**
+* Retrieves the child of this JSONValue at the given key.
+* Use IsObject() before using this method.
+*
+* @access public
+*
+* @return JSONValue* Returns JSONValue for the given key in the object
+*                    or NULL if it doesn't exist.
+*/
+JSONValue* JSONValue::Child(const char* name) {
+	JSONObject::const_iterator it = object_value->find(name);
+	if (it != object_value->end()) {
+		return it->_value;
+	}
+	else {
+		return NULL;
+	}
+}
+
+/**
+* Retrieves the keys of the JSON Object or an empty vector
+* if this value is not an object.
+*
+* @access public
+*
+* @return std::vector<String> A vector containing the keys.
+*/
+Array<String> JSONValue::ObjectKeys() const {
+	Array<String> keys;
+
+	if (type == JSONType_Object) {
+		JSONObject::const_iterator iter = object_value->begin();
+		while (iter != object_value->end()) {
+			keys.push_back(iter->_key);
+
+			iter++;
+		}
+	}
+
+	return keys;
+}
+
+/**
+* Creates a JSON encoded string for the value with all necessary characters escaped
+*
+* @access public
+*
+* @param bool prettyprint Enable prettyprint
+*
+* @return String Returns the JSON string
+*/
+String JSONValue::Stringify(bool const prettyprint) const {
+	size_t const indentDepth = prettyprint ? 1 : 0;
+	return StringifyImpl(indentDepth);
+}
+
+
+/**
+* Creates a JSON encoded string for the value with all necessary characters escaped
+*
+* @access private
+*
+* @param size_t indentDepth The prettyprint indentation depth (0 : no prettyprint)
+*
+* @return String Returns the JSON string
+*/
+String JSONValue::StringifyImpl(size_t const indentDepth) const {
+	String ret_string;
+	size_t const indentDepth1 = indentDepth ? indentDepth + 1 : 0;
+	String const indentStr = Indent(indentDepth);
+	String const indentStr1 = Indent(indentDepth1);
+
+	switch (type) {
+		case JSONType_Null:
+			ret_string = "null";
+			break;
+
+		case JSONType_String:
+			ret_string = StringifyString(*string_value);
+			break;
+
+		case JSONType_Bool:
+			ret_string = bool_value ? "true" : "false";
+			break;
+
+		case JSONType_Number: {
+			if (isinf(number_value) || isnan(number_value))
+				ret_string = "null";
+			else {
+				char str[80];
+				sprintf(str, "%.15Lf", number_value); //ss.precision(15);
+				ret_string = str;
+			}
+			break;
+		}
+
+		case JSONType_Array: {
+			ret_string = indentDepth ? "[\n" + indentStr1 : "[";
+			JSONArray::const_iterator iter = array_value->begin();
+			while (iter != array_value->end()) {
+				ret_string += (*iter)->StringifyImpl(indentDepth1);
+
+				// Not at the end - add a separator
+				if (++iter != array_value->end())
+					ret_string += ",";
+			}
+			ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
+			break;
+		}
+
+		case JSONType_Object: {
+			ret_string = indentDepth ? "{\n" + indentStr1 : "{";
+			JSONObject::const_iterator iter = object_value->begin();
+			while (iter != object_value->end()) {
+				ret_string += StringifyString((*iter)._key);
+				ret_string += ":";
+				ret_string += (*iter)._value->StringifyImpl(indentDepth1);
+
+				// Not at the end - add a separator
+				if (++iter != object_value->end())
+					ret_string += ",";
+			}
+			ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
+			break;
+		}
+	}
+
+	return ret_string;
+}
+
+/**
+* Creates a JSON encoded string with all required fields escaped
+* Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf
+* Section 15.12.3.
+*
+* @access private
+*
+* @param String str The string that needs to have the characters escaped
+*
+* @return String Returns the JSON string
+*/
+String JSONValue::StringifyString(const String& str) {
+	String str_out = "\"";
+
+	String::const_iterator iter = str.begin();
+	while (iter != str.end()) {
+		char chr = *iter;
+
+		if (chr == '"' || chr == '\\' || chr == '/') {
+			str_out += '\\';
+			str_out += chr;
+		}
+		else if (chr == '\b') {
+			str_out += "\\b";
+		}
+		else if (chr == '\f') {
+			str_out += "\\f";
+		}
+		else if (chr == '\n') {
+			str_out += "\\n";
+		}
+		else if (chr == '\r') {
+			str_out += "\\r";
+		}
+		else if (chr == '\t') {
+			str_out += "\\t";
+		}
+		else if (chr < ' ' || chr > 126) {
+			str_out += "\\u";
+			for (int i = 0; i < 4; i++) {
+				int value = (chr >> 12) & 0xf;
+				if (value >= 0 && value <= 9)
+					str_out += (char)('0' + value);
+				else if (value >= 10 && value <= 15)
+					str_out += (char)('A' + (value - 10));
+				chr <<= 4;
+			}
+		}
+		else {
+			str_out += chr;
+		}
+
+		iter++;
+	}
+
+	str_out += "\"";
+	return str_out;
+}
+
+/**
+* Creates the indentation string for the depth given
+*
+* @access private
+*
+* @param size_t indent The prettyprint indentation depth (0 : no indentation)
+*
+* @return String Returns the string
+*/
+String JSONValue::Indent(size_t depth) {
+	const size_t indent_step = 2;
+	depth ? --depth : 0;
+	String indentStr;
+	for (int i = 0; i < depth * indent_step; ++i) indentStr += ' ';
+	return indentStr;
+}
+
+}  // End of namespace Common
diff --git a/common/json.h b/common/json.h
new file mode 100644
index 0000000..9597102
--- /dev/null
+++ b/common/json.h
@@ -0,0 +1,205 @@
+/* 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.
+*
+*/
+
+/*
+ * Files JSON.h and JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json
+ *
+ * Copyright (C) 2010 Mike Anchor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef COMMON_JSON_H
+#define COMMON_JSON_H
+
+#include "array.h"
+#include "hashmap.h"
+#include "hash-str.h"
+#include "str.h"
+
+// Win32 incompatibilities
+#if defined(WIN32) && !defined(__GNUC__)
+#define wcsncasecmp _strnicmp
+
+static inline bool isnan(double x) {
+	return x != x;
+}
+
+static inline bool isinf(double x) {
+	return !isnan(x) && isnan(x - x);
+}
+#endif
+
+// Linux compile fix - from quaker66
+#ifdef __GNUC__
+	#include <cstring>
+	#include <cstdlib>
+#endif
+
+// Mac compile fixes - from quaker66, Lion fix by dabrahams
+// Tkachov: I probably broke those
+#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L || (defined(WIN32) && defined(__GNUC__)) || defined(ANDROID)
+	#include <wctype.h>
+	#include <wchar.h>
+	
+	static inline int wcsncasecmp(const char *s1, const char *s2, size_t n)
+	{
+		int lc1  = 0;
+		int lc2  = 0;
+
+		while (n--)
+		{
+			lc1 = towlower (*s1);
+			lc2 = towlower (*s2);
+
+			if (lc1 != lc2)
+				return (lc1 - lc2);
+
+			if (!lc1)
+				return 0;
+
+			++s1;
+			++s2;
+		}
+
+		return 0;
+	}
+#endif
+
+// Simple function to check a string 's' has at least 'n' characters
+// Tkachov: that's not wchar_t anymore, though it should work for C-strings too
+static inline bool simplejson_wcsnlen(const char* s, size_t n) {
+	if (s == 0)
+		return false;
+
+	const char* save = s;
+	while (n-- > 0) {
+		if (*(save++) == 0) return false;
+	}
+
+	return true;
+}
+
+namespace Common {
+
+// Custom types
+class JSONValue;
+typedef Array<JSONValue*> JSONArray;
+typedef HashMap<String, JSONValue*> JSONObject;
+
+//JSONValue.h:
+
+class JSON;
+
+enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object };
+
+class JSONValue {
+	friend class JSON;
+
+public:
+	JSONValue(/*NULL*/);
+	JSONValue(const char* m_char_value);
+	JSONValue(const String& m_string_value);
+	JSONValue(bool m_bool_value);
+	JSONValue(double m_number_value);
+	JSONValue(const JSONArray& m_array_value);
+	JSONValue(const JSONObject& m_object_value);
+	JSONValue(const JSONValue& m_source);
+	~JSONValue();
+
+	bool IsNull() const;
+	bool IsString() const;
+	bool IsBool() const;
+	bool IsNumber() const;
+	bool IsArray() const;
+	bool IsObject() const;
+
+	const String& AsString() const;
+	bool AsBool() const;
+	double AsNumber() const;
+	const JSONArray& AsArray() const;
+	const JSONObject& AsObject() const;
+
+	std::size_t CountChildren() const;
+	bool HasChild(std::size_t index) const;
+	JSONValue* Child(std::size_t index);
+	bool HasChild(const char* name) const;
+	JSONValue* Child(const char* name);
+	Array<String> ObjectKeys() const;
+
+	String Stringify(bool const prettyprint = false) const;
+protected:
+	static JSONValue* Parse(const char** data);
+
+private:
+	static String StringifyString(const String& str);
+	String StringifyImpl(size_t const indentDepth) const;
+	static String Indent(size_t depth);
+
+	JSONType type;
+
+	union {
+		bool bool_value;
+		double number_value;
+		String* string_value;
+		JSONArray* array_value;
+		JSONObject* object_value;
+	};
+
+};
+
+//EOF JSONValue.h
+
+class JSON {
+	friend class JSONValue;
+
+public:
+	static JSONValue* Parse(const char* data);
+	static String Stringify(const JSONValue* value);
+protected:
+	static bool SkipWhitespace(const char** data);
+	static bool ExtractString(const char** data, String& str);
+	static double ParseInt(const char** data);
+	static double ParseDecimal(const char** data);
+private:
+	JSON();
+};
+
+}  // End of namespace Common
+
+#endif


Commit: 79e6368b4214936dfbc52719259c4930bb7d0eaf
    https://github.com/scummvm/scummvm/commit/79e6368b4214936dfbc52719259c4930bb7d0eaf
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Add SimpleJSON library (module.mk hotfix)

Forgot to edit those.

Changed paths:
    backends/module.mk
    common/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index 4c1ca42..fc2fb99 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -249,5 +249,11 @@ MODULE_OBJS += \
 	saves/recorder/recorder-saves.o
 endif
 
+# I don't have any define, so I'd just add my files without any
+# ifndef USE_CLOUD ?
+MODULE_OBJS += \
+	cloud/cloudthread.o
+# endif
+
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/common/module.mk b/common/module.mk
index 570040c..54aa16f 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS := \
 	iff_container.o \
 	ini-file.o \
 	installshield_cab.o \
+	json.o \
 	language.o \
 	localization.o \
 	macresman.o \


Commit: a7fb8c72ab0ca60161f5acad42774340ee08abab
    https://github.com/scummvm/scummvm/commit/a7fb8c72ab0ca60161f5acad42774340ee08abab
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: SimpleJSON refactor

Resharper + manual methods & fields renaming.

Changed paths:
    backends/cloud/cloudthread.cpp
    backends/cloud/cloudthread.h
    common/json.cpp
    common/json.h



diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp
index ac405e0..e375d6f 100644
--- a/backends/cloud/cloudthread.cpp
+++ b/backends/cloud/cloudthread.cpp
@@ -12,14 +12,13 @@ void cloudThread(void *thread) {
 };
 
 void CloudThread::work() {
-	if(firstTime) {		
-		firstTime = false;
+	if (_firstTime) {
+		_firstTime = false;
 
 		example1();
 		example2();
 		example3();
-	} else {		
-	}
+	} else { }
 }
 
 /// SimpleJSON examples:
@@ -30,7 +29,7 @@ using Common::JSONArray;
 using Common::JSONObject;
 
 // Just some sample JSON text, feel free to change but could break demo
-const char* EXAMPLE = "\
+const char *EXAMPLE = "\
 { \
 	\"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \
 	\"bool_name\" : true, \
@@ -46,66 +45,54 @@ const char* EXAMPLE = "\
 }    ";
 
 // Example 1
-void example1()
-{
+void example1() {
 	// Parse example data
-	JSONValue *value = JSON::Parse(EXAMPLE);
+	JSONValue *value = JSON::parse(EXAMPLE);
 
 	// Did it go wrong?
-	if (value == NULL)
-	{
+	if (value == NULL) {
 		debug("Example code failed to parse, did you change it?\r\n");
-	}
-	else
-	{
+	} else {
 		// Retrieve the main object
 		JSONObject root;
-		if (value->IsObject() == false)
-		{
+		if (value->isObject() == false) {
 			debug("The root element is not an object, did you change the example?\r\n");
-		}
-		else
-		{
-			root = value->AsObject();
+		} else {
+			root = value->asObject();
 
 			// Retrieving a string
-			if (root.find("string_name") != root.end() && root["string_name"]->IsString())
-			{
+			if (root.find("string_name") != root.end() && root["string_name"]->isString()) {
 				debug("string_name:\r\n");
 				debug("------------\r\n");
-				debug(root["string_name"]->AsString().c_str());
+				debug(root["string_name"]->asString().c_str());
 				debug("\r\n\r\n");
 			}
 
 			// Retrieving a boolean
-			if (root.find("bool_second") != root.end() && root["bool_second"]->IsBool())
-			{
+			if (root.find("bool_second") != root.end() && root["bool_second"]->isBool()) {
 				debug("bool_second:\r\n");
 				debug("------------\r\n");
-				debug(root["bool_second"]->AsBool() ? "it's true!" : "it's false!");
+				debug(root["bool_second"]->asBool() ? "it's true!" : "it's false!");
 				debug("\r\n\r\n");
 			}
 
 			// Retrieving an array
-			if (root.find("array_letters") != root.end() && root["array_letters"]->IsArray())
-			{
-				JSONArray array = root["array_letters"]->AsArray();
+			if (root.find("array_letters") != root.end() && root["array_letters"]->isArray()) {
+				JSONArray array = root["array_letters"]->asArray();
 				debug("array_letters:\r\n");
 				debug("--------------\r\n");
-				for (unsigned int i = 0; i < array.size(); i++)
-				{
+				for (unsigned int i = 0; i < array.size(); i++) {
 					//wstringstream output;
-					debug("[%d] => %s\r\n", i, array[i]->Stringify().c_str());
+					debug("[%d] => %s\r\n", i, array[i]->stringify().c_str());
 				}
 				debug("\r\n");
 			}
 
 			// Retrieving nested object
-			if (root.find("sub_object") != root.end() && root["sub_object"]->IsObject())
-			{
+			if (root.find("sub_object") != root.end() && root["sub_object"]->isObject()) {
 				debug("sub_object:\r\n");
 				debug("-----------\r\n");
-				debug(root["sub_object"]->Stringify().c_str());
+				debug(root["sub_object"]->stringify().c_str());
 				debug("\r\n\r\n");
 			}
 		}
@@ -115,10 +102,9 @@ void example1()
 }
 
 // Example 3 : compact vs. prettyprint
-void example2()
-{
-	const char* EXAMPLE3 =
-		"{\
+void example2() {
+	const char *EXAMPLE3 =
+			"{\
 	 \"SelectedTab\":\"Math\",\
 	 	\"Widgets\":[\
 			{\"WidgetPosition\":[0,369,800,582],\"WidgetIndex\":1,\"WidgetType\":\"WidgetCheckbox.1\"},\
@@ -133,14 +119,13 @@ void example2()
 	 }";
 
 	// Parse example data
-	JSONValue *value = JSON::Parse(EXAMPLE3);
-	if (value)
-	{
+	JSONValue *value = JSON::parse(EXAMPLE3);
+	if (value) {
 		debug("-----------\r\n");
-		debug(value->Stringify().c_str());
+		debug(value->stringify().c_str());
 		debug("\r\n");
 		debug("-----------\r\n");
-		debug(value->Stringify(true).c_str());
+		debug(value->stringify(true).c_str());
 		debug("\r\n");
 		debug("-----------\r\n");
 	}
@@ -150,42 +135,34 @@ void example2()
 }
 
 // Example 4 : List keys in an object.
-void example3()
-{
+void example3() {
 	// Parse the example.
-	JSONValue *main_object = JSON::Parse(EXAMPLE);
-	if (main_object == NULL)
-	{
+	JSONValue *main_object = JSON::parse(EXAMPLE);
+	if (main_object == NULL) {
 		debug("Example code failed to parse, did you change it?\r\n");
-	}
-	else if (!main_object->IsObject())
-	{
+	} else if (!main_object->isObject()) {
 		debug("Example code is not an object, did you change it?\r\n");
 		delete main_object;
-	}
-	else
-	{
+	} else {
 		// Print the main object.
 		debug("Main object:\r\n");
-		debug(main_object->Stringify(true).c_str());
+		debug(main_object->stringify(true).c_str());
 		debug("-----------\r\n");
 
 		// Fetch the keys and print them out.
-		Common::Array<Common::String> keys = main_object->ObjectKeys();
+		Common::Array<Common::String> keys = main_object->objectKeys();
 
 		Common::Array<Common::String>::iterator iter = keys.begin();
-		while (iter != keys.end())
-		{
+		while (iter != keys.end()) {
 			debug("Key: ");
 			debug((*iter).c_str());
 			debug("\r\n");
 
 			// Get the key's value.
-			JSONValue *key_value = main_object->Child((*iter).c_str());
-			if (key_value)
-			{
+			JSONValue *key_value = main_object->child((*iter).c_str());
+			if (key_value) {
 				debug("Value: ");
-				debug(key_value->Stringify().c_str());
+				debug(key_value->stringify().c_str());
 				debug("\r\n");
 				debug("-----------\r\n");
 			}
diff --git a/backends/cloud/cloudthread.h b/backends/cloud/cloudthread.h
index dcab42f..ce448b7 100644
--- a/backends/cloud/cloudthread.h
+++ b/backends/cloud/cloudthread.h
@@ -26,9 +26,10 @@
 void cloudThread(void *thread); //this one is passed to TimerManager in main()
 
 class CloudThread {
-	bool firstTime;
+	bool _firstTime;
 public:
-	CloudThread(): firstTime(true) {};
+	CloudThread(): _firstTime(true) {};
+
 	void work();
 };
 
diff --git a/common/json.cpp b/common/json.cpp
index f84ad70..a0bab11 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -21,28 +21,28 @@
 */
 
 /*
- * Files JSON.cpp and JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json
- *
- * Copyright (C) 2010 Mike Anchor
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
+* Files JSON.cpp and JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json
+*
+* Copyright (C) 2010 Mike Anchor
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
 
 #include "JSON.h"
 
@@ -78,18 +78,18 @@ JSON::JSON() {}
 *
 * @return JSONValue* Returns a JSON Value representing the root, or NULL on error
 */
-JSONValue* JSON::Parse(const char* data) {
+JSONValue *JSON::parse(const char *data) {
 	// Skip any preceding whitespace, end of data = no JSON = fail
-	if (!SkipWhitespace(&data))
+	if (!skipWhitespace(&data))
 		return NULL;
 
 	// We need the start of a value here now...
-	JSONValue* value = JSONValue::Parse(&data);
+	JSONValue *value = JSONValue::parse(&data);
 	if (value == NULL)
 		return NULL;
 
 	// Can be white space now and should be at the end of the string then...
-	if (SkipWhitespace(&data)) {
+	if (skipWhitespace(&data)) {
 		delete value;
 		return NULL;
 	}
@@ -107,9 +107,9 @@ JSONValue* JSON::Parse(const char* data) {
 *
 * @return String Returns a JSON encoded string representation of the given value
 */
-String JSON::Stringify(const JSONValue* value) {
+String JSON::stringify(const JSONValue *value) {
 	if (value != NULL)
-		return value->Stringify();
+		return value->stringify();
 	else
 		return "";
 }
@@ -123,7 +123,7 @@ String JSON::Stringify(const JSONValue* value) {
 *
 * @return bool Returns true if there is more data, or false if the end of the text was reached
 */
-bool JSON::SkipWhitespace(const char** data) {
+bool JSON::skipWhitespace(const char **data) {
 	while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
 		(*data)++;
 
@@ -141,7 +141,7 @@ bool JSON::SkipWhitespace(const char** data) {
 *
 * @return bool Returns true on success, false on failure
 */
-bool JSON::ExtractString(const char** data, String& str) {
+bool JSON::extractString(const char **data, String &str) {
 	str = "";
 
 	while (**data != 0) {
@@ -239,7 +239,7 @@ bool JSON::ExtractString(const char** data, String& str) {
 *
 * @return double Returns the double value of the number found
 */
-double JSON::ParseInt(const char** data) {
+double JSON::parseInt(const char **data) {
 	double integer = 0;
 	while (**data != 0 && **data >= '0' && **data <= '9')
 		integer = integer * 10 + (*(*data)++ - '0');
@@ -256,7 +256,7 @@ double JSON::ParseInt(const char** data) {
 *
 * @return double Returns the double value of the decimal found
 */
-double JSON::ParseDecimal(const char** data) {
+double JSON::parseDecimal(const char **data) {
 	double decimal = 0.0;
 	double factor = 0.1;
 	while (**data != 0 && **data >= '0' && **data <= '9') {
@@ -276,11 +276,11 @@ double JSON::ParseDecimal(const char** data) {
 *
 * @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error
 */
-JSONValue* JSONValue::Parse(const char** data) {
+JSONValue *JSONValue::parse(const char **data) {
 	// Is it a string?
 	if (**data == '"') {
 		String str;
-		if (!JSON::ExtractString(&(++(*data)), str))
+		if (!JSON::extractString(&(++(*data)), str))
 			return NULL;
 		else
 			return new JSONValue(str);
@@ -311,7 +311,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 		if (**data == '0')
 			(*data)++;
 		else if (**data >= '1' && **data <= '9')
-			number = JSON::ParseInt(data);
+			number = JSON::parseInt(data);
 		else
 			return NULL;
 
@@ -326,7 +326,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 			// Find the decimal and sort the decimal place out
 			// Use ParseDecimal as ParseInt won't work with decimals less than 0.1
 			// thanks to Javier Abadia for the report & fix
-			double decimal = JSON::ParseDecimal(data);
+			double decimal = JSON::parseDecimal(data);
 
 			// Save the number
 			number += decimal;
@@ -348,7 +348,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 				return NULL;
 
 			// Sort the expo out
-			double expo = JSON::ParseInt(data);
+			double expo = JSON::parseInt(data);
 			for (double i = 0.0; i < expo; i++)
 				number = neg_expo ? (number / 10.0) : (number * 10.0);
 		}
@@ -367,7 +367,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 
 		while (**data != 0) {
 			// Whitespace at the start?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_OBJECT(object);
 				return NULL;
 			}
@@ -380,13 +380,13 @@ JSONValue* JSONValue::Parse(const char** data) {
 
 			// We want a string now...
 			String name;
-			if (!JSON::ExtractString(&(++(*data)), name)) {
+			if (!JSON::extractString(&(++(*data)), name)) {
 				FREE_OBJECT(object);
 				return NULL;
 			}
 
 			// More whitespace?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_OBJECT(object);
 				return NULL;
 			}
@@ -398,13 +398,13 @@ JSONValue* JSONValue::Parse(const char** data) {
 			}
 
 			// More whitespace?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_OBJECT(object);
 				return NULL;
 			}
 
 			// The value is here
-			JSONValue* value = Parse(data);
+			JSONValue *value = parse(data);
 			if (value == NULL) {
 				FREE_OBJECT(object);
 				return NULL;
@@ -416,7 +416,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 			object[name] = value;
 
 			// More whitespace?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_OBJECT(object);
 				return NULL;
 			}
@@ -449,7 +449,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 
 		while (**data != 0) {
 			// Whitespace at the start?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_ARRAY(array);
 				return NULL;
 			}
@@ -461,7 +461,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 			}
 
 			// Get the value
-			JSONValue* value = Parse(data);
+			JSONValue *value = parse(data);
 			if (value == NULL) {
 				FREE_ARRAY(array);
 				return NULL;
@@ -471,7 +471,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 			array.push_back(value);
 
 			// More whitespace?
-			if (!JSON::SkipWhitespace(data)) {
+			if (!JSON::skipWhitespace(data)) {
 				FREE_ARRAY(array);
 				return NULL;
 			}
@@ -508,7 +508,7 @@ JSONValue* JSONValue::Parse(const char** data) {
 * @access public
 */
 JSONValue::JSONValue(/*NULL*/) {
-	type = JSONType_Null;
+	_type = JSONType_Null;
 }
 
 /**
@@ -518,9 +518,9 @@ JSONValue::JSONValue(/*NULL*/) {
 *
 * @param char* m_char_value The string to use as the value
 */
-JSONValue::JSONValue(const char* m_char_value) {
-	type = JSONType_String;
-	string_value = new String(String(m_char_value));
+JSONValue::JSONValue(const char *charValue) {
+	_type = JSONType_String;
+	_stringValue = new String(String(charValue));
 }
 
 /**
@@ -530,9 +530,9 @@ JSONValue::JSONValue(const char* m_char_value) {
 *
 * @param String m_string_value The string to use as the value
 */
-JSONValue::JSONValue(const String& m_string_value) {
-	type = JSONType_String;
-	string_value = new String(m_string_value);
+JSONValue::JSONValue(const String &stringValue) {
+	_type = JSONType_String;
+	_stringValue = new String(stringValue);
 }
 
 /**
@@ -542,9 +542,9 @@ JSONValue::JSONValue(const String& m_string_value) {
 *
 * @param bool m_bool_value The bool to use as the value
 */
-JSONValue::JSONValue(bool m_bool_value) {
-	type = JSONType_Bool;
-	bool_value = m_bool_value;
+JSONValue::JSONValue(bool boolValue) {
+	_type = JSONType_Bool;
+	_boolValue = boolValue;
 }
 
 /**
@@ -554,9 +554,9 @@ JSONValue::JSONValue(bool m_bool_value) {
 *
 * @param double m_number_value The number to use as the value
 */
-JSONValue::JSONValue(double m_number_value) {
-	type = JSONType_Number;
-	number_value = m_number_value;
+JSONValue::JSONValue(double numberValue) {
+	_type = JSONType_Number;
+	_numberValue = numberValue;
 }
 
 /**
@@ -566,9 +566,9 @@ JSONValue::JSONValue(double m_number_value) {
 *
 * @param JSONArray m_array_value The JSONArray to use as the value
 */
-JSONValue::JSONValue(const JSONArray& m_array_value) {
-	type = JSONType_Array;
-	array_value = new JSONArray(m_array_value);
+JSONValue::JSONValue(const JSONArray &arrayValue) {
+	_type = JSONType_Array;
+	_arrayValue = new JSONArray(arrayValue);
 }
 
 /**
@@ -578,9 +578,9 @@ JSONValue::JSONValue(const JSONArray& m_array_value) {
 *
 * @param JSONObject m_object_value The JSONObject to use as the value
 */
-JSONValue::JSONValue(const JSONObject& m_object_value) {
-	type = JSONType_Object;
-	object_value = new JSONObject(m_object_value);
+JSONValue::JSONValue(const JSONObject &objectValue) {
+	_type = JSONType_Object;
+	_objectValue = new JSONObject(objectValue);
 }
 
 /**
@@ -590,38 +590,38 @@ JSONValue::JSONValue(const JSONObject& m_object_value) {
 *
 * @param JSONValue m_source The source JSONValue that is being copied
 */
-JSONValue::JSONValue(const JSONValue& m_source) {
-	type = m_source.type;
+JSONValue::JSONValue(const JSONValue &source) {
+	_type = source._type;
 
-	switch (type) {
+	switch (_type) {
 		case JSONType_String:
-			string_value = new String(*m_source.string_value);
+			_stringValue = new String(*source._stringValue);
 			break;
 
 		case JSONType_Bool:
-			bool_value = m_source.bool_value;
+			_boolValue = source._boolValue;
 			break;
 
 		case JSONType_Number:
-			number_value = m_source.number_value;
+			_numberValue = source._numberValue;
 			break;
 
 		case JSONType_Array: {
-			JSONArray source_array = *m_source.array_value;
+			JSONArray source_array = *source._arrayValue;
 			JSONArray::iterator iter;
-			array_value = new JSONArray();
+			_arrayValue = new JSONArray();
 			for (iter = source_array.begin(); iter != source_array.end(); iter++)
-				array_value->push_back(new JSONValue(**iter));
+				_arrayValue->push_back(new JSONValue(**iter));
 			break;
 		}
 
 		case JSONType_Object: {
-			JSONObject source_object = *m_source.object_value;
-			object_value = new JSONObject();
+			JSONObject source_object = *source._objectValue;
+			_objectValue = new JSONObject();
 			JSONObject::iterator iter;
 			for (iter = source_object.begin(); iter != source_object.end(); iter++) {
 				String name = (*iter)._key;
-				(*object_value)[name] = new JSONValue(*((*iter)._value));
+				(*_objectValue)[name] = new JSONValue(*((*iter)._value));
 			}
 			break;
 		}
@@ -639,21 +639,19 @@ JSONValue::JSONValue(const JSONValue& m_source) {
 * @access public
 */
 JSONValue::~JSONValue() {
-	if (type == JSONType_Array) {
+	if (_type == JSONType_Array) {
 		JSONArray::iterator iter;
-		for (iter = array_value->begin(); iter != array_value->end(); iter++)
+		for (iter = _arrayValue->begin(); iter != _arrayValue->end(); iter++)
 			delete *iter;
-		delete array_value;
-	}
-	else if (type == JSONType_Object) {
+		delete _arrayValue;
+	} else if (_type == JSONType_Object) {
 		JSONObject::iterator iter;
-		for (iter = object_value->begin(); iter != object_value->end(); iter++) {
+		for (iter = _objectValue->begin(); iter != _objectValue->end(); iter++) {
 			delete (*iter)._value;
 		}
-		delete object_value;
-	}
-	else if (type == JSONType_String) {
-		delete string_value;
+		delete _objectValue;
+	} else if (_type == JSONType_String) {
+		delete _stringValue;
 	}
 }
 
@@ -664,8 +662,8 @@ JSONValue::~JSONValue() {
 *
 * @return bool Returns true if it is a NULL value, false otherwise
 */
-bool JSONValue::IsNull() const {
-	return type == JSONType_Null;
+bool JSONValue::isNull() const {
+	return _type == JSONType_Null;
 }
 
 /**
@@ -675,8 +673,8 @@ bool JSONValue::IsNull() const {
 *
 * @return bool Returns true if it is a String value, false otherwise
 */
-bool JSONValue::IsString() const {
-	return type == JSONType_String;
+bool JSONValue::isString() const {
+	return _type == JSONType_String;
 }
 
 /**
@@ -686,8 +684,8 @@ bool JSONValue::IsString() const {
 *
 * @return bool Returns true if it is a Bool value, false otherwise
 */
-bool JSONValue::IsBool() const {
-	return type == JSONType_Bool;
+bool JSONValue::isBool() const {
+	return _type == JSONType_Bool;
 }
 
 /**
@@ -697,8 +695,8 @@ bool JSONValue::IsBool() const {
 *
 * @return bool Returns true if it is a Number value, false otherwise
 */
-bool JSONValue::IsNumber() const {
-	return type == JSONType_Number;
+bool JSONValue::isNumber() const {
+	return _type == JSONType_Number;
 }
 
 /**
@@ -708,8 +706,8 @@ bool JSONValue::IsNumber() const {
 *
 * @return bool Returns true if it is an Array value, false otherwise
 */
-bool JSONValue::IsArray() const {
-	return type == JSONType_Array;
+bool JSONValue::isArray() const {
+	return _type == JSONType_Array;
 }
 
 /**
@@ -719,85 +717,85 @@ bool JSONValue::IsArray() const {
 *
 * @return bool Returns true if it is an Object value, false otherwise
 */
-bool JSONValue::IsObject() const {
-	return type == JSONType_Object;
+bool JSONValue::isObject() const {
+	return _type == JSONType_Object;
 }
 
 /**
 * Retrieves the String value of this JSONValue
-* Use IsString() before using this method.
+* Use isString() before using this method.
 *
 * @access public
 *
 * @return String Returns the string value
 */
-const String& JSONValue::AsString() const {
-	return (*string_value);
+const String &JSONValue::asString() const {
+	return (*_stringValue);
 }
 
 /**
 * Retrieves the Bool value of this JSONValue
-* Use IsBool() before using this method.
+* Use isBool() before using this method.
 *
 * @access public
 *
 * @return bool Returns the bool value
 */
-bool JSONValue::AsBool() const {
-	return bool_value;
+bool JSONValue::asBool() const {
+	return _boolValue;
 }
 
 /**
 * Retrieves the Number value of this JSONValue
-* Use IsNumber() before using this method.
+* Use isNumber() before using this method.
 *
 * @access public
 *
 * @return double Returns the number value
 */
-double JSONValue::AsNumber() const {
-	return number_value;
+double JSONValue::asNumber() const {
+	return _numberValue;
 }
 
 /**
 * Retrieves the Array value of this JSONValue
-* Use IsArray() before using this method.
+* Use isArray() before using this method.
 *
 * @access public
 *
 * @return JSONArray Returns the array value
 */
-const JSONArray& JSONValue::AsArray() const {
-	return (*array_value);
+const JSONArray &JSONValue::asArray() const {
+	return (*_arrayValue);
 }
 
 /**
 * Retrieves the Object value of this JSONValue
-* Use IsObject() before using this method.
+* Use isObject() before using this method.
 *
 * @access public
 *
 * @return JSONObject Returns the object value
 */
-const JSONObject& JSONValue::AsObject() const {
-	return (*object_value);
+const JSONObject &JSONValue::asObject() const {
+	return (*_objectValue);
 }
 
 /**
 * Retrieves the number of children of this JSONValue.
 * This number will be 0 or the actual number of children
-* if IsArray() or IsObject().
+* if isArray() or isObject().
 *
 * @access public
 *
 * @return The number of children.
 */
-std::size_t JSONValue::CountChildren() const {
-	switch (type) {
+std::size_t JSONValue::countChildren() const {
+	switch (_type) {
 		case JSONType_Array:
-			return array_value->size();
+			return _arrayValue->size();
 		case JSONType_Object:
-			return object_value->size();
+			return _objectValue->size();
 		default:
 			return 0;
 	}
@@ -805,71 +803,67 @@ std::size_t JSONValue::CountChildren() const {
 
 /**
 * Checks if this JSONValue has a child at the given index.
-* Use IsArray() before using this method.
+* Use isArray() before using this method.
 *
 * @access public
 *
 * @return bool Returns true if the array has a value at the given index.
 */
-bool JSONValue::HasChild(std::size_t index) const {
-	if (type == JSONType_Array) {
-		return index < array_value->size();
-	}
-	else {
+bool JSONValue::hasChild(std::size_t index) const {
+	if (_type == JSONType_Array) {
+		return index < _arrayValue->size();
+	} else {
 		return false;
 	}
 }
 
 /**
 * Retrieves the child of this JSONValue at the given index.
-* Use IsArray() before using this method.
+* Use isArray() before using this method.
 *
 * @access public
 *
 * @return JSONValue* Returns JSONValue at the given index or NULL
 *                    if it doesn't exist.
 */
-JSONValue* JSONValue::Child(std::size_t index) {
-	if (index < array_value->size()) {
-		return (*array_value)[index];
-	}
-	else {
+JSONValue *JSONValue::child(std::size_t index) {
+	if (index < _arrayValue->size()) {
+		return (*_arrayValue)[index];
+	} else {
 		return NULL;
 	}
 }
 
 /**
 * Checks if this JSONValue has a child at the given key.
-* Use IsObject() before using this method.
+* Use isObject() before using this method.
 *
 * @access public
 *
 * @return bool Returns true if the object has a value at the given key.
 */
-bool JSONValue::HasChild(const char* name) const {
-	if (type == JSONType_Object) {
-		return object_value->find(name) != object_value->end();
-	}
-	else {
+bool JSONValue::hasChild(const char *name) const {
+	if (_type == JSONType_Object) {
+		return _objectValue->find(name) != _objectValue->end();
+	} else {
 		return false;
 	}
 }
 
 /**
 * Retrieves the child of this JSONValue at the given key.
-* Use IsObject() before using this method.
+* Use isObject() before using this method.
 *
 * @access public
 *
 * @return JSONValue* Returns JSONValue for the given key in the object
 *                    or NULL if it doesn't exist.
 */
-JSONValue* JSONValue::Child(const char* name) {
-	JSONObject::const_iterator it = object_value->find(name);
-	if (it != object_value->end()) {
+JSONValue *JSONValue::child(const char *name) {
+	JSONObject::const_iterator it = _objectValue->find(name);
+	if (it != _objectValue->end()) {
 		return it->_value;
-	}
-	else {
+	} else {
 		return NULL;
 	}
 }
@@ -882,12 +876,12 @@ JSONValue* JSONValue::Child(const char* name) {
 *
 * @return std::vector<String> A vector containing the keys.
 */
-Array<String> JSONValue::ObjectKeys() const {
+Array<String> JSONValue::objectKeys() const {
 	Array<String> keys;
 
-	if (type == JSONType_Object) {
-		JSONObject::const_iterator iter = object_value->begin();
-		while (iter != object_value->end()) {
+	if (_type == JSONType_Object) {
+		JSONObject::const_iterator iter = _objectValue->begin();
+		while (iter != _objectValue->end()) {
 			keys.push_back(iter->_key);
 
 			iter++;
@@ -906,9 +900,9 @@ Array<String> JSONValue::ObjectKeys() const {
 *
 * @return String Returns the JSON string
 */
-String JSONValue::Stringify(bool const prettyprint) const {
+String JSONValue::stringify(bool const prettyprint) const {
 	size_t const indentDepth = prettyprint ? 1 : 0;
-	return StringifyImpl(indentDepth);
+	return stringifyImpl(indentDepth);
 }
 
 
@@ -921,31 +915,31 @@ String JSONValue::Stringify(bool const prettyprint) const {
 *
 * @return String Returns the JSON string
 */
-String JSONValue::StringifyImpl(size_t const indentDepth) const {
+String JSONValue::stringifyImpl(size_t const indentDepth) const {
 	String ret_string;
 	size_t const indentDepth1 = indentDepth ? indentDepth + 1 : 0;
-	String const indentStr = Indent(indentDepth);
-	String const indentStr1 = Indent(indentDepth1);
+	String const indentStr = indent(indentDepth);
+	String const indentStr1 = indent(indentDepth1);
 
-	switch (type) {
+	switch (_type) {
 		case JSONType_Null:
 			ret_string = "null";
 			break;
 
 		case JSONType_String:
-			ret_string = StringifyString(*string_value);
+			ret_string = stringifyString(*_stringValue);
 			break;
 
 		case JSONType_Bool:
-			ret_string = bool_value ? "true" : "false";
+			ret_string = _boolValue ? "true" : "false";
 			break;
 
 		case JSONType_Number: {
-			if (isinf(number_value) || isnan(number_value))
+			if (isinf(_numberValue) || isnan(_numberValue))
 				ret_string = "null";
 			else {
 				char str[80];
-				sprintf(str, "%.15Lf", number_value); //ss.precision(15);
+				sprintf(str, "%.15Lf", _numberValue); //ss.precision(15);
 				ret_string = str;
 			}
 			break;
@@ -953,12 +947,12 @@ String JSONValue::StringifyImpl(size_t const indentDepth) const {
 
 		case JSONType_Array: {
 			ret_string = indentDepth ? "[\n" + indentStr1 : "[";
-			JSONArray::const_iterator iter = array_value->begin();
-			while (iter != array_value->end()) {
-				ret_string += (*iter)->StringifyImpl(indentDepth1);
+			JSONArray::const_iterator iter = _arrayValue->begin();
+			while (iter != _arrayValue->end()) {
+				ret_string += (*iter)->stringifyImpl(indentDepth1);
 
 				// Not at the end - add a separator
-				if (++iter != array_value->end())
+				if (++iter != _arrayValue->end())
 					ret_string += ",";
 			}
 			ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
@@ -967,14 +961,14 @@ String JSONValue::StringifyImpl(size_t const indentDepth) const {
 
 		case JSONType_Object: {
 			ret_string = indentDepth ? "{\n" + indentStr1 : "{";
-			JSONObject::const_iterator iter = object_value->begin();
-			while (iter != object_value->end()) {
-				ret_string += StringifyString((*iter)._key);
+			JSONObject::const_iterator iter = _objectValue->begin();
+			while (iter != _objectValue->end()) {
+				ret_string += stringifyString((*iter)._key);
 				ret_string += ":";
-				ret_string += (*iter)._value->StringifyImpl(indentDepth1);
+				ret_string += (*iter)._value->stringifyImpl(indentDepth1);
 
 				// Not at the end - add a separator
-				if (++iter != object_value->end())
+				if (++iter != _objectValue->end())
 					ret_string += ",";
 			}
 			ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
@@ -996,7 +990,7 @@ String JSONValue::StringifyImpl(size_t const indentDepth) const {
 *
 * @return String Returns the JSON string
 */
-String JSONValue::StringifyString(const String& str) {
+String JSONValue::stringifyString(const String &str) {
 	String str_out = "\"";
 
 	String::const_iterator iter = str.begin();
@@ -1006,23 +1000,17 @@ String JSONValue::StringifyString(const String& str) {
 		if (chr == '"' || chr == '\\' || chr == '/') {
 			str_out += '\\';
 			str_out += chr;
-		}
-		else if (chr == '\b') {
+		} else if (chr == '\b') {
 			str_out += "\\b";
-		}
-		else if (chr == '\f') {
+		} else if (chr == '\f') {
 			str_out += "\\f";
-		}
-		else if (chr == '\n') {
+		} else if (chr == '\n') {
 			str_out += "\\n";
-		}
-		else if (chr == '\r') {
+		} else if (chr == '\r') {
 			str_out += "\\r";
-		}
-		else if (chr == '\t') {
+		} else if (chr == '\t') {
 			str_out += "\\t";
-		}
-		else if (chr < ' ' || chr > 126) {
+		} else if (chr < ' ' || chr > 126) {
 			str_out += "\\u";
 			for (int i = 0; i < 4; i++) {
 				int value = (chr >> 12) & 0xf;
@@ -1032,8 +1020,7 @@ String JSONValue::StringifyString(const String& str) {
 					str_out += (char)('A' + (value - 10));
 				chr <<= 4;
 			}
-		}
-		else {
+		} else {
 			str_out += chr;
 		}
 
@@ -1053,7 +1040,7 @@ String JSONValue::StringifyString(const String& str) {
 *
 * @return String Returns the string
 */
-String JSONValue::Indent(size_t depth) {
+String JSONValue::indent(size_t depth) {
 	const size_t indent_step = 2;
 	depth ? --depth : 0;
 	String indentStr;
@@ -1061,4 +1048,4 @@ String JSONValue::Indent(size_t depth) {
 	return indentStr;
 }
 
-}  // End of namespace Common
+} // End of namespace Common
diff --git a/common/json.h b/common/json.h
index 9597102..64a1e03 100644
--- a/common/json.h
+++ b/common/json.h
@@ -21,28 +21,28 @@
 */
 
 /*
- * Files JSON.h and JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json
- *
- * Copyright (C) 2010 Mike Anchor
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
+* Files JSON.h and JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json
+*
+* Copyright (C) 2010 Mike Anchor
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
 
 #ifndef COMMON_JSON_H
 #define COMMON_JSON_H
@@ -103,11 +103,11 @@ static inline bool isinf(double x) {
 
 // Simple function to check a string 's' has at least 'n' characters
 // Tkachov: that's not wchar_t anymore, though it should work for C-strings too
-static inline bool simplejson_wcsnlen(const char* s, size_t n) {
+static inline bool simplejson_wcsnlen(const char *s, size_t n) {
 	if (s == 0)
 		return false;
 
-	const char* save = s;
+	const char *save = s;
 	while (n-- > 0) {
 		if (*(save++) == 0) return false;
 	}
@@ -133,52 +133,52 @@ class JSONValue {
 
 public:
 	JSONValue(/*NULL*/);
-	JSONValue(const char* m_char_value);
-	JSONValue(const String& m_string_value);
-	JSONValue(bool m_bool_value);
-	JSONValue(double m_number_value);
-	JSONValue(const JSONArray& m_array_value);
-	JSONValue(const JSONObject& m_object_value);
-	JSONValue(const JSONValue& m_source);
+	JSONValue(const char *charValue);
+	JSONValue(const String &stringValue);
+	JSONValue(bool boolValue);
+	JSONValue(double numberValue);
+	JSONValue(const JSONArray &arrayValue);
+	JSONValue(const JSONObject &objectValue);
+	JSONValue(const JSONValue &source);
 	~JSONValue();
 
-	bool IsNull() const;
-	bool IsString() const;
-	bool IsBool() const;
-	bool IsNumber() const;
-	bool IsArray() const;
-	bool IsObject() const;
-
-	const String& AsString() const;
-	bool AsBool() const;
-	double AsNumber() const;
-	const JSONArray& AsArray() const;
-	const JSONObject& AsObject() const;
-
-	std::size_t CountChildren() const;
-	bool HasChild(std::size_t index) const;
-	JSONValue* Child(std::size_t index);
-	bool HasChild(const char* name) const;
-	JSONValue* Child(const char* name);
-	Array<String> ObjectKeys() const;
-
-	String Stringify(bool const prettyprint = false) const;
+	bool isNull() const;
+	bool isString() const;
+	bool isBool() const;
+	bool isNumber() const;
+	bool isArray() const;
+	bool isObject() const;
+
+	const String &asString() const;
+	bool asBool() const;
+	double asNumber() const;
+	const JSONArray &asArray() const;
+	const JSONObject &asObject() const;
+
+	size_t countChildren() const;
+	bool hasChild(size_t index) const;
+	JSONValue *child(size_t index);
+	bool hasChild(const char *name) const;
+	JSONValue *child(const char *name);
+	Array<String> objectKeys() const;
+
+	String stringify(bool const prettyprint = false) const;
 protected:
-	static JSONValue* Parse(const char** data);
+	static JSONValue *parse(const char **data);
 
 private:
-	static String StringifyString(const String& str);
-	String StringifyImpl(size_t const indentDepth) const;
-	static String Indent(size_t depth);
+	static String stringifyString(const String &str);
+	String stringifyImpl(size_t const indentDepth) const;
+	static String indent(size_t depth);
 
-	JSONType type;
+	JSONType _type;
 
 	union {
-		bool bool_value;
-		double number_value;
-		String* string_value;
-		JSONArray* array_value;
-		JSONObject* object_value;
+		bool _boolValue;
+		double _numberValue;
+		String *_stringValue;
+		JSONArray *_arrayValue;
+		JSONObject *_objectValue;
 	};
 
 };
@@ -189,17 +189,17 @@ class JSON {
 	friend class JSONValue;
 
 public:
-	static JSONValue* Parse(const char* data);
-	static String Stringify(const JSONValue* value);
+	static JSONValue *parse(const char *data);
+	static String stringify(const JSONValue *value);
 protected:
-	static bool SkipWhitespace(const char** data);
-	static bool ExtractString(const char** data, String& str);
-	static double ParseInt(const char** data);
-	static double ParseDecimal(const char** data);
+	static bool skipWhitespace(const char **data);
+	static bool extractString(const char **data, String &str);
+	static double parseInt(const char **data);
+	static double parseDecimal(const char **data);
 private:
 	JSON();
 };
 
-}  // End of namespace Common
+} // End of namespace Common
 
 #endif


Commit: 2ac2816d68c11b50796457f7d41896a1ed7d571e
    https://github.com/scummvm/scummvm/commit/2ac2816d68c11b50796457f7d41896a1ed7d571e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Refactor SimpleJSON

Changed paths:
    backends/cloud/cloudthread.cpp
    common/json.cpp
    common/json.h



diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp
index e375d6f..f8d93d2 100644
--- a/backends/cloud/cloudthread.cpp
+++ b/backends/cloud/cloudthread.cpp
@@ -1,6 +1,28 @@
+/* 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 "cloudthread.h"
-#include "../../common/debug.h"
-#include "../../common/json.h"
+#include "common/debug.h"
+#include "common/json.h"
 
 void example1();
 void example2();
diff --git a/common/json.cpp b/common/json.cpp
index a0bab11..878c67e 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -44,12 +44,7 @@
 * THE SOFTWARE.
 */
 
-#include "JSON.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
+#include "common/json.h"
 
 #ifdef __MINGW32__
 #define wcsncasecmp wcsnicmp
@@ -155,54 +150,54 @@ bool JSON::extractString(const char **data, String &str) {
 
 			// Deal with the escaped char
 			switch (**data) {
-				case '"': next_char = '"';
-					break;
-				case '\\': next_char = '\\';
-					break;
-				case '/': next_char = '/';
-					break;
-				case 'b': next_char = '\b';
-					break;
-				case 'f': next_char = '\f';
-					break;
-				case 'n': next_char = '\n';
-					break;
-				case 'r': next_char = '\r';
-					break;
-				case 't': next_char = '\t';
-					break;
-				case 'u': {
-					// We need 5 chars (4 hex + the 'u') or its not valid
-					if (!simplejson_wcsnlen(*data, 5))
-						return false;
+			case '"': next_char = '"';
+				break;
+			case '\\': next_char = '\\';
+				break;
+			case '/': next_char = '/';
+				break;
+			case 'b': next_char = '\b';
+				break;
+			case 'f': next_char = '\f';
+				break;
+			case 'n': next_char = '\n';
+				break;
+			case 'r': next_char = '\r';
+				break;
+			case 't': next_char = '\t';
+				break;
+			case 'u': {
+				// We need 5 chars (4 hex + the 'u') or its not valid
+				if (!simplejson_wcsnlen(*data, 5))
+					return false;
 
-					// Deal with the chars
-					next_char = 0;
-					for (int i = 0; i < 4; i++) {
-						// Do it first to move off the 'u' and leave us on the
-						// final hex digit as we move on by one later on
-						(*data)++;
-
-						next_char <<= 4;
-
-						// Parse the hex digit
-						if (**data >= '0' && **data <= '9')
-							next_char |= (**data - '0');
-						else if (**data >= 'A' && **data <= 'F')
-							next_char |= (10 + (**data - 'A'));
-						else if (**data >= 'a' && **data <= 'f')
-							next_char |= (10 + (**data - 'a'));
-						else {
-							// Invalid hex digit = invalid JSON
-							return false;
-						}
+				// Deal with the chars
+				next_char = 0;
+				for (int i = 0; i < 4; i++) {
+					// Do it first to move off the 'u' and leave us on the
+					// final hex digit as we move on by one later on
+					(*data)++;
+
+					next_char <<= 4;
+
+					// Parse the hex digit
+					if (**data >= '0' && **data <= '9')
+						next_char |= (**data - '0');
+					else if (**data >= 'A' && **data <= 'F')
+						next_char |= (10 + (**data - 'A'));
+					else if (**data >= 'a' && **data <= 'f')
+						next_char |= (10 + (**data - 'a'));
+					else {
+						// Invalid hex digit = invalid JSON
+						return false;
 					}
-					break;
 				}
+				break;
+			}
 
-					// By the spec, only the above cases are allowed
-				default:
-					return false;
+				// By the spec, only the above cases are allowed
+			default:
+				return false;
 			}
 		}
 
@@ -594,41 +589,41 @@ JSONValue::JSONValue(const JSONValue &source) {
 	_type = source._type;
 
 	switch (_type) {
-		case JSONType_String:
-			_stringValue = new String(*source._stringValue);
-			break;
-
-		case JSONType_Bool:
-			_boolValue = source._boolValue;
-			break;
-
-		case JSONType_Number:
-			_numberValue = source._numberValue;
-			break;
-
-		case JSONType_Array: {
-			JSONArray source_array = *source._arrayValue;
-			JSONArray::iterator iter;
-			_arrayValue = new JSONArray();
-			for (iter = source_array.begin(); iter != source_array.end(); iter++)
-				_arrayValue->push_back(new JSONValue(**iter));
-			break;
-		}
+	case JSONType_String:
+		_stringValue = new String(*source._stringValue);
+		break;
 
-		case JSONType_Object: {
-			JSONObject source_object = *source._objectValue;
-			_objectValue = new JSONObject();
-			JSONObject::iterator iter;
-			for (iter = source_object.begin(); iter != source_object.end(); iter++) {
-				String name = (*iter)._key;
-				(*_objectValue)[name] = new JSONValue(*((*iter)._value));
-			}
-			break;
+	case JSONType_Bool:
+		_boolValue = source._boolValue;
+		break;
+
+	case JSONType_Number:
+		_numberValue = source._numberValue;
+		break;
+
+	case JSONType_Array: {
+		JSONArray source_array = *source._arrayValue;
+		JSONArray::iterator iter;
+		_arrayValue = new JSONArray();
+		for (iter = source_array.begin(); iter != source_array.end(); iter++)
+			_arrayValue->push_back(new JSONValue(**iter));
+		break;
+	}
+
+	case JSONType_Object: {
+		JSONObject source_object = *source._objectValue;
+		_objectValue = new JSONObject();
+		JSONObject::iterator iter;
+		for (iter = source_object.begin(); iter != source_object.end(); iter++) {
+			String name = (*iter)._key;
+			(*_objectValue)[name] = new JSONValue(*((*iter)._value));
 		}
+		break;
+	}
 
-		case JSONType_Null:
-			// Nothing to do.
-			break;
+	case JSONType_Null:
+		// Nothing to do.
+		break;
 	}
 }
 
@@ -792,12 +787,12 @@ const JSONObject &JSONValue::asObject() const {
 */
 std::size_t JSONValue::countChildren() const {
 	switch (_type) {
-		case JSONType_Array:
-			return _arrayValue->size();
-		case JSONType_Object:
-			return _objectValue->size();
-		default:
-			return 0;
+	case JSONType_Array:
+		return _arrayValue->size();
+	case JSONType_Object:
+		return _objectValue->size();
+	default:
+		return 0;
 	}
 }
 
@@ -922,58 +917,58 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
 	String const indentStr1 = indent(indentDepth1);
 
 	switch (_type) {
-		case JSONType_Null:
+	case JSONType_Null:
+		ret_string = "null";
+		break;
+
+	case JSONType_String:
+		ret_string = stringifyString(*_stringValue);
+		break;
+
+	case JSONType_Bool:
+		ret_string = _boolValue ? "true" : "false";
+		break;
+
+	case JSONType_Number: {
+		if (isinf(_numberValue) || isnan(_numberValue))
 			ret_string = "null";
-			break;
-
-		case JSONType_String:
-			ret_string = stringifyString(*_stringValue);
-			break;
-
-		case JSONType_Bool:
-			ret_string = _boolValue ? "true" : "false";
-			break;
-
-		case JSONType_Number: {
-			if (isinf(_numberValue) || isnan(_numberValue))
-				ret_string = "null";
-			else {
-				char str[80];
-				sprintf(str, "%.15Lf", _numberValue); //ss.precision(15);
-				ret_string = str;
-			}
-			break;
+		else {
+			char str[80];
+			sprintf(str, "%.15Lf", _numberValue); //ss.precision(15);
+			ret_string = str;
 		}
+		break;
+	}
 
-		case JSONType_Array: {
-			ret_string = indentDepth ? "[\n" + indentStr1 : "[";
-			JSONArray::const_iterator iter = _arrayValue->begin();
-			while (iter != _arrayValue->end()) {
-				ret_string += (*iter)->stringifyImpl(indentDepth1);
+	case JSONType_Array: {
+		ret_string = indentDepth ? "[\n" + indentStr1 : "[";
+		JSONArray::const_iterator iter = _arrayValue->begin();
+		while (iter != _arrayValue->end()) {
+			ret_string += (*iter)->stringifyImpl(indentDepth1);
 
-				// Not at the end - add a separator
-				if (++iter != _arrayValue->end())
-					ret_string += ",";
-			}
-			ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
-			break;
+			// Not at the end - add a separator
+			if (++iter != _arrayValue->end())
+				ret_string += ",";
 		}
+		ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
+		break;
+	}
 
-		case JSONType_Object: {
-			ret_string = indentDepth ? "{\n" + indentStr1 : "{";
-			JSONObject::const_iterator iter = _objectValue->begin();
-			while (iter != _objectValue->end()) {
-				ret_string += stringifyString((*iter)._key);
-				ret_string += ":";
-				ret_string += (*iter)._value->stringifyImpl(indentDepth1);
-
-				// Not at the end - add a separator
-				if (++iter != _objectValue->end())
-					ret_string += ",";
-			}
-			ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
-			break;
+	case JSONType_Object: {
+		ret_string = indentDepth ? "{\n" + indentStr1 : "{";
+		JSONObject::const_iterator iter = _objectValue->begin();
+		while (iter != _objectValue->end()) {
+			ret_string += stringifyString((*iter)._key);
+			ret_string += ":";
+			ret_string += (*iter)._value->stringifyImpl(indentDepth1);
+
+			// Not at the end - add a separator
+			if (++iter != _objectValue->end())
+				ret_string += ",";
 		}
+		ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
+		break;
+	}
 	}
 
 	return ret_string;
diff --git a/common/json.h b/common/json.h
index 64a1e03..5114947 100644
--- a/common/json.h
+++ b/common/json.h
@@ -47,10 +47,10 @@
 #ifndef COMMON_JSON_H
 #define COMMON_JSON_H
 
-#include "array.h"
-#include "hashmap.h"
-#include "hash-str.h"
-#include "str.h"
+#include "common/array.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/str.h"
 
 // Win32 incompatibilities
 #if defined(WIN32) && !defined(__GNUC__)


Commit: e4e2ec390d364770d16a3a478796c1b949e8645f
    https://github.com/scummvm/scummvm/commit/e4e2ec390d364770d16a3a478796c1b949e8645f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Remove wcsncasecmp() usage from SimpleJSON

Replaced with scumm_strnicmp().

Changed paths:
    common/json.cpp
    common/json.h



diff --git a/common/json.cpp b/common/json.cpp
index 878c67e..83ce0db 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -281,15 +281,15 @@ JSONValue *JSONValue::parse(const char **data) {
 			return new JSONValue(str);
 	}
 
-	// Is it a boolean?
-	else if ((simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, "true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && wcsncasecmp(*data, "false", 5) == 0)) {
-		bool value = wcsncasecmp(*data, "true", 4) == 0;
+	// Is it a boolean?	
+	else if ((simplejson_wcsnlen(*data, 4) && scumm_strnicmp(*data, "true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && scumm_strnicmp(*data, "false", 5) == 0)) {
+		bool value = scumm_strnicmp(*data, "true", 4) == 0;
 		(*data) += value ? 4 : 5;
 		return new JSONValue(value);
 	}
 
 	// Is it a null?
-	else if (simplejson_wcsnlen(*data, 4) && wcsncasecmp(*data, "null", 4) == 0) {
+	else if (simplejson_wcsnlen(*data, 4) && scumm_strnicmp(*data, "null", 4) == 0) {
 		(*data) += 4;
 		return new JSONValue();
 	}
diff --git a/common/json.h b/common/json.h
index 5114947..c9debf0 100644
--- a/common/json.h
+++ b/common/json.h
@@ -54,8 +54,6 @@
 
 // Win32 incompatibilities
 #if defined(WIN32) && !defined(__GNUC__)
-#define wcsncasecmp _strnicmp
-
 static inline bool isnan(double x) {
 	return x != x;
 }
@@ -65,44 +63,7 @@ static inline bool isinf(double x) {
 }
 #endif
 
-// Linux compile fix - from quaker66
-#ifdef __GNUC__
-	#include <cstring>
-	#include <cstdlib>
-#endif
-
-// Mac compile fixes - from quaker66, Lion fix by dabrahams
-// Tkachov: I probably broke those
-#if defined(__APPLE__) && __DARWIN_C_LEVEL < 200809L || (defined(WIN32) && defined(__GNUC__)) || defined(ANDROID)
-	#include <wctype.h>
-	#include <wchar.h>
-	
-	static inline int wcsncasecmp(const char *s1, const char *s2, size_t n)
-	{
-		int lc1  = 0;
-		int lc2  = 0;
-
-		while (n--)
-		{
-			lc1 = towlower (*s1);
-			lc2 = towlower (*s2);
-
-			if (lc1 != lc2)
-				return (lc1 - lc2);
-
-			if (!lc1)
-				return 0;
-
-			++s1;
-			++s2;
-		}
-
-		return 0;
-	}
-#endif
-
 // Simple function to check a string 's' has at least 'n' characters
-// Tkachov: that's not wchar_t anymore, though it should work for C-strings too
 static inline bool simplejson_wcsnlen(const char *s, size_t n) {
 	if (s == 0)
 		return false;
@@ -122,8 +83,6 @@ class JSONValue;
 typedef Array<JSONValue*> JSONArray;
 typedef HashMap<String, JSONValue*> JSONObject;
 
-//JSONValue.h:
-
 class JSON;
 
 enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object };
@@ -183,8 +142,6 @@ private:
 
 };
 
-//EOF JSONValue.h
-
 class JSON {
 	friend class JSONValue;
 


Commit: 52240c68c7301b941f51ea315994ee7e4665707b
    https://github.com/scummvm/scummvm/commit/52240c68c7301b941f51ea315994ee7e4665707b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CONFIGURE: Added detection for SDL_net

Changed paths:
    configure



diff --git a/configure b/configure
index e77351a..b663701 100755
--- a/configure
+++ b/configure
@@ -117,6 +117,7 @@ done
 #
 # Default lib behavior yes/no/auto
 _vorbis=auto
+_sdlnet=auto
 _tremor=auto
 _tremolo=no
 _flac=auto
@@ -3677,6 +3678,24 @@ EOF
 cc_check -lm && append_var LIBS "-lm"
 
 #
+# Check for SDL_Net
+#
+echocheck "SDL_Net"
+if test "$_sdlnet" = auto ; then
+	_sdlnet=no
+	cat > $TMPC << EOF
+#include "SDL/SDL_net.h"
+int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
+EOF
+	cc_check $LIBS $INCLUDES -lSDL_net && _sdlnet=yes
+fi
+if test "$_sdlnet" = yes ; then
+	LIBS="$LIBS -lSDL_net"
+fi
+define_in_config_if_yes "$_sdlnet" 'USE_SDL_NET'
+echo "$_sdlnet"
+
+#
 # Check for Ogg Vorbis
 #
 echocheck "Ogg Vorbis"


Commit: 7446ffd73bd184610d550a354a8e252b0b7f334d
    https://github.com/scummvm/scummvm/commit/7446ffd73bd184610d550a354a8e252b0b7f334d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Integrate CloudThread into OSystem

Would be changed soon.

Changed paths:
    backends/cloud/cloudthread.cpp
    backends/cloud/cloudthread.h
    backends/platform/sdl/sdl.cpp
    base/main.cpp
    common/system.h



diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp
index f8d93d2..813354f 100644
--- a/backends/cloud/cloudthread.cpp
+++ b/backends/cloud/cloudthread.cpp
@@ -23,6 +23,8 @@
 #include "cloudthread.h"
 #include "common/debug.h"
 #include "common/json.h"
+#include "common/system.h"
+#include "common/timer.h"
 
 void example1();
 void example2();
@@ -30,10 +32,10 @@ void example3();
 
 void cloudThread(void *thread) {
 	CloudThread *cloudThread = (CloudThread *)thread;
-	cloudThread->work();
+	cloudThread->handler();
 };
 
-void CloudThread::work() {
+void CloudThread::handler() {
 	if (_firstTime) {
 		_firstTime = false;
 
@@ -43,6 +45,21 @@ void CloudThread::work() {
 	} else { }
 }
 
+void CloudThread::setTimeout(int interval) {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	if (!manager->installTimerProc(cloudThread, interval, this, "Cloud Thread"))
+		warning("Failed to create cloud thread");
+}
+
+void CloudThread::unsetTimeout() {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	manager->removeTimerProc(cloudThread);
+}
+
+void CloudThread::start() {
+	setTimeout(1000000); //in one second
+}
+
 /// SimpleJSON examples:
 
 using Common::JSON;
diff --git a/backends/cloud/cloudthread.h b/backends/cloud/cloudthread.h
index ce448b7..334a163 100644
--- a/backends/cloud/cloudthread.h
+++ b/backends/cloud/cloudthread.h
@@ -23,14 +23,19 @@
 #ifndef BACKENDS_CLOUD_CLOUDTHREAD_H
 #define BACKENDS_CLOUD_CLOUDTHREAD_H
 
-void cloudThread(void *thread); //this one is passed to TimerManager in main()
-
 class CloudThread {
+	friend void cloudThread(void*); //calls private handler()
+
 	bool _firstTime;
+
+	void handler();
+	void setTimeout(int interval);
+	void unsetTimeout();
+
 public:
 	CloudThread(): _firstTime(true) {};
 
-	void work();
+	void start();
 };
 
 #endif
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index dca6891..84aa5c8 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -33,6 +33,7 @@
 #include "gui/EventRecorder.h"
 #include "common/taskbar.h"
 #include "common/textconsole.h"
+#include "backends/cloud/cloudthread.h"
 
 #include "backends/saves/default/default-saves.h"
 
@@ -158,6 +159,12 @@ void OSystem_SDL::init() {
 		_taskbarManager = new Common::TaskbarManager();
 #endif
 
+//TODO: define USE_CLOUD
+//#if defined(USE_CLOUD)
+	if (_cloudThread == 0)
+		_cloudThread = new CloudThread();
+//#endif
+
 }
 
 void OSystem_SDL::initBackend() {
diff --git a/base/main.cpp b/base/main.cpp
index 593179d..ce69117 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -477,10 +477,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	}
 #endif
 	
-	CloudThread thread;
-	Common::TimerManager *manager = system.getTimerManager();
-	if (!manager->installTimerProc(cloudThread, 1000000, &thread, "Cloud Thread"))
-		warning("Failed to create cloud thread");
+	//TODO: define USE_CLOUD
+//#ifdef USE_CLOUD
+	system.getCloudThread()->start();
+//#endif
 
 	// Unless a game was specified, show the launcher dialog
 	if (0 == ConfMan.getActiveDomain())
diff --git a/common/system.h b/common/system.h
index 6d185d3..1af45fb 100644
--- a/common/system.h
+++ b/common/system.h
@@ -58,6 +58,12 @@ class KeymapperDefaultBindings;
 #endif
 }
 
+//TODO: define USE_CLOUD
+//TODO: probably move to common and name CloudManager
+//#if defined(USE_CLOUD)
+class CloudThread;
+//#endif
+
 class AudioCDManager;
 class FilesystemFactory;
 class PaletteManager;
@@ -178,6 +184,16 @@ protected:
 	Common::UpdateManager *_updateManager;
 #endif
 
+//TODO: define USE_CLOUD
+//#if defined(USE_CLOUD)
+	/**
+	* No default value is provided for _cloudThread by OSystem.
+	*
+	* @note _cloudThread is deleted by the OSystem destructor.
+	*/
+	CloudThread *_cloudThread;
+//#endif
+
 	/**
 	 * No default value is provided for _fsFactory by OSystem.
 	 *
@@ -1116,6 +1132,19 @@ public:
 	}
 #endif
 
+//TODO: define USE_CLOUD
+//#if defined(USE_CLOUD)
+	/**
+	* Returns the CloudThread, used to sync save games and
+	* upload/download files from user's cloud storage.
+	*
+	* @return the CloudThread for the current architecture
+	*/
+	virtual CloudThread *getCloudThread() {
+		return _cloudThread;
+	}
+//#endif
+
 	/**
 	 * Returns the FilesystemFactory object, depending on the current architecture.
 	 *


Commit: ca2eeb221455e54505aaef7039cfc0b01b807179
    https://github.com/scummvm/scummvm/commit/ca2eeb221455e54505aaef7039cfc0b01b807179
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:05:07+06:00

Commit Message:
CLOUD: Add Cloud::Manager and Cloud::Storage

This commit introduces Common::CloudManager, which can be accessed from
OSystem.

The backend for this manager is Cloud::Manager (defined in
backends/cloud/manager.h). It should load all users storages from
configs and provide access to current Storage instance. For now it just
creates a new one.

Cloud::Storage (backends/cloud/storage.h) provides an API to interact
with cloud storage, for example, create new directory or sync files.
Right now it's not ready and has only two dummy methods: listDirectory()
and syncSaves().

There is Cloud::Dropbox::DropboxStorage backend
(backends/cloud/dropbox/dropboxstorage.h) for Cloud::Storage. Right now
it implements both listDirectory() and syncSaves() with starting timer
task and handling it by printing out some JSON examples.

Changed paths:
  A backends/cloud/dropbox/dropboxstorage.cpp
  A backends/cloud/dropbox/dropboxstorage.h
  A backends/cloud/manager.cpp
  A backends/cloud/manager.h
  A backends/cloud/storage.cpp
  A backends/cloud/storage.h
  A common/cloudmanager.h
  R backends/cloud/cloudthread.cpp
  R backends/cloud/cloudthread.h
    backends/module.mk
    backends/platform/sdl/sdl.cpp
    base/main.cpp
    common/module.mk
    common/system.h



diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp
deleted file mode 100644
index 813354f..0000000
--- a/backends/cloud/cloudthread.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/* 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 "cloudthread.h"
-#include "common/debug.h"
-#include "common/json.h"
-#include "common/system.h"
-#include "common/timer.h"
-
-void example1();
-void example2();
-void example3();
-
-void cloudThread(void *thread) {
-	CloudThread *cloudThread = (CloudThread *)thread;
-	cloudThread->handler();
-};
-
-void CloudThread::handler() {
-	if (_firstTime) {
-		_firstTime = false;
-
-		example1();
-		example2();
-		example3();
-	} else { }
-}
-
-void CloudThread::setTimeout(int interval) {
-	Common::TimerManager *manager = g_system->getTimerManager();
-	if (!manager->installTimerProc(cloudThread, interval, this, "Cloud Thread"))
-		warning("Failed to create cloud thread");
-}
-
-void CloudThread::unsetTimeout() {
-	Common::TimerManager *manager = g_system->getTimerManager();
-	manager->removeTimerProc(cloudThread);
-}
-
-void CloudThread::start() {
-	setTimeout(1000000); //in one second
-}
-
-/// SimpleJSON examples:
-
-using Common::JSON;
-using Common::JSONValue;
-using Common::JSONArray;
-using Common::JSONObject;
-
-// Just some sample JSON text, feel free to change but could break demo
-const char *EXAMPLE = "\
-{ \
-	\"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \
-	\"bool_name\" : true, \
-	\"bool_second\" : FaLsE, \
-	\"null_name\" : nULl, \
-	\"negative\" : -34.276, \
-	\"sub_object\" : { \
-						\"foo\" : \"abc\", \
-						 \"bar\" : 1.35e2, \
-						 \"blah\" : { \"a\" : \"A\", \"b\" : \"B\", \"c\" : \"C\" } \
-					}, \
-	\"array_letters\" : [ \"a\", \"b\", \"c\", [ 1, 2, 3  ]  ] \
-}    ";
-
-// Example 1
-void example1() {
-	// Parse example data
-	JSONValue *value = JSON::parse(EXAMPLE);
-
-	// Did it go wrong?
-	if (value == NULL) {
-		debug("Example code failed to parse, did you change it?\r\n");
-	} else {
-		// Retrieve the main object
-		JSONObject root;
-		if (value->isObject() == false) {
-			debug("The root element is not an object, did you change the example?\r\n");
-		} else {
-			root = value->asObject();
-
-			// Retrieving a string
-			if (root.find("string_name") != root.end() && root["string_name"]->isString()) {
-				debug("string_name:\r\n");
-				debug("------------\r\n");
-				debug(root["string_name"]->asString().c_str());
-				debug("\r\n\r\n");
-			}
-
-			// Retrieving a boolean
-			if (root.find("bool_second") != root.end() && root["bool_second"]->isBool()) {
-				debug("bool_second:\r\n");
-				debug("------------\r\n");
-				debug(root["bool_second"]->asBool() ? "it's true!" : "it's false!");
-				debug("\r\n\r\n");
-			}
-
-			// Retrieving an array
-			if (root.find("array_letters") != root.end() && root["array_letters"]->isArray()) {
-				JSONArray array = root["array_letters"]->asArray();
-				debug("array_letters:\r\n");
-				debug("--------------\r\n");
-				for (unsigned int i = 0; i < array.size(); i++) {
-					//wstringstream output;
-					debug("[%d] => %s\r\n", i, array[i]->stringify().c_str());
-				}
-				debug("\r\n");
-			}
-
-			// Retrieving nested object
-			if (root.find("sub_object") != root.end() && root["sub_object"]->isObject()) {
-				debug("sub_object:\r\n");
-				debug("-----------\r\n");
-				debug(root["sub_object"]->stringify().c_str());
-				debug("\r\n\r\n");
-			}
-		}
-
-		delete value;
-	}
-}
-
-// Example 3 : compact vs. prettyprint
-void example2() {
-	const char *EXAMPLE3 =
-			"{\
-	 \"SelectedTab\":\"Math\",\
-	 	\"Widgets\":[\
-			{\"WidgetPosition\":[0,369,800,582],\"WidgetIndex\":1,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,453,283,489],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F2.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":2,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,494,283,530],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F3.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":3,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,536,283,572],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F4.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":4,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[287,417,400,439],\"IsWidgetVisible\":-1,\"Caption\":\"\",\"EnableCaption\":0,\"Name\":\"F1.Equation\",\"CaptionPosition\":1,\"ControlWidth\":113,\"ControlHeight\":22,\"Font\":0,\"AlignText\":0,\"EnableBorder\":0,\"CaptionOnly\":0,\"Value\":\"FFT(C1)\",\"WidgetIndex\":9,\"WidgetType\":\"WidgetStaticText.1\"},\
-			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":0,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.MeasureOpGui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Measurement To Graph\",\"Value\":\"Amplitude\",\"WidgetIndex\":17,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
-			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":-1,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"FFT\",\"WidgetIndex\":25,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
-			{\"WidgetPosition\":[191,452,230,487],\"IsWidgetVisible\":-1,\"Caption\":\"F2\",\"EnableCaption\":0,\"Name\":\"F2.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"Zoom\",\"WidgetIndex\":26,\"WidgetType\":\"WidgetProcessorCombobox.1\"}\
-		]\
-	 }";
-
-	// Parse example data
-	JSONValue *value = JSON::parse(EXAMPLE3);
-	if (value) {
-		debug("-----------\r\n");
-		debug(value->stringify().c_str());
-		debug("\r\n");
-		debug("-----------\r\n");
-		debug(value->stringify(true).c_str());
-		debug("\r\n");
-		debug("-----------\r\n");
-	}
-
-	// Clean up
-	delete value;
-}
-
-// Example 4 : List keys in an object.
-void example3() {
-	// Parse the example.
-	JSONValue *main_object = JSON::parse(EXAMPLE);
-	if (main_object == NULL) {
-		debug("Example code failed to parse, did you change it?\r\n");
-	} else if (!main_object->isObject()) {
-		debug("Example code is not an object, did you change it?\r\n");
-		delete main_object;
-	} else {
-		// Print the main object.
-		debug("Main object:\r\n");
-		debug(main_object->stringify(true).c_str());
-		debug("-----------\r\n");
-
-		// Fetch the keys and print them out.
-		Common::Array<Common::String> keys = main_object->objectKeys();
-
-		Common::Array<Common::String>::iterator iter = keys.begin();
-		while (iter != keys.end()) {
-			debug("Key: ");
-			debug((*iter).c_str());
-			debug("\r\n");
-
-			// Get the key's value.
-			JSONValue *key_value = main_object->child((*iter).c_str());
-			if (key_value) {
-				debug("Value: ");
-				debug(key_value->stringify().c_str());
-				debug("\r\n");
-				debug("-----------\r\n");
-			}
-
-			// Next key.
-			iter++;
-		}
-
-		delete main_object;
-	}
-}
diff --git a/backends/cloud/cloudthread.h b/backends/cloud/cloudthread.h
deleted file mode 100644
index 334a163..0000000
--- a/backends/cloud/cloudthread.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_CLOUDTHREAD_H
-#define BACKENDS_CLOUD_CLOUDTHREAD_H
-
-class CloudThread {
-	friend void cloudThread(void*); //calls private handler()
-
-	bool _firstTime;
-
-	void handler();
-	void setTimeout(int interval);
-	void unsetTimeout();
-
-public:
-	CloudThread(): _firstTime(true) {};
-
-	void start();
-};
-
-#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
new file mode 100644
index 0000000..b841575
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -0,0 +1,205 @@
+/* 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 "backends/cloud/dropbox/dropboxstorage.h"
+#include "common/debug.h"
+#include "common/json.h"
+
+void example1();
+void example2();
+void example3();
+
+namespace Cloud { namespace Dropbox {
+
+void DropboxStorage::handler() {
+	if (_firstTime) {
+		_firstTime = false;
+
+		example1();
+		example2();
+		example3();
+	} else { }
+}
+
+void DropboxStorage::listDirectory(Common::String path) {
+	setTimeout(1000000); //in one second
+}
+
+void DropboxStorage::syncSaves() {
+	setTimeout(1000000); //in one second
+}
+
+} } //end of namespace Cloud::Dropbox
+
+/// SimpleJSON examples:
+
+using Common::JSON;
+using Common::JSONValue;
+using Common::JSONArray;
+using Common::JSONObject;
+
+// Just some sample JSON text, feel free to change but could break demo
+const char *EXAMPLE = "\
+{ \
+	\"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \
+	\"bool_name\" : true, \
+	\"bool_second\" : FaLsE, \
+	\"null_name\" : nULl, \
+	\"negative\" : -34.276, \
+	\"sub_object\" : { \
+						\"foo\" : \"abc\", \
+						 \"bar\" : 1.35e2, \
+						 \"blah\" : { \"a\" : \"A\", \"b\" : \"B\", \"c\" : \"C\" } \
+					}, \
+	\"array_letters\" : [ \"a\", \"b\", \"c\", [ 1, 2, 3  ]  ] \
+}    ";
+
+// Example 1
+void example1() {
+	// Parse example data
+	JSONValue *value = JSON::parse(EXAMPLE);
+
+	// Did it go wrong?
+	if (value == NULL) {
+		debug("Example code failed to parse, did you change it?\r\n");
+	} else {
+		// Retrieve the main object
+		JSONObject root;
+		if (value->isObject() == false) {
+			debug("The root element is not an object, did you change the example?\r\n");
+		} else {
+			root = value->asObject();
+
+			// Retrieving a string
+			if (root.find("string_name") != root.end() && root["string_name"]->isString()) {
+				debug("string_name:\r\n");
+				debug("------------\r\n");
+				debug(root["string_name"]->asString().c_str());
+				debug("\r\n\r\n");
+			}
+
+			// Retrieving a boolean
+			if (root.find("bool_second") != root.end() && root["bool_second"]->isBool()) {
+				debug("bool_second:\r\n");
+				debug("------------\r\n");
+				debug(root["bool_second"]->asBool() ? "it's true!" : "it's false!");
+				debug("\r\n\r\n");
+			}
+
+			// Retrieving an array
+			if (root.find("array_letters") != root.end() && root["array_letters"]->isArray()) {
+				JSONArray array = root["array_letters"]->asArray();
+				debug("array_letters:\r\n");
+				debug("--------------\r\n");
+				for (unsigned int i = 0; i < array.size(); i++) {
+					//wstringstream output;
+					debug("[%d] => %s\r\n", i, array[i]->stringify().c_str());
+				}
+				debug("\r\n");
+			}
+
+			// Retrieving nested object
+			if (root.find("sub_object") != root.end() && root["sub_object"]->isObject()) {
+				debug("sub_object:\r\n");
+				debug("-----------\r\n");
+				debug(root["sub_object"]->stringify().c_str());
+				debug("\r\n\r\n");
+			}
+		}
+
+		delete value;
+	}
+}
+
+// Example 3 : compact vs. prettyprint
+void example2() {
+	const char *EXAMPLE3 =
+			"{\
+	 \"SelectedTab\":\"Math\",\
+	 	\"Widgets\":[\
+			{\"WidgetPosition\":[0,369,800,582],\"WidgetIndex\":1,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,453,283,489],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F2.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":2,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,494,283,530],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F3.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":3,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[235,536,283,572],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F4.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":4,\"WidgetType\":\"WidgetCheckbox.1\"},\
+			{\"WidgetPosition\":[287,417,400,439],\"IsWidgetVisible\":-1,\"Caption\":\"\",\"EnableCaption\":0,\"Name\":\"F1.Equation\",\"CaptionPosition\":1,\"ControlWidth\":113,\"ControlHeight\":22,\"Font\":0,\"AlignText\":0,\"EnableBorder\":0,\"CaptionOnly\":0,\"Value\":\"FFT(C1)\",\"WidgetIndex\":9,\"WidgetType\":\"WidgetStaticText.1\"},\
+			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":0,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.MeasureOpGui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Measurement To Graph\",\"Value\":\"Amplitude\",\"WidgetIndex\":17,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
+			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":-1,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"FFT\",\"WidgetIndex\":25,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
+			{\"WidgetPosition\":[191,452,230,487],\"IsWidgetVisible\":-1,\"Caption\":\"F2\",\"EnableCaption\":0,\"Name\":\"F2.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"Zoom\",\"WidgetIndex\":26,\"WidgetType\":\"WidgetProcessorCombobox.1\"}\
+		]\
+	 }";
+
+	// Parse example data
+	JSONValue *value = JSON::parse(EXAMPLE3);
+	if (value) {
+		debug("-----------\r\n");
+		debug(value->stringify().c_str());
+		debug("\r\n");
+		debug("-----------\r\n");
+		debug(value->stringify(true).c_str());
+		debug("\r\n");
+		debug("-----------\r\n");
+	}
+
+	// Clean up
+	delete value;
+}
+
+// Example 4 : List keys in an object.
+void example3() {
+	// Parse the example.
+	JSONValue *main_object = JSON::parse(EXAMPLE);
+	if (main_object == NULL) {
+		debug("Example code failed to parse, did you change it?\r\n");
+	} else if (!main_object->isObject()) {
+		debug("Example code is not an object, did you change it?\r\n");
+		delete main_object;
+	} else {
+		// Print the main object.
+		debug("Main object:\r\n");
+		debug(main_object->stringify(true).c_str());
+		debug("-----------\r\n");
+
+		// Fetch the keys and print them out.
+		Common::Array<Common::String> keys = main_object->objectKeys();
+
+		Common::Array<Common::String>::iterator iter = keys.begin();
+		while (iter != keys.end()) {
+			debug("Key: ");
+			debug((*iter).c_str());
+			debug("\r\n");
+
+			// Get the key's value.
+			JSONValue *key_value = main_object->child((*iter).c_str());
+			if (key_value) {
+				debug("Value: ");
+				debug(key_value->stringify().c_str());
+				debug("\r\n");
+				debug("-----------\r\n");
+			}
+
+			// Next key.
+			iter++;
+		}
+
+		delete main_object;
+	}
+}
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
new file mode 100644
index 0000000..43ed9dc
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -0,0 +1,45 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_STORAGE_H
+#define BACKENDS_CLOUD_DROPBOX_STORAGE_H
+
+#include "backends/cloud/storage.h"
+
+namespace Cloud { namespace Dropbox {
+
+class DropboxStorage: public Cloud::Storage {
+	bool _firstTime;
+
+protected:
+	virtual void handler();
+
+public:
+	DropboxStorage() : _firstTime(true) {};
+
+	virtual void listDirectory(Common::String path);
+	virtual void syncSaves();
+};
+
+} }  //end of namespace Cloud::Dropbox
+
+#endif
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
new file mode 100644
index 0000000..05b2377
--- /dev/null
+++ b/backends/cloud/manager.cpp
@@ -0,0 +1,41 @@
+/* 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 "backends/cloud/manager.h"
+#include "backends/cloud/dropbox/dropboxstorage.h"
+
+namespace Cloud {
+
+Manager::Manager(): _currentStorage(new Dropbox::DropboxStorage()) {};
+
+Manager::~Manager() { delete _currentStorage; }
+
+Storage* Manager::getCurrentStorage() {
+	return _currentStorage;
+}
+
+void Manager::syncSaves() {
+	Storage* storage = getCurrentStorage();
+	if (storage) storage->syncSaves();
+}
+
+} //end of namespace Cloud
\ No newline at end of file
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
new file mode 100644
index 0000000..11cc595
--- /dev/null
+++ b/backends/cloud/manager.h
@@ -0,0 +1,44 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_MANAGER_H
+#define BACKENDS_CLOUD_MANAGER_H
+
+#include "common/cloudmanager.h"
+#include "common/str.h"
+
+namespace Cloud {
+
+class Manager: public Common::CloudManager {
+	Storage* _currentStorage;
+
+public:
+	Manager();
+	virtual ~Manager();
+
+	virtual Storage* getCurrentStorage();
+	virtual void syncSaves();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
new file mode 100644
index 0000000..3272ecf
--- /dev/null
+++ b/backends/cloud/storage.cpp
@@ -0,0 +1,49 @@
+/* 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 "backends/cloud/storage.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+namespace Cloud {
+
+void cloudThread(void *thread) {
+	Storage *cloudThread = (Storage *)thread;
+	cloudThread->handler();
+}
+
+void Storage::handler() {
+	unsetTimeout();
+}
+
+void Storage::setTimeout(int interval) {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	if (!manager->installTimerProc(cloudThread, interval, this, "Cloud Thread"))
+		; // warning("Failed to create cloud thread");
+}
+
+void Storage::unsetTimeout() {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	manager->removeTimerProc(cloudThread);
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
new file mode 100644
index 0000000..4afa36f
--- /dev/null
+++ b/backends/cloud/storage.h
@@ -0,0 +1,61 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_STORAGE_H
+#define BACKENDS_CLOUD_STORAGE_H
+
+#include "common/str.h"
+
+namespace Cloud {
+
+class Storage {
+	friend void cloudThread(void*); //calls handler()
+
+protected:
+	virtual void handler();
+	virtual void setTimeout(int interval);
+	virtual void unsetTimeout();
+
+public:
+	Storage() {};
+	virtual ~Storage() {};
+
+	/**
+	* Lists given directory.
+	*
+	* @param path		directory to list	
+	*/
+
+	//TODO: actually make it list directories and some callback to pass gathered files list
+
+	virtual void listDirectory(Common::String path) = 0;
+
+	/**
+	* Starts saves syncing process.
+	*/
+
+	virtual void syncSaves() = 0;
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index fc2fb99..a1e9c11 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -251,8 +251,10 @@ endif
 
 # I don't have any define, so I'd just add my files without any
 # ifndef USE_CLOUD ?
-MODULE_OBJS += \
-	cloud/cloudthread.o
+MODULE_OBJS += \	
+	cloud/manager.o \
+	cloud/storage.o \
+	cloud/dropbox/storage.o
 # endif
 
 # Include common rules
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index 84aa5c8..e743bdf 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -33,7 +33,7 @@
 #include "gui/EventRecorder.h"
 #include "common/taskbar.h"
 #include "common/textconsole.h"
-#include "backends/cloud/cloudthread.h"
+#include "backends/cloud/manager.h"
 
 #include "backends/saves/default/default-saves.h"
 
@@ -162,7 +162,7 @@ void OSystem_SDL::init() {
 //TODO: define USE_CLOUD
 //#if defined(USE_CLOUD)
 	if (_cloudThread == 0)
-		_cloudThread = new CloudThread();
+		_cloudThread = new Cloud::Manager();
 //#endif
 
 }
diff --git a/base/main.cpp b/base/main.cpp
index ce69117..001d864 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -66,7 +66,7 @@
 #endif
 
 #include "backends/keymapper/keymapper.h"
-#include "backends/cloud/cloudthread.h"
+#include "common/cloudmanager.h"
 
 #if defined(_WIN32_WCE)
 #include "backends/platform/wince/CELauncherDialog.h"
@@ -479,7 +479,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	
 	//TODO: define USE_CLOUD
 //#ifdef USE_CLOUD
-	system.getCloudThread()->start();
+	system.getCloudManager()->syncSaves();
 //#endif
 
 	// Unless a game was specified, show the launcher dialog
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
new file mode 100644
index 0000000..6b57682
--- /dev/null
+++ b/common/cloudmanager.h
@@ -0,0 +1,53 @@
+/* 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.
+*
+*/
+
+#ifndef COMMON_CLOUDMANAGER_H
+#define COMMON_CLOUDMANAGER_H
+
+#include "backends/cloud/storage.h"
+
+namespace Common {
+
+class CloudManager {
+public:
+	CloudManager() {};
+	virtual ~CloudManager() {};
+
+	/**
+	* Returns active Storage, which could be used to interact
+	*  with cloud storage.
+	*
+	* @return	active Cloud::Storage or null, if there is no active Storage.
+	*/
+
+	virtual Cloud::Storage* getCurrentStorage() = 0;
+
+	/**
+	* Starts saves syncing process in currently active storage if there is any.
+	*/
+
+	virtual void syncSaves() = 0;
+};
+
+} //end of namespace Common
+
+#endif
diff --git a/common/module.mk b/common/module.mk
index 54aa16f..29def4b 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -62,5 +62,9 @@ MODULE_OBJS += \
 	updates.o
 endif
 
+#TODO define USE_CLOUD
+#ifdef USE_CLOUD
+#endif
+
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/common/system.h b/common/system.h
index 1af45fb..b1c74cb 100644
--- a/common/system.h
+++ b/common/system.h
@@ -56,13 +56,11 @@ class HardwareInputSet;
 class Keymap;
 class KeymapperDefaultBindings;
 #endif
-}
-
 //TODO: define USE_CLOUD
-//TODO: probably move to common and name CloudManager
 //#if defined(USE_CLOUD)
-class CloudThread;
+class CloudManager;
 //#endif
+}
 
 class AudioCDManager;
 class FilesystemFactory;
@@ -191,7 +189,7 @@ protected:
 	*
 	* @note _cloudThread is deleted by the OSystem destructor.
 	*/
-	CloudThread *_cloudThread;
+	Common::CloudManager *_cloudThread;
 //#endif
 
 	/**
@@ -1135,12 +1133,12 @@ public:
 //TODO: define USE_CLOUD
 //#if defined(USE_CLOUD)
 	/**
-	* Returns the CloudThread, used to sync save games and
+	* Returns the CloudManager, used to sync save games and
 	* upload/download files from user's cloud storage.
 	*
-	* @return the CloudThread for the current architecture
+	* @return the CloudManager for the current architecture
 	*/
-	virtual CloudThread *getCloudThread() {
+	virtual Common::CloudManager *getCloudManager() {
 		return _cloudThread;
 	}
 //#endif


Commit: 6c83930126a2dcb9af4fa631fc7226db1756f801
    https://github.com/scummvm/scummvm/commit/6c83930126a2dcb9af4fa631fc7226db1756f801
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CONFIGURE: Added libcurl detection

Changed paths:
    configure



diff --git a/configure b/configure
index b663701..9c3da0a 100755
--- a/configure
+++ b/configure
@@ -118,6 +118,7 @@ done
 # Default lib behavior yes/no/auto
 _vorbis=auto
 _sdlnet=auto
+_libcurl=auto
 _tremor=auto
 _tremolo=no
 _flac=auto
@@ -186,9 +187,11 @@ _staticlibpath=
 _xcodetoolspath=
 _sparklepath=
 _sdlconfig=sdl2-config
+_libcurlconfig=curl-config
 _freetypeconfig=freetype-config
 _sdlpath="$PATH"
 _freetypepath="$PATH"
+_libcurlpath="$PATH"
 _nasmpath="$PATH"
 NASMFLAGS=""
 NASM=""
@@ -202,6 +205,7 @@ _have_x86=no
 
 # Add (virtual) features
 add_feature 16bit "16bit color" "_16bit"
+add_feature cloud "cloud" "_cloud"
 add_feature faad "libfaad" "_faad"
 add_feature flac "FLAC" "_flac"
 add_feature freetype2 "FreeType2" "_freetype2"
@@ -434,6 +438,41 @@ find_freetypeconfig() {
 }
 
 #
+# Determine curl-config
+#
+find_libcurlconfig() {
+	echo_n "Looking for curl-config... "
+	libcurlconfigs="$_libcurlconfig"
+	_libcurlconfig=
+
+	IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS="$SEPARATOR"
+	for path_dir in $_libcurlpath; do
+		#reset separator to parse sdlconfigs
+		IFS=":"
+		for libcurlconfig in $libcurlconfigs; do
+			if test -f "$path_dir/$libcurlconfig" ; then
+				_libcurlconfig="$path_dir/$libcurlconfig"
+				echo $_libcurlconfig
+				# Save the prefix
+				_libcurlpath=$path_dir
+				if test `basename $path_dir` = bin ; then
+					_sdlpath=`dirname $path_dir`
+				fi
+				# break at first sdl-config found in path
+				break 2
+			fi
+		done
+	done
+
+	IFS="$ac_save_ifs"
+
+	if test -z "$_libcurlconfig"; then
+		echo "none found!"
+		exit 1
+	fi
+}
+
+#
 # Determine extension used for executables
 #
 get_system_exe_extension() {
@@ -3678,24 +3717,6 @@ EOF
 cc_check -lm && append_var LIBS "-lm"
 
 #
-# Check for SDL_Net
-#
-echocheck "SDL_Net"
-if test "$_sdlnet" = auto ; then
-	_sdlnet=no
-	cat > $TMPC << EOF
-#include "SDL/SDL_net.h"
-int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
-EOF
-	cc_check $LIBS $INCLUDES -lSDL_net && _sdlnet=yes
-fi
-if test "$_sdlnet" = yes ; then
-	LIBS="$LIBS -lSDL_net"
-fi
-define_in_config_if_yes "$_sdlnet" 'USE_SDL_NET'
-echo "$_sdlnet"
-
-#
 # Check for Ogg Vorbis
 #
 echocheck "Ogg Vorbis"
@@ -4091,6 +4112,85 @@ EOF
 esac
 
 #
+# Check for SDL_Net
+#
+echocheck "SDL_Net"
+if test "$_sdlnet" = auto ; then
+	_sdlnet=no
+	cat > $TMPC << EOF
+#include "SDL/SDL_net.h"
+int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
+EOF
+	cc_check $LIBS $INCLUDES -lSDL_net && _sdlnet=yes
+fi
+if test "$_sdlnet" = yes ; then
+	LIBS="$LIBS -lSDL_net"
+fi
+define_in_config_if_yes "$_sdlnet" 'USE_SDL_NET'
+echo "$_sdlnet"
+
+#
+# Check for libcurl to be present
+#
+if test "$_libcurl" != "no"; then
+
+	# Look for the curl-config script
+	find_libcurlconfig
+
+	if test -z "$_libcurlconfig"; then
+		_libcurl=no
+	else
+		LIBCURL_LIBS=`$_libcurlconfig --libs`
+		LIBCURL_CFLAGS=`$_libcurlconfig --cflags`
+
+		if test "$_libcurl" = "auto"; then
+			_libcurl=no
+
+			cat > $TMPC << EOF
+			#include <curl/curl.h>
+			int main(int argc, char *argv[]) {
+				int x;
+				curl_easy_setopt(NULL,CURLOPT_URL,NULL);
+				x=CURL_ERROR_SIZE;
+				x=CURLOPT_WRITEFUNCTION;
+				x=CURLOPT_WRITEDATA;
+				x=CURLOPT_ERRORBUFFER;
+				x=CURLOPT_STDERR;
+				x=CURLOPT_VERBOSE;
+
+				curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
+				if (data->features & CURL_VERSION_SSL)
+					return 0;
+				return 1;
+			}
+EOF
+
+			cc_check_no_clean $LIBCURL_CFLAGS $LIBCURL_LIBS
+			if test "$?" -eq 0; then
+				$TMPO$HOSTEXEEXT
+				if test "$?" -eq 0; then
+					_libcurl=yes
+				else
+					_libcurl="no SSL support"
+				fi
+			fi
+			cc_check_clean
+		fi
+
+		if test "$_libcurl" = "yes"; then
+			append_var LIBS "$LIBCURL_LIBS"
+			append_var INCLUDES "$LIBCURL_CFLAGS"
+		fi
+	fi
+
+fi
+
+echocheck "libcurl"
+echo "$_libcurl"
+
+define_in_config_if_yes "$_libcurl" "USE_LIBCURL"
+
+#
 # Check is NSDockTilePlugIn protocol is supported
 #
 case $_host_os in


Commit: fade746f374cc801870e68934f1dbb3e9c726198
    https://github.com/scummvm/scummvm/commit/fade746f374cc801870e68934f1dbb3e9c726198
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add USE_CLOUD feature

Adds USE_CLOUD in both configure and create_project.

Changed paths:
    backends/module.mk
    backends/platform/sdl/sdl.cpp
    base/main.cpp
    base/version.cpp
    common/module.mk
    common/system.h
    configure
    devtools/create_project/create_project.cpp



diff --git a/backends/module.mk b/backends/module.mk
index a1e9c11..aa9e976 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -19,6 +19,12 @@ MODULE_OBJS := \
 	saves/default/default-saves.o \
 	timer/default/default-timer.o
 
+ifdef USE_CLOUD
+MODULE_OBJS += \
+	cloud/manager.o \
+	cloud/storage.o \
+	cloud/dropbox/dropboxstorage.o
+endif
 
 ifdef USE_ELF_LOADER
 MODULE_OBJS += \
@@ -249,13 +255,5 @@ MODULE_OBJS += \
 	saves/recorder/recorder-saves.o
 endif
 
-# I don't have any define, so I'd just add my files without any
-# ifndef USE_CLOUD ?
-MODULE_OBJS += \	
-	cloud/manager.o \
-	cloud/storage.o \
-	cloud/dropbox/storage.o
-# endif
-
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index e743bdf..f07f568 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -159,11 +159,10 @@ void OSystem_SDL::init() {
 		_taskbarManager = new Common::TaskbarManager();
 #endif
 
-//TODO: define USE_CLOUD
-//#if defined(USE_CLOUD)
+#if defined(USE_CLOUD)
 	if (_cloudThread == 0)
 		_cloudThread = new Cloud::Manager();
-//#endif
+#endif
 
 }
 
diff --git a/base/main.cpp b/base/main.cpp
index 001d864..ac24376 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -476,11 +476,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 		dlg.runModal();
 	}
 #endif
-	
-	//TODO: define USE_CLOUD
-//#ifdef USE_CLOUD
+		
+#ifdef USE_CLOUD
 	system.getCloudManager()->syncSaves();
-//#endif
+#endif
 
 	// Unless a game was specified, show the launcher dialog
 	if (0 == ConfMan.getActiveDomain())
diff --git a/base/version.cpp b/base/version.cpp
index 299e4ce..b2a7111 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -154,4 +154,25 @@ const char *gScummVMFeatures = ""
 #ifdef ENABLE_VKEYBD
 	"virtual keyboard "
 #endif
+
+#ifdef USE_CLOUD
+	"cloud ("
+#ifdef USE_LIBCURL
+	"servers"
+#ifdef USE_SDL_NET
+	" "
+#endif
+#endif
+#ifdef USE_SDL_NET
+	"local"
+#endif
+	") "
+#else
+#ifdef USE_LIBCURL
+	"libcurl "
+#endif
+#ifdef USE_SDL_NET
+	"SDL_net "
+#endif
+#endif
 	;
diff --git a/common/module.mk b/common/module.mk
index 29def4b..7118ea3 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -62,9 +62,8 @@ MODULE_OBJS += \
 	updates.o
 endif
 
-#TODO define USE_CLOUD
-#ifdef USE_CLOUD
-#endif
+ifdef USE_CLOUD
+endif
 
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/common/system.h b/common/system.h
index b1c74cb..b24bc0a 100644
--- a/common/system.h
+++ b/common/system.h
@@ -56,10 +56,9 @@ class HardwareInputSet;
 class Keymap;
 class KeymapperDefaultBindings;
 #endif
-//TODO: define USE_CLOUD
-//#if defined(USE_CLOUD)
+#if defined(USE_CLOUD)
 class CloudManager;
-//#endif
+#endif
 }
 
 class AudioCDManager;
@@ -182,15 +181,14 @@ protected:
 	Common::UpdateManager *_updateManager;
 #endif
 
-//TODO: define USE_CLOUD
-//#if defined(USE_CLOUD)
+#if defined(USE_CLOUD)
 	/**
 	* No default value is provided for _cloudThread by OSystem.
 	*
 	* @note _cloudThread is deleted by the OSystem destructor.
 	*/
 	Common::CloudManager *_cloudThread;
-//#endif
+#endif
 
 	/**
 	 * No default value is provided for _fsFactory by OSystem.
@@ -1130,8 +1128,7 @@ public:
 	}
 #endif
 
-//TODO: define USE_CLOUD
-//#if defined(USE_CLOUD)
+#if defined(USE_CLOUD)
 	/**
 	* Returns the CloudManager, used to sync save games and
 	* upload/download files from user's cloud storage.
@@ -1141,7 +1138,7 @@ public:
 	virtual Common::CloudManager *getCloudManager() {
 		return _cloudThread;
 	}
-//#endif
+#endif
 
 	/**
 	 * Returns the FilesystemFactory object, depending on the current architecture.
diff --git a/configure b/configure
index 9c3da0a..8b6068b 100755
--- a/configure
+++ b/configure
@@ -4191,6 +4191,28 @@ echo "$_libcurl"
 define_in_config_if_yes "$_libcurl" "USE_LIBCURL"
 
 #
+# Check whether to build cloud integration support
+#
+echo_n "Cloud integration"
+if test "$_cloud" = "no"; then
+	echo "no"
+else
+	if test "_sdl_net" = "yes" -or "_libcurl" = "yes"; then		
+		_cloud=yes
+		if test "_sdl_net" = "yes"; then
+			echo "local"
+		fi
+		if test "_libcurl" = "yes"; then
+			echo "servers"		
+		fi
+	else
+		_cloud=no
+		echo "no"
+	fi
+fi
+define_in_config_if_yes $_cloud 'USE_CLOUD'
+
+#
 # Check is NSDockTilePlugIn protocol is supported
 #
 case $_host_os in
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index 70781ff..91690c2 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1000,10 +1000,12 @@ const Feature s_features[] = {
 	{       "png",         "USE_PNG", "libpng16",         true,  "libpng support" },
 	{      "faad",        "USE_FAAD", "libfaad",          false, "AAC support" },
 	{     "mpeg2",       "USE_MPEG2", "libmpeg2",         false, "MPEG-2 support" },
-	{    "theora",   "USE_THEORADEC", "libtheora_static", true,  "Theora decoding support" },
-	{  "freetype",   "USE_FREETYPE2", "freetype",         true,  "FreeType support" },
-	{      "jpeg",        "USE_JPEG", "jpeg-static",      true,  "libjpeg support" },
-	{"fluidsynth",  "USE_FLUIDSYNTH", "libfluidsynth",    true,  "FluidSynth support" },
+	{    "theora",   "USE_THEORADEC", "libtheora_static", true, "Theora decoding support" },
+	{  "freetype",   "USE_FREETYPE2", "freetype",         true, "FreeType support" },
+	{      "jpeg",        "USE_JPEG", "jpeg-static",      true, "libjpeg support" },
+	{"fluidsynth",  "USE_FLUIDSYNTH", "libfluidsynth",    true, "FluidSynth support" },
+	{   "libcurl",     "USE_LIBCURL", "libcurl",          true, "libcurl support" },
+	{    "sdlnet",     "USE_SDL_NET", "SDL_net",          true, "SDL_net support" },
 
 	// Feature flags
 	{            "bink",             "USE_BINK",         "", true,  "Bink video support" },
@@ -1015,6 +1017,7 @@ const Feature s_features[] = {
 	{          "opengl",           "USE_OPENGL",         "", true,  "OpenGL support" },
 	{        "opengles",             "USE_GLES",         "", true,  "forced OpenGL ES mode" },
 	{         "taskbar",          "USE_TASKBAR",         "", true,  "Taskbar integration support" },
+	{           "cloud",            "USE_CLOUD",         "", true,  "Cloud integration support" },
 	{     "translation",      "USE_TRANSLATION",         "", true,  "Translation support" },
 	{          "vkeybd",        "ENABLE_VKEYBD",         "", false, "Virtual keyboard support"},
 	{       "keymapper",     "ENABLE_KEYMAPPER",         "", false, "Keymapper support"},


Commit: 14785b12d35882f5de0aa197213ae7eda505a463
    https://github.com/scummvm/scummvm/commit/14785b12d35882f5de0aa197213ae7eda505a463
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CONFIGURE: Fix cloud support detection

Changed paths:
    configure



diff --git a/configure b/configure
index 8b6068b..de01ca7 100755
--- a/configure
+++ b/configure
@@ -4193,22 +4193,24 @@ define_in_config_if_yes "$_libcurl" "USE_LIBCURL"
 #
 # Check whether to build cloud integration support
 #
-echo_n "Cloud integration"
+echo_n "Cloud integration..."
 if test "$_cloud" = "no"; then
 	echo "no"
 else
-	if test "_sdl_net" = "yes" -or "_libcurl" = "yes"; then		
+	_cloud=no
+	if test "$_sdlnet" = "yes"; then
 		_cloud=yes
-		if test "_sdl_net" = "yes"; then
-			echo "local"
-		fi
-		if test "_libcurl" = "yes"; then
-			echo "servers"		
-		fi
-	else
-		_cloud=no
-		echo "no"
+		echo_n "local"
+	fi
+	if test "$_libcurl" = "yes"; then
+		if test "$_cloud" = "yes"; then echo_n ", "; fi
+		_cloud=yes
+		echo_n "servers"
+	fi
+	if test "$_cloud" = "no"; then
+		echo_n "no"
 	fi
+	echo  # newline
 fi
 define_in_config_if_yes $_cloud 'USE_CLOUD'
 


Commit: b272bba7519951f38aa1aa34c70197c7be1b63fd
    https://github.com/scummvm/scummvm/commit/b272bba7519951f38aa1aa34c70197c7be1b63fd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Do minor fixes

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    base/version.cpp
    common/module.mk
    common/system.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b841575..a8e3076 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -28,7 +28,8 @@ void example1();
 void example2();
 void example3();
 
-namespace Cloud { namespace Dropbox {
+namespace Cloud {
+namespace Dropbox {
 
 void DropboxStorage::handler() {
 	if (_firstTime) {
@@ -48,7 +49,8 @@ void DropboxStorage::syncSaves() {
 	setTimeout(1000000); //in one second
 }
 
-} } //end of namespace Cloud::Dropbox
+} //end of namespace Dropbox
+} //end of namespace Cloud
 
 /// SimpleJSON examples:
 
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 3272ecf..ca6b9bf 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -26,7 +26,7 @@
 
 namespace Cloud {
 
-void cloudThread(void *thread) {
+static void cloudThread(void *thread) {
 	Storage *cloudThread = (Storage *)thread;
 	cloudThread->handler();
 }
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 4afa36f..e71b8b7 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -28,7 +28,7 @@
 namespace Cloud {
 
 class Storage {
-	friend void cloudThread(void*); //calls handler()
+	friend void cloudThread(void *); //calls handler()
 
 protected:
 	virtual void handler();
@@ -45,7 +45,8 @@ public:
 	* @param path		directory to list	
 	*/
 
-	//TODO: actually make it list directories and some callback to pass gathered files list
+	//TODO: actually make it list directories
+	//TODO: add some callback to pass gathered files list
 
 	virtual void listDirectory(Common::String path) = 0;
 
diff --git a/base/version.cpp b/base/version.cpp
index b2a7111..d40dd57 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -160,7 +160,7 @@ const char *gScummVMFeatures = ""
 #ifdef USE_LIBCURL
 	"servers"
 #ifdef USE_SDL_NET
-	" "
+	", "
 #endif
 #endif
 #ifdef USE_SDL_NET
diff --git a/common/module.mk b/common/module.mk
index 7118ea3..54aa16f 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -62,8 +62,5 @@ MODULE_OBJS += \
 	updates.o
 endif
 
-ifdef USE_CLOUD
-endif
-
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/common/system.h b/common/system.h
index b24bc0a..a3092e7 100644
--- a/common/system.h
+++ b/common/system.h
@@ -56,9 +56,7 @@ class HardwareInputSet;
 class Keymap;
 class KeymapperDefaultBindings;
 #endif
-#if defined(USE_CLOUD)
 class CloudManager;
-#endif
 }
 
 class AudioCDManager;


Commit: 1b89e25580c186fc2cc1821acee942e23fea3682
    https://github.com/scummvm/scummvm/commit/1b89e25580c186fc2cc1821acee942e23fea3682
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add first Request

Just fooling around with the example Request, but the idea works well.

Changed paths:
  A backends/cloud/dropbox/finalcountdownrequest.h
  A backends/cloud/request.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index a8e3076..8a0772d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -21,187 +21,25 @@
 */
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/cloud/dropbox/finalcountdownrequest.h"
 #include "common/debug.h"
-#include "common/json.h"
-
-void example1();
-void example2();
-void example3();
 
 namespace Cloud {
 namespace Dropbox {
 
-void DropboxStorage::handler() {
-	if (_firstTime) {
-		_firstTime = false;
-
-		example1();
-		example2();
-		example3();
-	} else { }
+static void finalCountdownCallback(void *ptr) {
+	warning("ladies and gentlemen, this is your callback speaking");
+	warning("the returned pointer is %d", ptr);
+	warning("thank you for your attention");
 }
 
 void DropboxStorage::listDirectory(Common::String path) {
-	setTimeout(1000000); //in one second
+	startTimer(1000000); //in one second
 }
 
-void DropboxStorage::syncSaves() {
-	setTimeout(1000000); //in one second
+void DropboxStorage::syncSaves() {	
+	addRequest(new FinalCountdownRequest(finalCountdownCallback));	
 }
 
 } //end of namespace Dropbox
 } //end of namespace Cloud
-
-/// SimpleJSON examples:
-
-using Common::JSON;
-using Common::JSONValue;
-using Common::JSONArray;
-using Common::JSONObject;
-
-// Just some sample JSON text, feel free to change but could break demo
-const char *EXAMPLE = "\
-{ \
-	\"string_name\" : \"string\tvalue and a \\\"quote\\\" and a unicode char \\u00BE and a c:\\\\path\\\\ or a \\/unix\\/path\\/ :D\", \
-	\"bool_name\" : true, \
-	\"bool_second\" : FaLsE, \
-	\"null_name\" : nULl, \
-	\"negative\" : -34.276, \
-	\"sub_object\" : { \
-						\"foo\" : \"abc\", \
-						 \"bar\" : 1.35e2, \
-						 \"blah\" : { \"a\" : \"A\", \"b\" : \"B\", \"c\" : \"C\" } \
-					}, \
-	\"array_letters\" : [ \"a\", \"b\", \"c\", [ 1, 2, 3  ]  ] \
-}    ";
-
-// Example 1
-void example1() {
-	// Parse example data
-	JSONValue *value = JSON::parse(EXAMPLE);
-
-	// Did it go wrong?
-	if (value == NULL) {
-		debug("Example code failed to parse, did you change it?\r\n");
-	} else {
-		// Retrieve the main object
-		JSONObject root;
-		if (value->isObject() == false) {
-			debug("The root element is not an object, did you change the example?\r\n");
-		} else {
-			root = value->asObject();
-
-			// Retrieving a string
-			if (root.find("string_name") != root.end() && root["string_name"]->isString()) {
-				debug("string_name:\r\n");
-				debug("------------\r\n");
-				debug(root["string_name"]->asString().c_str());
-				debug("\r\n\r\n");
-			}
-
-			// Retrieving a boolean
-			if (root.find("bool_second") != root.end() && root["bool_second"]->isBool()) {
-				debug("bool_second:\r\n");
-				debug("------------\r\n");
-				debug(root["bool_second"]->asBool() ? "it's true!" : "it's false!");
-				debug("\r\n\r\n");
-			}
-
-			// Retrieving an array
-			if (root.find("array_letters") != root.end() && root["array_letters"]->isArray()) {
-				JSONArray array = root["array_letters"]->asArray();
-				debug("array_letters:\r\n");
-				debug("--------------\r\n");
-				for (unsigned int i = 0; i < array.size(); i++) {
-					//wstringstream output;
-					debug("[%d] => %s\r\n", i, array[i]->stringify().c_str());
-				}
-				debug("\r\n");
-			}
-
-			// Retrieving nested object
-			if (root.find("sub_object") != root.end() && root["sub_object"]->isObject()) {
-				debug("sub_object:\r\n");
-				debug("-----------\r\n");
-				debug(root["sub_object"]->stringify().c_str());
-				debug("\r\n\r\n");
-			}
-		}
-
-		delete value;
-	}
-}
-
-// Example 3 : compact vs. prettyprint
-void example2() {
-	const char *EXAMPLE3 =
-			"{\
-	 \"SelectedTab\":\"Math\",\
-	 	\"Widgets\":[\
-			{\"WidgetPosition\":[0,369,800,582],\"WidgetIndex\":1,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,453,283,489],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F2.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":2,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,494,283,530],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F3.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":3,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[235,536,283,572],\"IsWidgetVisible\":-1,\"Caption\":\"On\",\"EnableCaption\":-1,\"Name\":\"F4.View\",\"CaptionPosition\":2,\"ControlWidth\":25,\"ControlHeight\":36,\"Font\":0,\"Value\":\"Off\",\"WidgetIndex\":4,\"WidgetType\":\"WidgetCheckbox.1\"},\
-			{\"WidgetPosition\":[287,417,400,439],\"IsWidgetVisible\":-1,\"Caption\":\"\",\"EnableCaption\":0,\"Name\":\"F1.Equation\",\"CaptionPosition\":1,\"ControlWidth\":113,\"ControlHeight\":22,\"Font\":0,\"AlignText\":0,\"EnableBorder\":0,\"CaptionOnly\":0,\"Value\":\"FFT(C1)\",\"WidgetIndex\":9,\"WidgetType\":\"WidgetStaticText.1\"},\
-			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":0,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.MeasureOpGui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Measurement To Graph\",\"Value\":\"Amplitude\",\"WidgetIndex\":17,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
-			{\"WidgetPosition\":[191,409,230,445],\"IsWidgetVisible\":-1,\"Caption\":\"F1\",\"EnableCaption\":0,\"Name\":\"F1.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"FFT\",\"WidgetIndex\":25,\"WidgetType\":\"WidgetProcessorCombobox.1\"},\
-			{\"WidgetPosition\":[191,452,230,487],\"IsWidgetVisible\":-1,\"Caption\":\"F2\",\"EnableCaption\":0,\"Name\":\"F2.Operator1gui\",\"CaptionPosition\":1,\"ControlWidth\":39,\"ControlHeight\":36,\"Font\":0,\"ButtonOnly\":-1,\"PickerTitle\":\"Select Math Operator\",\"Value\":\"Zoom\",\"WidgetIndex\":26,\"WidgetType\":\"WidgetProcessorCombobox.1\"}\
-		]\
-	 }";
-
-	// Parse example data
-	JSONValue *value = JSON::parse(EXAMPLE3);
-	if (value) {
-		debug("-----------\r\n");
-		debug(value->stringify().c_str());
-		debug("\r\n");
-		debug("-----------\r\n");
-		debug(value->stringify(true).c_str());
-		debug("\r\n");
-		debug("-----------\r\n");
-	}
-
-	// Clean up
-	delete value;
-}
-
-// Example 4 : List keys in an object.
-void example3() {
-	// Parse the example.
-	JSONValue *main_object = JSON::parse(EXAMPLE);
-	if (main_object == NULL) {
-		debug("Example code failed to parse, did you change it?\r\n");
-	} else if (!main_object->isObject()) {
-		debug("Example code is not an object, did you change it?\r\n");
-		delete main_object;
-	} else {
-		// Print the main object.
-		debug("Main object:\r\n");
-		debug(main_object->stringify(true).c_str());
-		debug("-----------\r\n");
-
-		// Fetch the keys and print them out.
-		Common::Array<Common::String> keys = main_object->objectKeys();
-
-		Common::Array<Common::String>::iterator iter = keys.begin();
-		while (iter != keys.end()) {
-			debug("Key: ");
-			debug((*iter).c_str());
-			debug("\r\n");
-
-			// Get the key's value.
-			JSONValue *key_value = main_object->child((*iter).c_str());
-			if (key_value) {
-				debug("Value: ");
-				debug(key_value->stringify().c_str());
-				debug("\r\n");
-				debug("-----------\r\n");
-			}
-
-			// Next key.
-			iter++;
-		}
-
-		delete main_object;
-	}
-}
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 43ed9dc..f80854b 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -27,14 +27,9 @@
 
 namespace Cloud { namespace Dropbox {
 
-class DropboxStorage: public Cloud::Storage {
-	bool _firstTime;
-
-protected:
-	virtual void handler();
-
+class DropboxStorage: public Cloud::Storage {	
 public:
-	DropboxStorage() : _firstTime(true) {};
+	DropboxStorage() {};
 
 	virtual void listDirectory(Common::String path);
 	virtual void syncSaves();
diff --git a/backends/cloud/dropbox/finalcountdownrequest.h b/backends/cloud/dropbox/finalcountdownrequest.h
new file mode 100644
index 0000000..34e8a80
--- /dev/null
+++ b/backends/cloud/dropbox/finalcountdownrequest.h
@@ -0,0 +1,52 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_FINALCOUNTDOWNREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_FINALCOUNTDOWNREQUEST_H
+
+#include "backends/cloud/request.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+class FinalCountdownRequest : public Cloud::Request {
+	int _times;
+
+public:
+	FinalCountdownRequest(Callback cb) : Request(cb), _times(5) {};
+
+	virtual bool handle() {
+		if(--_times == 0) {
+			warning("It's the final countdown!");
+			_callback(0); //meh, don't have anything for you, my caller
+			return true;
+		}
+
+		warning("%d...", _times);
+		return false;
+	}
+};
+
+}
+}  //end of namespace Cloud::Dropbox
+
+#endif
diff --git a/backends/cloud/request.h b/backends/cloud/request.h
new file mode 100644
index 0000000..ae85c23
--- /dev/null
+++ b/backends/cloud/request.h
@@ -0,0 +1,53 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_REQUEST_H
+#define BACKENDS_CLOUD_REQUEST_H
+
+namespace Cloud {
+
+class Request {
+protected:
+	/**
+	* Callback, which should be called before Request returns true in handle().
+	* That's the way Requests pass the result to the code which asked to create this request.	
+	*/
+
+	typedef void(*Callback)(void *result);
+	Callback _callback;
+
+public:
+	Request(Callback cb): _callback(cb) {};
+	virtual ~Request() {};
+
+	/**
+	* Method, which does actual work. Depends on what this Request is doing.
+	*
+	* @return true if request's work is complete and it may be removed from Storage's list
+	*/
+
+	virtual bool handle() = 0;
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index ca6b9bf..597eb6b 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -31,19 +31,37 @@ static void cloudThread(void *thread) {
 	cloudThread->handler();
 }
 
+Storage::Storage() : _timerStarted(false) {}
+
+void Storage::addRequest(Request *request) {
+	_requests.push_back(request);
+	if (!_timerStarted) startTimer();
+}
+
 void Storage::handler() {
-	unsetTimeout();
+	//TODO: lock mutex here (in case another handler() would be called before this one ends)
+	warning("handler's here");
+	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
+		if ((*i)->handle()) _requests.erase(i);
+		else ++i;
+	}
+	if (_requests.empty()) stopTimer();
+	//TODO: unlock mutex here
 }
 
-void Storage::setTimeout(int interval) {
+void Storage::startTimer(int interval) {
 	Common::TimerManager *manager = g_system->getTimerManager();
-	if (!manager->installTimerProc(cloudThread, interval, this, "Cloud Thread"))
-		; // warning("Failed to create cloud thread");
+	if (manager->installTimerProc(cloudThread, interval, this, "Cloud Thread")) {
+		_timerStarted = true;
+	} else {
+		warning("Failed to create cloud thread");		
+	}
 }
 
-void Storage::unsetTimeout() {
+void Storage::stopTimer() {
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(cloudThread);
+	_timerStarted = false;
 }
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index e71b8b7..dcaa6af 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -24,19 +24,25 @@
 #define BACKENDS_CLOUD_STORAGE_H
 
 #include "common/str.h"
+#include "common/array.h"
+#include "backends/cloud/request.h"
 
 namespace Cloud {
 
 class Storage {
 	friend void cloudThread(void *); //calls handler()
+	bool _timerStarted;
 
 protected:
-	virtual void handler();
-	virtual void setTimeout(int interval);
-	virtual void unsetTimeout();
+	Common::Array<Request *> _requests;
+
+	virtual void addRequest(Request *request); //starts the timer if it's not started
+	virtual void handler();	
+	virtual void startTimer(int interval = 1000000); //1 second is the default interval
+	virtual void stopTimer();
 
 public:
-	Storage() {};
+	Storage();
 	virtual ~Storage() {};
 
 	/**


Commit: 6ad983bb7220eed2f635163a7fcfdac811b80ca0
    https://github.com/scummvm/scummvm/commit/6ad983bb7220eed2f635163a7fcfdac811b80ca0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix GCC static cloudThread compile error

It's not static anymore.

Changed paths:
    backends/cloud/dropbox/finalcountdownrequest.h
    backends/cloud/manager.cpp
    backends/cloud/storage.cpp



diff --git a/backends/cloud/dropbox/finalcountdownrequest.h b/backends/cloud/dropbox/finalcountdownrequest.h
index 34e8a80..5ef9421 100644
--- a/backends/cloud/dropbox/finalcountdownrequest.h
+++ b/backends/cloud/dropbox/finalcountdownrequest.h
@@ -35,7 +35,7 @@ public:
 	FinalCountdownRequest(Callback cb) : Request(cb), _times(5) {};
 
 	virtual bool handle() {
-		if(--_times == 0) {
+		if (--_times == 0) {
 			warning("It's the final countdown!");
 			_callback(0); //meh, don't have anything for you, my caller
 			return true;
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 05b2377..58bb0ce 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -25,7 +25,7 @@
 
 namespace Cloud {
 
-Manager::Manager(): _currentStorage(new Dropbox::DropboxStorage()) {};
+Manager::Manager(): _currentStorage(new Dropbox::DropboxStorage()) {}
 
 Manager::~Manager() { delete _currentStorage; }
 
@@ -38,4 +38,4 @@ void Manager::syncSaves() {
 	if (storage) storage->syncSaves();
 }
 
-} //end of namespace Cloud
\ No newline at end of file
+} //end of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 597eb6b..23dcd8c 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -26,7 +26,7 @@
 
 namespace Cloud {
 
-static void cloudThread(void *thread) {
+void cloudThread(void *thread) {
 	Storage *cloudThread = (Storage *)thread;
 	cloudThread->handler();
 }


Commit: 8b585d631b21441dfa3de69d2c7c848d172c73be
    https://github.com/scummvm/scummvm/commit/8b585d631b21441dfa3de69d2c7c848d172c73be
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CurlRequest

CurlRequest uses own multi_handle, in which it creates an easy_handle to
make a request.

Every time `handle()` is called it checks whether request is complete
and, if it is, stops.

Changed paths:
  A backends/cloud/dropbox/curlrequest.cpp
  A backends/cloud/dropbox/curlrequest.h
  R backends/cloud/dropbox/finalcountdownrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.cpp
    backends/module.mk



diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp
new file mode 100644
index 0000000..216b56c
--- /dev/null
+++ b/backends/cloud/dropbox/curlrequest.cpp
@@ -0,0 +1,82 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/dropbox/curlrequest.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace Dropbox {
+
+static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
+	debug("%p got %d more bytes", p, n * l);
+	return n * l;
+}
+
+CurlRequest::CurlRequest(Callback cb, char *url) : Request(cb), _firstTime(true) {
+	_curlm = curl_multi_init();
+	_url = url;
+}
+
+CurlRequest::~CurlRequest() {
+	curl_multi_cleanup(_curlm);
+}
+
+bool CurlRequest::handle() {
+	if (_firstTime) {
+		CURL *eh = curl_easy_init();
+		curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, curlDataCallback);
+		curl_easy_setopt(eh, CURLOPT_WRITEDATA, this);
+		curl_easy_setopt(eh, CURLOPT_HEADER, 0L);
+		curl_easy_setopt(eh, CURLOPT_URL, _url);
+		curl_easy_setopt(eh, CURLOPT_VERBOSE, 0L);
+		curl_multi_add_handle(_curlm, eh);
+
+		_firstTime = false;
+	}
+
+	int U;
+	curl_multi_perform(_curlm, &U);
+
+	int Q;
+	CURLMsg *_curlMsg;
+	while ((_curlMsg = curl_multi_info_read(_curlm, &Q))) {
+		if (_curlMsg->msg == CURLMSG_DONE) {			
+			CURL *e = _curlMsg->easy_handle;
+			debug("R: %d - %s\n", _curlMsg->data.result, curl_easy_strerror(_curlMsg->data.result));
+			curl_multi_remove_handle(_curlm, e);
+			curl_easy_cleanup(e);
+
+			_callback(0);
+			return true;
+		} else {
+			debug("E: CURLMsg (%d)\n", _curlMsg->msg);
+		}
+	}
+
+	return false;
+}
+
+} //end of namespace Dropbox
+} //end of namespace Cloud
diff --git a/backends/cloud/dropbox/curlrequest.h b/backends/cloud/dropbox/curlrequest.h
new file mode 100644
index 0000000..8453fbb
--- /dev/null
+++ b/backends/cloud/dropbox/curlrequest.h
@@ -0,0 +1,48 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_CURLREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_CURLREQUEST_H
+
+#include "backends/cloud/request.h"
+
+typedef void CURLM;
+
+namespace Cloud {
+namespace Dropbox {
+
+class CurlRequest : public Cloud::Request {
+	bool _firstTime;
+	CURLM *_curlm;
+	char *_url;
+
+public:
+	CurlRequest(Callback cb, char *url);
+	virtual ~CurlRequest();
+
+	virtual bool handle();
+};
+
+}  //end of namespace Dropbox
+}  //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 8a0772d..142a059 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -19,18 +19,26 @@
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
-#include "backends/cloud/dropbox/finalcountdownrequest.h"
+#include "backends/cloud/dropbox/curlrequest.h"
 #include "common/debug.h"
+#include <curl/curl.h>
 
 namespace Cloud {
 namespace Dropbox {
 
-static void finalCountdownCallback(void *ptr) {
-	warning("ladies and gentlemen, this is your callback speaking");
-	warning("the returned pointer is %d", ptr);
-	warning("thank you for your attention");
+static void curlCallback(void *ptr) {
+	debug("--- curl request is complete ---");
+}
+
+DropboxStorage::DropboxStorage() {
+	curl_global_init(CURL_GLOBAL_ALL);
+}
+
+DropboxStorage::~DropboxStorage() {
+	curl_global_cleanup();
 }
 
 void DropboxStorage::listDirectory(Common::String path) {
@@ -38,7 +46,8 @@ void DropboxStorage::listDirectory(Common::String path) {
 }
 
 void DropboxStorage::syncSaves() {	
-	addRequest(new FinalCountdownRequest(finalCountdownCallback));	
+	addRequest(new CurlRequest(curlCallback, "tkachov.ru"));
+	addRequest(new CurlRequest(curlCallback, "bash.im"));
 }
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index f80854b..a022943 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -29,7 +29,8 @@ namespace Cloud { namespace Dropbox {
 
 class DropboxStorage: public Cloud::Storage {	
 public:
-	DropboxStorage() {};
+	DropboxStorage();
+	virtual ~DropboxStorage();
 
 	virtual void listDirectory(Common::String path);
 	virtual void syncSaves();
diff --git a/backends/cloud/dropbox/finalcountdownrequest.h b/backends/cloud/dropbox/finalcountdownrequest.h
deleted file mode 100644
index 5ef9421..0000000
--- a/backends/cloud/dropbox/finalcountdownrequest.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_DROPBOX_FINALCOUNTDOWNREQUEST_H
-#define BACKENDS_CLOUD_DROPBOX_FINALCOUNTDOWNREQUEST_H
-
-#include "backends/cloud/request.h"
-
-namespace Cloud {
-namespace Dropbox {
-
-class FinalCountdownRequest : public Cloud::Request {
-	int _times;
-
-public:
-	FinalCountdownRequest(Callback cb) : Request(cb), _times(5) {};
-
-	virtual bool handle() {
-		if (--_times == 0) {
-			warning("It's the final countdown!");
-			_callback(0); //meh, don't have anything for you, my caller
-			return true;
-		}
-
-		warning("%d...", _times);
-		return false;
-	}
-};
-
-}
-}  //end of namespace Cloud::Dropbox
-
-#endif
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 23dcd8c..0c5f6a3 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -42,7 +42,10 @@ void Storage::handler() {
 	//TODO: lock mutex here (in case another handler() would be called before this one ends)
 	warning("handler's here");
 	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		if ((*i)->handle()) _requests.erase(i);
+		if ((*i)->handle()) {
+			delete (*i);
+			_requests.erase(i);
+		}
 		else ++i;
 	}
 	if (_requests.empty()) stopTimer();
diff --git a/backends/module.mk b/backends/module.mk
index aa9e976..ccf5151 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -23,7 +23,8 @@ ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloud/manager.o \
 	cloud/storage.o \
-	cloud/dropbox/dropboxstorage.o
+	cloud/dropbox/dropboxstorage.o \
+	cloud/dropbox/curlrequest.o
 endif
 
 ifdef USE_ELF_LOADER


Commit: 01abba4f1dc9febbe99a4af6af19eb2afa3f618a
    https://github.com/scummvm/scummvm/commit/01abba4f1dc9febbe99a4af6af19eb2afa3f618a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ConnectionManager and NetworkReadStream

NetworkReadStream actually saves whole response in the memory now.

There is a pause mechanism in libcurl, but if libcurl is requesting
something compressed, it would have to uncompress data as it goes even
if we paused the request. Even though our own stream won't be notified
about this data when when "pause" the request, libcurl's own buffer
wound be expanding.

Changed paths:
  A backends/cloud/curl/connectionmanager.cpp
  A backends/cloud/curl/connectionmanager.h
  A backends/cloud/curl/networkreadstream.cpp
  A backends/cloud/curl/networkreadstream.h
    backends/cloud/dropbox/curlrequest.cpp
    backends/cloud/dropbox/curlrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/request.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    backends/module.mk



diff --git a/backends/cloud/curl/connectionmanager.cpp b/backends/cloud/curl/connectionmanager.cpp
new file mode 100644
index 0000000..d0a0ad8
--- /dev/null
+++ b/backends/cloud/curl/connectionmanager.cpp
@@ -0,0 +1,72 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/curl/connectionmanager.h"
+#include "backends/cloud/curl/networkreadstream.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+
+ConnectionManager::ConnectionManager(): _multi(0) {
+	curl_global_init(CURL_GLOBAL_ALL);
+	_multi = curl_multi_init();
+}
+
+ConnectionManager::~ConnectionManager() {
+	curl_multi_cleanup(_multi);
+	curl_global_cleanup();
+}
+
+NetworkReadStream *ConnectionManager::makeRequest(const char *url) {
+	NetworkReadStream *stream = new NetworkReadStream(url);
+	curl_multi_add_handle(_multi, stream->getEasyHandle());
+	return stream;
+}
+
+void ConnectionManager::handle() {
+	int U;
+	curl_multi_perform(_multi, &U);
+
+	int Q;
+	CURLMsg *curlMsg;
+	while ((curlMsg = curl_multi_info_read(_multi, &Q))) {
+		if (curlMsg->msg == CURLMSG_DONE) {
+			CURL *e = curlMsg->easy_handle;
+
+			NetworkReadStream *stream;
+			curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream);
+			if (stream) stream->done();			
+
+			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
+			curl_multi_remove_handle(_multi, e);			
+		}
+		else {
+			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
+			//TODO: notify stream on this case also
+		}
+	}
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/curl/connectionmanager.h b/backends/cloud/curl/connectionmanager.h
new file mode 100644
index 0000000..0ea2720
--- /dev/null
+++ b/backends/cloud/curl/connectionmanager.h
@@ -0,0 +1,47 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H
+#define BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H
+
+#include "common/str.h"
+
+typedef void CURLM;
+
+namespace Cloud {
+
+class NetworkReadStream;
+
+class ConnectionManager {
+	CURLM *_multi;
+
+public:
+	ConnectionManager();
+	virtual ~ConnectionManager();
+
+	NetworkReadStream *makeRequest(const char *url);
+	void handle();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/curl/networkreadstream.cpp b/backends/cloud/curl/networkreadstream.cpp
new file mode 100644
index 0000000..8adac67
--- /dev/null
+++ b/backends/cloud/curl/networkreadstream.cpp
@@ -0,0 +1,83 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/curl/networkreadstream.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+
+static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {	
+	NetworkReadStream *stream = (NetworkReadStream *)p;
+	if (stream) return stream->dataCallback(d, n, l);
+	return 0;
+}
+
+NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) {
+	_easy = curl_easy_init();
+	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
+	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
+	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+	curl_easy_setopt(_easy, CURLOPT_URL, url);
+	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+}
+
+NetworkReadStream::~NetworkReadStream() {
+	curl_easy_cleanup(_easy);
+}
+
+bool NetworkReadStream::eos() const {
+	return _eos;
+}
+
+uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+	uint32 available = _bytes.size();
+
+	if (available == 0) {
+		if (_requestComplete) _eos = true;
+		return 0;
+	}
+
+	char *data = (char *)dataPtr;
+	uint32 actuallyRead = (dataSize < available ? dataSize : available);	
+	for (uint32 i = 0; i < actuallyRead; ++i) data[i] = _bytes[i];
+	data[actuallyRead] = 0;
+	_bytes.erase(0, actuallyRead);
+	return actuallyRead;
+}
+
+void NetworkReadStream::done() {
+	_requestComplete = true;
+}
+
+size_t NetworkReadStream::dataCallback(char *d, size_t n, size_t l) {
+	//TODO: return CURL_WRITEFUNC_PAUSE if _bytes is too long
+	//TODO: remember https://curl.haxx.se/libcurl/c/curl_easy_pause.html (Memory Use / compressed data case)
+	//TODO: if using pause, don't forget to unpause it somehow from read() up there
+	_bytes += Common::String(d, n*l);
+	return n*l;
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/curl/networkreadstream.h b/backends/cloud/curl/networkreadstream.h
new file mode 100644
index 0000000..e469784
--- /dev/null
+++ b/backends/cloud/curl/networkreadstream.h
@@ -0,0 +1,76 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H
+#define BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H
+
+#include "common/stream.h"
+#include "common/str.h"
+
+typedef void CURL;
+
+namespace Cloud {
+
+class NetworkReadStream: public Common::ReadStream {	
+	CURL *_easy;
+	bool _eos, _requestComplete;
+	Common::String _bytes;
+
+public:	
+	NetworkReadStream(const char *url);
+	virtual ~NetworkReadStream();
+
+	CURL *getEasyHandle() const { return _easy; }
+
+	/**
+	* Returns true if a read failed because the stream end has been reached.
+	* This flag is cleared by clearErr().
+	* For a SeekableReadStream, it is also cleared by a successful seek.
+	*
+	* @note The semantics of any implementation of this method are
+	* supposed to match those of ISO C feof(). In particular, in a stream
+	* with N bytes, reading exactly N bytes from the start should *not*
+	* set eos; only reading *beyond* the available data should set it.
+	*/
+	virtual bool eos() const;
+
+	/**
+	* Read data from the stream. Subclasses must implement this
+	* method; all other read methods are implemented using it.
+	*
+	* @note The semantics of any implementation of this method are
+	* supposed to match those of ISO C fread(), in particular where
+	* it concerns setting error and end of file/stream flags.
+	*
+	* @param dataPtr	pointer to a buffer into which the data is read
+	* @param dataSize	number of bytes to be read
+	* @return the number of bytes which were actually read.
+	*/
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+
+	void done();
+	size_t dataCallback(char *d, size_t n, size_t l);
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp
index 216b56c..1dfdd9e 100644
--- a/backends/cloud/dropbox/curlrequest.cpp
+++ b/backends/cloud/dropbox/curlrequest.cpp
@@ -23,55 +23,37 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/curlrequest.h"
+#include "backends/cloud/curl/networkreadstream.h"
 #include "common/debug.h"
 #include <curl/curl.h>
 
 namespace Cloud {
 namespace Dropbox {
 
-static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
-	debug("%p got %d more bytes", p, n * l);
-	return n * l;
-}
-
-CurlRequest::CurlRequest(Callback cb, char *url) : Request(cb), _firstTime(true) {
-	_curlm = curl_multi_init();
+CurlRequest::CurlRequest(Callback cb, const char *url) : Request(cb), _firstTime(true), _stream(0) {	
 	_url = url;
 }
 
 CurlRequest::~CurlRequest() {
-	curl_multi_cleanup(_curlm);
+	if (_stream) delete _stream;
 }
 
-bool CurlRequest::handle() {
+bool CurlRequest::handle(ConnectionManager& manager) {
 	if (_firstTime) {
-		CURL *eh = curl_easy_init();
-		curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, curlDataCallback);
-		curl_easy_setopt(eh, CURLOPT_WRITEDATA, this);
-		curl_easy_setopt(eh, CURLOPT_HEADER, 0L);
-		curl_easy_setopt(eh, CURLOPT_URL, _url);
-		curl_easy_setopt(eh, CURLOPT_VERBOSE, 0L);
-		curl_multi_add_handle(_curlm, eh);
-
+		_stream = manager.makeRequest(_url);
 		_firstTime = false;
 	}
 
-	int U;
-	curl_multi_perform(_curlm, &U);
-
-	int Q;
-	CURLMsg *_curlMsg;
-	while ((_curlMsg = curl_multi_info_read(_curlm, &Q))) {
-		if (_curlMsg->msg == CURLMSG_DONE) {			
-			CURL *e = _curlMsg->easy_handle;
-			debug("R: %d - %s\n", _curlMsg->data.result, curl_easy_strerror(_curlMsg->data.result));
-			curl_multi_remove_handle(_curlm, e);
-			curl_easy_cleanup(e);
+	if (_stream) {
+		const int kBufSize = 10000;
+		char buf[kBufSize+1];
+		uint32 readBytes = _stream->read(buf, kBufSize);
+		debug("%d", readBytes);
+		//if(readBytes != 0) debug("%s", buf);
 
+		if(_stream->eos()) {
 			_callback(0);
 			return true;
-		} else {
-			debug("E: CURLMsg (%d)\n", _curlMsg->msg);
 		}
 	}
 
diff --git a/backends/cloud/dropbox/curlrequest.h b/backends/cloud/dropbox/curlrequest.h
index 8453fbb..2c8ab6b 100644
--- a/backends/cloud/dropbox/curlrequest.h
+++ b/backends/cloud/dropbox/curlrequest.h
@@ -25,21 +25,22 @@
 
 #include "backends/cloud/request.h"
 
-typedef void CURLM;
-
 namespace Cloud {
+
+class NetworkReadStream;
+
 namespace Dropbox {
 
 class CurlRequest : public Cloud::Request {
 	bool _firstTime;
-	CURLM *_curlm;
-	char *_url;
+	const char *_url;
+	NetworkReadStream *_stream;
 
 public:
-	CurlRequest(Callback cb, char *url);
+	CurlRequest(Callback cb, const char *url);
 	virtual ~CurlRequest();
 
-	virtual bool handle();
+	virtual bool handle(ConnectionManager& manager);
 };
 
 }  //end of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 142a059..9298cde 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -47,7 +47,7 @@ void DropboxStorage::listDirectory(Common::String path) {
 
 void DropboxStorage::syncSaves() {	
 	addRequest(new CurlRequest(curlCallback, "tkachov.ru"));
-	addRequest(new CurlRequest(curlCallback, "bash.im"));
+	addRequest(new CurlRequest(curlCallback, "scummvm.org"));
 }
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/request.h b/backends/cloud/request.h
index ae85c23..b4f5cca 100644
--- a/backends/cloud/request.h
+++ b/backends/cloud/request.h
@@ -23,6 +23,8 @@
 #ifndef BACKENDS_CLOUD_REQUEST_H
 #define BACKENDS_CLOUD_REQUEST_H
 
+#include "backends/cloud/curl/connectionmanager.h"
+
 namespace Cloud {
 
 class Request {
@@ -45,7 +47,7 @@ public:
 	* @return true if request's work is complete and it may be removed from Storage's list
 	*/
 
-	virtual bool handle() = 0;
+	virtual bool handle(ConnectionManager& manager) = 0;
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 0c5f6a3..d7217a5 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -21,6 +21,7 @@
 */
 
 #include "backends/cloud/storage.h"
+#include "common/debug.h"
 #include "common/system.h"
 #include "common/timer.h"
 
@@ -40,15 +41,17 @@ void Storage::addRequest(Request *request) {
 
 void Storage::handler() {
 	//TODO: lock mutex here (in case another handler() would be called before this one ends)
-	warning("handler's here");
+	debug("\nhandler's here");
 	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		if ((*i)->handle()) {
+		if ((*i)->handle(_connectionManager)) {
 			delete (*i);
 			_requests.erase(i);
 		}
 		else ++i;
 	}
 	if (_requests.empty()) stopTimer();
+
+	_connectionManager.handle();
 	//TODO: unlock mutex here
 }
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index dcaa6af..0949ff8 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -26,6 +26,7 @@
 #include "common/str.h"
 #include "common/array.h"
 #include "backends/cloud/request.h"
+#include "backends/cloud/curl/connectionmanager.h"
 
 namespace Cloud {
 
@@ -35,6 +36,7 @@ class Storage {
 
 protected:
 	Common::Array<Request *> _requests;
+	ConnectionManager _connectionManager;
 
 	virtual void addRequest(Request *request); //starts the timer if it's not started
 	virtual void handler();	
diff --git a/backends/module.mk b/backends/module.mk
index ccf5151..cf10089 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -24,7 +24,9 @@ MODULE_OBJS += \
 	cloud/manager.o \
 	cloud/storage.o \
 	cloud/dropbox/dropboxstorage.o \
-	cloud/dropbox/curlrequest.o
+	cloud/dropbox/curlrequest.o \
+	cloud/curl/connectionmanager.o \
+	cloud/curl/networkreadstream.o
 endif
 
 ifdef USE_ELF_LOADER


Commit: 9c22b7cc64a3bd074f7cec012bd2f29f210d4ccf
    https://github.com/scummvm/scummvm/commit/9c22b7cc64a3bd074f7cec012bd2f29f210d4ccf
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Rewrite NetworkReadStream

Now it is based on MemoryReadWriteStream, which is introduced by this
commit. This stream is using ring buffer and is dynamically increasing
its size when necessary.

Changed paths:
  A backends/networking/curl/connectionmanager.cpp
  A backends/networking/curl/connectionmanager.h
  A backends/networking/curl/networkreadstream.cpp
  A backends/networking/curl/networkreadstream.h
  R backends/cloud/curl/connectionmanager.cpp
  R backends/cloud/curl/connectionmanager.h
  R backends/cloud/curl/networkreadstream.cpp
  R backends/cloud/curl/networkreadstream.h
    backends/cloud/dropbox/curlrequest.cpp
    backends/cloud/dropbox/curlrequest.h
    backends/cloud/request.h
    backends/cloud/storage.h
    backends/module.mk
    common/memstream.h



diff --git a/backends/cloud/curl/connectionmanager.cpp b/backends/cloud/curl/connectionmanager.cpp
deleted file mode 100644
index d0a0ad8..0000000
--- a/backends/cloud/curl/connectionmanager.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/* 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.
-*
-*/
-
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
-#include "backends/cloud/curl/connectionmanager.h"
-#include "backends/cloud/curl/networkreadstream.h"
-#include "common/debug.h"
-#include <curl/curl.h>
-
-namespace Cloud {
-
-ConnectionManager::ConnectionManager(): _multi(0) {
-	curl_global_init(CURL_GLOBAL_ALL);
-	_multi = curl_multi_init();
-}
-
-ConnectionManager::~ConnectionManager() {
-	curl_multi_cleanup(_multi);
-	curl_global_cleanup();
-}
-
-NetworkReadStream *ConnectionManager::makeRequest(const char *url) {
-	NetworkReadStream *stream = new NetworkReadStream(url);
-	curl_multi_add_handle(_multi, stream->getEasyHandle());
-	return stream;
-}
-
-void ConnectionManager::handle() {
-	int U;
-	curl_multi_perform(_multi, &U);
-
-	int Q;
-	CURLMsg *curlMsg;
-	while ((curlMsg = curl_multi_info_read(_multi, &Q))) {
-		if (curlMsg->msg == CURLMSG_DONE) {
-			CURL *e = curlMsg->easy_handle;
-
-			NetworkReadStream *stream;
-			curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream);
-			if (stream) stream->done();			
-
-			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
-			curl_multi_remove_handle(_multi, e);			
-		}
-		else {
-			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
-			//TODO: notify stream on this case also
-		}
-	}
-}
-
-} //end of namespace Cloud
diff --git a/backends/cloud/curl/connectionmanager.h b/backends/cloud/curl/connectionmanager.h
deleted file mode 100644
index 0ea2720..0000000
--- a/backends/cloud/curl/connectionmanager.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H
-#define BACKENDS_CLOUD_CURL_CONNECTIONMANAGER_H
-
-#include "common/str.h"
-
-typedef void CURLM;
-
-namespace Cloud {
-
-class NetworkReadStream;
-
-class ConnectionManager {
-	CURLM *_multi;
-
-public:
-	ConnectionManager();
-	virtual ~ConnectionManager();
-
-	NetworkReadStream *makeRequest(const char *url);
-	void handle();
-};
-
-} //end of namespace Cloud
-
-#endif
diff --git a/backends/cloud/curl/networkreadstream.cpp b/backends/cloud/curl/networkreadstream.cpp
deleted file mode 100644
index 8adac67..0000000
--- a/backends/cloud/curl/networkreadstream.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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.
-*
-*/
-
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
-#include "backends/cloud/curl/networkreadstream.h"
-#include "common/debug.h"
-#include <curl/curl.h>
-
-namespace Cloud {
-
-static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {	
-	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->dataCallback(d, n, l);
-	return 0;
-}
-
-NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) {
-	_easy = curl_easy_init();
-	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
-	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
-	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
-	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
-	curl_easy_setopt(_easy, CURLOPT_URL, url);
-	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
-}
-
-NetworkReadStream::~NetworkReadStream() {
-	curl_easy_cleanup(_easy);
-}
-
-bool NetworkReadStream::eos() const {
-	return _eos;
-}
-
-uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
-	uint32 available = _bytes.size();
-
-	if (available == 0) {
-		if (_requestComplete) _eos = true;
-		return 0;
-	}
-
-	char *data = (char *)dataPtr;
-	uint32 actuallyRead = (dataSize < available ? dataSize : available);	
-	for (uint32 i = 0; i < actuallyRead; ++i) data[i] = _bytes[i];
-	data[actuallyRead] = 0;
-	_bytes.erase(0, actuallyRead);
-	return actuallyRead;
-}
-
-void NetworkReadStream::done() {
-	_requestComplete = true;
-}
-
-size_t NetworkReadStream::dataCallback(char *d, size_t n, size_t l) {
-	//TODO: return CURL_WRITEFUNC_PAUSE if _bytes is too long
-	//TODO: remember https://curl.haxx.se/libcurl/c/curl_easy_pause.html (Memory Use / compressed data case)
-	//TODO: if using pause, don't forget to unpause it somehow from read() up there
-	_bytes += Common::String(d, n*l);
-	return n*l;
-}
-
-} //end of namespace Cloud
diff --git a/backends/cloud/curl/networkreadstream.h b/backends/cloud/curl/networkreadstream.h
deleted file mode 100644
index e469784..0000000
--- a/backends/cloud/curl/networkreadstream.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H
-#define BACKENDS_CLOUD_CURL_NETWORKREADSTREAM_H
-
-#include "common/stream.h"
-#include "common/str.h"
-
-typedef void CURL;
-
-namespace Cloud {
-
-class NetworkReadStream: public Common::ReadStream {	
-	CURL *_easy;
-	bool _eos, _requestComplete;
-	Common::String _bytes;
-
-public:	
-	NetworkReadStream(const char *url);
-	virtual ~NetworkReadStream();
-
-	CURL *getEasyHandle() const { return _easy; }
-
-	/**
-	* Returns true if a read failed because the stream end has been reached.
-	* This flag is cleared by clearErr().
-	* For a SeekableReadStream, it is also cleared by a successful seek.
-	*
-	* @note The semantics of any implementation of this method are
-	* supposed to match those of ISO C feof(). In particular, in a stream
-	* with N bytes, reading exactly N bytes from the start should *not*
-	* set eos; only reading *beyond* the available data should set it.
-	*/
-	virtual bool eos() const;
-
-	/**
-	* Read data from the stream. Subclasses must implement this
-	* method; all other read methods are implemented using it.
-	*
-	* @note The semantics of any implementation of this method are
-	* supposed to match those of ISO C fread(), in particular where
-	* it concerns setting error and end of file/stream flags.
-	*
-	* @param dataPtr	pointer to a buffer into which the data is read
-	* @param dataSize	number of bytes to be read
-	* @return the number of bytes which were actually read.
-	*/
-	virtual uint32 read(void *dataPtr, uint32 dataSize);
-
-	void done();
-	size_t dataCallback(char *d, size_t n, size_t l);
-};
-
-} //end of namespace Cloud
-
-#endif
diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp
index 1dfdd9e..ecc868c 100644
--- a/backends/cloud/dropbox/curlrequest.cpp
+++ b/backends/cloud/dropbox/curlrequest.cpp
@@ -23,7 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/curlrequest.h"
-#include "backends/cloud/curl/networkreadstream.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/debug.h"
 #include <curl/curl.h>
 
@@ -38,7 +38,7 @@ CurlRequest::~CurlRequest() {
 	if (_stream) delete _stream;
 }
 
-bool CurlRequest::handle(ConnectionManager& manager) {
+bool CurlRequest::handle(Networking::ConnectionManager &manager) {
 	if (_firstTime) {
 		_stream = manager.makeRequest(_url);
 		_firstTime = false;
diff --git a/backends/cloud/dropbox/curlrequest.h b/backends/cloud/dropbox/curlrequest.h
index 2c8ab6b..3d5d4ad 100644
--- a/backends/cloud/dropbox/curlrequest.h
+++ b/backends/cloud/dropbox/curlrequest.h
@@ -25,22 +25,23 @@
 
 #include "backends/cloud/request.h"
 
-namespace Cloud {
-
+namespace Networking {
 class NetworkReadStream;
+}
 
+namespace Cloud {
 namespace Dropbox {
 
 class CurlRequest : public Cloud::Request {
 	bool _firstTime;
 	const char *_url;
-	NetworkReadStream *_stream;
+	Networking::NetworkReadStream *_stream;
 
 public:
 	CurlRequest(Callback cb, const char *url);
 	virtual ~CurlRequest();
 
-	virtual bool handle(ConnectionManager& manager);
+	virtual bool handle(Networking::ConnectionManager &manager);
 };
 
 }  //end of namespace Dropbox
diff --git a/backends/cloud/request.h b/backends/cloud/request.h
index b4f5cca..d85a68d 100644
--- a/backends/cloud/request.h
+++ b/backends/cloud/request.h
@@ -23,7 +23,7 @@
 #ifndef BACKENDS_CLOUD_REQUEST_H
 #define BACKENDS_CLOUD_REQUEST_H
 
-#include "backends/cloud/curl/connectionmanager.h"
+#include "backends/networking/curl/connectionmanager.h"
 
 namespace Cloud {
 
@@ -47,7 +47,7 @@ public:
 	* @return true if request's work is complete and it may be removed from Storage's list
 	*/
 
-	virtual bool handle(ConnectionManager& manager) = 0;
+	virtual bool handle(Networking::ConnectionManager &manager) = 0;
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 0949ff8..84b6157 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -26,7 +26,7 @@
 #include "common/str.h"
 #include "common/array.h"
 #include "backends/cloud/request.h"
-#include "backends/cloud/curl/connectionmanager.h"
+#include "backends/networking/curl/connectionmanager.h"
 
 namespace Cloud {
 
@@ -36,7 +36,7 @@ class Storage {
 
 protected:
 	Common::Array<Request *> _requests;
-	ConnectionManager _connectionManager;
+	Networking::ConnectionManager _connectionManager;
 
 	virtual void addRequest(Request *request); //starts the timer if it's not started
 	virtual void handler();	
diff --git a/backends/module.mk b/backends/module.mk
index cf10089..0be8ef9 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -24,9 +24,13 @@ MODULE_OBJS += \
 	cloud/manager.o \
 	cloud/storage.o \
 	cloud/dropbox/dropboxstorage.o \
-	cloud/dropbox/curlrequest.o \
-	cloud/curl/connectionmanager.o \
-	cloud/curl/networkreadstream.o
+	cloud/dropbox/curlrequest.o
+endif
+
+ifdef USE_LIBCURL
+MODULE_OBJS += \
+	networking/curl/connectionmanager.o \
+	networking/curl/networkreadstream.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
new file mode 100644
index 0000000..b7b7112
--- /dev/null
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -0,0 +1,71 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+ConnectionManager::ConnectionManager(): _multi(0) {
+	curl_global_init(CURL_GLOBAL_ALL);
+	_multi = curl_multi_init();
+}
+
+ConnectionManager::~ConnectionManager() {
+	curl_multi_cleanup(_multi);
+	curl_global_cleanup();
+}
+
+NetworkReadStream *ConnectionManager::makeRequest(const char *url) {
+	NetworkReadStream *stream = new NetworkReadStream(url);
+	curl_multi_add_handle(_multi, stream->getEasyHandle());
+	return stream;
+}
+
+void ConnectionManager::handle() {
+	int U;
+	curl_multi_perform(_multi, &U);
+
+	int Q;
+	CURLMsg *curlMsg;
+	while ((curlMsg = curl_multi_info_read(_multi, &Q))) {
+		if (curlMsg->msg == CURLMSG_DONE) {
+			CURL *e = curlMsg->easy_handle;
+
+			NetworkReadStream *stream;
+			curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream);
+			if (stream) stream->done();			
+
+			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
+			curl_multi_remove_handle(_multi, e);
+		} else {
+			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
+			//TODO: notify stream on this case also
+		}
+	}
+}
+
+} //end of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
new file mode 100644
index 0000000..fadcdf1
--- /dev/null
+++ b/backends/networking/curl/connectionmanager.h
@@ -0,0 +1,47 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
+#define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
+
+#include "common/str.h"
+
+typedef void CURLM;
+
+namespace Networking {
+
+class NetworkReadStream;
+
+class ConnectionManager {
+	CURLM *_multi;
+
+public:
+	ConnectionManager();
+	virtual ~ConnectionManager();
+
+	NetworkReadStream *makeRequest(const char *url);
+	void handle();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
new file mode 100644
index 0000000..5049ae5
--- /dev/null
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -0,0 +1,70 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {	
+	NetworkReadStream *stream = (NetworkReadStream *)p;
+	if (stream) return stream->write(d, n*l);
+	return 0;
+}
+
+NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) {
+	_easy = curl_easy_init();
+	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
+	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
+	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+	curl_easy_setopt(_easy, CURLOPT_URL, url);
+	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+}
+
+NetworkReadStream::~NetworkReadStream() {
+	curl_easy_cleanup(_easy);
+}
+
+bool NetworkReadStream::eos() const {
+	return _eos;
+}
+
+uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+	uint32 actuallyRead = MemoryReadWriteStream::read(dataPtr, dataSize);
+
+	if (actuallyRead == 0) {
+		if (_requestComplete) _eos = true;
+		return 0;
+	}
+
+	return actuallyRead;
+}
+
+void NetworkReadStream::done() {
+	_requestComplete = true;
+}
+
+} //end of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
new file mode 100644
index 0000000..9c7d8f8
--- /dev/null
+++ b/backends/networking/curl/networkreadstream.h
@@ -0,0 +1,75 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
+#define BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
+
+#include "common/memstream.h"
+#include "common/stream.h"
+#include "common/str.h"
+
+typedef void CURL;
+
+namespace Networking {
+
+class NetworkReadStream: public Common::MemoryReadWriteStream {	
+	CURL *_easy;
+	bool _eos, _requestComplete;
+
+public:	
+	NetworkReadStream(const char *url);
+	virtual ~NetworkReadStream();
+
+	CURL *getEasyHandle() const { return _easy; }
+
+	/**
+	* Returns true if a read failed because the stream end has been reached.
+	* This flag is cleared by clearErr().
+	* For a SeekableReadStream, it is also cleared by a successful seek.
+	*
+	* @note The semantics of any implementation of this method are
+	* supposed to match those of ISO C feof(). In particular, in a stream
+	* with N bytes, reading exactly N bytes from the start should *not*
+	* set eos; only reading *beyond* the available data should set it.
+	*/
+	virtual bool eos() const;
+
+	/**
+	* Read data from the stream. Subclasses must implement this
+	* method; all other read methods are implemented using it.
+	*
+	* @note The semantics of any implementation of this method are
+	* supposed to match those of ISO C fread(), in particular where
+	* it concerns setting error and end of file/stream flags.
+	*
+	* @param dataPtr	pointer to a buffer into which the data is read
+	* @param dataSize	number of bytes to be read
+	* @return the number of bytes which were actually read.
+	*/
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+
+	void done();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/common/memstream.h b/common/memstream.h
index 59d5f15..963637c 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -209,6 +209,89 @@ public:
 	bool seek(int32 offset, int whence = SEEK_SET);
 };
 
+/**
+* MemoryStream based on RingBuffer. Grows if has insufficient buffer size.
+*/
+class MemoryReadWriteStream : public WriteStream {
+private:
+	uint32 _capacity;
+	uint32 _size;
+	byte *_data;
+	uint32 _writePos, _readPos, _pos, _length;
+	DisposeAfterUse::Flag _disposeMemory;
+
+	void ensureCapacity(uint32 new_len) {
+		if (new_len <= _capacity)
+			return;
+
+		byte *old_data = _data;
+		uint32 oldCapacity = _capacity;
+
+		_capacity = MAX(new_len + 32, _capacity * 2);
+		_data = (byte *)malloc(_capacity);
+
+		if (old_data) {			
+			// Copy old data
+			if (_readPos < _writePos) {
+				memcpy(_data, old_data + _readPos, _writePos - _readPos);
+				_writePos -= _readPos;
+				_readPos = 0;
+			} else {
+				memcpy(_data, old_data + _readPos, oldCapacity - _readPos);
+				memcpy(_data + oldCapacity - _readPos, old_data, _writePos);
+				_writePos += oldCapacity - _readPos;
+				_readPos = 0;
+			}
+			free(old_data);
+		}
+	}
+public:
+	MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory) {}
+
+	~MemoryReadWriteStream() {
+		if (_disposeMemory)
+			free(_data);
+	}
+
+	uint32 write(const void *dataPtr, uint32 dataSize) {
+		ensureCapacity(_length + dataSize);
+		if (_writePos + dataSize < _capacity) {
+			memcpy(_data + _writePos, dataPtr, dataSize);
+		} else {
+			memcpy(_data + _writePos, dataPtr, _capacity - _writePos);
+			const byte *shiftedPtr = (const byte *)dataPtr + _capacity - _writePos;
+			memcpy(_data, shiftedPtr, dataSize - (_capacity - _writePos));
+		}
+		_writePos = (_writePos + dataSize) % _capacity;
+		_pos += dataSize;
+		_length += dataSize;
+		if (_pos > _size)
+			_size = _pos;
+		return dataSize;
+	}
+
+	uint32 read(void *dataPtr, uint32 dataSize) {
+		uint32 length = _length;
+		if (length < dataSize) dataSize = length;
+		if (dataSize == 0 || _capacity == 0) return 0;
+		if (_readPos + dataSize < _capacity) {
+			memcpy(dataPtr, _data + _readPos, dataSize);
+		} else {
+			memcpy(dataPtr, _data + _readPos, _capacity - _readPos);
+			byte *shiftedPtr = (byte *)dataPtr + _capacity - _readPos;
+			memcpy(shiftedPtr, _data, dataSize - (_capacity - _readPos));
+		}
+		_readPos = (_readPos + dataSize) % _capacity;
+		_length -= dataSize;
+		return dataSize;
+	}
+
+	uint32 pos() const { return _pos - _length; } //'read' position in the stream
+	uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+
+	byte *getData() { return _data; }	
+};
+
 } // End of namespace Common
 
 #endif


Commit: 03217cd5c3de3c17739a246f5967dfd4a14eb120
    https://github.com/scummvm/scummvm/commit/03217cd5c3de3c17739a246f5967dfd4a14eb120
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CurlJsonRequest

Now we can do REST API request by creating CurlJsonRequest and waiting
for it to call our callback. Passed pointer is Common::JSONValue.

This commit also does some minor variable renaming fixes.

Changed paths:
  A backends/networking/curl/curljsonrequest.cpp
  A backends/networking/curl/curljsonrequest.h
  R backends/cloud/dropbox/curlrequest.cpp
  R backends/cloud/dropbox/curlrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/module.mk
    backends/networking/curl/connectionmanager.cpp
    backends/platform/sdl/sdl.cpp
    common/system.h



diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp
deleted file mode 100644
index ecc868c..0000000
--- a/backends/cloud/dropbox/curlrequest.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/* 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.
-*
-*/
-
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
-#include "backends/cloud/dropbox/curlrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
-#include "common/debug.h"
-#include <curl/curl.h>
-
-namespace Cloud {
-namespace Dropbox {
-
-CurlRequest::CurlRequest(Callback cb, const char *url) : Request(cb), _firstTime(true), _stream(0) {	
-	_url = url;
-}
-
-CurlRequest::~CurlRequest() {
-	if (_stream) delete _stream;
-}
-
-bool CurlRequest::handle(Networking::ConnectionManager &manager) {
-	if (_firstTime) {
-		_stream = manager.makeRequest(_url);
-		_firstTime = false;
-	}
-
-	if (_stream) {
-		const int kBufSize = 10000;
-		char buf[kBufSize+1];
-		uint32 readBytes = _stream->read(buf, kBufSize);
-		debug("%d", readBytes);
-		//if(readBytes != 0) debug("%s", buf);
-
-		if(_stream->eos()) {
-			_callback(0);
-			return true;
-		}
-	}
-
-	return false;
-}
-
-} //end of namespace Dropbox
-} //end of namespace Cloud
diff --git a/backends/cloud/dropbox/curlrequest.h b/backends/cloud/dropbox/curlrequest.h
deleted file mode 100644
index 3d5d4ad..0000000
--- a/backends/cloud/dropbox/curlrequest.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_DROPBOX_CURLREQUEST_H
-#define BACKENDS_CLOUD_DROPBOX_CURLREQUEST_H
-
-#include "backends/cloud/request.h"
-
-namespace Networking {
-class NetworkReadStream;
-}
-
-namespace Cloud {
-namespace Dropbox {
-
-class CurlRequest : public Cloud::Request {
-	bool _firstTime;
-	const char *_url;
-	Networking::NetworkReadStream *_stream;
-
-public:
-	CurlRequest(Callback cb, const char *url);
-	virtual ~CurlRequest();
-
-	virtual bool handle(Networking::ConnectionManager &manager);
-};
-
-}  //end of namespace Dropbox
-}  //end of namespace Cloud
-
-#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 9298cde..2db915d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -22,15 +22,23 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
-#include "backends/cloud/dropbox/curlrequest.h"
+#include "backends/networking/curl/curljsonrequest.h"
 #include "common/debug.h"
+#include "common/json.h"
 #include <curl/curl.h>
 
 namespace Cloud {
 namespace Dropbox {
 
-static void curlCallback(void *ptr) {
-	debug("--- curl request is complete ---");
+static void curlJsonCallback(void *ptr) {
+	Common::JSONValue *json = (Common::JSONValue *)ptr;
+	if (json) {
+		debug("curlJsonCallback:");
+		debug("%s", json->stringify(true).c_str());
+		delete json;
+	} else {
+		debug("curlJsonCallback: got NULL instead of JSON!");
+	}
 }
 
 DropboxStorage::DropboxStorage() {
@@ -45,9 +53,9 @@ void DropboxStorage::listDirectory(Common::String path) {
 	startTimer(1000000); //in one second
 }
 
-void DropboxStorage::syncSaves() {	
-	addRequest(new CurlRequest(curlCallback, "tkachov.ru"));
-	addRequest(new CurlRequest(curlCallback, "scummvm.org"));
+void DropboxStorage::syncSaves() {
+	//not so Dropbox, just testing JSON requesting & parsing:
+	addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=205387401"));
 }
 
 } //end of namespace Dropbox
diff --git a/backends/module.mk b/backends/module.mk
index 0be8ef9..72183f9 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -23,14 +23,14 @@ ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloud/manager.o \
 	cloud/storage.o \
-	cloud/dropbox/dropboxstorage.o \
-	cloud/dropbox/curlrequest.o
+	cloud/dropbox/dropboxstorage.o
 endif
 
 ifdef USE_LIBCURL
 MODULE_OBJS += \
 	networking/curl/connectionmanager.o \
-	networking/curl/networkreadstream.o
+	networking/curl/networkreadstream.o \
+	networking/curl/curljsonrequest.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index b7b7112..ceff8b6 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -46,25 +46,25 @@ NetworkReadStream *ConnectionManager::makeRequest(const char *url) {
 }
 
 void ConnectionManager::handle() {
-	int U;
-	curl_multi_perform(_multi, &U);
+	int transfersRunning;
+	curl_multi_perform(_multi, &transfersRunning);
 
-	int Q;
+	int messagesInQueue;
 	CURLMsg *curlMsg;
-	while ((curlMsg = curl_multi_info_read(_multi, &Q))) {
-		if (curlMsg->msg == CURLMSG_DONE) {
-			CURL *e = curlMsg->easy_handle;
+	while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
+		CURL *easyHandle = curlMsg->easy_handle;
 
-			NetworkReadStream *stream;
-			curl_easy_getinfo(e, CURLINFO_PRIVATE, &stream);
-			if (stream) stream->done();			
+		NetworkReadStream *stream;
+		curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
+		if (stream) stream->done(); //I'm not sure it's OK to notify "done()" on failure
 
-			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
-			curl_multi_remove_handle(_multi, e);
+		if (curlMsg->msg == CURLMSG_DONE) {
+			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));			
 		} else {
-			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
-			//TODO: notify stream on this case also
+			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);			
 		}
+
+		curl_multi_remove_handle(_multi, easyHandle);
 	}
 }
 
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
new file mode 100644
index 0000000..acd9675
--- /dev/null
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -0,0 +1,64 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0) {	
+	_url = url;
+}
+
+CurlJsonRequest::~CurlJsonRequest() {
+	if (_stream) delete _stream;
+}
+
+bool CurlJsonRequest::handle(ConnectionManager &manager) {
+	if (!_stream) _stream = manager.makeRequest(_url);
+
+	if (_stream) {
+		const int kBufSize = 16*1024;
+		char buf[kBufSize+1];
+		uint32 readBytes = _stream->read(buf, kBufSize);
+		if (readBytes != 0) _contents += Common::String(buf, readBytes);
+		if (_stream->eos()) {
+			//TODO: check that stream's CURL easy handle's HTTP response code is 200 OK
+			debug("CurlJsonRequest: contents:\n%s", _contents.c_str());
+			if (_callback) {
+				Common::JSONValue *json = Common::JSON::parse(_contents.c_str()); //TODO: somehow fix JSON to work with UTF-8
+				debug("CurlJsonRequest: JSONValue pointer = %p", json);
+				_callback(json); //potential memory leak, free it in your callbacks!
+			}
+			return true;
+		}
+	}
+
+	return false;
+}
+
+} //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
new file mode 100644
index 0000000..73e0144
--- /dev/null
+++ b/backends/networking/curl/curljsonrequest.h
@@ -0,0 +1,46 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
+#define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
+
+#include "backends/cloud/request.h"
+
+namespace Networking {
+
+class NetworkReadStream;
+
+class CurlJsonRequest : public Cloud::Request {	
+	const char *_url;
+	NetworkReadStream *_stream;
+	Common::String _contents;
+
+public:
+	CurlJsonRequest(Callback cb, const char *url);
+	virtual ~CurlJsonRequest();
+
+	virtual bool handle(ConnectionManager &manager);
+};
+
+}  //end of namespace Networking
+
+#endif
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index f07f568..acb4d99 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -160,8 +160,8 @@ void OSystem_SDL::init() {
 #endif
 
 #if defined(USE_CLOUD)
-	if (_cloudThread == 0)
-		_cloudThread = new Cloud::Manager();
+	if (_cloudManager == 0)
+		_cloudManager = new Cloud::Manager();
 #endif
 
 }
diff --git a/common/system.h b/common/system.h
index a3092e7..815fa9d 100644
--- a/common/system.h
+++ b/common/system.h
@@ -185,7 +185,7 @@ protected:
 	*
 	* @note _cloudThread is deleted by the OSystem destructor.
 	*/
-	Common::CloudManager *_cloudThread;
+	Common::CloudManager *_cloudManager;
 #endif
 
 	/**
@@ -1134,7 +1134,7 @@ public:
 	* @return the CloudManager for the current architecture
 	*/
 	virtual Common::CloudManager *getCloudManager() {
-		return _cloudThread;
+		return _cloudManager;
 	}
 #endif
 


Commit: 5df8c5140292520bafe92efa94935a776d63d108
    https://github.com/scummvm/scummvm/commit/5df8c5140292520bafe92efa94935a776d63d108
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CurlJsonRequest

It's using MemoryWriteStreamDynamic instead of String and it prepares
raw byte contents of this stream for JSON::parse().

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 2db915d..729283b 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -56,6 +56,7 @@ void DropboxStorage::listDirectory(Common::String path) {
 void DropboxStorage::syncSaves() {
 	//not so Dropbox, just testing JSON requesting & parsing:
 	addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=205387401"));
+	addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=28870501"));
 }
 
 } //end of namespace Dropbox
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index ceff8b6..8c718e0 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -56,7 +56,7 @@ void ConnectionManager::handle() {
 
 		NetworkReadStream *stream;
 		curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
-		if (stream) stream->done(); //I'm not sure it's OK to notify "done()" on failure
+		if (stream) stream->finished();
 
 		if (curlMsg->msg == CURLMSG_DONE) {
 			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));			
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index acd9675..f5a7a7c 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -30,7 +30,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0) {	
+CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _contentsStream(DisposeAfterUse::YES) {
 	_url = url;
 }
 
@@ -38,6 +38,24 @@ CurlJsonRequest::~CurlJsonRequest() {
 	if (_stream) delete _stream;
 }
 
+char *CurlJsonRequest::getPreparedContents() {
+	//write one more byte in the end
+	byte zero[1] = { 0 };
+	_contentsStream.write(zero, 1);
+
+	//replace all "bad" bytes with '.' character
+	byte *result = _contentsStream.getData();
+	uint32 size = _contentsStream.size();
+	for (uint32 i = 0; i < size; ++i)
+		if (result[i] < 0x20 || result[i] > 0x7f)
+			result[i] = '.';
+
+	//make it zero-terminated string
+	result[size - 1] = '\0';
+
+	return (char *)result;
+}
+
 bool CurlJsonRequest::handle(ConnectionManager &manager) {
 	if (!_stream) _stream = manager.makeRequest(_url);
 
@@ -45,13 +63,17 @@ bool CurlJsonRequest::handle(ConnectionManager &manager) {
 		const int kBufSize = 16*1024;
 		char buf[kBufSize+1];
 		uint32 readBytes = _stream->read(buf, kBufSize);
-		if (readBytes != 0) _contents += Common::String(buf, readBytes);
-		if (_stream->eos()) {
-			//TODO: check that stream's CURL easy handle's HTTP response code is 200 OK
-			debug("CurlJsonRequest: contents:\n%s", _contents.c_str());
+		if (readBytes != 0)
+			if (_contentsStream.write(buf, readBytes) != readBytes)
+				warning("MemoryWriteStreamDynamic was unable to write all the bytes");
+
+		if (_stream->eos()) {			
+			if (_stream->httpResponseCode() != 200)
+				warning("HTTP response code is not 200 OK");
+
 			if (_callback) {
-				Common::JSONValue *json = Common::JSON::parse(_contents.c_str()); //TODO: somehow fix JSON to work with UTF-8
-				debug("CurlJsonRequest: JSONValue pointer = %p", json);
+				char *contents = getPreparedContents();				
+				Common::JSONValue *json = Common::JSON::parse(contents);				
 				_callback(json); //potential memory leak, free it in your callbacks!
 			}
 			return true;
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 73e0144..e963439 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -24,6 +24,7 @@
 #define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
 
 #include "backends/cloud/request.h"
+#include "common/memstream.h"
 
 namespace Networking {
 
@@ -32,7 +33,10 @@ class NetworkReadStream;
 class CurlJsonRequest : public Cloud::Request {	
 	const char *_url;
 	NetworkReadStream *_stream;
-	Common::String _contents;
+	Common::MemoryWriteStreamDynamic _contentsStream;
+
+	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
+	char *getPreparedContents();
 
 public:
 	CurlJsonRequest(Callback cb, const char *url);
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 5049ae5..b8cb81a 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -63,8 +63,15 @@ uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
 	return actuallyRead;
 }
 
-void NetworkReadStream::done() {
+void NetworkReadStream::finished() {
 	_requestComplete = true;
 }
 
+long NetworkReadStream::httpResponseCode() {
+	long responseCode = -1;
+	if (_easy)
+		curl_easy_getinfo(_easy, CURLINFO_RESPONSE_CODE, &responseCode);
+	return responseCode;
+}
+
 } //end of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 9c7d8f8..0333e4f 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -67,7 +67,21 @@ public:
 	*/
 	virtual uint32 read(void *dataPtr, uint32 dataSize);
 
-	void done();
+	/**
+	* This method is called by ConnectionManager to indicate
+	* that transfer is finished.
+	*
+	* @note It's called on failure too.
+	*/
+	void finished();
+
+	/**
+	* Returns HTTP response code from inner CURL handle.
+	* It returns -1 to indicate there is no inner handle.
+	*
+	* @note This method should be called when eos() == true.
+	*/
+	long httpResponseCode();
 };
 
 } //end of namespace Cloud


Commit: e743a65636a674def45e955cb7a5632aead2a033
    https://github.com/scummvm/scummvm/commit/e743a65636a674def45e955cb7a5632aead2a033
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Dropbox into CloudManager's configs

This commit adds:
* ConfMan's new "cloud" domain;
* CloudManager's init() method, where it loads keys from "cloud" configs
domain;
* CurlJsonRequest's addHeader() and addPostField() methods;
* temporary Storage's printInfo() method;
* DropboxStorage's implementation of printInfo(), which is using access
token and user id;
* DropboxStorage's loadFromConfig() static method to load access token
and user id from configs and create a Storage instance with those;
* temporary DropboxStorage's authThroughConsole() static method, which
guides user through auth process from the console.

So, in CloudManager's init() implementation ScummVM checks that there is
"current_storage_type" key in "cloud" domain of configs, and loads
corresponding storage if there is such key.

If there is no such key, ScummVM offers user to auth with Dropbox.
That's done through console, and thus it's temporary (it also requires
restarting ScummVM twice and manually editing config.ini file).

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h
    base/main.cpp
    common/cloudmanager.h
    common/config-manager.cpp
    common/config-manager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 729283b..add6bff 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -23,6 +23,7 @@
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
@@ -30,18 +31,44 @@
 namespace Cloud {
 namespace Dropbox {
 
-static void curlJsonCallback(void *ptr) {
+Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
+Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
+
+static void printJsonCallback(void *ptr) {
+	Common::JSONValue *json = (Common::JSONValue *)ptr;
+	if (json) {
+		debug("printJsonCallback:");
+		debug("%s", json->stringify(true).c_str());
+		delete json;
+	} else {
+		debug("printJsonCallback: got NULL instead of JSON!");
+	}
+}
+
+static void saveAccessTokenCallback(void *ptr) {
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
 	if (json) {
-		debug("curlJsonCallback:");
+		debug("saveAccessTokenCallback:");
 		debug("%s", json->stringify(true).c_str());
+
+		Common::JSONObject result = json->asObject();
+		if (!result.contains("access_token") || !result.contains("uid")) {
+			warning("Bad response, no token/uid passed");
+		} else {
+			ConfMan.set("current_storage_type", "Dropbox", "cloud");
+			ConfMan.set("current_storage_access_token", result.getVal("access_token")->asString(), "cloud");
+			ConfMan.set("current_storage_user_id", result.getVal("uid")->asString(), "cloud");
+			ConfMan.removeKey("dropbox_code", "cloud");
+			debug("Now please restart ScummVM to apply the changes.");
+		}
+
 		delete json;
 	} else {
-		debug("curlJsonCallback: got NULL instead of JSON!");
+		debug("saveAccessTokenCallback: got NULL instead of JSON!");
 	}
 }
 
-DropboxStorage::DropboxStorage() {
+DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
 	curl_global_init(CURL_GLOBAL_ALL);
 }
 
@@ -54,9 +81,78 @@ void DropboxStorage::listDirectory(Common::String path) {
 }
 
 void DropboxStorage::syncSaves() {
-	//not so Dropbox, just testing JSON requesting & parsing:
-	addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=205387401"));
-	addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=28870501"));
+	//not syncing, but already something:
+	printInfo();
+}
+
+void DropboxStorage::printInfo() {
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info");
+	request->addHeader("Authorization: Bearer " + _token);
+	addRequest(request);
+}
+
+DropboxStorage *DropboxStorage::loadFromConfig() {
+	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
+	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
+
+	if (!ConfMan.hasKey("current_storage_access_token", "cloud")) {
+		warning("No access_token found");
+		return 0;
+	}
+
+	if (!ConfMan.hasKey("current_storage_user_id", "cloud")) {
+		warning("No user_id found");
+		return 0;
+	}
+
+	Common::String accessToken = ConfMan.get("current_storage_access_token", "cloud");
+	Common::String userId = ConfMan.get("current_storage_user_id", "cloud");
+	return new DropboxStorage(accessToken, userId);
+}
+
+Common::String DropboxStorage::getAuthLink() {
+	Common::String url = "https://www.dropbox.com/1/oauth2/authorize";
+	url += "?response_type=code";
+	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
+	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
+	url += "&client_id=" + KEY;
+	return url;
+}
+
+DropboxStorage *DropboxStorage::authThroughConsole() {
+	if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) {
+		warning("No Dropbox keys available, cannot do auth");
+		return 0;
+	}
+
+	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
+	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
+
+	if (ConfMan.hasKey("dropbox_code", "cloud")) {
+		//phase 2: get access_token using specified code
+		return getAccessToken(ConfMan.get("dropbox_code", "cloud"));
+	}
+
+	debug("Navigate to this URL and press \"Allow\":");
+	debug("%s\n", getAuthLink().c_str());
+	debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
+	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
+	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");
+	return 0;
+}
+
+DropboxStorage *DropboxStorage::getAccessToken(Common::String code) {
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token");
+	request->addPostField("code=" + code);
+	request->addPostField("grant_type=authorization_code");
+	request->addPostField("client_id=" + KEY);
+	request->addPostField("client_secret=" + SECRET);
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+	
+	//OK, that's not how I imagined that...	
+	DropboxStorage *storage = new DropboxStorage("", "");
+	storage->addRequest(request);
+	return storage;
 }
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index a022943..f2281f1 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -24,18 +24,47 @@
 #define BACKENDS_CLOUD_DROPBOX_STORAGE_H
 
 #include "backends/cloud/storage.h"
+#include "../manager.h"
 
-namespace Cloud { namespace Dropbox {
+namespace Cloud {
+namespace Dropbox {
 
-class DropboxStorage: public Cloud::Storage {	
-public:
-	DropboxStorage();
+class DropboxStorage: public Cloud::Storage {
+	static Common::String KEY, SECRET;
+
+	Common::String _token, _uid;
+
+	/** This private constructor is called from loadFromConfig(). */
+	DropboxStorage(Common::String token, Common::String uid);
+
+	static DropboxStorage *getAccessToken(Common::String code);
+
+public:	
 	virtual ~DropboxStorage();
 
 	virtual void listDirectory(Common::String path);
 	virtual void syncSaves();
+	virtual void printInfo();	
+	/**
+	* Load token and user id from configs and return DropboxStorage for those.	
+	* @return pointer to the newly created DropboxStorage or 0 if some problem occured.
+	*/
+	static DropboxStorage *loadFromConfig();
+
+	/**
+	* Returns Dropbox auth link.
+	*/
+	static Common::String DropboxStorage::getAuthLink();
+
+	/**
+	* Show message with Dropbox auth instructions. (Temporary)
+	* Returns temporary DropboxStorage, which does network requests
+	* to get access token.
+	*/
+	static DropboxStorage *authThroughConsole();
 };
 
-} }  //end of namespace Cloud::Dropbox
+} //end of namespace Dropbox
+} //end of namespace Cloud
 
 #endif
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 58bb0ce..08340e9 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -22,13 +22,26 @@
 
 #include "backends/cloud/manager.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "common/config-manager.h"
 
 namespace Cloud {
 
-Manager::Manager(): _currentStorage(new Dropbox::DropboxStorage()) {}
+Manager::Manager(): _currentStorage(0) {}
 
 Manager::~Manager() { delete _currentStorage; }
 
+void Manager::init() {
+	if (ConfMan.hasKey("current_storage_type", "cloud")) {
+		Common::String storageType = ConfMan.get("current_storage_type", "cloud");
+		if (storageType == "Dropbox") _currentStorage = Dropbox::DropboxStorage::loadFromConfig();
+		else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
+	}
+	else {
+		//this is temporary console offer to auth with Dropbox (because there is no other storage type yet anyway)
+		Dropbox::DropboxStorage::authThroughConsole();
+	}
+}
+
 Storage* Manager::getCurrentStorage() {
 	return _currentStorage;
 }
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 11cc595..a1f046d 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -35,6 +35,8 @@ public:
 	Manager();
 	virtual ~Manager();
 
+	virtual void init();
+
 	virtual Storage* getCurrentStorage();
 	virtual void syncSaves();
 };
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 84b6157..a5d048d 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -63,6 +63,12 @@ public:
 	*/
 
 	virtual void syncSaves() = 0;
+
+	/**
+	* Prints user info on console. (Temporary)
+	*/
+
+	virtual void printInfo() = 0;
 };
 
 } //end of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 8c718e0..d34eab2 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -39,8 +39,8 @@ ConnectionManager::~ConnectionManager() {
 	curl_global_cleanup();
 }
 
-NetworkReadStream *ConnectionManager::makeRequest(const char *url) {
-	NetworkReadStream *stream = new NetworkReadStream(url);
+NetworkReadStream *ConnectionManager::makeRequest(const char *url, curl_slist *headersList, Common::String postFields) {
+	NetworkReadStream *stream = new NetworkReadStream(url, headersList, postFields);
 	curl_multi_add_handle(_multi, stream->getEasyHandle());
 	return stream;
 }
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index fadcdf1..b83de61 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -26,6 +26,7 @@
 #include "common/str.h"
 
 typedef void CURLM;
+struct curl_slist;
 
 namespace Networking {
 
@@ -38,7 +39,7 @@ public:
 	ConnectionManager();
 	virtual ~ConnectionManager();
 
-	NetworkReadStream *makeRequest(const char *url);
+	NetworkReadStream *makeRequest(const char *url, curl_slist *headersList, Common::String postFields);
 	void handle();
 };
 
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index f5a7a7c..0c63634 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -30,7 +30,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _contentsStream(DisposeAfterUse::YES) {
+CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
 	_url = url;
 }
 
@@ -57,7 +57,7 @@ char *CurlJsonRequest::getPreparedContents() {
 }
 
 bool CurlJsonRequest::handle(ConnectionManager &manager) {
-	if (!_stream) _stream = manager.makeRequest(_url);
+	if (!_stream) _stream = manager.makeRequest(_url, _headersList, _postFields);
 
 	if (_stream) {
 		const int kBufSize = 16*1024;
@@ -83,4 +83,16 @@ bool CurlJsonRequest::handle(ConnectionManager &manager) {
 	return false;
 }
 
+void CurlJsonRequest::addHeader(Common::String header) {
+	_headersList = curl_slist_append(_headersList, header.c_str());
+}
+
+void CurlJsonRequest::addPostField(Common::String keyValuePair) {
+	if (_postFields == "")
+		_postFields = keyValuePair;
+	else
+		_postFields += "&" + keyValuePair;
+}
+
+
 } //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index e963439..17df969 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -26,6 +26,8 @@
 #include "backends/cloud/request.h"
 #include "common/memstream.h"
 
+struct curl_slist;
+
 namespace Networking {
 
 class NetworkReadStream;
@@ -33,6 +35,8 @@ class NetworkReadStream;
 class CurlJsonRequest : public Cloud::Request {	
 	const char *_url;
 	NetworkReadStream *_stream;
+	curl_slist *_headersList;
+	Common::String _postFields;
 	Common::MemoryWriteStreamDynamic _contentsStream;
 
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
@@ -43,6 +47,10 @@ public:
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle(ConnectionManager &manager);
+
+	void addHeader(Common::String header);
+
+	void addPostField(Common::String header);
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index b8cb81a..f2af4fd 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -34,7 +34,9 @@ static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
-NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) {
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields):
+	_easy(0), _eos(false), _requestComplete(false)
+{
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
@@ -42,6 +44,9 @@ NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _r
 	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
+	curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
 }
 
 NetworkReadStream::~NetworkReadStream() {
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 0333e4f..bc4b761 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -28,6 +28,7 @@
 #include "common/str.h"
 
 typedef void CURL;
+struct curl_slist;
 
 namespace Networking {
 
@@ -36,7 +37,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 	bool _eos, _requestComplete;
 
 public:	
-	NetworkReadStream(const char *url);
+	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields);
 	virtual ~NetworkReadStream();
 
 	CURL *getEasyHandle() const { return _easy; }
diff --git a/base/main.cpp b/base/main.cpp
index ac24376..f629eb9 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -478,6 +478,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #endif
 		
 #ifdef USE_CLOUD
+	system.getCloudManager()->init();
 	system.getCloudManager()->syncSaves();
 #endif
 
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 6b57682..8a10a1a 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -29,8 +29,16 @@ namespace Common {
 
 class CloudManager {
 public:
-	CloudManager() {};
-	virtual ~CloudManager() {};
+	CloudManager() {}
+	virtual ~CloudManager() {}
+
+	/**
+	* Loads all information from configs and creates current Storage instance.
+	*
+	* @note It's called once on startup in scummvm_main().
+	*/
+
+	virtual void init() = 0;
 
 	/**
 	* Returns active Storage, which could be used to interact
diff --git a/common/config-manager.cpp b/common/config-manager.cpp
index 24c877a..40d8aca 100644
--- a/common/config-manager.cpp
+++ b/common/config-manager.cpp
@@ -45,6 +45,10 @@ char const *const ConfigManager::kTransientDomain = "__TRANSIENT";
 char const *const ConfigManager::kKeymapperDomain = "keymapper";
 #endif
 
+#ifdef USE_CLOUD
+char const *const ConfigManager::kCloudDomain = "cloud";
+#endif
+
 #pragma mark -
 
 
@@ -67,6 +71,9 @@ void ConfigManager::copyFrom(ConfigManager &source) {
 #ifdef ENABLE_KEYMAPPER
 	_keymapperDomain = source._keymapperDomain;
 #endif
+#ifdef USE_CLOUD
+	_cloudDomain = source._cloudDomain;
+#endif
 	_domainSaveOrder = source._domainSaveOrder;
 	_activeDomainName = source._activeDomainName;
 	_activeDomain = &_gameDomains[_activeDomainName];
@@ -121,6 +128,10 @@ void ConfigManager::addDomain(const String &domainName, const ConfigManager::Dom
 	} else if (domainName == kKeymapperDomain) {
 		_keymapperDomain = domain;
 #endif
+#ifdef USE_CLOUD
+	} else if (domainName == kCloudDomain) {		
+		_cloudDomain = domain;
+#endif
 	} else if (domain.contains("gameid")) {
 		// If the domain contains "gameid" we assume it's a game domain
 		if (_gameDomains.contains(domainName))
@@ -160,6 +171,9 @@ void ConfigManager::loadFromStream(SeekableReadStream &stream) {
 #ifdef ENABLE_KEYMAPPER
 	_keymapperDomain.clear();
 #endif
+#ifdef USE_CLOUD
+	_cloudDomain.clear();
+#endif
 
 	// TODO: Detect if a domain occurs multiple times (or likewise, if
 	// a key occurs multiple times inside one domain).
@@ -272,6 +286,10 @@ void ConfigManager::flushToDisk() {
 	// Write the keymapper domain
 	writeDomain(*stream, kKeymapperDomain, _keymapperDomain);
 #endif
+#ifdef USE_CLOUD
+	// Write the cloud domain
+	writeDomain(*stream, kCloudDomain, _cloudDomain);
+#endif
 
 	DomainMap::const_iterator d;
 
@@ -359,6 +377,10 @@ const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) con
 	if (domName == kKeymapperDomain)
 		return &_keymapperDomain;
 #endif
+#ifdef USE_CLOUD
+	if (domName == kCloudDomain)
+		return &_cloudDomain;
+#endif
 	if (_gameDomains.contains(domName))
 		return &_gameDomains[domName];
 	if (_miscDomains.contains(domName))
@@ -379,6 +401,10 @@ ConfigManager::Domain *ConfigManager::getDomain(const String &domName) {
 	if (domName == kKeymapperDomain)
 		return &_keymapperDomain;
 #endif
+#ifdef USE_CLOUD
+	if (domName == kCloudDomain)
+		return &_cloudDomain;
+#endif
 	if (_gameDomains.contains(domName))
 		return &_gameDomains[domName];
 	if (_miscDomains.contains(domName))
diff --git a/common/config-manager.h b/common/config-manager.h
index 14f911f..669faaa 100644
--- a/common/config-manager.h
+++ b/common/config-manager.h
@@ -94,6 +94,11 @@ public:
 	static char const *const kKeymapperDomain;
 #endif
 
+#ifdef USE_CLOUD
+	/** The name of cloud domain used to store user's tokens */
+	static char const *const kCloudDomain;
+#endif
+
 	void				loadDefaultConfigFile();
 	void				loadConfigFile(const String &filename);
 
@@ -188,6 +193,10 @@ private:
 	Domain			_keymapperDomain;
 #endif
 
+#ifdef USE_CLOUD
+	Domain			_cloudDomain;
+#endif
+
 	Array<String>	_domainSaveOrder;
 
 	String			_activeDomainName;


Commit: 0a947618fb1faa023df03d355e9749905424dd58
    https://github.com/scummvm/scummvm/commit/0a947618fb1faa023df03d355e9749905424dd58
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make ConnectionManager singleton

With ConnectionManager singleton one can start their Requests without
creating Storage instance. Moreover, Storage instance should contain
cloud API, not Requests-related handling and timer starting methods.
Thus, these methods were moved into ConnectionManager itself.

Changed paths:
  A backends/networking/curl/request.h
  R backends/cloud/request.h
  R backends/cloud/storage.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.h
    backends/module.mk
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index add6bff..93f0eeb 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
@@ -76,8 +77,7 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
-void DropboxStorage::listDirectory(Common::String path) {
-	startTimer(1000000); //in one second
+void DropboxStorage::listDirectory(Common::String path) {	
 }
 
 void DropboxStorage::syncSaves() {
@@ -88,7 +88,7 @@ void DropboxStorage::syncSaves() {
 void DropboxStorage::printInfo() {
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
-	addRequest(request);
+	ConnMan.addRequest(request);
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig() {
@@ -119,10 +119,10 @@ Common::String DropboxStorage::getAuthLink() {
 	return url;
 }
 
-DropboxStorage *DropboxStorage::authThroughConsole() {
+void DropboxStorage::authThroughConsole() {
 	if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) {
 		warning("No Dropbox keys available, cannot do auth");
-		return 0;
+		return;
 	}
 
 	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
@@ -130,29 +130,25 @@ DropboxStorage *DropboxStorage::authThroughConsole() {
 
 	if (ConfMan.hasKey("dropbox_code", "cloud")) {
 		//phase 2: get access_token using specified code
-		return getAccessToken(ConfMan.get("dropbox_code", "cloud"));
+		getAccessToken(ConfMan.get("dropbox_code", "cloud"));
+		return;
 	}
 
 	debug("Navigate to this URL and press \"Allow\":");
 	debug("%s\n", getAuthLink().c_str());
 	debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
 	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
-	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");
-	return 0;
+	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
 }
 
-DropboxStorage *DropboxStorage::getAccessToken(Common::String code) {
+void DropboxStorage::getAccessToken(Common::String code) {
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + KEY);
 	request->addPostField("client_secret=" + SECRET);
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
-	
-	//OK, that's not how I imagined that...	
-	DropboxStorage *storage = new DropboxStorage("", "");
-	storage->addRequest(request);
-	return storage;
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");	
+	ConnMan.addRequest(request);	
 }
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index f2281f1..0500db5 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -24,7 +24,7 @@
 #define BACKENDS_CLOUD_DROPBOX_STORAGE_H
 
 #include "backends/cloud/storage.h"
-#include "../manager.h"
+#include "backends/cloud/manager.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -37,7 +37,7 @@ class DropboxStorage: public Cloud::Storage {
 	/** This private constructor is called from loadFromConfig(). */
 	DropboxStorage(Common::String token, Common::String uid);
 
-	static DropboxStorage *getAccessToken(Common::String code);
+	static void getAccessToken(Common::String code);
 
 public:	
 	virtual ~DropboxStorage();
@@ -58,10 +58,8 @@ public:
 
 	/**
 	* Show message with Dropbox auth instructions. (Temporary)
-	* Returns temporary DropboxStorage, which does network requests
-	* to get access token.
 	*/
-	static DropboxStorage *authThroughConsole();
+	static void authThroughConsole();
 };
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/request.h b/backends/cloud/request.h
deleted file mode 100644
index d85a68d..0000000
--- a/backends/cloud/request.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_REQUEST_H
-#define BACKENDS_CLOUD_REQUEST_H
-
-#include "backends/networking/curl/connectionmanager.h"
-
-namespace Cloud {
-
-class Request {
-protected:
-	/**
-	* Callback, which should be called before Request returns true in handle().
-	* That's the way Requests pass the result to the code which asked to create this request.	
-	*/
-
-	typedef void(*Callback)(void *result);
-	Callback _callback;
-
-public:
-	Request(Callback cb): _callback(cb) {};
-	virtual ~Request() {};
-
-	/**
-	* Method, which does actual work. Depends on what this Request is doing.
-	*
-	* @return true if request's work is complete and it may be removed from Storage's list
-	*/
-
-	virtual bool handle(Networking::ConnectionManager &manager) = 0;
-};
-
-} //end of namespace Cloud
-
-#endif
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
deleted file mode 100644
index d7217a5..0000000
--- a/backends/cloud/storage.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/* 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 "backends/cloud/storage.h"
-#include "common/debug.h"
-#include "common/system.h"
-#include "common/timer.h"
-
-namespace Cloud {
-
-void cloudThread(void *thread) {
-	Storage *cloudThread = (Storage *)thread;
-	cloudThread->handler();
-}
-
-Storage::Storage() : _timerStarted(false) {}
-
-void Storage::addRequest(Request *request) {
-	_requests.push_back(request);
-	if (!_timerStarted) startTimer();
-}
-
-void Storage::handler() {
-	//TODO: lock mutex here (in case another handler() would be called before this one ends)
-	debug("\nhandler's here");
-	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		if ((*i)->handle(_connectionManager)) {
-			delete (*i);
-			_requests.erase(i);
-		}
-		else ++i;
-	}
-	if (_requests.empty()) stopTimer();
-
-	_connectionManager.handle();
-	//TODO: unlock mutex here
-}
-
-void Storage::startTimer(int interval) {
-	Common::TimerManager *manager = g_system->getTimerManager();
-	if (manager->installTimerProc(cloudThread, interval, this, "Cloud Thread")) {
-		_timerStarted = true;
-	} else {
-		warning("Failed to create cloud thread");		
-	}
-}
-
-void Storage::stopTimer() {
-	Common::TimerManager *manager = g_system->getTimerManager();
-	manager->removeTimerProc(cloudThread);
-	_timerStarted = false;
-}
-
-} //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index a5d048d..9e23e97 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -24,28 +24,13 @@
 #define BACKENDS_CLOUD_STORAGE_H
 
 #include "common/str.h"
-#include "common/array.h"
-#include "backends/cloud/request.h"
-#include "backends/networking/curl/connectionmanager.h"
 
 namespace Cloud {
 
 class Storage {
-	friend void cloudThread(void *); //calls handler()
-	bool _timerStarted;
-
-protected:
-	Common::Array<Request *> _requests;
-	Networking::ConnectionManager _connectionManager;
-
-	virtual void addRequest(Request *request); //starts the timer if it's not started
-	virtual void handler();	
-	virtual void startTimer(int interval = 1000000); //1 second is the default interval
-	virtual void stopTimer();
-
 public:
-	Storage();
-	virtual ~Storage() {};
+	Storage() {}
+	virtual ~Storage() {}
 
 	/**
 	* Lists given directory.
diff --git a/backends/module.mk b/backends/module.mk
index 72183f9..0142531 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,8 +21,7 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
-	cloud/manager.o \
-	cloud/storage.o \
+	cloud/manager.o \	
 	cloud/dropbox/dropboxstorage.o
 endif
 
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index d34eab2..31e99f9 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -25,11 +25,16 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/debug.h"
+#include "common/system.h"
+#include "common/timer.h"
 #include <curl/curl.h>
+using Common::Singleton;
+
+DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
@@ -39,13 +44,57 @@ ConnectionManager::~ConnectionManager() {
 	curl_global_cleanup();
 }
 
-NetworkReadStream *ConnectionManager::makeRequest(const char *url, curl_slist *headersList, Common::String postFields) {
-	NetworkReadStream *stream = new NetworkReadStream(url, headersList, postFields);
-	curl_multi_add_handle(_multi, stream->getEasyHandle());
-	return stream;
+void ConnectionManager::registerEasyHandle(CURL *easy) {
+	curl_multi_add_handle(_multi, easy);
+}
+
+void ConnectionManager::addRequest(Request *request) {
+	_requests.push_back(request);
+	if (!_timerStarted) startTimer();
+}
+
+//private goes here:
+
+void connectionsThread(void *ignored) {
+	ConnMan.handle();
+}
+
+void ConnectionManager::startTimer(int interval) {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	if (manager->installTimerProc(connectionsThread, interval, 0, "Networking::ConnectionManager's Timer")) {
+		_timerStarted = true;
+	} else {
+		warning("Failed to install Networking::ConnectionManager's timer");
+	}
+}
+
+void ConnectionManager::stopTimer() {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	manager->removeTimerProc(connectionsThread);
+	_timerStarted = false;
 }
 
 void ConnectionManager::handle() {
+	//TODO: lock mutex here (in case another handle() would be called before this one ends)
+	interateRequests();
+	processTransfers();
+	//TODO: unlock mutex here
+}
+
+void ConnectionManager::interateRequests() {
+	//call handle() of all running requests (so they can do their work)
+	debug("handler's here");
+	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
+		if ((*i)->handle()) {
+			delete (*i);
+			_requests.erase(i);
+		} else ++i;
+	}
+	if (_requests.empty()) stopTimer();
+}
+
+void ConnectionManager::processTransfers() {
+	//check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
 	int transfersRunning;
 	curl_multi_perform(_multi, &transfersRunning);
 
@@ -59,9 +108,9 @@ void ConnectionManager::handle() {
 		if (stream) stream->finished();
 
 		if (curlMsg->msg == CURLMSG_DONE) {
-			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));			
+			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
 		} else {
-			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);			
+			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
 		}
 
 		curl_multi_remove_handle(_multi, easyHandle);
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index b83de61..ed72644 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -23,8 +23,12 @@
 #ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
 #define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
 
+#include "backends/networking/curl/request.h"
 #include "common/str.h"
+#include "common/singleton.h"
+#include "common/array.h"
 
+typedef void CURL;
 typedef void CURLM;
 struct curl_slist;
 
@@ -32,17 +36,43 @@ namespace Networking {
 
 class NetworkReadStream;
 
-class ConnectionManager {
-	CURLM *_multi;
+class ConnectionManager : public Common::Singleton<ConnectionManager> {
+	friend void connectionsThread(void *); //calls handle()
+
+	CURLM *_multi;	
+	bool _timerStarted;
+	Common::Array<Request *> _requests;	
+	
+	void startTimer(int interval = 1000000); //1 second is the default interval
+	void stopTimer();
+	void handle();
+	void interateRequests();
+	void processTransfers();
 
 public:
 	ConnectionManager();
 	virtual ~ConnectionManager();
 
-	NetworkReadStream *makeRequest(const char *url, curl_slist *headersList, Common::String postFields);
-	void handle();
+	/**
+	* All libcurl transfers are going through this ConnectionManager.
+	* So, if you want to start any libcurl transfer, you must create
+	* an easy handle and register it using this method.
+	*/
+	void registerEasyHandle(CURL *easy);
+
+	/**
+	* Use this method to add new Request into manager's queue.
+	* Manager will periodically call handle() method of these
+	* Requests until they return true.
+	*
+	* @note This method starts the timer if it's not started yet.
+	*/
+	void addRequest(Request *request);
 };
 
-} //end of namespace Cloud
+/** Shortcut for accessing the connection manager. */
+#define ConnMan		Networking::ConnectionManager::instance()
+
+} //end of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 0c63634..59bc830 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -23,6 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/debug.h"
 #include "common/json.h"
@@ -56,8 +57,8 @@ char *CurlJsonRequest::getPreparedContents() {
 	return (char *)result;
 }
 
-bool CurlJsonRequest::handle(ConnectionManager &manager) {
-	if (!_stream) _stream = manager.makeRequest(_url, _headersList, _postFields);
+bool CurlJsonRequest::handle() {
+	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);
 
 	if (_stream) {
 		const int kBufSize = 16*1024;
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 17df969..cbf3f6d 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -23,7 +23,7 @@
 #ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
 #define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
 
-#include "backends/cloud/request.h"
+#include "backends/networking/curl/request.h"
 #include "common/memstream.h"
 
 struct curl_slist;
@@ -32,7 +32,7 @@ namespace Networking {
 
 class NetworkReadStream;
 
-class CurlJsonRequest : public Cloud::Request {	
+class CurlJsonRequest : public Request {	
 	const char *_url;
 	NetworkReadStream *_stream;
 	curl_slist *_headersList;
@@ -46,7 +46,7 @@ public:
 	CurlJsonRequest(Callback cb, const char *url);
 	virtual ~CurlJsonRequest();
 
-	virtual bool handle(ConnectionManager &manager);
+	virtual bool handle();
 
 	void addHeader(Common::String header);
 
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index f2af4fd..8fd39d6 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -23,6 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/curl/connectionmanager.h"
 #include "common/debug.h"
 #include <curl/curl.h>
 
@@ -47,6 +48,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
 	curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
 	curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
+	ConnMan.registerEasyHandle(_easy);
 }
 
 NetworkReadStream::~NetworkReadStream() {
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index bc4b761..6431a01 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -40,8 +40,6 @@ public:
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields);
 	virtual ~NetworkReadStream();
 
-	CURL *getEasyHandle() const { return _easy; }
-
 	/**
 	* Returns true if a read failed because the stream end has been reached.
 	* This flag is cleared by clearErr().
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
new file mode 100644
index 0000000..4f901f7
--- /dev/null
+++ b/backends/networking/curl/request.h
@@ -0,0 +1,54 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
+#define BACKENDS_NETWORKING_CURL_REQUEST_H
+
+namespace Networking {
+
+class Request {
+protected:
+	typedef void(*Callback)(void *result);
+
+	/**
+	* Callback, which should be called before Request returns true in handle().
+	* That's the way Requests pass the result to the code which asked to create this request.
+	*/
+
+	Callback _callback;
+
+public:
+	Request(Callback cb): _callback(cb) {};
+	virtual ~Request() {};
+
+	/**
+	* Method, which does actual work. Depends on what this Request is doing.
+	*
+	* @return true if request's work is complete and it may be removed from Storage's list
+	*/
+
+	virtual bool handle() = 0;
+};
+
+} //end of namespace Cloud
+
+#endif


Commit: f913675c43ada5c5f9128d904fd913129da35fe8
    https://github.com/scummvm/scummvm/commit/f913675c43ada5c5f9128d904fd913129da35fe8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ConnectionManager hotfix

Tried to compile these two last commits with GCC and found a few minor
problems.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.h
    backends/module.mk



diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 0500db5..afa9447 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -54,7 +54,7 @@ public:
 	/**
 	* Returns Dropbox auth link.
 	*/
-	static Common::String DropboxStorage::getAuthLink();
+	static Common::String getAuthLink();
 
 	/**
 	* Show message with Dropbox auth instructions. (Temporary)
diff --git a/backends/module.mk b/backends/module.mk
index 0142531..035eac2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,7 +21,7 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
-	cloud/manager.o \	
+	cloud/manager.o \
 	cloud/dropbox/dropboxstorage.o
 endif
 


Commit: 17eb5f91433f2414dc73f89abfdd316407259b61
    https://github.com/scummvm/scummvm/commit/17eb5f91433f2414dc73f89abfdd316407259b61
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add complex callbacks

Originally, I intended to add Storage API, StorageFile and StorageInfo
stubs. When I tried to implement a simple info() call, I ended up fixing
Request to contain some pointer field and all callbacks to have Request*
parameter. And, now I have to place callback pointer into Request. which
calls another callback.

And, eventually, these "simple" callbacks would again require another
pointer (to some caller class).

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/storage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/request.h
    common/cloudmanager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 93f0eeb..f2270f7 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -35,7 +35,7 @@ namespace Dropbox {
 Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
 
-static void printJsonCallback(void *ptr) {
+static void printJsonCallback(Networking::Request* rq, void *ptr) {
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
 	if (json) {
 		debug("printJsonCallback:");
@@ -46,7 +46,7 @@ static void printJsonCallback(void *ptr) {
 	}
 }
 
-static void saveAccessTokenCallback(void *ptr) {
+static void saveAccessTokenCallback(Networking::Request* rq, void *ptr) {
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
 	if (json) {
 		debug("saveAccessTokenCallback:");
@@ -69,6 +69,29 @@ static void saveAccessTokenCallback(void *ptr) {
 	}
 }
 
+void infoCallback(Networking::Request* request, void *jsonPointer) {
+	if (!request) {
+		warning("infoCallback: got NULL instead of Request");
+
+		Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+		if (json) delete json; //yeah I know we can delete NULL safely
+		return;
+	}
+
+	Storage::InfoCallback callback = (Storage::InfoCallback)request->pointer();
+
+	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+	if (json) {
+		//Common::JSONObject result = json->asObject();
+		if (callback) {
+			callback(StorageInfo(json->stringify()));
+		}
+		delete json;
+	} else {
+		warning("infoCallback: got NULL instead of JSON!");
+	}
+}
+
 DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
 	curl_global_init(CURL_GLOBAL_ALL);
 }
@@ -77,18 +100,21 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
-void DropboxStorage::listDirectory(Common::String path) {	
+void syncSavesInfoCallback(StorageInfo info) {
+	debug("info: %s", info.info().c_str());
 }
 
-void DropboxStorage::syncSaves() {
-	//not syncing, but already something:
-	printInfo();
+void DropboxStorage::syncSaves(OperationCallback callback) {
+	//this is not the real syncSaves() implementation
+	info(syncSavesInfoCallback);
 }
 
-void DropboxStorage::printInfo() {
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info");
+void DropboxStorage::info(InfoCallback callback) {	
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(infoCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	ConnMan.addRequest(request);
+
+	request->setPointer(callback);
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig() {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index afa9447..d60dec2 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -42,9 +42,36 @@ class DropboxStorage: public Cloud::Storage {
 public:	
 	virtual ~DropboxStorage();
 
-	virtual void listDirectory(Common::String path);
-	virtual void syncSaves();
-	virtual void printInfo();	
+	/** Returns pointer to Common::Array<CloudFile>. */
+	virtual void listDirectory(Common::String path, ListDirectoryCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void upload(Common::String path, Common::ReadStream* contents, OperationCallback callback) {} //TODO
+
+	/** Returns pointer to Common::ReadStream. */
+	virtual void download(Common::String path, DownloadCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void remove(Common::String path, OperationCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void syncSaves(OperationCallback callback);
+
+	/** Calls the callback when finished. */
+	virtual void createDirectory(Common::String path, OperationCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void touch(Common::String path, OperationCallback callback) {} //TODO
+
+	/** Returns pointer to the ServiceInfo struct. */
+	virtual void info(InfoCallback callback);
+
+	/** Returns whether saves sync process is running. */
+	virtual bool isSyncing() { return false; } //TODO
+
+	/** Returns whether there are any requests running. */
+	virtual bool isWorking() { return false; } //TODO
+
 	/**
 	* Load token and user id from configs and return DropboxStorage for those.	
 	* @return pointer to the newly created DropboxStorage or 0 if some problem occured.
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 08340e9..fc27148 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -46,9 +46,9 @@ Storage* Manager::getCurrentStorage() {
 	return _currentStorage;
 }
 
-void Manager::syncSaves() {
+void Manager::syncSaves(Storage::OperationCallback callback) {
 	Storage* storage = getCurrentStorage();
-	if (storage) storage->syncSaves();
+	if (storage) storage->syncSaves(callback);
 }
 
 } //end of namespace Cloud
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index a1f046d..47109cc 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -38,7 +38,7 @@ public:
 	virtual void init();
 
 	virtual Storage* getCurrentStorage();
-	virtual void syncSaves();
+	virtual void syncSaves(Storage::OperationCallback callback);
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 9e23e97..4fb06f6 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -23,37 +23,90 @@
 #ifndef BACKENDS_CLOUD_STORAGE_H
 #define BACKENDS_CLOUD_STORAGE_H
 
+#include "common/array.h"
+#include "common/stream.h"
 #include "common/str.h"
 
 namespace Cloud {
 
+class StorageFile {
+	Common::String _path, _name;
+	uint32 _size, _timestamp;
+	bool _isDirectory;
+
+public:
+	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
+		_path = pth;
+
+		_name = pth;
+		for (uint32 i = _name.size() - 1; i >= 0; --i) {
+			if (_name[i] == '/' || _name[i] == '\\') {
+				_name.erase(0, i);
+				break;
+			}
+			if (i == 0) break; //OK, I admit that's strange
+		}
+
+		_size = sz;
+		_timestamp = ts;
+		_isDirectory = dir;
+	}
+
+	Common::String path() const { return _path; }
+	Common::String name() const { return _name; }
+	uint32 size() const { return _size; }
+	uint32 timestamp() const { return _timestamp; }
+	bool isDirectory() const { return _isDirectory; }
+};
+
+class StorageInfo {
+	Common::String _info;
+
+public:
+	StorageInfo(Common::String info): _info(info) {}
+
+	Common::String info() const { return _info; }
+};
+
 class Storage {
 public:
+	typedef void(*ListDirectoryCallback)(Common::Array<StorageFile>& result);
+	typedef void(*DownloadCallback)(Common::ReadStream* result);
+	typedef void(*InfoCallback)(StorageInfo result);
+	typedef void(*OperationCallback)(bool successed);
+
 	Storage() {}
 	virtual ~Storage() {}
 
-	/**
-	* Lists given directory.
-	*
-	* @param path		directory to list	
-	*/
+	/** Returns pointer to Common::Array<CloudFile>. */
+	virtual void listDirectory(Common::String path, ListDirectoryCallback callback) = 0;
+
+	/** Calls the callback when finished. */
+	virtual void upload(Common::String path, Common::ReadStream* contents, OperationCallback callback) = 0;
+
+	/** Returns pointer to Common::ReadStream. */
+	virtual void download(Common::String path, DownloadCallback callback) = 0;
+
+	/** Calls the callback when finished. */
+	virtual void remove(Common::String path, OperationCallback callback) = 0;
 
-	//TODO: actually make it list directories
-	//TODO: add some callback to pass gathered files list
+	/** Calls the callback when finished. */
+	virtual void syncSaves(OperationCallback callback) = 0;
 
-	virtual void listDirectory(Common::String path) = 0;
+	/** Calls the callback when finished. */
+	virtual void createDirectory(Common::String path, OperationCallback callback) = 0;
 
-	/**
-	* Starts saves syncing process.
-	*/
+	/** Calls the callback when finished. */
+	virtual void touch(Common::String path, OperationCallback callback) = 0;
 
-	virtual void syncSaves() = 0;
+	/** Returns pointer to the ServiceInfo struct. */
+	virtual void info(InfoCallback callback) = 0;
 
-	/**
-	* Prints user info on console. (Temporary)
-	*/
+	/** Returns whether saves sync process is running. */
+	virtual bool isSyncing() = 0;
 
-	virtual void printInfo() = 0;
+	/** Returns whether there are any requests running. */
+	virtual bool isWorking() = 0;
 };
 
 } //end of namespace Cloud
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 59bc830..929723c 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -75,7 +75,7 @@ bool CurlJsonRequest::handle() {
 			if (_callback) {
 				char *contents = getPreparedContents();				
 				Common::JSONValue *json = Common::JSON::parse(contents);				
-				_callback(json); //potential memory leak, free it in your callbacks!
+				_callback(this, json); //potential memory leak, free it in your callbacks!
 			}
 			return true;
 		}
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 4f901f7..d405ec8 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -27,7 +27,7 @@ namespace Networking {
 
 class Request {
 protected:
-	typedef void(*Callback)(void *result);
+	typedef void(*Callback)(Request* request, void *result);
 
 	/**
 	* Callback, which should be called before Request returns true in handle().
@@ -36,6 +36,13 @@ protected:
 
 	Callback _callback;
 
+	/**
+	* Pointer, which could be set by Request creating code. It might be accessed
+	* from this Request when callback is called, for example.
+	*/
+
+	void *_pointer;
+
 public:
 	Request(Callback cb): _callback(cb) {};
 	virtual ~Request() {};
@@ -47,6 +54,9 @@ public:
 	*/
 
 	virtual bool handle() = 0;
+
+	void setPointer(void *ptr) { _pointer = ptr; }
+	void *pointer() const { return _pointer;  }
 };
 
 } //end of namespace Cloud
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 8a10a1a..08ba928 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -53,7 +53,7 @@ public:
 	* Starts saves syncing process in currently active storage if there is any.
 	*/
 
-	virtual void syncSaves() = 0;
+	virtual void syncSaves(Cloud::Storage::OperationCallback callback = 0) = 0;
 };
 
 } //end of namespace Common


Commit: ca456a7241fb4b46f9c76c86eeecc273ca31d8f6
    https://github.com/scummvm/scummvm/commit/ca456a7241fb4b46f9c76c86eeecc273ca31d8f6
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add object-oriented Callbacks

These callbacks can call object's methods, not some global C functions.

DropboxStorage::info2() and DropboxStorage::infoMethodCallback()
demonstrate the idea.

Changed paths:
  A common/callback.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/request.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index f2270f7..94bdb43 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -92,6 +92,30 @@ void infoCallback(Networking::Request* request, void *jsonPointer) {
 	}
 }
 
+void info2Callback(Networking::Request* request, void *jsonPointer) {
+	if (!request) {
+		warning("infoCallback: got NULL instead of Request");
+
+		Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+		if (json) delete json; //yeah I know we can delete NULL safely
+		return;
+	}
+
+	Common::BaseCallback *callback = (Common::BaseCallback *)request->pointer();
+
+	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+	if (json) {
+		//Common::JSONObject result = json->asObject();
+		if (callback) {
+			(*callback)(new StorageInfo(json->stringify()));
+			delete callback;
+		}
+		delete json;
+	} else {
+		warning("infoCallback: got NULL instead of JSON!");
+	}
+}
+
 DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
 	curl_global_init(CURL_GLOBAL_ALL);
 }
@@ -104,9 +128,14 @@ void syncSavesInfoCallback(StorageInfo info) {
 	debug("info: %s", info.info().c_str());
 }
 
+void DropboxStorage::infoMethodCallback(void *storageInfo) {
+	StorageInfo *info = (StorageInfo *)storageInfo;
+	debug("info: %s", info->info().c_str());
+}
+
 void DropboxStorage::syncSaves(OperationCallback callback) {
-	//this is not the real syncSaves() implementation
-	info(syncSavesInfoCallback);
+	//this is not the real syncSaves() implementation	
+	info2(new Common::Callback<DropboxStorage>(this, &DropboxStorage::infoMethodCallback));
 }
 
 void DropboxStorage::info(InfoCallback callback) {	
@@ -117,6 +146,14 @@ void DropboxStorage::info(InfoCallback callback) {
 	request->setPointer(callback);
 }
 
+void DropboxStorage::info2(Common::Callback<DropboxStorage> *callback) {
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(info2Callback, "https://api.dropboxapi.com/1/account/info");
+	request->addHeader("Authorization: Bearer " + _token);
+	ConnMan.addRequest(request);
+
+	request->setPointer(callback);
+}
+
 DropboxStorage *DropboxStorage::loadFromConfig() {
 	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
 	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index d60dec2..1cf657b 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -25,6 +25,7 @@
 
 #include "backends/cloud/storage.h"
 #include "backends/cloud/manager.h"
+#include "common/callback.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -39,6 +40,8 @@ class DropboxStorage: public Cloud::Storage {
 
 	static void getAccessToken(Common::String code);
 
+	void infoMethodCallback(void *serviceInfoPtr);
+
 public:	
 	virtual ~DropboxStorage();
 
@@ -65,6 +68,7 @@ public:
 
 	/** Returns pointer to the ServiceInfo struct. */
 	virtual void info(InfoCallback callback);
+	void info2(Common::Callback<DropboxStorage> *callback);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 929723c..fe6e218 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
+CurlJsonRequest::CurlJsonRequest(SimpleCallback cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
 	_url = url;
 }
 
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index cbf3f6d..56e7205 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -43,7 +43,7 @@ class CurlJsonRequest : public Request {
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(Callback cb, const char *url);
+	CurlJsonRequest(SimpleCallback cb, const char *url);
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle();
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index d405ec8..b957107 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -27,14 +27,14 @@ namespace Networking {
 
 class Request {
 protected:
-	typedef void(*Callback)(Request* request, void *result);
+	typedef void(*SimpleCallback)(Request* request, void *result);
 
 	/**
 	* Callback, which should be called before Request returns true in handle().
 	* That's the way Requests pass the result to the code which asked to create this request.
 	*/
 
-	Callback _callback;
+	SimpleCallback _callback;
 
 	/**
 	* Pointer, which could be set by Request creating code. It might be accessed
@@ -44,7 +44,7 @@ protected:
 	void *_pointer;
 
 public:
-	Request(Callback cb): _callback(cb) {};
+	Request(SimpleCallback cb): _callback(cb) {};
 	virtual ~Request() {};
 
 	/**
diff --git a/common/callback.h b/common/callback.h
new file mode 100644
index 0000000..4cfdd53
--- /dev/null
+++ b/common/callback.h
@@ -0,0 +1,47 @@
+/* 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.
+ *
+ */
+
+#ifndef COMMON_CALLBACK_H
+#define COMMON_CALLBACK_H
+
+namespace Common {
+
+class BaseCallback {
+public:
+	BaseCallback() {}
+	virtual ~BaseCallback() {}
+	virtual void operator()(void *ptr) = 0;
+};
+
+template<class T> class Callback: public BaseCallback {
+	typedef void(T::*TMethod)(void *);
+	T *_object;
+	TMethod _method;
+public:
+	Callback(T *object, TMethod method): _object(object), _method(method) {}
+	virtual ~Callback() {}
+	void operator()(void *ptr) { (_object->*_method)(ptr); }
+};
+
+} // End of namespace Common
+
+#endif


Commit: e1109c0c328aaf671e2b03b3b4e6de1ae9061754
    https://github.com/scummvm/scummvm/commit/e1109c0c328aaf671e2b03b3b4e6de1ae9061754
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CallbackBridge

This commit also adds GlobalFunctionCallback, because it was needed in
order to replace plain C pointers to functions (which were used in
Request) into our object-oriented BaseCallback pointers.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/request.h
    common/callback.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 94bdb43..04e9eee 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -46,7 +46,7 @@ static void printJsonCallback(Networking::Request* rq, void *ptr) {
 	}
 }
 
-static void saveAccessTokenCallback(Networking::Request* rq, void *ptr) {
+static void saveAccessTokenCallback(void *ptr) {
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
 	if (json) {
 		debug("saveAccessTokenCallback:");
@@ -139,19 +139,26 @@ void DropboxStorage::syncSaves(OperationCallback callback) {
 }
 
 void DropboxStorage::info(InfoCallback callback) {	
+	/*
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(infoCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	ConnMan.addRequest(request);
 
 	request->setPointer(callback);
+	*/
 }
 
-void DropboxStorage::info2(Common::Callback<DropboxStorage> *callback) {
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(info2Callback, "https://api.dropboxapi.com/1/account/info");
+void DropboxStorage::info2BridgeCallback(Common::BaseCallback *outerCallback, void *ptr) {
+	//no NULL checks, delete and such yet
+	Common::JSONValue *json = (Common::JSONValue *)ptr;
+	(*outerCallback)(new StorageInfo(json->stringify()));
+}
+
+void DropboxStorage::info2(Common::BaseCallback *outerCallback) {
+	Common::BaseCallback *innerCallback = new Common::CallbackBridge<DropboxStorage>(this, &DropboxStorage::info2BridgeCallback, outerCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	ConnMan.addRequest(request);
-
-	request->setPointer(callback);
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig() {
@@ -205,7 +212,8 @@ void DropboxStorage::authThroughConsole() {
 }
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token");
+	Common::BaseCallback *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + KEY);
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 1cf657b..efd0eea 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -68,7 +68,8 @@ public:
 
 	/** Returns pointer to the ServiceInfo struct. */
 	virtual void info(InfoCallback callback);
-	void info2(Common::Callback<DropboxStorage> *callback);
+	void info2(Common::BaseCallback *outerCallback);
+	void info2BridgeCallback(Common::BaseCallback *outerCallback, void *ptr);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index fe6e218..c306468 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(SimpleCallback cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
+CurlJsonRequest::CurlJsonRequest(Common::BaseCallback* cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
 	_url = url;
 }
 
@@ -75,7 +75,7 @@ bool CurlJsonRequest::handle() {
 			if (_callback) {
 				char *contents = getPreparedContents();				
 				Common::JSONValue *json = Common::JSON::parse(contents);				
-				_callback(this, json); //potential memory leak, free it in your callbacks!
+				(*_callback)(json); //potential memory leak, free it in your callbacks!
 			}
 			return true;
 		}
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 56e7205..1098638 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -26,6 +26,10 @@
 #include "backends/networking/curl/request.h"
 #include "common/memstream.h"
 
+namespace Common {
+class BaseCallback;
+}
+
 struct curl_slist;
 
 namespace Networking {
@@ -43,7 +47,7 @@ class CurlJsonRequest : public Request {
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(SimpleCallback cb, const char *url);
+	CurlJsonRequest(Common::BaseCallback *cb, const char *url);
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle();
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index b957107..860784f 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -22,6 +22,7 @@
 
 #ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
 #define BACKENDS_NETWORKING_CURL_REQUEST_H
+#include <common/callback.h>
 
 namespace Networking {
 
@@ -34,7 +35,7 @@ protected:
 	* That's the way Requests pass the result to the code which asked to create this request.
 	*/
 
-	SimpleCallback _callback;
+	Common::BaseCallback* _callback;
 
 	/**
 	* Pointer, which could be set by Request creating code. It might be accessed
@@ -44,7 +45,7 @@ protected:
 	void *_pointer;
 
 public:
-	Request(SimpleCallback cb): _callback(cb) {};
+	Request(Common::BaseCallback* cb): _callback(cb) {};
 	virtual ~Request() {};
 
 	/**
diff --git a/common/callback.h b/common/callback.h
index 4cfdd53..4cc63ed 100644
--- a/common/callback.h
+++ b/common/callback.h
@@ -32,6 +32,18 @@ public:
 	virtual void operator()(void *ptr) = 0;
 };
 
+class GlobalFunctionCallback: public BaseCallback {
+	typedef void(*GlobalFunction)(void *result);
+	GlobalFunction _callback;
+
+public:
+	GlobalFunctionCallback(GlobalFunction cb): _callback(cb) {}
+	virtual ~GlobalFunctionCallback() {}
+	virtual void operator()(void *ptr) {
+		if (_callback) _callback(ptr);
+	}
+};
+
 template<class T> class Callback: public BaseCallback {
 	typedef void(T::*TMethod)(void *);
 	T *_object;
@@ -42,6 +54,18 @@ public:
 	void operator()(void *ptr) { (_object->*_method)(ptr); }
 };
 
+template<class T> class CallbackBridge: public BaseCallback {
+	typedef void(T::*TCallbackMethod)(BaseCallback *, void *);
+	T *_object;
+	TCallbackMethod _method;
+	BaseCallback *_outerCallback;
+public:
+	CallbackBridge(T *object, TCallbackMethod method, BaseCallback *outerCallback):
+		_object(object), _method(method), _outerCallback(outerCallback) {}
+	virtual ~CallbackBridge() {}
+	void operator()(void *ptr) { (_object->*_method)(_outerCallback, ptr); }
+};
+
 } // End of namespace Common
 
 #endif


Commit: 9e531e3ce7f5b3a1cc87b43beb6f72911cb41bdd
    https://github.com/scummvm/scummvm/commit/9e531e3ce7f5b3a1cc87b43beb6f72911cb41bdd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Polish Callbacks

Cleaned up all example code and old callbacks.

New Callback classes are introduced in "common/callback.h" and
documented.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/storage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/request.h
    common/callback.h
    common/cloudmanager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 04e9eee..964b95b 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -35,17 +35,6 @@ namespace Dropbox {
 Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
 
-static void printJsonCallback(Networking::Request* rq, void *ptr) {
-	Common::JSONValue *json = (Common::JSONValue *)ptr;
-	if (json) {
-		debug("printJsonCallback:");
-		debug("%s", json->stringify(true).c_str());
-		delete json;
-	} else {
-		debug("printJsonCallback: got NULL instead of JSON!");
-	}
-}
-
 static void saveAccessTokenCallback(void *ptr) {
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
 	if (json) {
@@ -69,53 +58,6 @@ static void saveAccessTokenCallback(void *ptr) {
 	}
 }
 
-void infoCallback(Networking::Request* request, void *jsonPointer) {
-	if (!request) {
-		warning("infoCallback: got NULL instead of Request");
-
-		Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
-		if (json) delete json; //yeah I know we can delete NULL safely
-		return;
-	}
-
-	Storage::InfoCallback callback = (Storage::InfoCallback)request->pointer();
-
-	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
-	if (json) {
-		//Common::JSONObject result = json->asObject();
-		if (callback) {
-			callback(StorageInfo(json->stringify()));
-		}
-		delete json;
-	} else {
-		warning("infoCallback: got NULL instead of JSON!");
-	}
-}
-
-void info2Callback(Networking::Request* request, void *jsonPointer) {
-	if (!request) {
-		warning("infoCallback: got NULL instead of Request");
-
-		Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
-		if (json) delete json; //yeah I know we can delete NULL safely
-		return;
-	}
-
-	Common::BaseCallback *callback = (Common::BaseCallback *)request->pointer();
-
-	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
-	if (json) {
-		//Common::JSONObject result = json->asObject();
-		if (callback) {
-			(*callback)(new StorageInfo(json->stringify()));
-			delete callback;
-		}
-		delete json;
-	} else {
-		warning("infoCallback: got NULL instead of JSON!");
-	}
-}
-
 DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
 	curl_global_init(CURL_GLOBAL_ALL);
 }
@@ -124,41 +66,42 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
-void syncSavesInfoCallback(StorageInfo info) {
-	debug("info: %s", info.info().c_str());
-}
-
-void DropboxStorage::infoMethodCallback(void *storageInfo) {
-	StorageInfo *info = (StorageInfo *)storageInfo;
-	debug("info: %s", info->info().c_str());
+void DropboxStorage::syncSaves(Common::BaseCallback<bool> *callback) {
+	//this is not the real syncSaves() implementation
+	info(new Common::Callback<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoMethodCallback));
+	//that line meant the following:
+	//"please, do the info API request and, when it's finished, call the infoMethodCallback() of me"
 }
 
-void DropboxStorage::syncSaves(OperationCallback callback) {
-	//this is not the real syncSaves() implementation	
-	info2(new Common::Callback<DropboxStorage>(this, &DropboxStorage::infoMethodCallback));
-}
-
-void DropboxStorage::info(InfoCallback callback) {	
-	/*
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(infoCallback, "https://api.dropboxapi.com/1/account/info");
+void DropboxStorage::info(Common::BaseCallback<StorageInfo> *outerCallback) {
+	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoInnerCallback, outerCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	ConnMan.addRequest(request);
-
-	request->setPointer(callback);
-	*/
+	//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
+	//so, when CurlJsonRequest is finished, it calls the innerCallback
+	//innerCallback (which is DropboxStorage::infoInnerCallback in this case) processes the void *ptr
+	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::info2BridgeCallback(Common::BaseCallback *outerCallback, void *ptr) {
-	//no NULL checks, delete and such yet
+void DropboxStorage::infoInnerCallback(Common::BaseCallback<StorageInfo> *outerCallback, void *ptr) {	
 	Common::JSONValue *json = (Common::JSONValue *)ptr;
-	(*outerCallback)(new StorageInfo(json->stringify()));
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (outerCallback) {
+		(*outerCallback)(StorageInfo(json->stringify()));
+		delete outerCallback;
+	}
+	
+	delete json;
 }
 
-void DropboxStorage::info2(Common::BaseCallback *outerCallback) {
-	Common::BaseCallback *innerCallback = new Common::CallbackBridge<DropboxStorage>(this, &DropboxStorage::info2BridgeCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
-	request->addHeader("Authorization: Bearer " + _token);
-	ConnMan.addRequest(request);
+void DropboxStorage::infoMethodCallback(StorageInfo storageInfo) {
+	debug("info: %s", storageInfo.info().c_str());
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig() {
@@ -212,7 +155,7 @@ void DropboxStorage::authThroughConsole() {
 }
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	Common::BaseCallback *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
+	Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index efd0eea..8cc9312 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_DROPBOX_STORAGE_H
 
 #include "backends/cloud/storage.h"
-#include "backends/cloud/manager.h"
 #include "common/callback.h"
 
 namespace Cloud {
@@ -40,36 +39,38 @@ class DropboxStorage: public Cloud::Storage {
 
 	static void getAccessToken(Common::String code);
 
-	void infoMethodCallback(void *serviceInfoPtr);
-
 public:	
 	virtual ~DropboxStorage();
 
-	/** Returns pointer to Common::Array<CloudFile>. */
-	virtual void listDirectory(Common::String path, ListDirectoryCallback callback) {} //TODO
+	/** Returns pointer to Common::Array<StorageFile>. */
+	virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array<StorageFile> > *callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, OperationCallback callback) {} //TODO
+	virtual void upload(Common::String path, Common::ReadStream* contents, Common::BaseCallback<bool> *callback) {} //TODO
 
 	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, DownloadCallback callback) {} //TODO
+	virtual void download(Common::String path, Common::BaseCallback<Common::ReadStream> *callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, OperationCallback callback) {} //TODO
+	virtual void remove(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(OperationCallback callback);
+	virtual void syncSaves(Common::BaseCallback<bool> *callback);
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, OperationCallback callback) {} //TODO
+	virtual void createDirectory(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, OperationCallback callback) {} //TODO
+	virtual void touch(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
+
+	/** Returns pointer to the StorageInfo struct. */
+	virtual void info(Common::BaseCallback<StorageInfo> *callback);
+
+	/** This is what is called by CurlJsonRequest. */
+	void infoInnerCallback(Common::BaseCallback<StorageInfo> *outerCallback, void *ptr);
 
-	/** Returns pointer to the ServiceInfo struct. */
-	virtual void info(InfoCallback callback);
-	void info2(Common::BaseCallback *outerCallback);
-	void info2BridgeCallback(Common::BaseCallback *outerCallback, void *ptr);
+	/** This is what is called by infoInnerCallback() (it's its outer callback). */
+	void infoMethodCallback(StorageInfo storageInfo);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index fc27148..a9a2b8a 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -46,7 +46,7 @@ Storage* Manager::getCurrentStorage() {
 	return _currentStorage;
 }
 
-void Manager::syncSaves(Storage::OperationCallback callback) {
+void Manager::syncSaves(Common::BaseCallback<bool> *callback) {
 	Storage* storage = getCurrentStorage();
 	if (storage) storage->syncSaves(callback);
 }
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 47109cc..c247132 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -38,7 +38,7 @@ public:
 	virtual void init();
 
 	virtual Storage* getCurrentStorage();
-	virtual void syncSaves(Storage::OperationCallback callback);
+	virtual void syncSaves(Common::BaseCallback<bool> *callback);
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 4fb06f6..f2079eb 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -26,6 +26,7 @@
 #include "common/array.h"
 #include "common/stream.h"
 #include "common/str.h"
+#include "common/callback.h"
 
 namespace Cloud {
 
@@ -70,37 +71,32 @@ public:
 
 class Storage {
 public:
-	typedef void(*ListDirectoryCallback)(Common::Array<StorageFile>& result);
-	typedef void(*DownloadCallback)(Common::ReadStream* result);
-	typedef void(*InfoCallback)(StorageInfo result);
-	typedef void(*OperationCallback)(bool successed);
-
 	Storage() {}
 	virtual ~Storage() {}
 
-	/** Returns pointer to Common::Array<CloudFile>. */
-	virtual void listDirectory(Common::String path, ListDirectoryCallback callback) = 0;
+	/** Returns pointer to Common::Array<StorageFile>. */
+	virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array<StorageFile> > *callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, OperationCallback callback) = 0;
+	virtual void upload(Common::String path, Common::ReadStream* contents, Common::BaseCallback<bool> *callback) = 0;
 
 	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, DownloadCallback callback) = 0;
+	virtual void download(Common::String path, Common::BaseCallback<Common::ReadStream> *callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, OperationCallback callback) = 0;
+	virtual void remove(Common::String path, Common::BaseCallback<bool> *callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(OperationCallback callback) = 0;
+	virtual void syncSaves(Common::BaseCallback<bool> *callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, OperationCallback callback) = 0;
+	virtual void createDirectory(Common::String path, Common::BaseCallback<bool> *callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, OperationCallback callback) = 0;
+	virtual void touch(Common::String path, Common::BaseCallback<bool> *callback) = 0;
 
-	/** Returns pointer to the ServiceInfo struct. */
-	virtual void info(InfoCallback callback) = 0;
+	/** Returns pointer to the StorageInfo struct. */
+	 virtual void info(Common::BaseCallback<StorageInfo> *callback) = 0;
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index c306468..702de22 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Common::BaseCallback* cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
+CurlJsonRequest::CurlJsonRequest(Common::BaseCallback<> *cb, const char *url): Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
 	_url = url;
 }
 
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 1098638..50fa818 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -25,10 +25,7 @@
 
 #include "backends/networking/curl/request.h"
 #include "common/memstream.h"
-
-namespace Common {
-class BaseCallback;
-}
+#include "common/json.h"
 
 struct curl_slist;
 
@@ -47,7 +44,7 @@ class CurlJsonRequest : public Request {
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(Common::BaseCallback *cb, const char *url);
+	CurlJsonRequest(Common::BaseCallback<> *cb, const char *url);
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle();
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 860784f..d3efd58 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -22,30 +22,22 @@
 
 #ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
 #define BACKENDS_NETWORKING_CURL_REQUEST_H
-#include <common/callback.h>
+
+#include "common/callback.h"
 
 namespace Networking {
 
 class Request {
 protected:
-	typedef void(*SimpleCallback)(Request* request, void *result);
-
 	/**
 	* Callback, which should be called before Request returns true in handle().
 	* That's the way Requests pass the result to the code which asked to create this request.
 	*/
 
-	Common::BaseCallback* _callback;
-
-	/**
-	* Pointer, which could be set by Request creating code. It might be accessed
-	* from this Request when callback is called, for example.
-	*/
-
-	void *_pointer;
+	Common::BaseCallback<> *_callback;
 
 public:
-	Request(Common::BaseCallback* cb): _callback(cb) {};
+	Request(Common::BaseCallback<> *cb): _callback(cb) {};
 	virtual ~Request() {};
 
 	/**
@@ -55,9 +47,6 @@ public:
 	*/
 
 	virtual bool handle() = 0;
-
-	void setPointer(void *ptr) { _pointer = ptr; }
-	void *pointer() const { return _pointer;  }
 };
 
 } //end of namespace Cloud
diff --git a/common/callback.h b/common/callback.h
index 4cc63ed..2101331 100644
--- a/common/callback.h
+++ b/common/callback.h
@@ -25,45 +25,116 @@
 
 namespace Common {
 
-class BaseCallback {
+/**
+* BaseCallback<S> is a simple base class for object-oriented callbacks.
+*
+* Object-oriented callbacks are such callbacks that know exact instance
+* which method must be called.
+*
+* For backwards compatibility purposes, there is a GlobalFunctionCallback,
+* which is BaseCallback<void *>, so it can be used with global C-like
+* functions too.
+*
+* <S> is the type, which is passed to operator() of this callback.
+* This allows you to specify that you accept a callback, which wants
+* to receive an <S> object.
+*/
+
+template<typename S = void *> class BaseCallback {
 public:
 	BaseCallback() {}
 	virtual ~BaseCallback() {}
-	virtual void operator()(void *ptr) = 0;
+	virtual void operator()(S data) = 0;
 };
 
-class GlobalFunctionCallback: public BaseCallback {
+/**
+* GlobalFunctionCallback is a simple wrapper for global C functions.
+*
+* If there is a method, which accepts BaseCallback<void *>, you can
+* easily pass your C function by passing
+*     new GlobalFunctionCallback(yourFunction)
+*/
+
+class GlobalFunctionCallback: public BaseCallback<void *> {
 	typedef void(*GlobalFunction)(void *result);
 	GlobalFunction _callback;
 
 public:
 	GlobalFunctionCallback(GlobalFunction cb): _callback(cb) {}
 	virtual ~GlobalFunctionCallback() {}
-	virtual void operator()(void *ptr) {
-		if (_callback) _callback(ptr);
+	virtual void operator()(void *data) {
+		if (_callback) _callback(data);
 	}
 };
 
-template<class T> class Callback: public BaseCallback {
-	typedef void(T::*TMethod)(void *);
+/**
+* Callback<T, S> implements an object-oriented callback.
+*
+* <T> stands for a class which method you want to call.
+* <S>, again, is the type of an object passed to operator().
+*
+* So, if you have void MyClass::myMethod(AnotherClass) method,
+* the corresponding callback is Callback<MyClass, AnotherClass>.
+* You create it similarly to this:
+*     new Callback<MyClass, AnotherClass>(
+*         pointerToMyClassObject,
+*         &MyClass::myMethod
+*     )
+*/
+
+template<class T, typename S = void *> class Callback: public BaseCallback<S> {
+protected:
+	typedef void(T::*TMethod)(S);
 	T *_object;
 	TMethod _method;
 public:
 	Callback(T *object, TMethod method): _object(object), _method(method) {}
 	virtual ~Callback() {}
-	void operator()(void *ptr) { (_object->*_method)(ptr); }
+	void operator()(S data) { (_object->*_method)(data); }
 };
 
-template<class T> class CallbackBridge: public BaseCallback {
-	typedef void(T::*TCallbackMethod)(BaseCallback *, void *);
+/**
+* CallbackBridge<T, OS, S> helps you to chain callbacks.
+*
+* CallbackBridge keeps a pointer to BaseCallback<OS>.
+* When its operator() is called, it passes this pointer
+* along with the actual data (of type <S>) to the method
+* of <T> class.
+*
+* This is needed when you have to call a callback only
+* when your own callback is called. So, your callback
+* is "inner" and the other one is "outer".
+*
+* CallbackBridge implements the "inner" one and calls
+* the method you wanted. It passes the "outer", so you
+* can call it from your method. You can ignore it, but
+* probably there is no point in using CallbackBridge then.
+*
+* So, if you receive a BaseCallback<SomeClass> callback
+* and you want to call it from your MyClass::myMethod method,
+* you should create CallbackBridge<MyClass, SomeClass, S>,
+* where <S> is data type you want to receive in MyClass::myMethod.
+*
+* You create it similarly to this:
+*     new Callback<MyClass, SomeClass, AnotherClass>(
+*         pointerToMyClassObject,
+*         &MyClass::myMethod,
+*         outerCallback
+*     )
+* where `outerCallback` is BaseCallback<SomeClass> and `myMethod` is:
+* void MyClass::myMethod(BaseCallback<SomeClass> *, AnotherClass)
+*/
+
+template<class T, typename OS, typename S = void *> class CallbackBridge: public BaseCallback<S> {
+	typedef void(T::*TCallbackMethod)(BaseCallback<OS> *, S);
 	T *_object;
 	TCallbackMethod _method;
-	BaseCallback *_outerCallback;
+	BaseCallback<OS> *_outerCallback;
 public:
-	CallbackBridge(T *object, TCallbackMethod method, BaseCallback *outerCallback):
+	CallbackBridge(T *object, TCallbackMethod method, BaseCallback<OS> *outerCallback):
 		_object(object), _method(method), _outerCallback(outerCallback) {}
 	virtual ~CallbackBridge() {}
-	void operator()(void *ptr) { (_object->*_method)(_outerCallback, ptr); }
+	void operator()(S data) { (_object->*_method)(_outerCallback, data); }
 };
 
 } // End of namespace Common
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 08ba928..5ed67d3 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -53,7 +53,7 @@ public:
 	* Starts saves syncing process in currently active storage if there is any.
 	*/
 
-	virtual void syncSaves(Cloud::Storage::OperationCallback callback = 0) = 0;
+	virtual void syncSaves(BaseCallback<bool> *callback = 0) = 0;
 };
 
 } //end of namespace Common


Commit: b570499164bf94fc4735bad54e7a722498ae56ea
    https://github.com/scummvm/scummvm/commit/b570499164bf94fc4735bad54e7a722498ae56ea
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Callback typedefs

And do some minor cleanup work.

Changed paths:
  A backends/cloud/storagefile.cpp
  A backends/cloud/storagefile.h
  A backends/cloud/storageinfo.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/storage.h
    backends/module.mk
    common/cloudmanager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 964b95b..49355fa 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -66,14 +66,14 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
-void DropboxStorage::syncSaves(Common::BaseCallback<bool> *callback) {
+void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation
 	info(new Common::Callback<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoMethodCallback));
 	//that line meant the following:
 	//"please, do the info API request and, when it's finished, call the infoMethodCallback() of me"
 }
 
-void DropboxStorage::info(Common::BaseCallback<StorageInfo> *outerCallback) {
+void DropboxStorage::info(StorageInfoCallback outerCallback) {
 	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
@@ -84,8 +84,8 @@ void DropboxStorage::info(Common::BaseCallback<StorageInfo> *outerCallback) {
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::infoInnerCallback(Common::BaseCallback<StorageInfo> *outerCallback, void *ptr) {	
-	Common::JSONValue *json = (Common::JSONValue *)ptr;
+void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, void *jsonPointer) {
+	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		delete outerCallback;
@@ -93,6 +93,8 @@ void DropboxStorage::infoInnerCallback(Common::BaseCallback<StorageInfo> *outerC
 	}
 
 	if (outerCallback) {
+		//TODO: check that JSON doesn't contain some error message instead of an actual response
+		//TODO: use JSON fields to construct StorageInfo		
 		(*outerCallback)(StorageInfo(json->stringify()));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 8cc9312..3fc38bc 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -39,37 +39,37 @@ class DropboxStorage: public Cloud::Storage {
 
 	static void getAccessToken(Common::String code);
 
+	/** Constructs StorageInfo based on JSON response from cloud. */
+	void infoInnerCallback(StorageInfoCallback outerCallback, void *json);
+
 public:	
 	virtual ~DropboxStorage();
 
 	/** Returns pointer to Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array<StorageFile> > *callback) {} //TODO
+	virtual void listDirectory(Common::String path, FileArrayCallback callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, Common::BaseCallback<bool> *callback) {} //TODO
+	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) {} //TODO
 
 	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, Common::BaseCallback<Common::ReadStream> *callback) {} //TODO
+	virtual void download(Common::String path, ReadStreamCallback callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
+	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(Common::BaseCallback<bool> *callback);
+	virtual void syncSaves(BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
+	virtual void createDirectory(Common::String path, BoolCallback callback) {} //TODO
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, Common::BaseCallback<bool> *callback) {} //TODO
+	virtual void touch(Common::String path, BoolCallback callback) {} //TODO
 
 	/** Returns pointer to the StorageInfo struct. */
-	virtual void info(Common::BaseCallback<StorageInfo> *callback);
-
-	/** This is what is called by CurlJsonRequest. */
-	void infoInnerCallback(Common::BaseCallback<StorageInfo> *outerCallback, void *ptr);
+	virtual void info(StorageInfoCallback callback);
 
-	/** This is what is called by infoInnerCallback() (it's its outer callback). */
+	/** This method is passed into info(). (Temporary) */
 	void infoMethodCallback(StorageInfo storageInfo);
 
 	/** Returns whether saves sync process is running. */
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index a9a2b8a..7caf241 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -46,7 +46,7 @@ Storage* Manager::getCurrentStorage() {
 	return _currentStorage;
 }
 
-void Manager::syncSaves(Common::BaseCallback<bool> *callback) {
+void Manager::syncSaves(Storage::BoolCallback callback) {
 	Storage* storage = getCurrentStorage();
 	if (storage) storage->syncSaves(callback);
 }
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index c247132..3ad2e0d 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -38,7 +38,7 @@ public:
 	virtual void init();
 
 	virtual Storage* getCurrentStorage();
-	virtual void syncSaves(Common::BaseCallback<bool> *callback);
+	virtual void syncSaves(Storage::BoolCallback callback);
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index f2079eb..eaf0ba1 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -27,76 +27,44 @@
 #include "common/stream.h"
 #include "common/str.h"
 #include "common/callback.h"
+#include "backends/cloud/storagefile.h"
+#include "backends/cloud/storageinfo.h"
 
 namespace Cloud {
 
-class StorageFile {
-	Common::String _path, _name;
-	uint32 _size, _timestamp;
-	bool _isDirectory;
-
-public:
-	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
-		_path = pth;
-
-		_name = pth;
-		for (uint32 i = _name.size() - 1; i >= 0; --i) {
-			if (_name[i] == '/' || _name[i] == '\\') {
-				_name.erase(0, i);
-				break;
-			}
-			if (i == 0) break; //OK, I admit that's strange
-		}
-
-		_size = sz;
-		_timestamp = ts;
-		_isDirectory = dir;
-	}
-
-	Common::String path() const { return _path; }
-	Common::String name() const { return _name; }
-	uint32 size() const { return _size; }
-	uint32 timestamp() const { return _timestamp; }
-	bool isDirectory() const { return _isDirectory; }
-};
-
-class StorageInfo {
-	Common::String _info;
-
-public:
-	StorageInfo(Common::String info): _info(info) {}
-
-	Common::String info() const { return _info; }
-};
-
 class Storage {
 public:
+	typedef Common::BaseCallback< Common::Array<StorageFile> > *FileArrayCallback;
+	typedef Common::BaseCallback<Common::ReadStream *> *ReadStreamCallback;
+	typedef Common::BaseCallback<StorageInfo> *StorageInfoCallback;
+	typedef Common::BaseCallback<bool> *BoolCallback;
+
 	Storage() {}
 	virtual ~Storage() {}
 
-	/** Returns pointer to Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array<StorageFile> > *callback) = 0;
+	/** Returns Common::Array<StorageFile>. */
+	virtual void listDirectory(Common::String path, FileArrayCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, Common::BaseCallback<bool> *callback) = 0;
+	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) = 0;
 
 	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, Common::BaseCallback<Common::ReadStream> *callback) = 0;
+	virtual void download(Common::String path, ReadStreamCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, Common::BaseCallback<bool> *callback) = 0;
+	virtual void remove(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(Common::BaseCallback<bool> *callback) = 0;
+	virtual void syncSaves(BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, Common::BaseCallback<bool> *callback) = 0;
+	virtual void createDirectory(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, Common::BaseCallback<bool> *callback) = 0;
+	virtual void touch(Common::String path, BoolCallback callback) = 0;
 
-	/** Returns pointer to the StorageInfo struct. */
-	 virtual void info(Common::BaseCallback<StorageInfo> *callback) = 0;
+	/** Returns the StorageInfo struct. */
+	virtual void info(StorageInfoCallback callback) = 0;
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
new file mode 100644
index 0000000..0d40698
--- /dev/null
+++ b/backends/cloud/storagefile.cpp
@@ -0,0 +1,48 @@
+/* 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 "backends/cloud/storagefile.h"
+
+namespace Cloud {
+
+StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
+	_path = pth;
+
+	_name = pth;
+	if (_name.size() != 0) {
+		uint32 i = _name.size() - 1;
+		while (true) {
+			if (_name[i] == '/' || _name[i] == '\\') {
+				_name.erase(0, i);
+				break;
+			}
+			if (i == 0) break;
+			--i;
+		}
+	}
+
+	_size = sz;
+	_timestamp = ts;
+	_isDirectory = dir;
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
new file mode 100644
index 0000000..8706ba1
--- /dev/null
+++ b/backends/cloud/storagefile.h
@@ -0,0 +1,53 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_STORAGEFILE_H
+#define BACKENDS_CLOUD_STORAGEFILE_H
+
+#include "common/str.h"
+
+namespace Cloud {
+
+/**
+* StorageFile represents a file storaged on remote cloud storage.
+* It contains basic information about a file, and might be used
+* when listing directories or syncing files.
+*/
+
+class StorageFile {
+	Common::String _path, _name;
+	uint32 _size, _timestamp;
+	bool _isDirectory;
+
+public:
+	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir);
+
+	Common::String path() const { return _path; }
+	Common::String name() const { return _name; }
+	uint32 size() const { return _size; }
+	uint32 timestamp() const { return _timestamp; }
+	bool isDirectory() const { return _isDirectory; }
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
new file mode 100644
index 0000000..510acb3
--- /dev/null
+++ b/backends/cloud/storageinfo.h
@@ -0,0 +1,47 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_STORAGEINFO_H
+#define BACKENDS_CLOUD_STORAGEINFO_H
+
+#include "common/str.h"
+
+namespace Cloud {
+
+/**
+* StorageInfo contains information about remote cloud storage.
+* It's disk quota usage, owner name, and such.
+*/
+
+class StorageInfo {
+	/** Temporary StorageInfo just contains raw JSON, received from cloud storage. */
+	Common::String _info;
+
+public:
+	StorageInfo(Common::String info): _info(info) {}
+
+	Common::String info() const { return _info; }
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 035eac2..2cee007 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS := \
 ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloud/manager.o \
+	cloud/storagefile.o \
 	cloud/dropbox/dropboxstorage.o
 endif
 
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 5ed67d3..5919937 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -53,7 +53,7 @@ public:
 	* Starts saves syncing process in currently active storage if there is any.
 	*/
 
-	virtual void syncSaves(BaseCallback<bool> *callback = 0) = 0;
+	virtual void syncSaves(Cloud::Storage::BoolCallback callback = 0) = 0;
 };
 
 } //end of namespace Common


Commit: c4b1fdc72752516a81b83490035d8f71357d045b
    https://github.com/scummvm/scummvm/commit/c4b1fdc72752516a81b83490035d8f71357d045b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Request destructor

It now contains not a pointer to a function, but an actual pointer,
which must be freed.

Changed paths:
    backends/networking/curl/request.h



diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index d3efd58..37cb388 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -38,7 +38,7 @@ protected:
 
 public:
 	Request(Common::BaseCallback<> *cb): _callback(cb) {};
-	virtual ~Request() {};
+	virtual ~Request() { delete _callback; };
 
 	/**
 	* Method, which does actual work. Depends on what this Request is doing.


Commit: 41e65db7d0468b01f626c6ce21a1b8e6791f58fa
    https://github.com/scummvm/scummvm/commit/41e65db7d0468b01f626c6ce21a1b8e6791f58fa
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Storage saving mechanism

In this commit CloudManager starts supporting multiple Storage. Now, in
its init() it loads all the Storages and determines the current one.

It now also has save() method. In that method all Storages are saved
with their new saveConfig() method.

CloudManager::save() not called from anywhere, though. The only one
Storage that could be added is DropboxStorage in case you have no
cloud-related config keys or you have no storages connected.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/storage.h
    common/cloudmanager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 49355fa..d32d865 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -45,9 +45,12 @@ static void saveAccessTokenCallback(void *ptr) {
 		if (!result.contains("access_token") || !result.contains("uid")) {
 			warning("Bad response, no token/uid passed");
 		} else {
-			ConfMan.set("current_storage_type", "Dropbox", "cloud");
-			ConfMan.set("current_storage_access_token", result.getVal("access_token")->asString(), "cloud");
-			ConfMan.set("current_storage_user_id", result.getVal("uid")->asString(), "cloud");
+			//we suppose that's the first storage
+			ConfMan.set("storages_number", "1", "cloud");
+			ConfMan.set("current_storage", "1", "cloud");
+			ConfMan.set("storage1_type", "Dropbox", "cloud");
+			ConfMan.set("storage1_access_token", result.getVal("access_token")->asString(), "cloud");
+			ConfMan.set("storage1_user_id", result.getVal("uid")->asString(), "cloud");
 			ConfMan.removeKey("dropbox_code", "cloud");
 			debug("Now please restart ScummVM to apply the changes.");
 		}
@@ -66,6 +69,12 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
+void DropboxStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "type", "Dropbox", "cloud");
+	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
+	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
+}
+
 void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation
 	info(new Common::Callback<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoMethodCallback));
@@ -106,22 +115,22 @@ void DropboxStorage::infoMethodCallback(StorageInfo storageInfo) {
 	debug("info: %s", storageInfo.info().c_str());
 }
 
-DropboxStorage *DropboxStorage::loadFromConfig() {
+DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
 	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
 
-	if (!ConfMan.hasKey("current_storage_access_token", "cloud")) {
+	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!ConfMan.hasKey("current_storage_user_id", "cloud")) {
+	if (!ConfMan.hasKey(keyPrefix + "user_id", "cloud")) {
 		warning("No user_id found");
 		return 0;
 	}
 
-	Common::String accessToken = ConfMan.get("current_storage_access_token", "cloud");
-	Common::String userId = ConfMan.get("current_storage_user_id", "cloud");
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
+	Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
 	return new DropboxStorage(accessToken, userId);
 }
 
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 3fc38bc..493fcdd 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -45,7 +45,23 @@ class DropboxStorage: public Cloud::Storage {
 public:	
 	virtual ~DropboxStorage();
 
-	/** Returns pointer to Common::Array<StorageFile>. */
+	/**
+	* Storage methods, which are used by CloudManager to save
+	* storage in configuration file.
+	*/
+
+	/**
+	* Save storage data using ConfMan.
+	* @param keyPrefix all saved keys must start with this prefix.
+	* @note every Storage must write keyPrefix + "type" key
+	*       with common value (e.g. "Dropbox").
+	*/
+
+	virtual void saveConfig(Common::String keyPrefix);
+
+	/** Public Cloud API comes down there. */
+
+	/** Returns Common::Array<StorageFile>. */
 	virtual void listDirectory(Common::String path, FileArrayCallback callback) {} //TODO
 
 	/** Calls the callback when finished. */
@@ -66,7 +82,7 @@ public:
 	/** Calls the callback when finished. */
 	virtual void touch(Common::String path, BoolCallback callback) {} //TODO
 
-	/** Returns pointer to the StorageInfo struct. */
+	/** Returns the StorageInfo struct. */
 	virtual void info(StorageInfoCallback callback);
 
 	/** This method is passed into info(). (Temporary) */
@@ -82,7 +98,7 @@ public:
 	* Load token and user id from configs and return DropboxStorage for those.	
 	* @return pointer to the newly created DropboxStorage or 0 if some problem occured.
 	*/
-	static DropboxStorage *loadFromConfig();
+	static DropboxStorage *loadFromConfig(Common::String keyPrefix);
 
 	/**
 	* Returns Dropbox auth link.
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 7caf241..1c11efb 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -26,28 +26,67 @@
 
 namespace Cloud {
 
-Manager::Manager(): _currentStorage(0) {}
+Manager::Manager(): _currentStorageIndex(0) {}
 
-Manager::~Manager() { delete _currentStorage; }
+Manager::~Manager() {
+	//TODO: do we have to save storages on manager destruction?	
+	for (uint32 i = 0; i < _storages.size(); ++i)
+		delete _storages[i];
+	_storages.clear();	
+}
 
 void Manager::init() {
-	if (ConfMan.hasKey("current_storage_type", "cloud")) {
-		Common::String storageType = ConfMan.get("current_storage_type", "cloud");
-		if (storageType == "Dropbox") _currentStorage = Dropbox::DropboxStorage::loadFromConfig();
-		else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
+	bool offerDropbox = false;
+
+	if (ConfMan.hasKey("storages_number", "cloud")) {
+		int storages = ConfMan.getInt("storages_number", "cloud");
+		for (int i = 1; i <= storages; ++i) {
+			Storage *loaded = 0;
+			Common::String keyPrefix = Common::String::format("storage%d_", i);
+			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
+				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
+				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
+				else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
+			} else {
+				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
+			}
+			if (loaded) _storages.push_back(loaded);
+		}
+
+		uint32 index = 0;
+		if (ConfMan.hasKey("current_storage", "cloud")) {
+			index = ConfMan.getInt("current_storage", "cloud") - 1; //count from 1, all for UX
+		}
+		if (index >= _storages.size()) index = 0;
+		_currentStorageIndex = index;
+
+		if (_storages.size() == 0) offerDropbox = true;
+	} else {
+		offerDropbox = true;
 	}
-	else {
+
+	if (offerDropbox) {
 		//this is temporary console offer to auth with Dropbox (because there is no other storage type yet anyway)
 		Dropbox::DropboxStorage::authThroughConsole();
 	}
 }
 
-Storage* Manager::getCurrentStorage() {
-	return _currentStorage;
+void Manager::save() {
+	ConfMan.set("storages_number", Common::String::format("%d", _storages.size()), "cloud");
+	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex + 1), "cloud");
+	for (uint32 i = 0; i < _storages.size(); ++i)
+		_storages[i]->saveConfig(Common::String::format("storage%d_", i+1));
+	ConfMan.flushToDisk();
+}
+
+Storage *Manager::getCurrentStorage() {
+	if (_currentStorageIndex < _storages.size())
+		return _storages[_currentStorageIndex];
+	return 0;
 }
 
 void Manager::syncSaves(Storage::BoolCallback callback) {
-	Storage* storage = getCurrentStorage();
+	Storage *storage = getCurrentStorage();
 	if (storage) storage->syncSaves(callback);
 }
 
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 3ad2e0d..e531854 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -29,15 +29,17 @@
 namespace Cloud {
 
 class Manager: public Common::CloudManager {
-	Storage* _currentStorage;
+	Common::Array<Storage *> _storages;
+	uint _currentStorageIndex;
 
 public:
 	Manager();
 	virtual ~Manager();
 
 	virtual void init();
+	virtual void save();
 
-	virtual Storage* getCurrentStorage();
+	virtual Storage *getCurrentStorage();
 	virtual void syncSaves(Storage::BoolCallback callback);
 };
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index eaf0ba1..a6b6c48 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -42,6 +42,22 @@ public:
 	Storage() {}
 	virtual ~Storage() {}
 
+	/**
+	 * Storage methods, which are used by CloudManager to save
+	 * storage in configuration file.
+	 */
+
+	/**
+	 * Save storage data using ConfMan.
+	 * @param keyPrefix all saved keys must start with this prefix.
+	 * @note every Storage must write keyPrefix + "type" key
+	 *       with common value (e.g. "Dropbox").
+	 */
+
+	virtual void saveConfig(Common::String keyPrefix) = 0;
+
+	/** Public Cloud API comes down there. */
+
 	/** Returns Common::Array<StorageFile>. */
 	virtual void listDirectory(Common::String path, FileArrayCallback callback) = 0;
 
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 5919937..d1c8945 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -41,13 +41,19 @@ public:
 	virtual void init() = 0;
 
 	/**
+	* Saves all information into configuration file.
+	*/
+
+	virtual void save() = 0;
+
+	/**
 	* Returns active Storage, which could be used to interact
 	*  with cloud storage.
 	*
 	* @return	active Cloud::Storage or null, if there is no active Storage.
 	*/
 
-	virtual Cloud::Storage* getCurrentStorage() = 0;
+	virtual Cloud::Storage *getCurrentStorage() = 0;
 
 	/**
 	* Starts saves syncing process in currently active storage if there is any.


Commit: a439bd4c33e6f8a47a856d7cb4a4db7c540a85ab
    https://github.com/scummvm/scummvm/commit/a439bd4c33e6f8a47a856d7cb4a4db7c540a85ab
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make StorageInfo useful

It now contains a few useful methods to get name or quota usage.

DropboxStorage returns a finely filled StorageInfo.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/storageinfo.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d32d865..5a88121 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -101,10 +101,17 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, void *
 		return;
 	}
 
-	if (outerCallback) {
-		//TODO: check that JSON doesn't contain some error message instead of an actual response
-		//TODO: use JSON fields to construct StorageInfo		
-		(*outerCallback)(StorageInfo(json->stringify()));
+	if (outerCallback) {		
+		//Dropbox documentation states there is no errors for this API method
+		Common::JSONObject info = json->asObject();
+		Common::String uid = Common::String::format("%d", info.getVal("uid")->asNumber());
+		Common::String name = info.getVal("display_name")->asString();
+		Common::String email = info.getVal("email")->asString();
+		Common::JSONObject quota = info.getVal("quota_info")->asObject();
+		uint32 quotaNormal = quota.getVal("normal")->asNumber();
+		uint32 quotaShared = quota.getVal("shared")->asNumber();
+		uint32 quotaAllocated = quota.getVal("quota")->asNumber();		
+		(*outerCallback)(StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated));
 		delete outerCallback;
 	}
 	
@@ -112,7 +119,10 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, void *
 }
 
 void DropboxStorage::infoMethodCallback(StorageInfo storageInfo) {
-	debug("info: %s", storageInfo.info().c_str());
+	debug("\nStorage info:");
+	debug("User name: %s", storageInfo.name().c_str());
+	debug("Email: %s", storageInfo.email().c_str());
+	debug("Disk usage: %u/%u", storageInfo.used(), storageInfo.available());
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
index 510acb3..f095635 100644
--- a/backends/cloud/storageinfo.h
+++ b/backends/cloud/storageinfo.h
@@ -32,14 +32,20 @@ namespace Cloud {
 * It's disk quota usage, owner name, and such.
 */
 
-class StorageInfo {
-	/** Temporary StorageInfo just contains raw JSON, received from cloud storage. */
-	Common::String _info;
+class StorageInfo {	
+	Common::String _uid, _name, _email;
+	uint32 _usedBytes, _allocatedBytes;
 
 public:
-	StorageInfo(Common::String info): _info(info) {}
+	StorageInfo(Common::String uid, Common::String name, Common::String email, uint32 used, uint32 allocated):
+		_uid(uid), _name(name), _email(email), _usedBytes(used), _allocatedBytes(allocated) {}
+
+	Common::String uid() const { return _uid; }
+	Common::String name() const { return _name; }
+	Common::String email() const { return _email; }
+	uint32 used() const { return _usedBytes; }
+	uint32 available() const { return _allocatedBytes; }
 
-	Common::String info() const { return _info; }
 };
 
 } //end of namespace Cloud


Commit: 735db74b900d1e0a0654ca03983cd91cea36f41e
    https://github.com/scummvm/scummvm/commit/735db74b900d1e0a0654ca03983cd91cea36f41e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add DropboxStorage::listDirectory sketch

It doesn't support any "has_more", doesn't call user's callback and just
prints JSON instead of parsing in into an array of files.

I believe it would become DropboxListDirectoryRequest in the next
commit.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.h
    backends/networking/curl/curljsonrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 5a88121..28d14c6 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -75,11 +75,37 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
 
+void printJson(void *ptr) {
+	Common::JSONValue *json = (Common::JSONValue *)ptr;
+	if (json) {		
+		debug("%s", json->stringify(true).c_str());
+	} else {
+		warning("null, not json");
+	}
+}
+
+void DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
+	//Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::listDirectoryInnerCallback, outerCallback);
+	Common::BaseCallback<> *innerCallback = new Common::GlobalFunctionCallback(printJson); //okay
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/json");	
+
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
+	jsonRequestParameters.setVal("recursive", new Common::JSONValue(recursive));
+	jsonRequestParameters.setVal("include_media_info", new Common::JSONValue(false));
+	jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
+
+	Common::JSONValue value(jsonRequestParameters);	
+	request->addPostField(Common::JSON::stringify(&value));
+
+	ConnMan.addRequest(request);
+}
+
 void DropboxStorage::syncSaves(BoolCallback callback) {
-	//this is not the real syncSaves() implementation
-	info(new Common::Callback<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoMethodCallback));
-	//that line meant the following:
-	//"please, do the info API request and, when it's finished, call the infoMethodCallback() of me"
+	//this is not the real syncSaves() implementation	
+	listDirectory("", 0); //"" is root in Dropbox, not "/"
 }
 
 void DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 493fcdd..3077b98 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -62,7 +62,7 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, FileArrayCallback callback) {} //TODO
+	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
 	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) {} //TODO
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index a6b6c48..1435be8 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -59,7 +59,7 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, FileArrayCallback callback) = 0;
+	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
 	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) = 0;
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 702de22..805852e 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -70,10 +70,12 @@ bool CurlJsonRequest::handle() {
 
 		if (_stream->eos()) {			
 			if (_stream->httpResponseCode() != 200)
-				warning("HTTP response code is not 200 OK");
+				warning("HTTP response code is not 200 OK (it's %d)", _stream->httpResponseCode());
 
 			if (_callback) {
-				char *contents = getPreparedContents();				
+				char *contents = getPreparedContents();
+				if (_stream->httpResponseCode() != 200)
+					debug("%s", contents);
 				Common::JSONValue *json = Common::JSON::parse(contents);				
 				(*_callback)(json); //potential memory leak, free it in your callbacks!
 			}


Commit: e53e3d188b9da424af5e44e51bff265f077ce05e
    https://github.com/scummvm/scummvm/commit/e53e3d188b9da424af5e44e51bff265f077ce05e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add DropboxListDirectoryRequest

Does multiple CurlJsonRequests while Dropbox returns "has_more" = true.

Changed paths:
  A backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
  A backends/cloud/dropbox/dropboxlistdirectoryrequest.h
  A backends/cloud/iso8601.cpp
  A backends/cloud/iso8601.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storagefile.cpp
    backends/module.mk



diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
new file mode 100644
index 0000000..e28a445
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -0,0 +1,114 @@
+/* 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 "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive):
+	Networking::Request(0), _filesCallback(cb), _token(token), _complete(false) {	
+	Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);//new Common::GlobalFunctionCallback(printJson); //okay
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/json");
+
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
+	jsonRequestParameters.setVal("recursive", new Common::JSONValue(recursive));
+	jsonRequestParameters.setVal("include_media_info", new Common::JSONValue(false));
+	jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
+
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	ConnMan.addRequest(request);
+}
+
+void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
+	Common::JSONValue *json = (Common::JSONValue *)jsonPtr;
+	if (json) {
+		Common::JSONObject response = json->asObject();
+		
+		if (response.contains("error") || response.contains("error_summary")) {
+			warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
+			_complete = true;
+			delete json;
+			return;
+		}
+
+		//TODO: check that all keys exist to avoid segfaults
+		//TODO: get more files in the folder to check "has_more" case
+
+		Common::JSONArray items = response.getVal("entries")->asArray();
+		for (uint32 i = 0; i < items.size(); ++i) {
+			Common::JSONObject item = items[i]->asObject();
+			Common::String path = item.getVal("path_lower")->asString();			
+			bool isDirectory = (item.getVal(".tag")->asString() == "folder");
+			uint32 size = 0, timestamp = 0;
+			if (!isDirectory) {
+				size = item.getVal("size")->asNumber();
+				timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
+			}
+			_files.push_back(StorageFile(path, size, timestamp, isDirectory));
+		}
+
+		bool hasMore = response.getVal("has_more")->asBool();
+
+		if (hasMore) {
+			Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);
+			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
+			request->addHeader("Authorization: Bearer " + _token);
+			request->addHeader("Content-Type: application/json");
+
+			Common::JSONObject jsonRequestParameters;
+			jsonRequestParameters.setVal("cursor", new Common::JSONValue(response.getVal("cursor")->asString()));
+
+			Common::JSONValue value(jsonRequestParameters);
+			request->addPostField(Common::JSON::stringify(&value));
+
+			ConnMan.addRequest(request);
+		} else {
+			_complete = true;
+		}		
+	} else {
+		warning("null, not json");
+		_complete = true;
+	}
+
+	delete json;
+}
+
+bool DropboxListDirectoryRequest::handle() {
+	if (_complete && _filesCallback) {		
+		(*_filesCallback)(_files);
+	}
+
+	return _complete;
+}
+
+
+} //end of namespace Dropbox
+} //end of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
new file mode 100644
index 0000000..03b4fc1
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -0,0 +1,51 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "common/callback.h"
+#include "backends/networking/curl/request.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+class DropboxListDirectoryRequest: public Networking::Request {
+	Storage::FileArrayCallback _filesCallback;
+	Common::String _token;
+	bool _complete;
+	Common::Array<StorageFile> _files;
+
+	void responseCallback(void *jsonPtr);
+
+public:
+	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
+	virtual ~DropboxListDirectoryRequest() { delete _filesCallback; }
+
+	virtual bool handle();
+};
+
+} //end of namespace Dropbox
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 28d14c6..6de9424 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
@@ -75,37 +76,20 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
 
-void printJson(void *ptr) {
-	Common::JSONValue *json = (Common::JSONValue *)ptr;
-	if (json) {		
-		debug("%s", json->stringify(true).c_str());
-	} else {
-		warning("null, not json");
-	}
+void DropboxStorage::printFiles(Common::Array<StorageFile> files) {
+	debug("files:");
+	for (uint32 i = 0; i < files.size(); ++i)
+		debug("\t%s", files[i].name().c_str());
 }
 
 void DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
-	//Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::listDirectoryInnerCallback, outerCallback);
-	Common::BaseCallback<> *innerCallback = new Common::GlobalFunctionCallback(printJson); //okay
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
-	request->addHeader("Authorization: Bearer " + _token);
-	request->addHeader("Content-Type: application/json");	
-
-	Common::JSONObject jsonRequestParameters;
-	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
-	jsonRequestParameters.setVal("recursive", new Common::JSONValue(recursive));
-	jsonRequestParameters.setVal("include_media_info", new Common::JSONValue(false));
-	jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
-
-	Common::JSONValue value(jsonRequestParameters);	
-	request->addPostField(Common::JSON::stringify(&value));
-
-	ConnMan.addRequest(request);
+	ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
 void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
-	listDirectory("", 0); //"" is root in Dropbox, not "/"
+	//"" is root in Dropbox, not "/"
+	listDirectory("", new Common::Callback<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::printFiles), true);
 }
 
 void DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 3077b98..c1c2e03 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -42,6 +42,8 @@ class DropboxStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, void *json);
 
+	void printFiles(Common::Array<StorageFile> files);
+
 public:	
 	virtual ~DropboxStorage();
 
diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
new file mode 100644
index 0000000..d34c98e
--- /dev/null
+++ b/backends/cloud/iso8601.cpp
@@ -0,0 +1,117 @@
+/* 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 "backends/cloud/iso8601.h"
+#include "common/str.h"
+
+namespace {
+
+uint32 find(const Common::String &haystack, const Common::String &needle, uint32 pos = 0) {
+	if (pos >= haystack.size()) {
+		return Common::String::npos;
+	}
+
+	//TODO: write something smarter
+	uint32 lastIndex = haystack.size() - needle.size();
+	for (uint32 i = pos; i < lastIndex; ++i) {
+		bool found = true;
+		for (uint32 j = 0; j < needle.size(); ++j)
+			if (haystack[i + j] != needle[j]) {
+				found = false;
+				break;
+			}
+
+		if (found) return i;
+	}
+
+	return Common::String::npos;
+}
+
+Common::String getSubstring(const Common::String &s, uint32 beginning, uint32 ending) {
+	//beginning inclusive, ending exclusive
+	if (beginning == -1 || ending == -1) return ""; //bad
+	Common::String result = s;
+	result.erase(ending);
+	result.erase(0, beginning);
+	return result;
+}
+
+int parseInt(Common::String s) {
+	//TODO: not sure this is not forbidden at all
+	return atoi(s.c_str());
+}
+
+}
+
+namespace Cloud {
+namespace ISO8601 {
+
+uint32 convertToTimestamp(const Common::String &iso8601Date) {		
+	//2015-05-12T15:50:38Z
+	uint32 firstHyphen = find(iso8601Date, "-");
+	uint32 secondHyphen = find(iso8601Date, "-", firstHyphen + 1);
+	uint32 tSeparator = find(iso8601Date, "T", secondHyphen + 1);
+	uint32 firstColon = find(iso8601Date, ":", tSeparator + 1);
+	uint32 secondColon = find(iso8601Date, ":", firstColon + 1);
+	uint32 zSeparator = find(iso8601Date, "Z", secondColon + 1);
+	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char
+
+	Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
+	Common::String month = getSubstring(iso8601Date, firstHyphen + 1, secondHyphen);
+	Common::String day = getSubstring(iso8601Date, secondHyphen + 1, tSeparator);
+	Common::String hour = getSubstring(iso8601Date, tSeparator + 1, firstColon);
+	Common::String minute = getSubstring(iso8601Date, firstColon + 1, secondColon);
+	Common::String second = getSubstring(iso8601Date, secondColon + 1, zSeparator);
+	//now note only 'ending' argument was not '+1' (which means I could've make that function such that -1 means 'until the end')
+
+	int Y = parseInt(year);
+	int M = parseInt(month);
+	int D = parseInt(day);
+	int h = parseInt(hour);
+	int m = parseInt(minute);
+	int s = parseInt(second);
+
+	//ok, now I compose a timestamp based on my basic perception of time/date
+	//yeah, I know about leap years and leap seconds and all, but still we don't care there
+
+	uint32 days = D - 1;
+	for (int i = 1970; i < Y; ++i)
+		if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
+			days += 366;
+		else
+			days += 365;
+
+	int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	for (int i = 1; i < M; ++i) {
+		days += mdays[i - 1];
+		if (i == 2)
+			if ((Y % 4 == 0 && Y % 100 != 0) || (Y % 400 == 0))
+				days += 1;
+	}
+
+	uint32 hours = days * 24 + h;
+	uint32 minutes = hours * 60 + m;
+	return minutes * 60 + s;
+}
+
+} //end of namespace ISO8601
+} //end of namespace Cloud
diff --git a/backends/cloud/iso8601.h b/backends/cloud/iso8601.h
new file mode 100644
index 0000000..a53b3bb
--- /dev/null
+++ b/backends/cloud/iso8601.h
@@ -0,0 +1,37 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ISO8601_H
+#define BACKENDS_CLOUD_ISO8601_H
+
+#include "common/str.h"
+
+namespace Cloud {
+namespace ISO8601 {
+
+	/** Returns timestamp corresponding to given ISO 8601 date */
+	uint32 convertToTimestamp(const Common::String &iso8601Date);
+
+} //end of namespace ISO8601
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index 0d40698..02c9cae 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -32,7 +32,7 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 		uint32 i = _name.size() - 1;
 		while (true) {
 			if (_name[i] == '/' || _name[i] == '\\') {
-				_name.erase(0, i);
+				_name.erase(0, i+1);
 				break;
 			}
 			if (i == 0) break;
diff --git a/backends/module.mk b/backends/module.mk
index 2cee007..7e83192 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,9 +21,11 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
+	cloud/iso8601.o \
 	cloud/manager.o \
 	cloud/storagefile.o \
-	cloud/dropbox/dropboxstorage.o
+	cloud/dropbox/dropboxstorage.o \
+	cloud/dropbox/dropboxlistdirectoryrequest.o
 endif
 
 ifdef USE_LIBCURL


Commit: 3582f6165ce829e4990c15bf77d1792ee20dca55
    https://github.com/scummvm/scummvm/commit/3582f6165ce829e4990c15bf77d1792ee20dca55
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Change ISO8601 to use strchr

I'm not sure it works as it should though.

Changed paths:
    backends/cloud/iso8601.cpp



diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
index d34c98e..12cbb74 100644
--- a/backends/cloud/iso8601.cpp
+++ b/backends/cloud/iso8601.cpp
@@ -25,41 +25,14 @@
 
 namespace {
 
-uint32 find(const Common::String &haystack, const Common::String &needle, uint32 pos = 0) {
-	if (pos >= haystack.size()) {
-		return Common::String::npos;
-	}
-
-	//TODO: write something smarter
-	uint32 lastIndex = haystack.size() - needle.size();
-	for (uint32 i = pos; i < lastIndex; ++i) {
-		bool found = true;
-		for (uint32 j = 0; j < needle.size(); ++j)
-			if (haystack[i + j] != needle[j]) {
-				found = false;
-				break;
-			}
-
-		if (found) return i;
-	}
-
-	return Common::String::npos;
-}
-
 Common::String getSubstring(const Common::String &s, uint32 beginning, uint32 ending) {
 	//beginning inclusive, ending exclusive
-	if (beginning == -1 || ending == -1) return ""; //bad
 	Common::String result = s;
 	result.erase(ending);
 	result.erase(0, beginning);
 	return result;
 }
 
-int parseInt(Common::String s) {
-	//TODO: not sure this is not forbidden at all
-	return atoi(s.c_str());
-}
-
 }
 
 namespace Cloud {
@@ -67,13 +40,14 @@ namespace ISO8601 {
 
 uint32 convertToTimestamp(const Common::String &iso8601Date) {		
 	//2015-05-12T15:50:38Z
-	uint32 firstHyphen = find(iso8601Date, "-");
-	uint32 secondHyphen = find(iso8601Date, "-", firstHyphen + 1);
-	uint32 tSeparator = find(iso8601Date, "T", secondHyphen + 1);
-	uint32 firstColon = find(iso8601Date, ":", tSeparator + 1);
-	uint32 secondColon = find(iso8601Date, ":", firstColon + 1);
-	uint32 zSeparator = find(iso8601Date, "Z", secondColon + 1);
-	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char
+	const char *cstr = iso8601Date.c_str();	
+	uint32 firstHyphen = strchr(cstr, '-') - cstr;
+	uint32 secondHyphen = strchr(cstr + firstHyphen + 1, '-') - cstr;
+	uint32 tSeparator = strchr(cstr + secondHyphen + 1, 'T') - cstr;
+	uint32 firstColon = strchr(cstr + tSeparator + 1, ':') - cstr;
+	uint32 secondColon = strchr(cstr + firstColon + 1, ':') - cstr;
+	uint32 zSeparator = strchr(cstr + secondColon + 1, 'Z') - cstr;
+	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char	
 
 	Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
 	Common::String month = getSubstring(iso8601Date, firstHyphen + 1, secondHyphen);
@@ -83,12 +57,12 @@ uint32 convertToTimestamp(const Common::String &iso8601Date) {
 	Common::String second = getSubstring(iso8601Date, secondColon + 1, zSeparator);
 	//now note only 'ending' argument was not '+1' (which means I could've make that function such that -1 means 'until the end')
 
-	int Y = parseInt(year);
-	int M = parseInt(month);
-	int D = parseInt(day);
-	int h = parseInt(hour);
-	int m = parseInt(minute);
-	int s = parseInt(second);
+	int Y = atoi(year.c_str());
+	int M = atoi(month.c_str());
+	int D = atoi(day.c_str());
+	int h = atoi(hour.c_str());
+	int m = atoi(minute.c_str());
+	int s = atoi(second.c_str());
 
 	//ok, now I compose a timestamp based on my basic perception of time/date
 	//yeah, I know about leap years and leap seconds and all, but still we don't care there


Commit: 826a2a921cd0b0a72f71dd6f323097a2f449fab0
    https://github.com/scummvm/scummvm/commit/826a2a921cd0b0a72f71dd6f323097a2f449fab0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add DownloadRequest stub

It reads the passed NetworkReadStream and prints its contents onto
console (for now). It would be writing contents into file.

To simplify work with raw NetworkReadStream there is a new CurlRequest.
It basically does nothing, but as ConnMan handles transfers only if
there is an active Request, you need some Request to get
NetworkReadStream working. Thus, there is a CurlRequest, which is active
until NetworkReadStream is completely read. CurlRequest also has useful
addHeader() and addPostField() methods in order to customize the request
easily. Use execute() method to get its NetworkReadStream.

DropboxStorage implements streamFile() and download() API methods. As
DownloadRequest is incomplete, it is not actually downloading a file,
though.

Changed paths:
  A backends/cloud/downloadrequest.cpp
  A backends/cloud/downloadrequest.h
  A backends/networking/curl/curlrequest.cpp
  A backends/networking/curl/curlrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.h
    backends/module.mk
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
new file mode 100644
index 0000000..ed84d4f
--- /dev/null
+++ b/backends/cloud/downloadrequest.cpp
@@ -0,0 +1,67 @@
+/* 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 "backends/cloud/downloadrequest.h"
+#include "common/debug.h"
+#include "common/textconsole.h"
+
+namespace Cloud {
+
+DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream):
+	Request(0), _boolCallback(callback), _stream(stream) {}
+
+bool DownloadRequest::handle() {
+	if (!_stream) {
+		warning("DownloadRequest: no stream to read");
+		return true;
+	}
+
+	const int kBufSize = 16 * 1024;
+	char buf[kBufSize];
+	uint32 readBytes = _stream->read(buf, kBufSize);
+
+	//TODO: save into file
+	/*
+	if (readBytes != 0)
+		if (_outputStream.write(buf, readBytes) != readBytes)
+			warning("DropboxDownloadRequest: unable to write all received bytes into output stream");
+	*/
+
+	buf[readBytes] = 0;
+	debug("%s", buf); //TODO: remove
+
+	if (_stream->eos()) {
+		if (_stream->httpResponseCode() != 200) {
+			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+			//TODO: do something about it actually			
+		}
+
+		if (_boolCallback) (*_boolCallback)(_stream->httpResponseCode() == 200);
+
+		//TODO: close file stream
+		return true;
+	}
+
+	return false;
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
new file mode 100644
index 0000000..a819910
--- /dev/null
+++ b/backends/cloud/downloadrequest.h
@@ -0,0 +1,45 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DOWNLOADREQUEST_H
+#define BACKENDS_CLOUD_DOWNLOADREQUEST_H
+
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "backends/cloud/storage.h"
+
+namespace Cloud {
+
+class DownloadRequest: public Networking::Request {	
+	Networking::NetworkReadStream *_stream;
+	Storage::BoolCallback _boolCallback;
+
+public:
+	DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream);
+	virtual ~DownloadRequest() {}
+
+	virtual bool handle();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index e28a445..5e5957b 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -24,6 +24,7 @@
 #include "backends/cloud/iso8601.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "common/json.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 03b4fc1..0c10512 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -24,8 +24,8 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "common/callback.h"
 #include "backends/networking/curl/request.h"
+#include "common/callback.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 6de9424..1b6dc1b 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -23,6 +23,7 @@
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
+#include "backends/cloud/downloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
@@ -86,10 +87,28 @@ void DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerC
 	ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
+Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
+	Common::JSONValue value(jsonRequestParameters);
+
+	Networking::CurlRequest *request = new Networking::CurlRequest(0, "https://content.dropboxapi.com/2/files/download");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
+	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
+
+	return request->execute();
+}
+
+void DropboxStorage::download(Common::String path, BoolCallback callback) {
+	ConnMan.addRequest(new DownloadRequest(callback, streamFile(path)));
+}
+
 void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
-	listDirectory("", new Common::Callback<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::printFiles), true);
+	//listDirectory("", new Common::Callback<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::printFiles), true);
+	download("/notempty.txt", 0);
 }
 
 void DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index c1c2e03..92c3746 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -67,10 +67,13 @@ public:
 	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) {} //TODO
+	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) {} //TODO
 
-	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, ReadStreamCallback callback) {} //TODO
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::NetworkReadStream *streamFile(Common::String path);
+
+	/** Calls the callback when finished. */
+	virtual void download(Common::String path, BoolCallback callback);
 
 	/** Calls the callback when finished. */
 	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 1435be8..92f3bb9 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -29,6 +29,7 @@
 #include "common/callback.h"
 #include "backends/cloud/storagefile.h"
 #include "backends/cloud/storageinfo.h"
+#include "backends/networking/curl/networkreadstream.h"
 
 namespace Cloud {
 
@@ -62,10 +63,13 @@ public:
 	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream* contents, BoolCallback callback) = 0;
+	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
 
-	/** Returns pointer to Common::ReadStream. */
-	virtual void download(Common::String path, ReadStreamCallback callback) = 0;
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::NetworkReadStream *streamFile(Common::String path) = 0;
+
+	/** Calls the callback when finished. */
+	virtual void download(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
 	virtual void remove(Common::String path, BoolCallback callback) = 0;
diff --git a/backends/module.mk b/backends/module.mk
index 7e83192..eb5ce07 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS += \
 	cloud/iso8601.o \
 	cloud/manager.o \
 	cloud/storagefile.o \
+	cloud/downloadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o
 endif
@@ -32,6 +33,7 @@ ifdef USE_LIBCURL
 MODULE_OBJS += \
 	networking/curl/connectionmanager.o \
 	networking/curl/networkreadstream.o \
+	networking/curl/curlrequest.o \
 	networking/curl/curljsonrequest.o
 endif
 
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 31e99f9..97ae31a 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -83,7 +83,7 @@ void ConnectionManager::handle() {
 
 void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
-	debug("handler's here");
+	debug("handling %d request(s)", _requests.size());
 	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
 		if ((*i)->handle()) {
 			delete (*i);
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 805852e..0366e3b 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -23,7 +23,6 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/debug.h"
 #include "common/json.h"
@@ -31,13 +30,10 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Common::BaseCallback<> *cb, const char *url): Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) {
-	_url = url;
-}
+CurlJsonRequest::CurlJsonRequest(Common::BaseCallback<> *cb, const char *url):
+	CurlRequest(cb, url), _contentsStream(DisposeAfterUse::YES) {}
 
-CurlJsonRequest::~CurlJsonRequest() {
-	if (_stream) delete _stream;
-}
+CurlJsonRequest::~CurlJsonRequest() {}
 
 char *CurlJsonRequest::getPreparedContents() {
 	//write one more byte in the end
@@ -70,7 +66,7 @@ bool CurlJsonRequest::handle() {
 
 		if (_stream->eos()) {			
 			if (_stream->httpResponseCode() != 200)
-				warning("HTTP response code is not 200 OK (it's %d)", _stream->httpResponseCode());
+				warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 
 			if (_callback) {
 				char *contents = getPreparedContents();
@@ -86,16 +82,4 @@ bool CurlJsonRequest::handle() {
 	return false;
 }
 
-void CurlJsonRequest::addHeader(Common::String header) {
-	_headersList = curl_slist_append(_headersList, header.c_str());
-}
-
-void CurlJsonRequest::addPostField(Common::String keyValuePair) {
-	if (_postFields == "")
-		_postFields = keyValuePair;
-	else
-		_postFields += "&" + keyValuePair;
-}
-
-
 } //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 50fa818..cfb82e9 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -23,21 +23,14 @@
 #ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
 #define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curlrequest.h"
 #include "common/memstream.h"
-#include "common/json.h"
-
-struct curl_slist;
 
 namespace Networking {
 
 class NetworkReadStream;
 
-class CurlJsonRequest : public Request {	
-	const char *_url;
-	NetworkReadStream *_stream;
-	curl_slist *_headersList;
-	Common::String _postFields;
+class CurlJsonRequest: public CurlRequest {	
 	Common::MemoryWriteStreamDynamic _contentsStream;
 
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
@@ -48,10 +41,6 @@ public:
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle();
-
-	void addHeader(Common::String header);
-
-	void addPostField(Common::String header);
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
new file mode 100644
index 0000000..e13adac
--- /dev/null
+++ b/backends/networking/curl/curlrequest.cpp
@@ -0,0 +1,72 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/curlrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/textconsole.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+CurlRequest::CurlRequest(Common::BaseCallback<> *cb, const char *url): 
+	Request(cb), _url(url), _stream(0), _headersList(0) {}
+
+CurlRequest::~CurlRequest() {
+	if (_stream) delete _stream;
+}
+
+bool CurlRequest::handle() {
+	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);	
+
+	if (_stream && _stream->eos()) {		
+		if (_stream->httpResponseCode() != 200)
+			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());		
+		return true;
+	}
+
+	return false;
+}
+
+void CurlRequest::addHeader(Common::String header) {
+	_headersList = curl_slist_append(_headersList, header.c_str());
+}
+
+void CurlRequest::addPostField(Common::String keyValuePair) {
+	if (_postFields == "")
+		_postFields = keyValuePair;
+	else
+		_postFields += "&" + keyValuePair;
+}
+
+NetworkReadStream *CurlRequest::execute() {
+	if (!_stream) {
+		_stream = new NetworkReadStream(_url, _headersList, _postFields);
+		ConnMan.addRequest(this);
+	}
+
+	return _stream;
+}
+
+} //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
new file mode 100644
index 0000000..22f50be
--- /dev/null
+++ b/backends/networking/curl/curlrequest.h
@@ -0,0 +1,58 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CURLREQUEST_H
+#define BACKENDS_NETWORKING_CURL_CURLREQUEST_H
+
+#include "backends/networking/curl/request.h"
+#include "common/str.h"
+
+struct curl_slist;
+
+namespace Networking {
+
+class NetworkReadStream;
+
+class CurlRequest: public Request {
+protected:
+	const char *_url;
+	NetworkReadStream *_stream;
+	curl_slist *_headersList;
+	Common::String _postFields;
+
+public:
+	CurlRequest(Common::BaseCallback<> *cb, const char *url);
+	virtual ~CurlRequest();
+
+	virtual bool handle();
+
+	void addHeader(Common::String header);
+
+	void addPostField(Common::String header);
+
+	/** Start this Request with ConnMan. Returns its ReadStream. */
+	NetworkReadStream *execute();
+};
+
+}  //end of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 8fd39d6..0cf7118 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -52,7 +52,8 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
 }
 
 NetworkReadStream::~NetworkReadStream() {
-	curl_easy_cleanup(_easy);
+	if (_easy)
+		curl_easy_cleanup(_easy);
 }
 
 bool NetworkReadStream::eos() const {


Commit: caaa4c5a5d0bce7582cc6611d8bde53fbdb1f2d1
    https://github.com/scummvm/scummvm/commit/caaa4c5a5d0bce7582cc6611d8bde53fbdb1f2d1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make DownloadRequest write to local file

Tested with .jpg file. Transfer complete, CRC-32 is the same.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storage.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index ed84d4f..8124fd6 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -26,38 +26,45 @@
 
 namespace Cloud {
 
-DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream):
-	Request(0), _boolCallback(callback), _stream(stream) {}
+DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile):
+	Request(0), _boolCallback(callback), _remoteFileStream(stream), _localFile(dumpFile) {}
 
 bool DownloadRequest::handle() {
-	if (!_stream) {
+	if (!_remoteFileStream) {
 		warning("DownloadRequest: no stream to read");
 		return true;
 	}
 
+	if (!_localFile) {
+		warning("DownloadRequest: no file to write");
+		return true;
+	}
+
+	if (!_localFile->isOpen()) {
+		warning("DownloadRequest: failed to open file to write");
+		return true;
+	}
+
 	const int kBufSize = 16 * 1024;
 	char buf[kBufSize];
-	uint32 readBytes = _stream->read(buf, kBufSize);
+	uint32 readBytes = _remoteFileStream->read(buf, kBufSize);
 
-	//TODO: save into file
-	/*
 	if (readBytes != 0)
-		if (_outputStream.write(buf, readBytes) != readBytes)
-			warning("DropboxDownloadRequest: unable to write all received bytes into output stream");
-	*/
-
-	buf[readBytes] = 0;
-	debug("%s", buf); //TODO: remove
+		if (_localFile->write(buf, readBytes) != readBytes) {
+			warning("DownloadRequest: unable to write all received bytes into output file");
+			if (_boolCallback) (*_boolCallback)(false);
+			return true;
+		}
 
-	if (_stream->eos()) {
-		if (_stream->httpResponseCode() != 200) {
-			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+	if (_remoteFileStream->eos()) {
+		if (_remoteFileStream->httpResponseCode() != 200) {
+			warning("HTTP response code is not 200 OK (it's %ld)", _remoteFileStream->httpResponseCode());
 			//TODO: do something about it actually			
 		}
 
-		if (_boolCallback) (*_boolCallback)(_stream->httpResponseCode() == 200);
+		if (_boolCallback) (*_boolCallback)(_remoteFileStream->httpResponseCode() == 200);
 
-		//TODO: close file stream
+		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
 		return true;
 	}
 
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index a819910..b135b15 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -26,16 +26,18 @@
 #include "backends/networking/curl/request.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/cloud/storage.h"
+#include <common/file.h>
 
 namespace Cloud {
 
 class DownloadRequest: public Networking::Request {	
-	Networking::NetworkReadStream *_stream;
 	Storage::BoolCallback _boolCallback;
+	Networking::NetworkReadStream *_remoteFileStream;
+	Common::DumpFile *_localFile;	
 
 public:
-	DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream);
-	virtual ~DownloadRequest() {}
+	DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile);
+	virtual ~DownloadRequest() { delete _localFile; }
 
 	virtual bool handle();
 };
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 1b6dc1b..38ad12d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -30,6 +30,7 @@
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
+#include <common/file.h>
 
 namespace Cloud {
 namespace Dropbox {
@@ -100,15 +101,23 @@ Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
 	return request->execute();
 }
 
-void DropboxStorage::download(Common::String path, BoolCallback callback) {
-	ConnMan.addRequest(new DownloadRequest(callback, streamFile(path)));
+void DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+	Common::DumpFile *f = new Common::DumpFile();
+	if (!f->open(localPath)) {
+		warning("DropboxStorage: unable to open file to download into");
+		if (callback) (*callback)(false);
+		delete f;
+		return;
+	}
+
+	ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
 }
 
 void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
 	//listDirectory("", new Common::Callback<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::printFiles), true);
-	download("/notempty.txt", 0);
+	download("/remote/test.jpg", "local/test.jpg", 0);
 }
 
 void DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 92c3746..415a3fe 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -73,7 +73,7 @@ public:
 	virtual Networking::NetworkReadStream *streamFile(Common::String path);
 
 	/** Calls the callback when finished. */
-	virtual void download(Common::String path, BoolCallback callback);
+	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Calls the callback when finished. */
 	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 92f3bb9..e38c6be 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -69,7 +69,7 @@ public:
 	virtual Networking::NetworkReadStream *streamFile(Common::String path) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void download(Common::String path, BoolCallback callback) = 0;
+	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
 	virtual void remove(Common::String path, BoolCallback callback) = 0;


Commit: 5f4bbe6e9e08f5f76eada84497a7530ffb08fbf1
    https://github.com/scummvm/scummvm/commit/5f4bbe6e9e08f5f76eada84497a7530ffb08fbf1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDrive Storage stub

Knows how to OAuth already.

This commit also adds CloudManager::addStorage(), so OneDriveStorage can
add newly created Storage and CloudManager can save it in the
configuration file.

Changed paths:
  A backends/cloud/onedrive/onedrivestorage.cpp
  A backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/downloadrequest.cpp
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/module.mk
    common/cloudmanager.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 8124fd6..e86b655 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -45,7 +45,7 @@ bool DownloadRequest::handle() {
 		return true;
 	}
 
-	const int kBufSize = 16 * 1024;
+	const int kBufSize = 640 * 1024; //640 KB is enough to everyone?..
 	char buf[kBufSize];
 	uint32 readBytes = _remoteFileStream->read(buf, kBufSize);
 
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 1c11efb..a7e92df 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -23,6 +23,7 @@
 #include "backends/cloud/manager.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "common/config-manager.h"
+#include "onedrive/onedrivestorage.h"
 
 namespace Cloud {
 
@@ -37,6 +38,7 @@ Manager::~Manager() {
 
 void Manager::init() {
 	bool offerDropbox = false;
+	bool offerOneDrive = true;
 
 	if (ConfMan.hasKey("storages_number", "cloud")) {
 		int storages = ConfMan.getInt("storages_number", "cloud");
@@ -46,7 +48,10 @@ void Manager::init() {
 			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
 				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
 				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
-				else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
+				else if (storageType == "OneDrive") {
+					loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
+					offerOneDrive = false;
+				} else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
 			} else {
 				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
 			}
@@ -66,8 +71,11 @@ void Manager::init() {
 	}
 
 	if (offerDropbox) {
-		//this is temporary console offer to auth with Dropbox (because there is no other storage type yet anyway)
+		//this is temporary console offer to auth with Dropbox
 		Dropbox::DropboxStorage::authThroughConsole();
+	} else if(offerOneDrive) {
+		//OneDrive time
+		OneDrive::OneDriveStorage::authThroughConsole();
 	}
 }
 
@@ -79,6 +87,13 @@ void Manager::save() {
 	ConfMan.flushToDisk();
 }
 
+void Manager::addStorage(Cloud::Storage *storage, bool makeCurrent, bool saveConfig) {
+	if (!storage) error("Cloud::Manager: NULL storage passed");
+	_storages.push_back(storage);
+	if (makeCurrent) _currentStorageIndex = _storages.size() - 1;
+	if (saveConfig) save();
+}
+
 Storage *Manager::getCurrentStorage() {
 	if (_currentStorageIndex < _storages.size())
 		return _storages[_currentStorageIndex];
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index e531854..08106e0 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -38,6 +38,7 @@ public:
 
 	virtual void init();
 	virtual void save();
+	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true);
 
 	virtual Storage *getCurrentStorage();
 	virtual void syncSaves(Storage::BoolCallback callback);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
new file mode 100644
index 0000000..b632c74
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -0,0 +1,151 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+#include <common/file.h>
+#include "common/system.h"
+#include "common/cloudmanager.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+Common::String OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
+Common::String OneDriveStorage::SECRET; //TODO: hide these secrets somehow
+
+static void saveAccessTokenCallback(void *ptr) {
+	Common::JSONValue *json = (Common::JSONValue *)ptr;
+	if (json) {
+		debug("saveAccessTokenCallback:");
+		debug("%s", json->stringify(true).c_str());
+
+		//TODO: do something about refresh token
+		Common::JSONObject result = json->asObject();
+		if (!result.contains("access_token") || !result.contains("user_id")) {
+			warning("Bad response, no token/user_id passed");
+		} else {					
+			OneDriveStorage::addStorage(result.getVal("access_token")->asString(), result.getVal("user_id")->asString());
+			ConfMan.removeKey("onedrive_code", "cloud");
+			debug("Done! You can use OneDrive now! Look:");
+			g_system->getCloudManager()->syncSaves();
+		}
+
+		delete json;
+	} else {
+		debug("saveAccessTokenCallback: got NULL instead of JSON!");
+	}
+}
+
+OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
+	curl_global_init(CURL_GLOBAL_ALL);
+}
+
+OneDriveStorage::~OneDriveStorage() {
+	curl_global_cleanup();
+}
+
+void OneDriveStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "type", "OneDrive", "cloud");
+	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
+	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
+}
+
+void OneDriveStorage::syncSaves(BoolCallback callback) {
+	//this is not the real syncSaves() implementation
+}
+
+void OneDriveStorage::addStorage(Common::String token, Common::String uid) {
+	Storage *storage = new OneDriveStorage(token, uid);
+	g_system->getCloudManager()->addStorage(storage);
+}
+
+OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
+	KEY = ConfMan.get("ONEDRIVE_KEY", "cloud");
+	SECRET = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+
+	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
+		warning("No access_token found");
+		return 0;
+	}
+
+	if (!ConfMan.hasKey(keyPrefix + "user_id", "cloud")) {
+		warning("No user_id found");
+		return 0;
+	}
+
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
+	Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
+	return new OneDriveStorage(accessToken, userId);
+}
+
+Common::String OneDriveStorage::getAuthLink() {
+	Common::String url = "https://login.live.com/oauth20_authorize.srf";
+	url += "?response_type=code";
+	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
+	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
+	url += "&client_id=" + KEY;
+	url += "&scope=onedrive.appfolder"; //TODO
+	return url;
+}
+
+void OneDriveStorage::authThroughConsole() {
+	if (!ConfMan.hasKey("ONEDRIVE_KEY", "cloud") || !ConfMan.hasKey("ONEDRIVE_SECRET", "cloud")) {
+		warning("No OneDrive keys available, cannot do auth");
+		return;
+	}
+
+	KEY = ConfMan.get("ONEDRIVE_KEY", "cloud");
+	SECRET = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+
+	if (ConfMan.hasKey("onedrive_code", "cloud")) {
+		//phase 2: get access_token using specified code
+		getAccessToken(ConfMan.get("onedrive_code", "cloud"));
+		return;
+	}
+
+	debug("Navigate to this URL and press \"Allow\":");
+	debug("%s\n", getAuthLink().c_str());
+	debug("Then, add onedrive_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
+	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
+	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
+}
+
+void OneDriveStorage::getAccessToken(Common::String code) {
+	Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://login.live.com/oauth20_token.srf");
+	//Content-Type: application/x-www-form-urlencoded
+	request->addPostField("code=" + code);
+	request->addPostField("grant_type=authorization_code");
+	request->addPostField("client_id=" + KEY);
+	request->addPostField("client_secret=" + SECRET);
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");	
+	ConnMan.addRequest(request);	
+}
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
new file mode 100644
index 0000000..99f8476
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -0,0 +1,119 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
+
+#include "backends/cloud/storage.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage: public Cloud::Storage {
+	static Common::String KEY, SECRET;
+
+	Common::String _token, _uid;
+
+	/** This private constructor is called from loadFromConfig(). */
+	OneDriveStorage(Common::String token, Common::String uid);
+
+	static void getAccessToken(Common::String code);	
+
+public:	
+	virtual ~OneDriveStorage();
+
+	/**
+	* Storage methods, which are used by CloudManager to save
+	* storage in configuration file.
+	*/
+
+	/**
+	* Save storage data using ConfMan.
+	* @param keyPrefix all saved keys must start with this prefix.
+	* @note every Storage must write keyPrefix + "type" key
+	*       with common value (e.g. "Dropbox").
+	*/
+
+	virtual void saveConfig(Common::String keyPrefix);
+
+	/** Public Cloud API comes down there. */
+
+	/** Returns Common::Array<StorageFile>. */
+	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) {} //TODO
+
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::NetworkReadStream *streamFile(Common::String path) { return 0; } //TODO
+
+	/** Calls the callback when finished. */
+	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void syncSaves(BoolCallback callback);
+
+	/** Calls the callback when finished. */
+	virtual void createDirectory(Common::String path, BoolCallback callback) {} //TODO
+
+	/** Calls the callback when finished. */
+	virtual void touch(Common::String path, BoolCallback callback) {} //TODO
+
+	/** Returns the StorageInfo struct. */
+	virtual void info(StorageInfoCallback callback) {} //TODO
+
+	/** Returns whether saves sync process is running. */
+	virtual bool isSyncing() { return false; } //TODO
+
+	/** Returns whether there are any requests running. */
+	virtual bool isWorking() { return false; } //TODO
+
+	/**
+	* Add OneDriveStorage with given token and uid into Cloud::Manager.	
+	*/
+	static void addStorage(Common::String token, Common::String uid);
+
+	/**
+	* Load token and user id from configs and return OneDriveStorage for those.	
+	* @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
+	*/
+	static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
+
+	/**
+	* Returns OneDrive auth link.
+	*/
+	static Common::String getAuthLink();
+
+	/**
+	* Show message with OneDrive auth instructions. (Temporary)
+	*/
+	static void authThroughConsole();
+};
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index eb5ce07..c8842ca 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -26,7 +26,8 @@ MODULE_OBJS += \
 	cloud/storagefile.o \
 	cloud/downloadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
-	cloud/dropbox/dropboxlistdirectoryrequest.o
+	cloud/dropbox/dropboxlistdirectoryrequest.o \
+	cloud/onedrive/onedrivestorage.o
 endif
 
 ifdef USE_LIBCURL
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index d1c8945..a480bdf 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -47,6 +47,16 @@ public:
 	virtual void save() = 0;
 
 	/**
+	* Adds new Storage into list.	
+	*
+	* @param	storage Cloud::Storage to add.
+	* @param	makeCurrent whether added storage should be the new current storage.
+	* @param	saveConfig whether save() should be called to update configuration file.
+	*/
+
+	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true) = 0;
+
+	/**
 	* Returns active Storage, which could be used to interact
 	*  with cloud storage.
 	*


Commit: 5e346ea6fbcd4a028bc7dd63efd09ff42770f8ef
    https://github.com/scummvm/scummvm/commit/5e346ea6fbcd4a028bc7dd63efd09ff42770f8ef
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDrive refresh_token support

It might be not that easy to restart the request after new token
received, though.

Changed paths:
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index b632c74..2f10841 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -38,50 +38,111 @@ namespace OneDrive {
 Common::String OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String OneDriveStorage::SECRET; //TODO: hide these secrets somehow
 
-static void saveAccessTokenCallback(void *ptr) {
-	Common::JSONValue *json = (Common::JSONValue *)ptr;
-	if (json) {
-		debug("saveAccessTokenCallback:");
-		debug("%s", json->stringify(true).c_str());
-
-		//TODO: do something about refresh token
-		Common::JSONObject result = json->asObject();
-		if (!result.contains("access_token") || !result.contains("user_id")) {
-			warning("Bad response, no token/user_id passed");
-		} else {					
-			OneDriveStorage::addStorage(result.getVal("access_token")->asString(), result.getVal("user_id")->asString());
-			ConfMan.removeKey("onedrive_code", "cloud");
-			debug("Done! You can use OneDrive now! Look:");
-			g_system->getCloudManager()->syncSaves();
-		}
+OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):
+	_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
 
-		delete json;
+OneDriveStorage::OneDriveStorage(Common::String code) {
+	getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::codeFlowComplete), code);
+}
+
+OneDriveStorage::~OneDriveStorage() {}
+
+void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+	bool codeFlow = (code != "");
+
+	if (!codeFlow && _refreshToken == "") {
+		warning("OneDriveStorage: no refresh token available to get new access token.");
+		if (callback) (*callback)(false);
+		return;
+	}
+
+	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<OneDriveStorage, bool>(this, &OneDriveStorage::tokenRefreshed, callback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
+	if (codeFlow) {
+		request->addPostField("code=" + code);
+		request->addPostField("grant_type=authorization_code");
 	} else {
-		debug("saveAccessTokenCallback: got NULL instead of JSON!");
+		request->addPostField("refresh_token=" + _refreshToken);
+		request->addPostField("grant_type=refresh_token");
 	}
+	request->addPostField("client_id=" + KEY);
+	request->addPostField("client_secret=" + SECRET);
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+	ConnMan.addRequest(request);
 }
 
-OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
-	curl_global_init(CURL_GLOBAL_ALL);
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, void *jsonPointer) {
+	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+	if (!json) {
+		warning("OneDriveStorage: got NULL instead of JSON");
+		if (callback) (*callback)(false);
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
+		warning("Bad response, no token or user_id passed");
+		debug("%s", json->stringify().c_str());
+		if (callback) (*callback)(false);
+	} else {
+		_token = result.getVal("access_token")->asString();
+		_uid = result.getVal("user_id")->asString();
+		_refreshToken = result.getVal("refresh_token")->asString();
+		g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken
+		if (callback) (*callback)(true);
+	}
+	delete json;
 }
 
-OneDriveStorage::~OneDriveStorage() {
-	curl_global_cleanup();
+void OneDriveStorage::codeFlowComplete(bool success) {
+	if (!success) {
+		warning("OneDriveStorage: failed to get access token through code flow");
+		return;
+	}
+
+	g_system->getCloudManager()->addStorage(this);
+	ConfMan.removeKey("onedrive_code", "cloud");
+	debug("Done! You can use OneDrive now! Look:");
+	g_system->getCloudManager()->syncSaves();
 }
 
 void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "type", "OneDrive", "cloud");
 	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
+	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
-void OneDriveStorage::syncSaves(BoolCallback callback) {
-	//this is not the real syncSaves() implementation
+void OneDriveStorage::printJsonTokenReceived(bool success) {
+	if (success) syncSaves(0); //try again
+}
+
+void OneDriveStorage::printJson(void *jsonPointer) {
+	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+	if (!json) {
+		warning("printJson: NULL");
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (result.contains("error")) {
+		//Common::JSONObject error = result.getVal("error")->asObject();
+		debug("bad token, trying again...");
+		getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::printJsonTokenReceived));
+		delete json;
+		return;
+	}
+
+	debug("%s", json->stringify().c_str());
+	delete json;
 }
 
-void OneDriveStorage::addStorage(Common::String token, Common::String uid) {
-	Storage *storage = new OneDriveStorage(token, uid);
-	g_system->getCloudManager()->addStorage(storage);
+void OneDriveStorage::syncSaves(BoolCallback callback) {
+	//this is not the real syncSaves() implementation	
+	Common::BaseCallback<> *innerCallback = new Common::Callback<OneDriveStorage>(this, &OneDriveStorage::printJson);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");	
+	request->addHeader("Authorization: bearer " + _token);
+	ConnMan.addRequest(request);
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
@@ -98,9 +159,15 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 		return 0;
 	}
 
+	if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) {
+		warning("No refresh_token found");
+		return 0;
+	}
+
 	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
 	Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
-	return new OneDriveStorage(accessToken, userId);
+	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud");
+	return new OneDriveStorage(accessToken, userId, refreshToken);
 }
 
 Common::String OneDriveStorage::getAuthLink() {
@@ -109,7 +176,7 @@ Common::String OneDriveStorage::getAuthLink() {
 	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
 	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
 	url += "&client_id=" + KEY;
-	url += "&scope=onedrive.appfolder"; //TODO
+	url += "&scope=onedrive.appfolder%20offline_access"; //TODO
 	return url;
 }
 
@@ -124,7 +191,7 @@ void OneDriveStorage::authThroughConsole() {
 
 	if (ConfMan.hasKey("onedrive_code", "cloud")) {
 		//phase 2: get access_token using specified code
-		getAccessToken(ConfMan.get("onedrive_code", "cloud"));
+		new OneDriveStorage(ConfMan.get("onedrive_code", "cloud"));
 		return;
 	}
 
@@ -135,17 +202,5 @@ void OneDriveStorage::authThroughConsole() {
 	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
 }
 
-void OneDriveStorage::getAccessToken(Common::String code) {
-	Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://login.live.com/oauth20_token.srf");
-	//Content-Type: application/x-www-form-urlencoded
-	request->addPostField("code=" + code);
-	request->addPostField("grant_type=authorization_code");
-	request->addPostField("client_id=" + KEY);
-	request->addPostField("client_secret=" + SECRET);
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");	
-	ConnMan.addRequest(request);	
-}
-
 } //end of namespace OneDrive
 } //end of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 99f8476..28f37ae 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -32,13 +32,28 @@ namespace OneDrive {
 class OneDriveStorage: public Cloud::Storage {
 	static Common::String KEY, SECRET;
 
-	Common::String _token, _uid;
+	Common::String _token, _uid, _refreshToken;
 
 	/** This private constructor is called from loadFromConfig(). */
-	OneDriveStorage(Common::String token, Common::String uid);
+	OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
 
-	static void getAccessToken(Common::String code);	
+	/**
+	* This private constructor is called from authThroughConsole() (phase 2).
+	* It uses OAuth code flow to get tokens.
+	*/
+	OneDriveStorage(Common::String code);
+
+	/**
+	* Gets new access_token. If <code> passed is "", refresh_token is used.
+	* Use "" in order to refresh token and pass a callback, so you could
+	* continue your work when new token is available.
+	*/
+	void getAccessToken(BoolCallback callback, Common::String code = "");	
+	void tokenRefreshed(BoolCallback callback, void *jsonPointer);
+	void codeFlowComplete(bool success);
 
+	void printJson(void *jsonPointer);
+	void printJsonTokenReceived(bool success);
 public:	
 	virtual ~OneDriveStorage();
 
@@ -92,11 +107,6 @@ public:
 	virtual bool isWorking() { return false; } //TODO
 
 	/**
-	* Add OneDriveStorage with given token and uid into Cloud::Manager.	
-	*/
-	static void addStorage(Common::String token, Common::String uid);
-
-	/**
 	* Load token and user id from configs and return OneDriveStorage for those.	
 	* @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
 	*/
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 0cf7118..9a196b9 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -46,8 +46,10 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
-	curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
-	curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
+	if (postFields.size() != 0) {
+		curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
+		curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
+	}
 	ConnMan.registerEasyHandle(_easy);
 }
 


Commit: d014b5bf3826d78de269891ccf6722e58d7a5bcd
    https://github.com/scummvm/scummvm/commit/d014b5bf3826d78de269891ccf6722e58d7a5bcd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix MemoryReadWriteStream

There was a problem with file downloading in MinGW. Strangely, there is
no such problem in Visual Studio, yet this fixes the problem.

Changed paths:
    common/memstream.h



diff --git a/common/memstream.h b/common/memstream.h
index 963637c..76fa07c 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -234,12 +234,12 @@ private:
 			// Copy old data
 			if (_readPos < _writePos) {
 				memcpy(_data, old_data + _readPos, _writePos - _readPos);
-				_writePos -= _readPos;
+				_writePos = _length;
 				_readPos = 0;
 			} else {
 				memcpy(_data, old_data + _readPos, oldCapacity - _readPos);
 				memcpy(_data + oldCapacity - _readPos, old_data, _writePos);
-				_writePos += oldCapacity - _readPos;
+				_writePos = _length;
 				_readPos = 0;
 			}
 			free(old_data);
@@ -270,7 +270,7 @@ public:
 		return dataSize;
 	}
 
-	uint32 read(void *dataPtr, uint32 dataSize) {
+	virtual uint32 read(void *dataPtr, uint32 dataSize) {
 		uint32 length = _length;
 		if (length < dataSize) dataSize = length;
 		if (dataSize == 0 || _capacity == 0) return 0;


Commit: ae8e7f39f5b3e4f647a29d39d19cce7626528bb1
    https://github.com/scummvm/scummvm/commit/ae8e7f39f5b3e4f647a29d39d19cce7626528bb1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make download() create necessary directories

DumpFile::open() with createPath=true create would create the missing
directories from the path before opening a file. Thus, one can easily
create a file and avoid "can't open a file" error.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/fs/abstract-fs.h
    backends/fs/posix/posix-fs.cpp
    backends/fs/posix/posix-fs.h
    backends/fs/windows/windows-fs.cpp
    backends/fs/windows/windows-fs.h
    common/file.cpp
    common/file.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 38ad12d..e504146 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -103,7 +103,7 @@ Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
 
 void DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
 	Common::DumpFile *f = new Common::DumpFile();
-	if (!f->open(localPath)) {
+	if (!f->open(localPath, true)) {
 		warning("DropboxStorage: unable to open file to download into");
 		if (callback) (*callback)(false);
 		delete f;
@@ -116,8 +116,8 @@ void DropboxStorage::download(Common::String remotePath, Common::String localPat
 void DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
-	//listDirectory("", new Common::Callback<DropboxStorage, Common::Array<StorageFile> >(this, &DropboxStorage::printFiles), true);
-	download("/remote/test.jpg", "local/test.jpg", 0);
+	//this must create all these directories:
+	download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
 }
 
 void DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h
index dcfdc08..24286b6 100644
--- a/backends/fs/abstract-fs.h
+++ b/backends/fs/abstract-fs.h
@@ -191,6 +191,15 @@ public:
 	 * @return pointer to the stream object, 0 in case of a failure
 	 */
 	virtual Common::WriteStream *createWriteStream() = 0;
+
+	/**
+	* Creates a file referred by this node.
+	*
+	* @param isDirectory true if created file must be a directory
+	*
+	* @return true if file is created successfully
+	*/
+	virtual bool create(bool isDirectory) = 0;
 };
 
 
diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index 1a6c4e4..d388f30 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -252,6 +252,30 @@ Common::WriteStream *POSIXFilesystemNode::createWriteStream() {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
+bool POSIXFilesystemNode::create(bool isDirectory) {
+	bool success;
+
+	if (isDirectory) {
+		success = mkdir(_path.c_str(), 0755) == 0;
+	} else {
+		success = creat(_path.c_str(), 0755) != -1;
+	}
+
+	if (success) {		
+		setFlags();
+		if (_isValid) {
+			if (_isDirectory != isDirectory) warning("failed to create %s: got %s", isDirectory ? "directory" : "file", _isDirectory ? "directory" : "file");			
+			return _isDirectory == isDirectory;
+		}
+
+		warning("POSIXFilesystemNode: %s() was a success, but stat indicates there is no such %s",
+			isDirectory ? "mkdir" : "creat", isDirectory ? "directory" : "file");
+		return false;
+	}
+
+	return false;
+}
+
 namespace Posix {
 
 bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
diff --git a/backends/fs/posix/posix-fs.h b/backends/fs/posix/posix-fs.h
index 0703ac5..4ebce7e 100644
--- a/backends/fs/posix/posix-fs.h
+++ b/backends/fs/posix/posix-fs.h
@@ -73,6 +73,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 
 private:
 	/**
diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp
index 49549b8..b43686f 100644
--- a/backends/fs/windows/windows-fs.cpp
+++ b/backends/fs/windows/windows-fs.cpp
@@ -244,4 +244,36 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
+bool WindowsFilesystemNode::create(bool isDirectory) {
+	bool success;
+
+	if (isDirectory) {
+		success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0;
+	} else {
+		success = CreateFile(toUnicode(_path.c_str()), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) != INVALID_HANDLE_VALUE;
+	}
+
+	if (success) {
+		//this piece is copied from constructor, it checks that file exists and detects whether it's a directory
+		DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str()));
+		if (fileAttribs != INVALID_FILE_ATTRIBUTES) {
+			_isDirectory = ((fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0);
+			_isValid = true;
+			// Add a trailing slash, if necessary.
+			if (_isDirectory && _path.lastChar() != '\\') {
+				_path += '\\';
+			}
+
+			if (_isDirectory != isDirectory) warning("failed to create %s: got %s", isDirectory ? "directory" : "file", _isDirectory ? "directory" : "file");
+			return _isDirectory == isDirectory;
+		}
+
+		warning("WindowsFilesystemNode: Create%s() was a success, but GetFileAttributes() indicates there is no such %s",
+			    isDirectory ? "Directory" : "File", isDirectory ? "directory" : "file");
+		return false;
+	}
+
+	return false;
+}
+
 #endif //#ifdef WIN32
diff --git a/backends/fs/windows/windows-fs.h b/backends/fs/windows/windows-fs.h
index d060446..7c9f2c1 100644
--- a/backends/fs/windows/windows-fs.h
+++ b/backends/fs/windows/windows-fs.h
@@ -88,6 +88,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 
 private:
 	/**
diff --git a/common/file.cpp b/common/file.cpp
index 4d9c630..52b66bd 100644
--- a/common/file.cpp
+++ b/common/file.cpp
@@ -25,6 +25,8 @@
 #include "common/file.h"
 #include "common/fs.h"
 #include "common/textconsole.h"
+#include "common/system.h"
+#include "backends/fs/fs-factory.h"
 
 namespace Common {
 
@@ -149,11 +151,23 @@ DumpFile::~DumpFile() {
 	close();
 }
 
-bool DumpFile::open(const String &filename) {
+bool DumpFile::open(const String &filename, bool createPath) {
 	assert(!filename.empty());
 	assert(!_handle);
 
-	FSNode node(filename);
+	if (createPath) {
+		for (uint32 i = 0; i < filename.size(); ++i) {
+			if (filename[i] == '/' || filename[i] == '\\') {
+				Common::String subpath = filename;
+				subpath.erase(i);
+				AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(subpath);				
+				if (node->exists()) continue;				
+				if (!node->create(true)) warning("DumpFile: unable to create directories from path prefix");
+			}
+		}
+	}
+
+	FSNode node(filename);	
 	return open(node);
 }
 
diff --git a/common/file.h b/common/file.h
index 3d17483..8ad6249 100644
--- a/common/file.h
+++ b/common/file.h
@@ -143,7 +143,7 @@ public:
 	DumpFile();
 	virtual ~DumpFile();
 
-	virtual bool open(const String &filename);
+	virtual bool open(const String &filename, bool createPath = false);
 	virtual bool open(const FSNode &node);
 
 	virtual void close();


Commit: 17fc40a94436f970af75d6717c7a63c70eafcb12
    https://github.com/scummvm/scummvm/commit/17fc40a94436f970af75d6717c7a63c70eafcb12
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add AbstractFSNode::create() backends stubs

Changed paths:
    backends/fs/amigaos4/amigaos4-fs.cpp
    backends/fs/amigaos4/amigaos4-fs.h
    backends/fs/chroot/chroot-fs.cpp
    backends/fs/chroot/chroot-fs.h
    backends/fs/ds/ds-fs.cpp
    backends/fs/ds/ds-fs.h
    backends/fs/n64/n64-fs.cpp
    backends/fs/n64/n64-fs.h
    backends/fs/ps2/ps2-fs.cpp
    backends/fs/ps2/ps2-fs.h
    backends/fs/psp/psp-fs.cpp
    backends/fs/psp/psp-fs.h
    backends/fs/symbian/symbian-fs.cpp
    backends/fs/symbian/symbian-fs.h
    backends/fs/wii/wii-fs.cpp
    backends/fs/wii/wii-fs.h



diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp
index 6d5b099..24a8fb9 100644
--- a/backends/fs/amigaos4/amigaos4-fs.cpp
+++ b/backends/fs/amigaos4/amigaos4-fs.cpp
@@ -443,4 +443,9 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
+bool AmigaOSFilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif //defined(__amigaos4__)
diff --git a/backends/fs/amigaos4/amigaos4-fs.h b/backends/fs/amigaos4/amigaos4-fs.h
index 4083548..3ed45d3 100644
--- a/backends/fs/amigaos4/amigaos4-fs.h
+++ b/backends/fs/amigaos4/amigaos4-fs.h
@@ -116,6 +116,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 
diff --git a/backends/fs/chroot/chroot-fs.cpp b/backends/fs/chroot/chroot-fs.cpp
index 2cbb4af..f5f7c84 100644
--- a/backends/fs/chroot/chroot-fs.cpp
+++ b/backends/fs/chroot/chroot-fs.cpp
@@ -108,6 +108,11 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() {
 	return _realNode->createWriteStream();
 }
 
+bool ChRootFilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 Common::String ChRootFilesystemNode::addPathComponent(const Common::String &path, const Common::String &component) {
 	const char sep = '/';
 	if (path.lastChar() == sep && component.firstChar() == sep) {
diff --git a/backends/fs/chroot/chroot-fs.h b/backends/fs/chroot/chroot-fs.h
index 9ff913b..e0ecc1c 100644
--- a/backends/fs/chroot/chroot-fs.h
+++ b/backends/fs/chroot/chroot-fs.h
@@ -49,6 +49,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 
 private:
 	static Common::String addPathComponent(const Common::String &path, const Common::String &component);
diff --git a/backends/fs/ds/ds-fs.cpp b/backends/fs/ds/ds-fs.cpp
index 3782caf..83b1295 100644
--- a/backends/fs/ds/ds-fs.cpp
+++ b/backends/fs/ds/ds-fs.cpp
@@ -211,6 +211,11 @@ Common::WriteStream *DSFileSystemNode::createWriteStream() {
 	return Common::wrapBufferedWriteStream(stream, WRITE_BUFFER_SIZE);
 }
 
+bool DSFileSystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 //////////////////////////////////////////////////////////////////////////
 // GBAMPFileSystemNode - File system using GBA Movie Player and CF card //
 //////////////////////////////////////////////////////////////////////////
@@ -393,6 +398,11 @@ Common::WriteStream *GBAMPFileSystemNode::createWriteStream() {
 	return Common::wrapBufferedWriteStream(stream, WRITE_BUFFER_SIZE);
 }
 
+bool GBAMPFileSystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 
 
 DSFileStream::DSFileStream(void *handle) : _handle(handle) {
diff --git a/backends/fs/ds/ds-fs.h b/backends/fs/ds/ds-fs.h
index b9ccfcb..939d1a1 100644
--- a/backends/fs/ds/ds-fs.h
+++ b/backends/fs/ds/ds-fs.h
@@ -91,6 +91,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 
 	/**
 	 * Returns the zip file this node points to.
@@ -156,6 +157,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 struct fileHandle {
diff --git a/backends/fs/n64/n64-fs.cpp b/backends/fs/n64/n64-fs.cpp
index fe37dad..d568500 100644
--- a/backends/fs/n64/n64-fs.cpp
+++ b/backends/fs/n64/n64-fs.cpp
@@ -160,4 +160,9 @@ Common::WriteStream *N64FilesystemNode::createWriteStream() {
 	return RomfsStream::makeFromPath(getPath(), true);
 }
 
+bool N64FilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif //#ifdef __N64__
diff --git a/backends/fs/n64/n64-fs.h b/backends/fs/n64/n64-fs.h
index d503a66..d520ad5 100644
--- a/backends/fs/n64/n64-fs.h
+++ b/backends/fs/n64/n64-fs.h
@@ -73,6 +73,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 #endif
diff --git a/backends/fs/ps2/ps2-fs.cpp b/backends/fs/ps2/ps2-fs.cpp
index 9b6e127..8391e06 100644
--- a/backends/fs/ps2/ps2-fs.cpp
+++ b/backends/fs/ps2/ps2-fs.cpp
@@ -441,4 +441,9 @@ Common::WriteStream *Ps2FilesystemNode::createWriteStream() {
 	return PS2FileStream::makeFromPath(getPath(), true);
 }
 
+bool Ps2FilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif
diff --git a/backends/fs/ps2/ps2-fs.h b/backends/fs/ps2/ps2-fs.h
index 63b866b..c9da434 100644
--- a/backends/fs/ps2/ps2-fs.h
+++ b/backends/fs/ps2/ps2-fs.h
@@ -96,6 +96,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 
 	int getDev() { return 0; }
 };
diff --git a/backends/fs/psp/psp-fs.cpp b/backends/fs/psp/psp-fs.cpp
index e8aad9f..6bd5e93 100644
--- a/backends/fs/psp/psp-fs.cpp
+++ b/backends/fs/psp/psp-fs.cpp
@@ -239,4 +239,9 @@ Common::WriteStream *PSPFilesystemNode::createWriteStream() {
 	return Common::wrapBufferedWriteStream(stream, WRITE_BUFFER_SIZE);
 }
 
+bool PSPFilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif //#ifdef __PSP__
diff --git a/backends/fs/psp/psp-fs.h b/backends/fs/psp/psp-fs.h
index 1bb4543..6485446 100644
--- a/backends/fs/psp/psp-fs.h
+++ b/backends/fs/psp/psp-fs.h
@@ -65,6 +65,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 #endif
diff --git a/backends/fs/symbian/symbian-fs.cpp b/backends/fs/symbian/symbian-fs.cpp
index 8fbc3a4..e4163ca 100644
--- a/backends/fs/symbian/symbian-fs.cpp
+++ b/backends/fs/symbian/symbian-fs.cpp
@@ -231,4 +231,10 @@ Common::SeekableReadStream *SymbianFilesystemNode::createReadStream() {
 Common::WriteStream *SymbianFilesystemNode::createWriteStream() {
 	return SymbianStdioStream::makeFromPath(getPath(), true);
 }
+
+bool SymbianFilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif //#if defined(__SYMBIAN32__)
diff --git a/backends/fs/symbian/symbian-fs.h b/backends/fs/symbian/symbian-fs.h
index 339e998..1327f9f 100644
--- a/backends/fs/symbian/symbian-fs.h
+++ b/backends/fs/symbian/symbian-fs.h
@@ -66,6 +66,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 #endif
diff --git a/backends/fs/wii/wii-fs.cpp b/backends/fs/wii/wii-fs.cpp
index 43f4f59..46041bf 100644
--- a/backends/fs/wii/wii-fs.cpp
+++ b/backends/fs/wii/wii-fs.cpp
@@ -213,4 +213,9 @@ Common::WriteStream *WiiFilesystemNode::createWriteStream() {
 	return StdioStream::makeFromPath(getPath(), true);
 }
 
+bool WiiFilesystemNode::create(bool isDirectory) {
+	error("Not supported");
+	return false;
+}
+
 #endif //#if defined(__WII__)
diff --git a/backends/fs/wii/wii-fs.h b/backends/fs/wii/wii-fs.h
index c77c543..affb765 100644
--- a/backends/fs/wii/wii-fs.h
+++ b/backends/fs/wii/wii-fs.h
@@ -69,6 +69,7 @@ public:
 
 	virtual Common::SeekableReadStream *createReadStream();
 	virtual Common::WriteStream *createWriteStream();
+	virtual bool create(bool isDirectory);
 };
 
 #endif


Commit: eda575a660543884b1a4addd21b676a67d2c2a31
    https://github.com/scummvm/scummvm/commit/eda575a660543884b1a4addd21b676a67d2c2a31
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Requests id

(Upgrading ConnectionManager step by step.)

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/request.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index e504146..9acbfc0 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -84,8 +84,8 @@ void DropboxStorage::printFiles(Common::Array<StorageFile> files) {
 		debug("\t%s", files[i].name().c_str());
 }
 
-void DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
-	ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
+int32 DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
+	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
 Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
@@ -101,30 +101,30 @@ Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
 	return request->execute();
 }
 
-void DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+int32 DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("DropboxStorage: unable to open file to download into");
 		if (callback) (*callback)(false);
 		delete f;
-		return;
+		return -1;
 	}
 
-	ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
+	return ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
 }
 
-void DropboxStorage::syncSaves(BoolCallback callback) {
+int32 DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
 	//this must create all these directories:
-	download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
+	return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
 }
 
-void DropboxStorage::info(StorageInfoCallback outerCallback) {
+int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
 	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
-	ConnMan.addRequest(request);
+	return ConnMan.addRequest(request);
 	//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
 	//so, when CurlJsonRequest is finished, it calls the innerCallback
 	//innerCallback (which is DropboxStorage::infoInnerCallback in this case) processes the void *ptr
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 415a3fe..6abd3d1 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -64,31 +64,31 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
+	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) {} //TODO
+	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::NetworkReadStream *streamFile(Common::String path);
 
 	/** Calls the callback when finished. */
-	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback);
+	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(BoolCallback callback);
+	virtual int32 syncSaves(BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual void info(StorageInfoCallback callback);
+	virtual int32 info(StorageInfoCallback callback);
 
 	/** This method is passed into info(). (Temporary) */
 	void infoMethodCallback(StorageInfo storageInfo);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 2f10841..b0690be 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -137,12 +137,12 @@ void OneDriveStorage::printJson(void *jsonPointer) {
 	delete json;
 }
 
-void OneDriveStorage::syncSaves(BoolCallback callback) {
+int32 OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	Common::BaseCallback<> *innerCallback = new Common::Callback<OneDriveStorage>(this, &OneDriveStorage::printJson);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");	
 	request->addHeader("Authorization: bearer " + _token);
-	ConnMan.addRequest(request);
+	return ConnMan.addRequest(request);
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 28f37ae..4141771 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -74,31 +74,31 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) {} //TODO
+	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) {} //TODO
+	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::NetworkReadStream *streamFile(Common::String path) { return 0; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback) {} //TODO
+	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(BoolCallback callback);
+	virtual int32 syncSaves(BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, BoolCallback callback) {} //TODO
+	virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual void info(StorageInfoCallback callback) {} //TODO
+	virtual int32 info(StorageInfoCallback callback) { return -1; } //TODO
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index e38c6be..394fc2c 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -57,34 +57,40 @@ public:
 
 	virtual void saveConfig(Common::String keyPrefix) = 0;
 
-	/** Public Cloud API comes down there. */
+	/**
+	 * Public Cloud API comes down there.
+	 *
+	 * All Cloud API methods return int32 request id, which might be used to access
+	 * request through ConnectionManager. All methods also accept a callback, which
+	 * would be called, when request is complete.
+	 */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual void listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
+	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
+	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::NetworkReadStream *streamFile(Common::String path) = 0;
+	virtual Networking::NetworkReadStream *streamFile(Common::String path) = 0; //TODO: return int32 somehow
 
 	/** Calls the callback when finished. */
-	virtual void download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
+	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void remove(Common::String path, BoolCallback callback) = 0;
+	virtual int32 remove(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void syncSaves(BoolCallback callback) = 0;
+	virtual int32 syncSaves(BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void createDirectory(Common::String path, BoolCallback callback) = 0;
+	virtual int32 createDirectory(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual void touch(Common::String path, BoolCallback callback) = 0;
+	virtual int32 touch(Common::String path, BoolCallback callback) = 0;
 
 	/** Returns the StorageInfo struct. */
-	virtual void info(StorageInfoCallback callback) = 0;
+	virtual int32 info(StorageInfoCallback callback) = 0;
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 97ae31a..8428bd2 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -48,9 +48,12 @@ void ConnectionManager::registerEasyHandle(CURL *easy) {
 	curl_multi_add_handle(_multi, easy);
 }
 
-void ConnectionManager::addRequest(Request *request) {
-	_requests.push_back(request);
+int32 ConnectionManager::addRequest(Request *request) {
+	int32 newId = _nextId++;
+	_requests[newId] = request;
+	request->setId(newId);
 	if (!_timerStarted) startTimer();
+	return newId;
 }
 
 //private goes here:
@@ -84,10 +87,13 @@ void ConnectionManager::handle() {
 void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
 	debug("handling %d request(s)", _requests.size());
-	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		if ((*i)->handle()) {
-			delete (*i);
-			_requests.erase(i);
+	for (Common::HashMap<int32, Request *>::iterator i = _requests.begin(); i != _requests.end();) {
+		Request *request = i->_value;
+		if (request && request->handle()) {
+			delete request;
+			//_requests.erase(i);
+			_requests[i->_key] = 0;
+			++i; //that's temporary
 		} else ++i;
 	}
 	if (_requests.empty()) stopTimer();
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index ed72644..9651a01 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -26,7 +26,7 @@
 #include "backends/networking/curl/request.h"
 #include "common/str.h"
 #include "common/singleton.h"
-#include "common/array.h"
+#include "common/hashmap.h"
 
 typedef void CURL;
 typedef void CURLM;
@@ -41,7 +41,8 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 
 	CURLM *_multi;	
 	bool _timerStarted;
-	Common::Array<Request *> _requests;	
+	Common::HashMap<int32, Request *> _requests;
+	int32 _nextId;
 	
 	void startTimer(int interval = 1000000); //1 second is the default interval
 	void stopTimer();
@@ -66,8 +67,10 @@ public:
 	* Requests until they return true.
 	*
 	* @note This method starts the timer if it's not started yet.
+	*
+	* @return generated Request's id, which might be used to get its status
 	*/
-	void addRequest(Request *request);
+	int32 addRequest(Request *request);
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 37cb388..0265d3e 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -24,6 +24,7 @@
 #define BACKENDS_NETWORKING_CURL_REQUEST_H
 
 #include "common/callback.h"
+#include "common/scummsys.h"
 
 namespace Networking {
 
@@ -36,9 +37,11 @@ protected:
 
 	Common::BaseCallback<> *_callback;
 
+	int32 _id;
+
 public:
-	Request(Common::BaseCallback<> *cb): _callback(cb) {};
-	virtual ~Request() { delete _callback; };
+	Request(Common::BaseCallback<> *cb): _callback(cb), _id(-1) {}
+	virtual ~Request() { delete _callback; }
 
 	/**
 	* Method, which does actual work. Depends on what this Request is doing.
@@ -47,6 +50,8 @@ public:
 	*/
 
 	virtual bool handle() = 0;
+
+	void setId(int32 id) { _id = id; }
 };
 
 } //end of namespace Cloud


Commit: 62ccf1f902100febfb1be02b67e84a6e4938ebbf
    https://github.com/scummvm/scummvm/commit/62ccf1f902100febfb1be02b67e84a6e4938ebbf
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add RequestInfo struct

ConnectionManager upgrade: it now contains a special struct for each
request, so you can access request status and data by request id.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/request.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index e86b655..a96c298 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -21,6 +21,7 @@
 */
 
 #include "backends/cloud/downloadrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
 #include "common/debug.h"
 #include "common/textconsole.h"
 
@@ -32,16 +33,19 @@ DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::Net
 bool DownloadRequest::handle() {
 	if (!_remoteFileStream) {
 		warning("DownloadRequest: no stream to read");
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		return true;
 	}
 
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		return true;
 	}
 
 	if (!_localFile->isOpen()) {
 		warning("DownloadRequest: failed to open file to write");
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		return true;
 	}
 
@@ -52,7 +56,8 @@ bool DownloadRequest::handle() {
 	if (readBytes != 0)
 		if (_localFile->write(buf, readBytes) != readBytes) {
 			warning("DownloadRequest: unable to write all received bytes into output file");
-			if (_boolCallback) (*_boolCallback)(false);
+			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+			if (_boolCallback) (*_boolCallback)(false);			
 			return true;
 		}
 
@@ -62,6 +67,7 @@ bool DownloadRequest::handle() {
 			//TODO: do something about it actually			
 		}
 
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		if (_boolCallback) (*_boolCallback)(_remoteFileStream->httpResponseCode() == 200);
 
 		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
@@ -71,4 +77,13 @@ bool DownloadRequest::handle() {
 	return false;
 }
 
+void DownloadRequest::restart() {
+	//this request doesn't know anything about the _remoteFileStream it's reading
+	//thus, it can't restart it
+	warning("DownloadRequest: cannot be restarted");
+	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+	if (_boolCallback) (*_boolCallback)(false);
+	//TODO: fix that
+}
+
 } //end of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index b135b15..c156410 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -40,6 +40,7 @@ public:
 	virtual ~DownloadRequest() { delete _localFile; }
 
 	virtual bool handle();
+	virtual void restart();
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 5e5957b..3dada88 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -30,24 +30,33 @@ namespace Cloud {
 namespace Dropbox {
 
 DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive):
-	Networking::Request(0), _filesCallback(cb), _token(token), _complete(false) {	
-	Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);//new Common::GlobalFunctionCallback(printJson); //okay
+	Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _filesCallback(cb),
+	_token(token), _complete(false), _requestId(-1) {
+	startupWork();
+}
+
+void DropboxListDirectoryRequest::startupWork() {
+	_files.clear();
+	_complete = false;
+
+	Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 
 	Common::JSONObject jsonRequestParameters;
-	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
-	jsonRequestParameters.setVal("recursive", new Common::JSONValue(recursive));
+	jsonRequestParameters.setVal("path", new Common::JSONValue(_requestedPath));
+	jsonRequestParameters.setVal("recursive", new Common::JSONValue(_requestedRecursive));
 	jsonRequestParameters.setVal("include_media_info", new Common::JSONValue(false));
 	jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
 
 	Common::JSONValue value(jsonRequestParameters);
 	request->addPostField(Common::JSON::stringify(&value));
 
-	ConnMan.addRequest(request);
+	_requestId = ConnMan.addRequest(request);
 }
 
+
 void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
 	Common::JSONValue *json = (Common::JSONValue *)jsonPtr;
 	if (json) {
@@ -103,13 +112,24 @@ void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
 }
 
 bool DropboxListDirectoryRequest::handle() {
-	if (_complete && _filesCallback) {		
-		(*_filesCallback)(_files);
+	if (_complete && _filesCallback) {
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_filesCallback) (*_filesCallback)(_files);
 	}
 
 	return _complete;
 }
 
+void DropboxListDirectoryRequest::restart() {
+	if (_requestId != -1) {
+		Networking::RequestInfo &info = ConnMan.getRequestInfo(_requestId);
+		//TODO: I'm really not sure some CurlRequest would handle this (it must stop corresponding CURL transfer)
+		info.state = Networking::FINISHED; //may be CANCELED or INTERRUPTED or something?
+		_requestId = -1;
+	}
+
+	startupWork();
+}
 
 } //end of namespace Dropbox
 } //end of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 0c10512..36070a2 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -31,18 +31,24 @@ namespace Cloud {
 namespace Dropbox {
 
 class DropboxListDirectoryRequest: public Networking::Request {
+	Common::String _requestedPath;
+	bool _requestedRecursive;
+
 	Storage::FileArrayCallback _filesCallback;
 	Common::String _token;
 	bool _complete;
 	Common::Array<StorageFile> _files;
+	int32 _requestId;
 
 	void responseCallback(void *jsonPtr);
+	void startupWork();
 
 public:
 	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
 	virtual ~DropboxListDirectoryRequest() { delete _filesCallback; }
 
 	virtual bool handle();
+	virtual void restart();
 };
 
 } //end of namespace Dropbox
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 8428bd2..b448d8e 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -50,12 +50,16 @@ void ConnectionManager::registerEasyHandle(CURL *easy) {
 
 int32 ConnectionManager::addRequest(Request *request) {
 	int32 newId = _nextId++;
-	_requests[newId] = request;
+	_requests[newId] = RequestInfo(newId, request);
 	request->setId(newId);
 	if (!_timerStarted) startTimer();
 	return newId;
 }
 
+RequestInfo &ConnectionManager::getRequestInfo(int32 id) {
+	return _requests[id];
+}
+
 //private goes here:
 
 void connectionsThread(void *ignored) {
@@ -87,15 +91,34 @@ void ConnectionManager::handle() {
 void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
 	debug("handling %d request(s)", _requests.size());
-	for (Common::HashMap<int32, Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		Request *request = i->_value;
-		if (request && request->handle()) {
-			delete request;
-			//_requests.erase(i);
-			_requests[i->_key] = 0;
-			++i; //that's temporary
-		} else ++i;
+	Common::Array<int32> idsToRemove;
+	for (Common::HashMap<int32, RequestInfo>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
+		RequestInfo &info = _requests[i->_key];
+		
+		switch(info.state) {
+		case FINISHED:
+			delete info.request;
+			info.request = 0;
+			idsToRemove.push_back(info.id);
+			break;
+
+		case PROCESSING:
+			info.request->handle();
+			break;
+
+		case RETRY:
+			if (info.retryInSeconds > 0) --info.retryInSeconds;
+			else {
+				info.state = PROCESSING;
+				info.request->restart();
+			}
+
+		default:
+			; //nothing to do
+		}
 	}
+	for (uint32 i = 0; i < idsToRemove.size(); ++i)
+		_requests.erase(idsToRemove[i]);
 	if (_requests.empty()) stopTimer();
 }
 
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 9651a01..9ae52b3 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -36,12 +36,30 @@ namespace Networking {
 
 class NetworkReadStream;
 
+enum RequestState {
+	PROCESSING,
+	PAUSED,
+	RETRY,
+	FINISHED
+};
+
+struct RequestInfo {
+	int32 id;
+	Request *request;
+	RequestState state;
+	void *data;
+	uint32 retryInSeconds;
+
+	RequestInfo() : id(-1), request(0), state(FINISHED), data(0), retryInSeconds(0) {}
+	RequestInfo(int32 rqId, Request *rq) : id(rqId), request(rq), state(PROCESSING), data(0), retryInSeconds(0) {}
+};
+
 class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	friend void connectionsThread(void *); //calls handle()
 
 	CURLM *_multi;	
 	bool _timerStarted;
-	Common::HashMap<int32, Request *> _requests;
+	Common::HashMap<int32, RequestInfo> _requests;
 	int32 _nextId;
 	
 	void startTimer(int interval = 1000000); //1 second is the default interval
@@ -70,7 +88,9 @@ public:
 	*
 	* @return generated Request's id, which might be used to get its status
 	*/
-	int32 addRequest(Request *request);
+	int32 addRequest(Request *request);	
+
+	RequestInfo &getRequestInfo(int32 id);
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 0366e3b..21c0a0f 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -23,6 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/debug.h"
 #include "common/json.h"
@@ -68,6 +69,7 @@ bool CurlJsonRequest::handle() {
 			if (_stream->httpResponseCode() != 200)
 				warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 
+			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 			if (_callback) {
 				char *contents = getPreparedContents();
 				if (_stream->httpResponseCode() != 200)
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index e13adac..e1c8f2b 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -42,13 +42,20 @@ bool CurlRequest::handle() {
 
 	if (_stream && _stream->eos()) {		
 		if (_stream->httpResponseCode() != 200)
-			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());		
+			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		return true;
 	}
 
 	return false;
 }
 
+void CurlRequest::restart() {
+	if (_stream) delete _stream;
+	_stream = 0;
+	//with no stream available next handle() will create another one
+}
+
 void CurlRequest::addHeader(Common::String header) {
 	_headersList = curl_slist_append(_headersList, header.c_str());
 }
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 22f50be..ec1a9e3 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -44,9 +44,9 @@ public:
 	virtual ~CurlRequest();
 
 	virtual bool handle();
+	virtual void restart();
 
 	void addHeader(Common::String header);
-
 	void addPostField(Common::String header);
 
 	/** Start this Request with ConnMan. Returns its ReadStream. */
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 0265d3e..136f007 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -51,6 +51,8 @@ public:
 
 	virtual bool handle() = 0;
 
+	virtual void restart() = 0;
+
 	void setId(int32 id) { _id = id; }
 };
 


Commit: f4547f44df32ce1f49a6a36df083e7703751adcd
    https://github.com/scummvm/scummvm/commit/f4547f44df32ce1f49a6a36df083e7703751adcd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add RequestIdPair struct

Can be used with Callback<T> (means it's still type safe). It's used to
pass not only Request id to user's callback, but also a value user
wanted.

void *data field is removed from RequestInfo.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/request.h
    common/callback.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index a96c298..6f777b7 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -57,7 +57,7 @@ bool DownloadRequest::handle() {
 		if (_localFile->write(buf, readBytes) != readBytes) {
 			warning("DownloadRequest: unable to write all received bytes into output file");
 			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-			if (_boolCallback) (*_boolCallback)(false);			
+			if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
 			return true;
 		}
 
@@ -68,7 +68,7 @@ bool DownloadRequest::handle() {
 		}
 
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_boolCallback) (*_boolCallback)(_remoteFileStream->httpResponseCode() == 200);
+		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, _remoteFileStream->httpResponseCode() == 200));
 
 		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
 		return true;
@@ -82,7 +82,7 @@ void DownloadRequest::restart() {
 	//thus, it can't restart it
 	warning("DownloadRequest: cannot be restarted");
 	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-	if (_boolCallback) (*_boolCallback)(false);
+	if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
 	//TODO: fix that
 }
 
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 3dada88..be93040 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -25,6 +25,7 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/json.h"
+#include "backends/cloud/storage.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -39,7 +40,7 @@ void DropboxListDirectoryRequest::startupWork() {
 	_files.clear();
 	_complete = false;
 
-	Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);
+	Networking::DataCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestDataPair>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
@@ -57,8 +58,8 @@ void DropboxListDirectoryRequest::startupWork() {
 }
 
 
-void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
-	Common::JSONValue *json = (Common::JSONValue *)jsonPtr;
+void DropboxListDirectoryRequest::responseCallback(Networking::RequestDataPair pair) {
+	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (json) {
 		Common::JSONObject response = json->asObject();
 		
@@ -88,7 +89,7 @@ void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
 		bool hasMore = response.getVal("has_more")->asBool();
 
 		if (hasMore) {
-			Common::BaseCallback<> *innerCallback = new Common::Callback<DropboxListDirectoryRequest>(this, &DropboxListDirectoryRequest::responseCallback);
+			Networking::DataCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestDataPair>(this, &DropboxListDirectoryRequest::responseCallback);
 			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
 			request->addHeader("Authorization: Bearer " + _token);
 			request->addHeader("Content-Type: application/json");
@@ -114,7 +115,7 @@ void DropboxListDirectoryRequest::responseCallback(void *jsonPtr) {
 bool DropboxListDirectoryRequest::handle() {
 	if (_complete && _filesCallback) {
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_filesCallback) (*_filesCallback)(_files);
+		if (_filesCallback) (*_filesCallback)(Storage::RequestFileArrayPair(_id, _files));
 	}
 
 	return _complete;
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 36070a2..58f3dc6 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -40,7 +40,7 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Common::Array<StorageFile> _files;
 	int32 _requestId;
 
-	void responseCallback(void *jsonPtr);
+	void responseCallback(Networking::RequestDataPair pair);
 	void startupWork();
 
 public:
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 9acbfc0..02b033f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -38,8 +38,8 @@ namespace Dropbox {
 Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
 
-static void saveAccessTokenCallback(void *ptr) {
-	Common::JSONValue *json = (Common::JSONValue *)ptr;
+static void saveAccessTokenCallback(Networking::RequestDataPair pair) {
+	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (json) {
 		debug("saveAccessTokenCallback:");
 		debug("%s", json->stringify(true).c_str());
@@ -105,7 +105,7 @@ int32 DropboxStorage::download(Common::String remotePath, Common::String localPa
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("DropboxStorage: unable to open file to download into");
-		if (callback) (*callback)(false);
+		if (callback) (*callback)(RequestBoolPair(-1, false));
 		delete f;
 		return -1;
 	}
@@ -121,7 +121,7 @@ int32 DropboxStorage::syncSaves(BoolCallback callback) {
 }
 
 int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
-	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfo>(this, &DropboxStorage::infoInnerCallback, outerCallback);
+	Networking::DataCallback innerCallback = new Common::CallbackBridge<DropboxStorage, RequestStorageInfoPair, Networking::RequestDataPair>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
@@ -131,8 +131,8 @@ int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, void *jsonPointer) {
-	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestDataPair pair) {
+	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		delete outerCallback;
@@ -148,19 +148,19 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, void *
 		Common::JSONObject quota = info.getVal("quota_info")->asObject();
 		uint32 quotaNormal = quota.getVal("normal")->asNumber();
 		uint32 quotaShared = quota.getVal("shared")->asNumber();
-		uint32 quotaAllocated = quota.getVal("quota")->asNumber();		
-		(*outerCallback)(StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated));
+		uint32 quotaAllocated = quota.getVal("quota")->asNumber();
+		(*outerCallback)(RequestStorageInfoPair(-1, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
 	
 	delete json;
 }
 
-void DropboxStorage::infoMethodCallback(StorageInfo storageInfo) {
+void DropboxStorage::infoMethodCallback(RequestStorageInfoPair pair) {
 	debug("\nStorage info:");
-	debug("User name: %s", storageInfo.name().c_str());
-	debug("Email: %s", storageInfo.email().c_str());
-	debug("Disk usage: %u/%u", storageInfo.used(), storageInfo.available());
+	debug("User name: %s", pair.value.name().c_str());
+	debug("Email: %s", pair.value.email().c_str());
+	debug("Disk usage: %u/%u", pair.value.used(), pair.value.available());
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
@@ -214,7 +214,7 @@ void DropboxStorage::authThroughConsole() {
 }
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback);
+	Networking::DataCallback callback = new Common::GlobalFunctionCallback<Networking::RequestDataPair>(saveAccessTokenCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 6abd3d1..4fe6109 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -40,7 +40,7 @@ class DropboxStorage: public Cloud::Storage {
 	static void getAccessToken(Common::String code);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
-	void infoInnerCallback(StorageInfoCallback outerCallback, void *json);
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestDataPair json);
 
 	void printFiles(Common::Array<StorageFile> files);
 
@@ -91,7 +91,7 @@ public:
 	virtual int32 info(StorageInfoCallback callback);
 
 	/** This method is passed into info(). (Temporary) */
-	void infoMethodCallback(StorageInfo storageInfo);
+	void infoMethodCallback(RequestStorageInfoPair pair);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index b0690be..36b3e26 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -42,7 +42,7 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user
 	_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
 
 OneDriveStorage::OneDriveStorage(Common::String code) {
-	getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::codeFlowComplete), code);
+	getAccessToken(new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::codeFlowComplete), code);
 }
 
 OneDriveStorage::~OneDriveStorage() {}
@@ -52,11 +52,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 
 	if (!codeFlow && _refreshToken == "") {
 		warning("OneDriveStorage: no refresh token available to get new access token.");
-		if (callback) (*callback)(false);
+		if (callback) (*callback)(RequestBoolPair(-1, false));
 		return;
 	}
 
-	Common::BaseCallback<> *innerCallback = new Common::CallbackBridge<OneDriveStorage, bool>(this, &OneDriveStorage::tokenRefreshed, callback);
+	Networking::DataCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestBoolPair, Networking::RequestDataPair>(this, &OneDriveStorage::tokenRefreshed, callback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
 	if (codeFlow) {
 		request->addPostField("code=" + code);
@@ -71,11 +71,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	ConnMan.addRequest(request);
 }
 
-void OneDriveStorage::tokenRefreshed(BoolCallback callback, void *jsonPointer) {
-	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestDataPair pair) {
+	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (!json) {
 		warning("OneDriveStorage: got NULL instead of JSON");
-		if (callback) (*callback)(false);
+		if (callback) (*callback)(RequestBoolPair(-1, false));
 		return;
 	}
 
@@ -83,19 +83,19 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, void *jsonPointer) {
 	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
 		warning("Bad response, no token or user_id passed");
 		debug("%s", json->stringify().c_str());
-		if (callback) (*callback)(false);
+		if (callback) (*callback)(RequestBoolPair(-1, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		_uid = result.getVal("user_id")->asString();
 		_refreshToken = result.getVal("refresh_token")->asString();
 		g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken
-		if (callback) (*callback)(true);
+		if (callback) (*callback)(RequestBoolPair(-1, true));
 	}
 	delete json;
 }
 
-void OneDriveStorage::codeFlowComplete(bool success) {
-	if (!success) {
+void OneDriveStorage::codeFlowComplete(RequestBoolPair pair) {
+	if (!pair.value) {
 		warning("OneDriveStorage: failed to get access token through code flow");
 		return;
 	}
@@ -113,12 +113,12 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
-void OneDriveStorage::printJsonTokenReceived(bool success) {
-	if (success) syncSaves(0); //try again
+void OneDriveStorage::printJsonTokenReceived(RequestBoolPair pair) {
+	if (pair.value) syncSaves(0); //try again
 }
 
-void OneDriveStorage::printJson(void *jsonPointer) {
-	Common::JSONValue *json = (Common::JSONValue *)jsonPointer;
+void OneDriveStorage::printJson(Networking::RequestDataPair pair) {
+	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (!json) {
 		warning("printJson: NULL");
 		return;
@@ -128,7 +128,7 @@ void OneDriveStorage::printJson(void *jsonPointer) {
 	if (result.contains("error")) {
 		//Common::JSONObject error = result.getVal("error")->asObject();
 		debug("bad token, trying again...");
-		getAccessToken(new Common::Callback<OneDriveStorage, bool>(this, &OneDriveStorage::printJsonTokenReceived));
+		getAccessToken(new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::printJsonTokenReceived));
 		delete json;
 		return;
 	}
@@ -139,7 +139,7 @@ void OneDriveStorage::printJson(void *jsonPointer) {
 
 int32 OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
-	Common::BaseCallback<> *innerCallback = new Common::Callback<OneDriveStorage>(this, &OneDriveStorage::printJson);
+	Networking::DataCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestDataPair>(this, &OneDriveStorage::printJson);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");	
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 4141771..3c92880 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -49,11 +49,11 @@ class OneDriveStorage: public Cloud::Storage {
 	* continue your work when new token is available.
 	*/
 	void getAccessToken(BoolCallback callback, Common::String code = "");	
-	void tokenRefreshed(BoolCallback callback, void *jsonPointer);
-	void codeFlowComplete(bool success);
+	void tokenRefreshed(BoolCallback callback, Networking::RequestDataPair pair);
+	void codeFlowComplete(RequestBoolPair pair);
 
-	void printJson(void *jsonPointer);
-	void printJsonTokenReceived(bool success);
+	void printJson(Networking::RequestDataPair pair);
+	void printJsonTokenReceived(RequestBoolPair pair);
 public:	
 	virtual ~OneDriveStorage();
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 394fc2c..325d57d 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -30,15 +30,21 @@
 #include "backends/cloud/storagefile.h"
 #include "backends/cloud/storageinfo.h"
 #include "backends/networking/curl/networkreadstream.h"
+#include <backends/networking/curl/request.h>
 
 namespace Cloud {
 
 class Storage {
 public:
-	typedef Common::BaseCallback< Common::Array<StorageFile> > *FileArrayCallback;
-	typedef Common::BaseCallback<Common::ReadStream *> *ReadStreamCallback;
-	typedef Common::BaseCallback<StorageInfo> *StorageInfoCallback;
-	typedef Common::BaseCallback<bool> *BoolCallback;
+	typedef Networking::RequestIdPair<Common::Array<StorageFile>&> RequestFileArrayPair;
+	typedef Networking::RequestIdPair<Common::ReadStream *> RequestReadStreamPair;
+	typedef Networking::RequestIdPair<StorageInfo> RequestStorageInfoPair;
+	typedef Networking::RequestIdPair<bool> RequestBoolPair;	
+
+	typedef Common::BaseCallback<RequestFileArrayPair> *FileArrayCallback;
+	typedef Common::BaseCallback<RequestReadStreamPair> *ReadStreamCallback;
+	typedef Common::BaseCallback<RequestStorageInfoPair> *StorageInfoCallback;
+	typedef Common::BaseCallback<RequestBoolPair> *BoolCallback;
 
 	Storage() {}
 	virtual ~Storage() {}
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 9ae52b3..15327a2 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -46,12 +46,11 @@ enum RequestState {
 struct RequestInfo {
 	int32 id;
 	Request *request;
-	RequestState state;
-	void *data;
+	RequestState state;	
 	uint32 retryInSeconds;
 
-	RequestInfo() : id(-1), request(0), state(FINISHED), data(0), retryInSeconds(0) {}
-	RequestInfo(int32 rqId, Request *rq) : id(rqId), request(rq), state(PROCESSING), data(0), retryInSeconds(0) {}
+	RequestInfo() : id(-1), request(0), state(FINISHED), retryInSeconds(0) {}
+	RequestInfo(int32 rqId, Request *rq) : id(rqId), request(rq), state(PROCESSING), retryInSeconds(0) {}
 };
 
 class ConnectionManager : public Common::Singleton<ConnectionManager> {
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 21c0a0f..11eeb29 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(Common::BaseCallback<> *cb, const char *url):
+CurlJsonRequest::CurlJsonRequest(DataCallback cb, const char *url):
 	CurlRequest(cb, url), _contentsStream(DisposeAfterUse::YES) {}
 
 CurlJsonRequest::~CurlJsonRequest() {}
@@ -75,7 +75,7 @@ bool CurlJsonRequest::handle() {
 				if (_stream->httpResponseCode() != 200)
 					debug("%s", contents);
 				Common::JSONValue *json = Common::JSON::parse(contents);				
-				(*_callback)(json); //potential memory leak, free it in your callbacks!
+				(*_callback)(RequestDataPair(_id, json)); //potential memory leak, free it in your callbacks!
 			}
 			return true;
 		}
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index cfb82e9..9b23cd7 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -37,7 +37,7 @@ class CurlJsonRequest: public CurlRequest {
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(Common::BaseCallback<> *cb, const char *url);
+	CurlJsonRequest(DataCallback cb, const char *url); //TODO: use some Callback<JSON> already
 	virtual ~CurlJsonRequest();
 
 	virtual bool handle();
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index e1c8f2b..6f5c612 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -30,7 +30,7 @@
 
 namespace Networking {
 
-CurlRequest::CurlRequest(Common::BaseCallback<> *cb, const char *url): 
+CurlRequest::CurlRequest(DataCallback cb, const char *url): 
 	Request(cb), _url(url), _stream(0), _headersList(0) {}
 
 CurlRequest::~CurlRequest() {
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index ec1a9e3..c7f07fc 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -40,7 +40,7 @@ protected:
 	Common::String _postFields;
 
 public:
-	CurlRequest(Common::BaseCallback<> *cb, const char *url);
+	CurlRequest(DataCallback cb, const char *url);
 	virtual ~CurlRequest();
 
 	virtual bool handle();
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 136f007..f2c2f1f 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -28,6 +28,16 @@
 
 namespace Networking {
 
+template<typename T> struct RequestIdPair {
+	int32 id;
+	T value;
+
+	RequestIdPair(int32 rid, T v) : id(rid), value(v) {}
+};
+
+typedef RequestIdPair<void *> RequestDataPair;
+typedef Common::BaseCallback<RequestDataPair> *DataCallback;
+
 class Request {
 protected:
 	/**
@@ -35,12 +45,12 @@ protected:
 	* That's the way Requests pass the result to the code which asked to create this request.
 	*/
 
-	Common::BaseCallback<> *_callback;
+	DataCallback _callback;
 
 	int32 _id;
 
 public:
-	Request(Common::BaseCallback<> *cb): _callback(cb), _id(-1) {}
+	Request(DataCallback cb): _callback(cb), _id(-1) {}
 	virtual ~Request() { delete _callback; }
 
 	/**
diff --git a/common/callback.h b/common/callback.h
index 2101331..4356e4b 100644
--- a/common/callback.h
+++ b/common/callback.h
@@ -48,21 +48,21 @@ public:
 };
 
 /**
-* GlobalFunctionCallback is a simple wrapper for global C functions.
+* GlobalFunctionCallback<T> is a simple wrapper for global C functions.
 *
-* If there is a method, which accepts BaseCallback<void *>, you can
+* If there is a method, which accepts BaseCallback<T>, you can
 * easily pass your C function by passing
-*     new GlobalFunctionCallback(yourFunction)
+*     new GlobalFunctionCallback<T>(yourFunction)
 */
 
-class GlobalFunctionCallback: public BaseCallback<void *> {
-	typedef void(*GlobalFunction)(void *result);
+template<typename T> class GlobalFunctionCallback: public BaseCallback<T> {
+	typedef void(*GlobalFunction)(T result);
 	GlobalFunction _callback;
 
 public:
 	GlobalFunctionCallback(GlobalFunction cb): _callback(cb) {}
 	virtual ~GlobalFunctionCallback() {}
-	virtual void operator()(void *data) {
+	virtual void operator()(T data) {
 		if (_callback) _callback(data);
 	}
 };


Commit: a7b28605a01b59de6f3acc9df4cd1cac707c39e7
    https://github.com/scummvm/scummvm/commit/a7b28605a01b59de6f3acc9df4cd1cac707c39e7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Change Request::handle()

With new ConnectionManager upgrade Requests indicate that they are
finished with RequestInfo.state. No need to use handle() return value
anymore.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/request.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 6f777b7..756a904 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -30,23 +30,23 @@ namespace Cloud {
 DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile):
 	Request(0), _boolCallback(callback), _remoteFileStream(stream), _localFile(dumpFile) {}
 
-bool DownloadRequest::handle() {
+void DownloadRequest::handle() {
 	if (!_remoteFileStream) {
 		warning("DownloadRequest: no stream to read");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		return true;
+		return;
 	}
 
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		return true;
+		return;
 	}
 
 	if (!_localFile->isOpen()) {
 		warning("DownloadRequest: failed to open file to write");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		return true;
+		return;
 	}
 
 	const int kBufSize = 640 * 1024; //640 KB is enough to everyone?..
@@ -58,7 +58,7 @@ bool DownloadRequest::handle() {
 			warning("DownloadRequest: unable to write all received bytes into output file");
 			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 			if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
-			return true;
+			return;
 		}
 
 	if (_remoteFileStream->eos()) {
@@ -70,11 +70,8 @@ bool DownloadRequest::handle() {
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, _remoteFileStream->httpResponseCode() == 200));
 
-		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
-		return true;
+		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()		
 	}
-
-	return false;
 }
 
 void DownloadRequest::restart() {
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index c156410..724cf19 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -39,7 +39,7 @@ public:
 	DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile);
 	virtual ~DownloadRequest() { delete _localFile; }
 
-	virtual bool handle();
+	virtual void handle();
 	virtual void restart();
 };
 
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index be93040..31f015a 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -112,13 +112,11 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestDataPair p
 	delete json;
 }
 
-bool DropboxListDirectoryRequest::handle() {
-	if (_complete && _filesCallback) {
+void DropboxListDirectoryRequest::handle() {
+	if (_complete) {
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
 		if (_filesCallback) (*_filesCallback)(Storage::RequestFileArrayPair(_id, _files));
 	}
-
-	return _complete;
 }
 
 void DropboxListDirectoryRequest::restart() {
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 58f3dc6..afa544d 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -47,7 +47,7 @@ public:
 	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
 	virtual ~DropboxListDirectoryRequest() { delete _filesCallback; }
 
-	virtual bool handle();
+	virtual void handle();
 	virtual void restart();
 };
 
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 11eeb29..fd3d631 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -54,7 +54,7 @@ char *CurlJsonRequest::getPreparedContents() {
 	return (char *)result;
 }
 
-bool CurlJsonRequest::handle() {
+void CurlJsonRequest::handle() {
 	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);
 
 	if (_stream) {
@@ -77,11 +77,8 @@ bool CurlJsonRequest::handle() {
 				Common::JSONValue *json = Common::JSON::parse(contents);				
 				(*_callback)(RequestDataPair(_id, json)); //potential memory leak, free it in your callbacks!
 			}
-			return true;
 		}
 	}
-
-	return false;
 }
 
 } //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 9b23cd7..3d5dd78 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -40,7 +40,7 @@ public:
 	CurlJsonRequest(DataCallback cb, const char *url); //TODO: use some Callback<JSON> already
 	virtual ~CurlJsonRequest();
 
-	virtual bool handle();
+	virtual void handle();
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 6f5c612..e30b7ce 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -37,17 +37,14 @@ CurlRequest::~CurlRequest() {
 	if (_stream) delete _stream;
 }
 
-bool CurlRequest::handle() {
+void CurlRequest::handle() {
 	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);	
 
 	if (_stream && _stream->eos()) {		
 		if (_stream->httpResponseCode() != 200)
 			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		return true;
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;		
 	}
-
-	return false;
 }
 
 void CurlRequest::restart() {
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index c7f07fc..1a644e4 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -43,7 +43,7 @@ public:
 	CurlRequest(DataCallback cb, const char *url);
 	virtual ~CurlRequest();
 
-	virtual bool handle();
+	virtual void handle();
 	virtual void restart();
 
 	void addHeader(Common::String header);
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index f2c2f1f..d81fe90 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -55,11 +55,9 @@ public:
 
 	/**
 	* Method, which does actual work. Depends on what this Request is doing.
-	*
-	* @return true if request's work is complete and it may be removed from Storage's list
 	*/
 
-	virtual bool handle() = 0;
+	virtual void handle() = 0;
 
 	virtual void restart() = 0;
 


Commit: b246c17850687e7b15b644b761fbfe835ffc1c32
    https://github.com/scummvm/scummvm/commit/b246c17850687e7b15b644b761fbfe835ffc1c32
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CurlJsonRequest to use JsonCallback

Type safety first.

Changed paths:
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 31f015a..3158149 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -40,7 +40,7 @@ void DropboxListDirectoryRequest::startupWork() {
 	_files.clear();
 	_complete = false;
 
-	Networking::DataCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestDataPair>(this, &DropboxListDirectoryRequest::responseCallback);
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
@@ -58,8 +58,8 @@ void DropboxListDirectoryRequest::startupWork() {
 }
 
 
-void DropboxListDirectoryRequest::responseCallback(Networking::RequestDataPair pair) {
-	Common::JSONValue *json = (Common::JSONValue *)pair.value;
+void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair pair) {
+	Common::JSONValue *json = pair.value;
 	if (json) {
 		Common::JSONObject response = json->asObject();
 		
@@ -89,7 +89,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestDataPair p
 		bool hasMore = response.getVal("has_more")->asBool();
 
 		if (hasMore) {
-			Networking::DataCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestDataPair>(this, &DropboxListDirectoryRequest::responseCallback);
+			Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback);
 			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
 			request->addHeader("Authorization: Bearer " + _token);
 			request->addHeader("Content-Type: application/json");
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index afa544d..9a82ef7 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -26,6 +26,7 @@
 #include "backends/cloud/storage.h"
 #include "backends/networking/curl/request.h"
 #include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -40,7 +41,7 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Common::Array<StorageFile> _files;
 	int32 _requestId;
 
-	void responseCallback(Networking::RequestDataPair pair);
+	void responseCallback(Networking::RequestJsonPair pair);
 	void startupWork();
 
 public:
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 02b033f..47576d7 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -38,7 +38,7 @@ namespace Dropbox {
 Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
 
-static void saveAccessTokenCallback(Networking::RequestDataPair pair) {
+static void saveAccessTokenCallback(Networking::RequestJsonPair pair) {
 	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (json) {
 		debug("saveAccessTokenCallback:");
@@ -121,7 +121,7 @@ int32 DropboxStorage::syncSaves(BoolCallback callback) {
 }
 
 int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
-	Networking::DataCallback innerCallback = new Common::CallbackBridge<DropboxStorage, RequestStorageInfoPair, Networking::RequestDataPair>(this, &DropboxStorage::infoInnerCallback, outerCallback);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, RequestStorageInfoPair, Networking::RequestJsonPair>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
@@ -131,8 +131,8 @@ int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestDataPair pair) {
-	Common::JSONValue *json = (Common::JSONValue *)pair.value;
+void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair pair) {
+	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		delete outerCallback;
@@ -214,7 +214,7 @@ void DropboxStorage::authThroughConsole() {
 }
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	Networking::DataCallback callback = new Common::GlobalFunctionCallback<Networking::RequestDataPair>(saveAccessTokenCallback);
+	Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::RequestJsonPair>(saveAccessTokenCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 4fe6109..4f1e6cd 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -25,6 +25,7 @@
 
 #include "backends/cloud/storage.h"
 #include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -40,7 +41,7 @@ class DropboxStorage: public Cloud::Storage {
 	static void getAccessToken(Common::String code);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
-	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestDataPair json);
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair json);
 
 	void printFiles(Common::Array<StorageFile> files);
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 36b3e26..833ba8e 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -56,7 +56,7 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 		return;
 	}
 
-	Networking::DataCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestBoolPair, Networking::RequestDataPair>(this, &OneDriveStorage::tokenRefreshed, callback);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestBoolPair, Networking::RequestJsonPair>(this, &OneDriveStorage::tokenRefreshed, callback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
 	if (codeFlow) {
 		request->addPostField("code=" + code);
@@ -71,8 +71,8 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	ConnMan.addRequest(request);
 }
 
-void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestDataPair pair) {
-	Common::JSONValue *json = (Common::JSONValue *)pair.value;
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair) {
+	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("OneDriveStorage: got NULL instead of JSON");
 		if (callback) (*callback)(RequestBoolPair(-1, false));
@@ -117,8 +117,8 @@ void OneDriveStorage::printJsonTokenReceived(RequestBoolPair pair) {
 	if (pair.value) syncSaves(0); //try again
 }
 
-void OneDriveStorage::printJson(Networking::RequestDataPair pair) {
-	Common::JSONValue *json = (Common::JSONValue *)pair.value;
+void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
+	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("printJson: NULL");
 		return;
@@ -139,7 +139,7 @@ void OneDriveStorage::printJson(Networking::RequestDataPair pair) {
 
 int32 OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
-	Networking::DataCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestDataPair>(this, &OneDriveStorage::printJson);
+	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");	
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 3c92880..1cb7017 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -25,6 +25,7 @@
 
 #include "backends/cloud/storage.h"
 #include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
@@ -49,10 +50,10 @@ class OneDriveStorage: public Cloud::Storage {
 	* continue your work when new token is available.
 	*/
 	void getAccessToken(BoolCallback callback, Common::String code = "");	
-	void tokenRefreshed(BoolCallback callback, Networking::RequestDataPair pair);
+	void tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair);
 	void codeFlowComplete(RequestBoolPair pair);
 
-	void printJson(Networking::RequestDataPair pair);
+	void printJson(Networking::RequestJsonPair pair);
 	void printJsonTokenReceived(RequestBoolPair pair);
 public:	
 	virtual ~OneDriveStorage();
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index fd3d631..a323b34 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,8 +31,8 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(DataCallback cb, const char *url):
-	CurlRequest(cb, url), _contentsStream(DisposeAfterUse::YES) {}
+CurlJsonRequest::CurlJsonRequest(JsonCallback cb, const char *url):
+	CurlRequest(0, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
 
 CurlJsonRequest::~CurlJsonRequest() {}
 
@@ -70,12 +70,12 @@ void CurlJsonRequest::handle() {
 				warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 
 			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-			if (_callback) {
+			if (_jsonCallback) {
 				char *contents = getPreparedContents();
 				if (_stream->httpResponseCode() != 200)
 					debug("%s", contents);
 				Common::JSONValue *json = Common::JSON::parse(contents);				
-				(*_callback)(RequestDataPair(_id, json)); //potential memory leak, free it in your callbacks!
+				(*_jsonCallback)(RequestJsonPair(_id, json)); //potential memory leak, free it in your callbacks!
 			}
 		}
 	}
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 3d5dd78..75d4a6d 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -25,19 +25,24 @@
 
 #include "backends/networking/curl/curlrequest.h"
 #include "common/memstream.h"
+#include "common/json.h"
 
 namespace Networking {
 
 class NetworkReadStream;
 
-class CurlJsonRequest: public CurlRequest {	
+typedef RequestIdPair<Common::JSONValue*> RequestJsonPair;
+typedef Common::BaseCallback<RequestJsonPair> *JsonCallback;
+
+class CurlJsonRequest: public CurlRequest {
+	JsonCallback _jsonCallback;
 	Common::MemoryWriteStreamDynamic _contentsStream;
 
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(DataCallback cb, const char *url); //TODO: use some Callback<JSON> already
+	CurlJsonRequest(JsonCallback cb, const char *url);
 	virtual ~CurlJsonRequest();
 
 	virtual void handle();


Commit: 8f6bcdf55da97db98384c2a8cb9dcdf34232ac35
    https://github.com/scummvm/scummvm/commit/8f6bcdf55da97db98384c2a8cb9dcdf34232ac35
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveTokenRefresher

OneDriveTokenRefresher is a CurlJsonRequest replacement for
OneDriveStorage methods. It behaves very similarly, but checks received
JSON before passing it to user. If it contains "error" key, it attempts
to refresh the token through OneDriveStorage, and then restarts the
original request, so user won't notice that there ever was an error.

Changed paths:
  A backends/cloud/onedrive/onedrivetokenrefresher.cpp
  A backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/module.mk
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h



diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 833ba8e..6f91cad 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -31,6 +31,7 @@
 #include <common/file.h>
 #include "common/system.h"
 #include "common/cloudmanager.h"
+#include "onedrivetokenrefresher.h"
 
 namespace Cloud {
 namespace OneDrive {
@@ -113,10 +114,6 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
-void OneDriveStorage::printJsonTokenReceived(RequestBoolPair pair) {
-	if (pair.value) syncSaves(0); //try again
-}
-
 void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 	Common::JSONValue *json = pair.value;
 	if (!json) {
@@ -124,15 +121,6 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 		return;
 	}
 
-	Common::JSONObject result = json->asObject();
-	if (result.contains("error")) {
-		//Common::JSONObject error = result.getVal("error")->asObject();
-		debug("bad token, trying again...");
-		getAccessToken(new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::printJsonTokenReceived));
-		delete json;
-		return;
-	}
-
 	debug("%s", json->stringify().c_str());
 	delete json;
 }
@@ -140,7 +128,7 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 int32 OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/");	
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, "https://api.onedrive.com/v1.0/drives/");	
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 }
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 1cb7017..e120691 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -44,12 +44,6 @@ class OneDriveStorage: public Cloud::Storage {
 	*/
 	OneDriveStorage(Common::String code);
 
-	/**
-	* Gets new access_token. If <code> passed is "", refresh_token is used.
-	* Use "" in order to refresh token and pass a callback, so you could
-	* continue your work when new token is available.
-	*/
-	void getAccessToken(BoolCallback callback, Common::String code = "");	
 	void tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair);
 	void codeFlowComplete(RequestBoolPair pair);
 
@@ -122,6 +116,15 @@ public:
 	* Show message with OneDrive auth instructions. (Temporary)
 	*/
 	static void authThroughConsole();
+
+	/**
+	* Gets new access_token. If <code> passed is "", refresh_token is used.
+	* Use "" in order to refresh token and pass a callback, so you could
+	* continue your work when new token is available.
+	*/
+	void getAccessToken(BoolCallback callback, Common::String code = "");
+
+	Common::String accessToken() { return _token; }
 };
 
 } //end of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
new file mode 100644
index 0000000..5e72717
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -0,0 +1,124 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace OneDrive {
+
+OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url):
+	CurlJsonRequest(0, url),
+	_parentStorage(parent),
+	_innerRequest(
+		new CurlJsonRequest(
+			new Common::Callback<OneDriveTokenRefresher, Networking::RequestJsonPair>(this, &OneDriveTokenRefresher::innerRequestCallback),
+			url
+		)
+	), _jsonCallback(callback), _retryId(-1), _started(false) {}
+
+OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
+
+void OneDriveTokenRefresher::innerRequestCallback(Networking::RequestJsonPair pair) {
+	if (!pair.value) {
+		//notify user of failure
+		warning("OneDriveTokenRefresher: got NULL instead of JSON");
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+		return;
+	}
+
+	Common::JSONObject result = pair.value->asObject();
+	if (result.contains("error")) {
+		//new token needed => request token & then retry original request		
+		ConnMan.getRequestInfo(pair.id).state = Networking::PAUSED;
+		_retryId = pair.id;	
+		delete pair.value;
+		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::RequestBoolPair>(this, &OneDriveTokenRefresher::tokenRefreshed));
+		return;
+	}
+
+	//notify user of success	
+	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+	if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, pair.value));
+}
+
+void OneDriveTokenRefresher::tokenRefreshed(Storage::RequestBoolPair pair) {
+	if (!pair.value) {
+		//failed to refresh token, notify user with NULL in original callback
+		warning("OneDriveTokenRefresher: failed to refresh token");
+		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+		return;
+	}
+
+	//successfully received refreshed token, can restart the original request now	
+	Networking::RequestInfo &info = ConnMan.getRequestInfo(_retryId);
+	info.state = Networking::RETRY;
+	info.retryInSeconds = 1;
+
+	//update headers: first change header with token, then pass those to request
+	for (uint32 i = 0; i < _headers.size(); ++i) {
+		if (_headers[i].contains("Authorization: bearer ")) {			
+			_headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
+		}
+	}
+	CurlJsonRequest *retryRequest = (CurlJsonRequest *)info.request;
+	if (retryRequest) retryRequest->setHeaders(_headers);
+}
+
+void OneDriveTokenRefresher::handle() {
+	if (!_started) {
+		for (uint32 i = 0; i < _headers.size(); ++i)
+			_innerRequest->addHeader(_headers[i]);
+		_started = true;
+		ConnMan.addRequest(_innerRequest);
+	}
+}
+
+void OneDriveTokenRefresher::restart() {
+	//can't restart as all headers were passed to _innerRequest which is probably dead now
+	warning("OneDriveTokenRefresher: cannot be restarted");
+	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+	if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+}
+
+Networking::NetworkReadStream *OneDriveTokenRefresher::execute() {
+	if (!_started) {
+		for (uint32 i = 0; i < _headers.size(); ++i)
+			_innerRequest->addHeader(_headers[i]);
+		_started = true;
+	} else {
+		warning("OneDriveTokenRefresher: inner Request is already started");
+	}
+	return _innerRequest->execute();
+}
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
new file mode 100644
index 0000000..9768512
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -0,0 +1,61 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
+
+#include "backends/cloud/storage.h"
+#include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage;
+
+class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
+	OneDriveStorage *_parentStorage;
+	Common::Array<Common::String> _headers;
+	Networking::CurlJsonRequest *_innerRequest;
+	Networking::JsonCallback _jsonCallback;
+	int32 _retryId;
+	bool _started;
+
+	void innerRequestCallback(Networking::RequestJsonPair pair);
+	void tokenRefreshed(Storage::RequestBoolPair pair);
+public:	
+	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url);
+	virtual ~OneDriveTokenRefresher();
+
+	virtual void handle();
+	virtual void restart();
+
+	virtual void setHeaders(Common::Array<Common::String> &headers) { _headers = headers; }
+	virtual void addHeader(Common::String header) { _headers.push_back(header); }
+	virtual void addPostField(Common::String field) { _innerRequest->addPostField(field); }
+	virtual Networking::NetworkReadStream *execute();
+};
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index c8842ca..5ec3419 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -27,7 +27,8 @@ MODULE_OBJS += \
 	cloud/downloadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
-	cloud/onedrive/onedrivestorage.o
+	cloud/onedrive/onedrivestorage.o \
+	cloud/onedrive/onedrivetokenrefresher.o
 endif
 
 ifdef USE_LIBCURL
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index b448d8e..77a46ec 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -76,6 +76,7 @@ void ConnectionManager::startTimer(int interval) {
 }
 
 void ConnectionManager::stopTimer() {
+	debug("timer stopped");
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(connectionsThread);
 	_timerStarted = false;
@@ -111,6 +112,7 @@ void ConnectionManager::interateRequests() {
 			else {
 				info.state = PROCESSING;
 				info.request->restart();
+				debug("request restarted");
 			}
 
 		default:
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index a323b34..1231f9e 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -74,11 +74,18 @@ void CurlJsonRequest::handle() {
 				char *contents = getPreparedContents();
 				if (_stream->httpResponseCode() != 200)
 					debug("%s", contents);
-				Common::JSONValue *json = Common::JSON::parse(contents);				
+				Common::JSONValue *json = Common::JSON::parse(contents);
 				(*_jsonCallback)(RequestJsonPair(_id, json)); //potential memory leak, free it in your callbacks!
 			}
 		}
 	}
 }
 
+void CurlJsonRequest::restart() {
+	if (_stream) delete _stream;
+	_stream = 0;
+	_contentsStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
+	//with no stream available next handle() will create another one
+}
+
 } //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 75d4a6d..af19ec3 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -45,7 +45,8 @@ public:
 	CurlJsonRequest(JsonCallback cb, const char *url);
 	virtual ~CurlJsonRequest();
 
-	virtual void handle();
+	virtual void handle(); 
+	virtual void restart();
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index e30b7ce..1b42ac5 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -53,6 +53,13 @@ void CurlRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
+void CurlRequest::setHeaders(Common::Array<Common::String> &headers) {
+	curl_slist_free_all(_headersList);
+	_headersList = 0;
+	for (uint32 i = 0; i < headers.size(); ++i)
+		addHeader(headers[i]);
+}
+
 void CurlRequest::addHeader(Common::String header) {
 	_headersList = curl_slist_append(_headersList, header.c_str());
 }
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 1a644e4..c624194 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -25,6 +25,7 @@
 
 #include "backends/networking/curl/request.h"
 #include "common/str.h"
+#include <common/array.h>
 
 struct curl_slist;
 
@@ -46,11 +47,12 @@ public:
 	virtual void handle();
 	virtual void restart();
 
-	void addHeader(Common::String header);
-	void addPostField(Common::String header);
+	virtual void setHeaders(Common::Array<Common::String> &headers);
+	virtual void addHeader(Common::String header);
+	virtual void addPostField(Common::String field);
 
 	/** Start this Request with ConnMan. Returns its ReadStream. */
-	NetworkReadStream *execute();
+	virtual NetworkReadStream *execute();
 };
 
 }  //end of namespace Networking


Commit: 24007c029b53a5f4502ee1c48c5244b8cf8099ce
    https://github.com/scummvm/scummvm/commit/24007c029b53a5f4502ee1c48c5244b8cf8099ce
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveStorage::download()

Doesn't work when token is invalid, though.

Changed paths:
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 6f91cad..877a1d2 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -22,16 +22,17 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/cloud/downloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "common/cloudmanager.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
+#include "common/file.h"
 #include "common/json.h"
-#include <curl/curl.h>
-#include <common/file.h>
 #include "common/system.h"
-#include "common/cloudmanager.h"
-#include "onedrivetokenrefresher.h"
+#include <curl/curl.h>
 
 namespace Cloud {
 namespace OneDrive {
@@ -125,12 +126,42 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 	delete json;
 }
 
+Networking::NetworkReadStream *OneDriveStorage::streamFile(Common::String path) {
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path + ":/content";
+	//NOT USING OneDriveTokenRefresher, because it's CurlJsonRequest, which saves all contents in memory to parse as JSON
+	//we actually don't even need a token if the download is "pre-authenticated" (whatever it means)
+	//still, we'd have to know direct URL (might be found in Item's "@content.downloadUrl", received from the server)
+	Networking::CurlRequest *request = new Networking::CurlRequest(0, url.c_str());	
+	request->addHeader("Authorization: Bearer " + _token);
+	return request->execute();
+}
+
+int32 OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+	Common::DumpFile *f = new Common::DumpFile();
+	if (!f->open(localPath, true)) {
+		warning("OneDriveStorage: unable to open file to download into");
+		if (callback) (*callback)(RequestBoolPair(-1, false));
+		delete f;
+		return -1;
+	}
+
+	return ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
+}
+
+void OneDriveStorage::fileDownloaded(RequestBoolPair pair) {
+	if (pair.value) debug("file downloaded!");
+	else debug("download failed!");
+}
+
 int32 OneDriveStorage::syncSaves(BoolCallback callback) {
-	//this is not the real syncSaves() implementation	
+	//this is not the real syncSaves() implementation
+	/*
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, "https://api.onedrive.com/v1.0/drives/");	
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
+	*/
+	return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::fileDownloaded));
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index e120691..391cabe 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -48,7 +48,7 @@ class OneDriveStorage: public Cloud::Storage {
 	void codeFlowComplete(RequestBoolPair pair);
 
 	void printJson(Networking::RequestJsonPair pair);
-	void printJsonTokenReceived(RequestBoolPair pair);
+	void fileDownloaded(RequestBoolPair pair);
 public:	
 	virtual ~OneDriveStorage();
 
@@ -75,10 +75,10 @@ public:
 	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::NetworkReadStream *streamFile(Common::String path) { return 0; } //TODO
+	virtual int32 streamFile(Common::String path);
 
 	/** Calls the callback when finished. */
-	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) { return -1; } //TODO
+	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Calls the callback when finished. */
 	virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 77a46ec..9d88c59 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -34,7 +34,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _nextId(0) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 9a196b9..39316de 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -45,6 +45,7 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
 	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
 	if (postFields.size() != 0) {
 		curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());


Commit: 83b349a033d71e92e292d1f1da0578d557ec6411
    https://github.com/scummvm/scummvm/commit/83b349a033d71e92e292d1f1da0578d557ec6411
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make OneDriveStorage::download() work fine

Well, it takes two API calls instead of one now, but there are no
problems with expired token because of it.

This commit changes Storage::streamFile() to pass NetworkReadStream *
through callback.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/storage.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 756a904..661d6fd 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -27,25 +27,39 @@
 
 namespace Cloud {
 
-DownloadRequest::DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile):
-	Request(0), _boolCallback(callback), _remoteFileStream(stream), _localFile(dumpFile) {}
+DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile):
+	Request(0), _boolCallback(callback), _remoteFileStream(0), _localFile(dumpFile) {
+	storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Storage::RequestReadStreamPair>(this, &DownloadRequest::streamCallback));
+}
 
-void DownloadRequest::handle() {
-	if (!_remoteFileStream) {
-		warning("DownloadRequest: no stream to read");
+void DownloadRequest::streamCallback(Storage::RequestReadStreamPair pair) {
+	if (!pair.value) {
+		warning("DownloadRequest: no ReadStream passed");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
 		return;
 	}
 
+	_remoteFileStream = (Networking::NetworkReadStream *)pair.value;
+}
+
+void DownloadRequest::handle() {	
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
 		return;
 	}
 
 	if (!_localFile->isOpen()) {
 		warning("DownloadRequest: failed to open file to write");
 		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
+		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+		return;
+	}
+
+	if (!_remoteFileStream) {
+		//waiting for callback
 		return;
 	}
 
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 724cf19..181536b 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -35,8 +35,9 @@ class DownloadRequest: public Networking::Request {
 	Networking::NetworkReadStream *_remoteFileStream;
 	Common::DumpFile *_localFile;	
 
+	void streamCallback(Storage::RequestReadStreamPair pair);
 public:
-	DownloadRequest(Storage::BoolCallback callback, Networking::NetworkReadStream *stream, Common::DumpFile *dumpFile);
+	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile);
 	virtual ~DownloadRequest() { delete _localFile; }
 
 	virtual void handle();
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 47576d7..b5292c8 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -88,7 +88,7 @@ int32 DropboxStorage::listDirectory(Common::String path, FileArrayCallback outer
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
-Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
+int32 DropboxStorage::streamFile(Common::String path, ReadStreamCallback callback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
@@ -98,7 +98,9 @@ Networking::NetworkReadStream *DropboxStorage::streamFile(Common::String path) {
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
 
-	return request->execute();
+	RequestReadStreamPair pair = request->execute();
+	if (callback) (*callback)(pair);
+	return pair.id;
 }
 
 int32 DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
@@ -110,7 +112,7 @@ int32 DropboxStorage::download(Common::String remotePath, Common::String localPa
 		return -1;
 	}
 
-	return ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
+	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
 int32 DropboxStorage::syncSaves(BoolCallback callback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 4f1e6cd..dd082b2 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -71,7 +71,7 @@ public:
 	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::NetworkReadStream *streamFile(Common::String path);
+	virtual int32 streamFile(Common::String path, ReadStreamCallback callback);
 
 	/** Calls the callback when finished. */
 	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 877a1d2..237557d 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -126,14 +126,35 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 	delete json;
 }
 
-Networking::NetworkReadStream *OneDriveStorage::streamFile(Common::String path) {
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path + ":/content";
-	//NOT USING OneDriveTokenRefresher, because it's CurlJsonRequest, which saves all contents in memory to parse as JSON
-	//we actually don't even need a token if the download is "pre-authenticated" (whatever it means)
-	//still, we'd have to know direct URL (might be found in Item's "@content.downloadUrl", received from the server)
-	Networking::CurlRequest *request = new Networking::CurlRequest(0, url.c_str());	
+void OneDriveStorage::fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair) {
+	if (!pair.value) {
+		warning("fileInfoCallback: NULL");
+		if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0));
+		return;
+	}
+
+	Common::JSONObject result = pair.value->asObject();
+	if (result.contains("@content.downloadUrl")) {
+		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
+		if (outerCallback)
+			(*outerCallback)(RequestReadStreamPair(
+				pair.id,
+				new Networking::NetworkReadStream(url, 0, "")
+			));
+	} else {
+		warning("downloadUrl not found in passed JSON");
+		debug("%s", pair.value->stringify().c_str());
+		if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0));
+	}
+	delete pair.value;
+}
+
+int32 OneDriveStorage::streamFile(Common::String path, ReadStreamCallback outerCallback) {
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path + ":/";	
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestReadStreamPair, Networking::RequestJsonPair>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
-	return request->execute();
+	return ConnMan.addRequest(request);
 }
 
 int32 OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
@@ -145,7 +166,7 @@ int32 OneDriveStorage::download(Common::String remotePath, Common::String localP
 		return -1;
 	}
 
-	return ConnMan.addRequest(new DownloadRequest(callback, streamFile(remotePath), f));
+	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
 void OneDriveStorage::fileDownloaded(RequestBoolPair pair) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 391cabe..be2bcdf 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -49,6 +49,8 @@ class OneDriveStorage: public Cloud::Storage {
 
 	void printJson(Networking::RequestJsonPair pair);
 	void fileDownloaded(RequestBoolPair pair);
+
+	void fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair);
 public:	
 	virtual ~OneDriveStorage();
 
@@ -75,7 +77,7 @@ public:
 	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual int32 streamFile(Common::String path);
+	virtual int32 streamFile(Common::String path, ReadStreamCallback callback);
 
 	/** Calls the callback when finished. */
 	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 5e72717..d3ec0cc 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -109,7 +109,7 @@ void OneDriveTokenRefresher::restart() {
 	if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
 }
 
-Networking::NetworkReadStream *OneDriveTokenRefresher::execute() {
+Cloud::Storage::RequestReadStreamPair OneDriveTokenRefresher::execute() {
 	if (!_started) {
 		for (uint32 i = 0; i < _headers.size(); ++i)
 			_innerRequest->addHeader(_headers[i]);
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 9768512..58b7dce 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -52,7 +52,7 @@ public:
 	virtual void setHeaders(Common::Array<Common::String> &headers) { _headers = headers; }
 	virtual void addHeader(Common::String header) { _headers.push_back(header); }
 	virtual void addPostField(Common::String field) { _innerRequest->addPostField(field); }
-	virtual Networking::NetworkReadStream *execute();
+	virtual Cloud::Storage::RequestReadStreamPair execute();
 };
 
 } //end of namespace OneDrive
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 325d57d..0114b46 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -37,7 +37,7 @@ namespace Cloud {
 class Storage {
 public:
 	typedef Networking::RequestIdPair<Common::Array<StorageFile>&> RequestFileArrayPair;
-	typedef Networking::RequestIdPair<Common::ReadStream *> RequestReadStreamPair;
+	typedef Networking::RequestIdPair<Networking::NetworkReadStream *> RequestReadStreamPair;
 	typedef Networking::RequestIdPair<StorageInfo> RequestStorageInfoPair;
 	typedef Networking::RequestIdPair<bool> RequestBoolPair;	
 
@@ -78,7 +78,7 @@ public:
 	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::NetworkReadStream *streamFile(Common::String path) = 0; //TODO: return int32 somehow
+	virtual int32 streamFile(Common::String path, ReadStreamCallback callback) = 0;
 
 	/** Calls the callback when finished. */
 	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 1231f9e..326d8e2 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(JsonCallback cb, const char *url):
+CurlJsonRequest::CurlJsonRequest(JsonCallback cb, Common::String url):
 	CurlRequest(0, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
 
 CurlJsonRequest::~CurlJsonRequest() {}
@@ -55,7 +55,7 @@ char *CurlJsonRequest::getPreparedContents() {
 }
 
 void CurlJsonRequest::handle() {
-	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);
+	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);
 
 	if (_stream) {
 		const int kBufSize = 16*1024;
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index af19ec3..5e78bd1 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -42,7 +42,7 @@ class CurlJsonRequest: public CurlRequest {
 	char *getPreparedContents();
 
 public:
-	CurlJsonRequest(JsonCallback cb, const char *url);
+	CurlJsonRequest(JsonCallback cb, Common::String url);
 	virtual ~CurlJsonRequest();
 
 	virtual void handle(); 
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 1b42ac5..f01a430 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -30,7 +30,7 @@
 
 namespace Networking {
 
-CurlRequest::CurlRequest(DataCallback cb, const char *url): 
+CurlRequest::CurlRequest(DataCallback cb, Common::String url):
 	Request(cb), _url(url), _stream(0), _headersList(0) {}
 
 CurlRequest::~CurlRequest() {
@@ -38,7 +38,7 @@ CurlRequest::~CurlRequest() {
 }
 
 void CurlRequest::handle() {
-	if (!_stream) _stream = new NetworkReadStream(_url, _headersList, _postFields);	
+	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);	
 
 	if (_stream && _stream->eos()) {		
 		if (_stream->httpResponseCode() != 200)
@@ -71,13 +71,13 @@ void CurlRequest::addPostField(Common::String keyValuePair) {
 		_postFields += "&" + keyValuePair;
 }
 
-NetworkReadStream *CurlRequest::execute() {
+Cloud::Storage::RequestReadStreamPair CurlRequest::execute() {
 	if (!_stream) {
-		_stream = new NetworkReadStream(_url, _headersList, _postFields);
+		_stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);
 		ConnMan.addRequest(this);
 	}
 
-	return _stream;
+	return Cloud::Storage::RequestReadStreamPair(_id, _stream);
 }
 
 } //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index c624194..18a41a1 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -24,8 +24,9 @@
 #define BACKENDS_NETWORKING_CURL_CURLREQUEST_H
 
 #include "backends/networking/curl/request.h"
+#include "backends/cloud/storage.h"
 #include "common/str.h"
-#include <common/array.h>
+#include "common/array.h"
 
 struct curl_slist;
 
@@ -35,13 +36,13 @@ class NetworkReadStream;
 
 class CurlRequest: public Request {
 protected:
-	const char *_url;
+	Common::String _url;
 	NetworkReadStream *_stream;
 	curl_slist *_headersList;
 	Common::String _postFields;
 
 public:
-	CurlRequest(DataCallback cb, const char *url);
+	CurlRequest(DataCallback cb, Common::String url);
 	virtual ~CurlRequest();
 
 	virtual void handle();
@@ -51,8 +52,8 @@ public:
 	virtual void addHeader(Common::String header);
 	virtual void addPostField(Common::String field);
 
-	/** Start this Request with ConnMan. Returns its ReadStream. */
-	virtual NetworkReadStream *execute();
+	/** Start this Request with ConnMan. Returns its ReadStream and request id. */
+	virtual Cloud::Storage::RequestReadStreamPair execute();
 };
 
 }  //end of namespace Networking


Commit: 98150beb38f73b56c7bc76f95dcc1d72290e4ac7
    https://github.com/scummvm/scummvm/commit/98150beb38f73b56c7bc76f95dcc1d72290e4ac7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Refactor ConnectionManager/Requests system

ConnectionManager now storages Request * (not generates ids for it),
Requests have control on their RequestState, RequestIdPair is now called
Response and storages Request * with some response together.

All related classes are changed to use it in more clean and
understandable way.

Request, RequestState and Response are carefully commented/documented.

Changed paths:
  A local/onedrive/2/doom.jpg
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.h
    backends/networking/curl/request.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 661d6fd..c7f6f75 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -29,14 +29,13 @@ namespace Cloud {
 
 DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile):
 	Request(0), _boolCallback(callback), _remoteFileStream(0), _localFile(dumpFile) {
-	storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Storage::RequestReadStreamPair>(this, &DownloadRequest::streamCallback));
+	storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback));
 }
 
-void DownloadRequest::streamCallback(Storage::RequestReadStreamPair pair) {
+void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse pair) {
 	if (!pair.value) {
 		warning("DownloadRequest: no ReadStream passed");
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+		finish();
 		return;
 	}
 
@@ -46,15 +45,13 @@ void DownloadRequest::streamCallback(Storage::RequestReadStreamPair pair) {
 void DownloadRequest::handle() {	
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+		finish();
 		return;
 	}
 
 	if (!_localFile->isOpen()) {
 		warning("DownloadRequest: failed to open file to write");
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+		finish();
 		return;
 	}
 
@@ -70,8 +67,7 @@ void DownloadRequest::handle() {
 	if (readBytes != 0)
 		if (_localFile->write(buf, readBytes) != readBytes) {
 			warning("DownloadRequest: unable to write all received bytes into output file");
-			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-			if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+			finish();
 			return;
 		}
 
@@ -81,8 +77,7 @@ void DownloadRequest::handle() {
 			//TODO: do something about it actually			
 		}
 
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, _remoteFileStream->httpResponseCode() == 200));
+		finishBool(_remoteFileStream->httpResponseCode() == 200);
 
 		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()		
 	}
@@ -92,9 +87,17 @@ void DownloadRequest::restart() {
 	//this request doesn't know anything about the _remoteFileStream it's reading
 	//thus, it can't restart it
 	warning("DownloadRequest: cannot be restarted");
-	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-	if (_boolCallback) (*_boolCallback)(Storage::RequestBoolPair(_id, false));
+	finish();
 	//TODO: fix that
 }
 
+void DownloadRequest::finish() {
+	finishBool(false);
+}
+
+void DownloadRequest::finishBool(bool success) {
+	Request::finish();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
 } //end of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 181536b..4ea8576 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -26,7 +26,7 @@
 #include "backends/networking/curl/request.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/cloud/storage.h"
-#include <common/file.h>
+#include "common/file.h"
 
 namespace Cloud {
 
@@ -35,13 +35,16 @@ class DownloadRequest: public Networking::Request {
 	Networking::NetworkReadStream *_remoteFileStream;
 	Common::DumpFile *_localFile;	
 
-	void streamCallback(Storage::RequestReadStreamPair pair);
+	void streamCallback(Networking::NetworkReadStreamResponse pair);
+
+	void finishBool(bool success);
 public:
 	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile);
 	virtual ~DownloadRequest() { delete _localFile; }
 
 	virtual void handle();
 	virtual void restart();
+	virtual void finish();
 };
 
 } //end of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 3158149..04fbf46 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -32,7 +32,7 @@ namespace Dropbox {
 
 DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive):
 	Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _filesCallback(cb),
-	_token(token), _complete(false), _requestId(-1) {
+	_token(token), _complete(false), _innerRequest(nullptr) {
 	startupWork();
 }
 
@@ -40,7 +40,7 @@ void DropboxListDirectoryRequest::startupWork() {
 	_files.clear();
 	_complete = false;
 
-	Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback);
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
@@ -54,11 +54,11 @@ void DropboxListDirectoryRequest::startupWork() {
 	Common::JSONValue value(jsonRequestParameters);
 	request->addPostField(Common::JSON::stringify(&value));
 
-	_requestId = ConnMan.addRequest(request);
+	_innerRequest = ConnMan.addRequest(request);
 }
 
 
-void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair pair) {
+void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair) {
 	Common::JSONValue *json = pair.value;
 	if (json) {
 		Common::JSONObject response = json->asObject();
@@ -89,7 +89,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair p
 		bool hasMore = response.getVal("has_more")->asBool();
 
 		if (hasMore) {
-			Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::RequestJsonPair>(this, &DropboxListDirectoryRequest::responseCallback);
+			Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
 			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
 			request->addHeader("Authorization: Bearer " + _token);
 			request->addHeader("Content-Type: application/json");
@@ -113,22 +113,28 @@ void DropboxListDirectoryRequest::responseCallback(Networking::RequestJsonPair p
 }
 
 void DropboxListDirectoryRequest::handle() {
-	if (_complete) {
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_filesCallback) (*_filesCallback)(Storage::RequestFileArrayPair(_id, _files));
-	}
+	if (_complete) finishFiles(_files);	
 }
 
 void DropboxListDirectoryRequest::restart() {
-	if (_requestId != -1) {
-		Networking::RequestInfo &info = ConnMan.getRequestInfo(_requestId);
+	if (_innerRequest) {		
 		//TODO: I'm really not sure some CurlRequest would handle this (it must stop corresponding CURL transfer)
-		info.state = Networking::FINISHED; //may be CANCELED or INTERRUPTED or something?
-		_requestId = -1;
+		_innerRequest->finish(); //may be CANCELED or INTERRUPTED or something?
+		_innerRequest = nullptr;
 	}
 
 	startupWork();
 }
 
+void DropboxListDirectoryRequest::finish() {
+	Common::Array<StorageFile> files;
+	finishFiles(files);
+}
+
+void DropboxListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files) {
+	Request::finish();
+	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
+}
+
 } //end of namespace Dropbox
 } //end of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 9a82ef7..e2a9ebf 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -39,17 +39,20 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Common::String _token;
 	bool _complete;
 	Common::Array<StorageFile> _files;
-	int32 _requestId;
+	Request *_innerRequest;
 
-	void responseCallback(Networking::RequestJsonPair pair);
+	void responseCallback(Networking::JsonResponse pair);
 	void startupWork();
 
+	void finishFiles(Common::Array<StorageFile> &files);
+
 public:
 	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
 	virtual ~DropboxListDirectoryRequest() { delete _filesCallback; }
 
 	virtual void handle();
 	virtual void restart();
+	virtual void finish();
 };
 
 } //end of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b5292c8..a1a97e0 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -38,7 +38,7 @@ namespace Dropbox {
 Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
 Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
 
-static void saveAccessTokenCallback(Networking::RequestJsonPair pair) {
+static void saveAccessTokenCallback(Networking::JsonResponse pair) {
 	Common::JSONValue *json = (Common::JSONValue *)pair.value;
 	if (json) {
 		debug("saveAccessTokenCallback:");
@@ -55,6 +55,7 @@ static void saveAccessTokenCallback(Networking::RequestJsonPair pair) {
 			ConfMan.set("storage1_access_token", result.getVal("access_token")->asString(), "cloud");
 			ConfMan.set("storage1_user_id", result.getVal("uid")->asString(), "cloud");
 			ConfMan.removeKey("dropbox_code", "cloud");
+			ConfMan.flushToDisk();
 			debug("Now please restart ScummVM to apply the changes.");
 		}
 
@@ -84,11 +85,11 @@ void DropboxStorage::printFiles(Common::Array<StorageFile> files) {
 		debug("\t%s", files[i].name().c_str());
 }
 
-int32 DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
+Networking::Request *DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
-int32 DropboxStorage::streamFile(Common::String path, ReadStreamCallback callback) {
+Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
@@ -98,32 +99,32 @@ int32 DropboxStorage::streamFile(Common::String path, ReadStreamCallback callbac
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
 
-	RequestReadStreamPair pair = request->execute();
+	Networking::NetworkReadStreamResponse pair = request->execute();
 	if (callback) (*callback)(pair);
-	return pair.id;
+	return pair.request;
 }
 
-int32 DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("DropboxStorage: unable to open file to download into");
-		if (callback) (*callback)(RequestBoolPair(-1, false));
+		if (callback) (*callback)(BoolResponse(nullptr, false));
 		delete f;
-		return -1;
+		return nullptr;
 	}
 
 	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
-int32 DropboxStorage::syncSaves(BoolCallback callback) {
+Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
 	//this must create all these directories:
 	return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
 }
 
-int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, RequestStorageInfoPair, Networking::RequestJsonPair>(this, &DropboxStorage::infoInnerCallback, outerCallback);
+Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
@@ -133,7 +134,7 @@ int32 DropboxStorage::info(StorageInfoCallback outerCallback) {
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair pair) {
+void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse pair) {
 	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("NULL passed instead of JSON");
@@ -151,14 +152,14 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 		uint32 quotaNormal = quota.getVal("normal")->asNumber();
 		uint32 quotaShared = quota.getVal("shared")->asNumber();
 		uint32 quotaAllocated = quota.getVal("quota")->asNumber();
-		(*outerCallback)(RequestStorageInfoPair(-1, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
+		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
 	
 	delete json;
 }
 
-void DropboxStorage::infoMethodCallback(RequestStorageInfoPair pair) {
+void DropboxStorage::infoMethodCallback(StorageInfoResponse pair) {
 	debug("\nStorage info:");
 	debug("User name: %s", pair.value.name().c_str());
 	debug("Email: %s", pair.value.email().c_str());
@@ -216,7 +217,7 @@ void DropboxStorage::authThroughConsole() {
 }
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::RequestJsonPair>(saveAccessTokenCallback);
+	Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::JsonResponse>(saveAccessTokenCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index dd082b2..f95d0c9 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -41,7 +41,7 @@ class DropboxStorage: public Cloud::Storage {
 	static void getAccessToken(Common::String code);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
-	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::RequestJsonPair json);
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
 	void printFiles(Common::Array<StorageFile> files);
 
@@ -65,34 +65,34 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
+	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual int32 streamFile(Common::String path, ReadStreamCallback callback);
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual int32 syncSaves(BoolCallback callback);
+	virtual Networking::Request *syncSaves(BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual int32 info(StorageInfoCallback callback);
+	virtual Networking::Request *info(StorageInfoCallback callback);
 
 	/** This method is passed into info(). (Temporary) */
-	void infoMethodCallback(RequestStorageInfoPair pair);
+	void infoMethodCallback(StorageInfoResponse pair);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 237557d..e181fec 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -44,7 +44,7 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user
 	_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
 
 OneDriveStorage::OneDriveStorage(Common::String code) {
-	getAccessToken(new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::codeFlowComplete), code);
+	getAccessToken(new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete), code);
 }
 
 OneDriveStorage::~OneDriveStorage() {}
@@ -54,11 +54,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 
 	if (!codeFlow && _refreshToken == "") {
 		warning("OneDriveStorage: no refresh token available to get new access token.");
-		if (callback) (*callback)(RequestBoolPair(-1, false));
+		if (callback) (*callback)(BoolResponse(nullptr, false));
 		return;
 	}
 
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestBoolPair, Networking::RequestJsonPair>(this, &OneDriveStorage::tokenRefreshed, callback);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
 	if (codeFlow) {
 		request->addPostField("code=" + code);
@@ -73,11 +73,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	ConnMan.addRequest(request);
 }
 
-void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair) {
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair) {
 	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("OneDriveStorage: got NULL instead of JSON");
-		if (callback) (*callback)(RequestBoolPair(-1, false));
+		if (callback) (*callback)(BoolResponse(nullptr, false));
 		return;
 	}
 
@@ -85,18 +85,18 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::RequestJ
 	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
 		warning("Bad response, no token or user_id passed");
 		debug("%s", json->stringify().c_str());
-		if (callback) (*callback)(RequestBoolPair(-1, false));
+		if (callback) (*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		_uid = result.getVal("user_id")->asString();
 		_refreshToken = result.getVal("refresh_token")->asString();
 		g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken
-		if (callback) (*callback)(RequestBoolPair(-1, true));
+		if (callback) (*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
 }
 
-void OneDriveStorage::codeFlowComplete(RequestBoolPair pair) {
+void OneDriveStorage::codeFlowComplete(BoolResponse pair) {
 	if (!pair.value) {
 		warning("OneDriveStorage: failed to get access token through code flow");
 		return;
@@ -115,7 +115,7 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
-void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
+void OneDriveStorage::printJson(Networking::JsonResponse pair) {
 	Common::JSONValue *json = pair.value;
 	if (!json) {
 		warning("printJson: NULL");
@@ -126,10 +126,10 @@ void OneDriveStorage::printJson(Networking::RequestJsonPair pair) {
 	delete json;
 }
 
-void OneDriveStorage::fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair) {
+void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair) {
 	if (!pair.value) {
 		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0));
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0));
 		return;
 	}
 
@@ -137,44 +137,44 @@ void OneDriveStorage::fileInfoCallback(ReadStreamCallback outerCallback, Network
 	if (result.contains("@content.downloadUrl")) {
 		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
 		if (outerCallback)
-			(*outerCallback)(RequestReadStreamPair(
-				pair.id,
+			(*outerCallback)(Networking::NetworkReadStreamResponse(
+				pair.request,
 				new Networking::NetworkReadStream(url, 0, "")
 			));
 	} else {
 		warning("downloadUrl not found in passed JSON");
 		debug("%s", pair.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(RequestReadStreamPair(pair.id, 0));
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0));
 	}
 	delete pair.value;
 }
 
-int32 OneDriveStorage::streamFile(Common::String path, ReadStreamCallback outerCallback) {
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path + ":/";	
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, RequestReadStreamPair, Networking::RequestJsonPair>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
+Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback) {
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
 }
 
-int32 OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("OneDriveStorage: unable to open file to download into");
-		if (callback) (*callback)(RequestBoolPair(-1, false));
+		if (callback) (*callback)(BoolResponse(nullptr, false));
 		delete f;
-		return -1;
+		return nullptr;
 	}
 
 	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
-void OneDriveStorage::fileDownloaded(RequestBoolPair pair) {
+void OneDriveStorage::fileDownloaded(BoolResponse pair) {
 	if (pair.value) debug("file downloaded!");
 	else debug("download failed!");
 }
 
-int32 OneDriveStorage::syncSaves(BoolCallback callback) {
+Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation
 	/*
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
@@ -182,7 +182,7 @@ int32 OneDriveStorage::syncSaves(BoolCallback callback) {
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 	*/
-	return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, RequestBoolPair>(this, &OneDriveStorage::fileDownloaded));
+	return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::fileDownloaded));
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index be2bcdf..858af6c 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -44,13 +44,13 @@ class OneDriveStorage: public Cloud::Storage {
 	*/
 	OneDriveStorage(Common::String code);
 
-	void tokenRefreshed(BoolCallback callback, Networking::RequestJsonPair pair);
-	void codeFlowComplete(RequestBoolPair pair);
+	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair);
+	void codeFlowComplete(BoolResponse pair);
 
-	void printJson(Networking::RequestJsonPair pair);
-	void fileDownloaded(RequestBoolPair pair);
+	void printJson(Networking::JsonResponse pair);
+	void fileDownloaded(BoolResponse pair);
 
-	void fileInfoCallback(ReadStreamCallback outerCallback, Networking::RequestJsonPair pair);
+	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair);
 public:	
 	virtual ~OneDriveStorage();
 
@@ -71,31 +71,31 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return -1; } //TODO
+	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual int32 streamFile(Common::String path, ReadStreamCallback callback);
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback);
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 remove(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual int32 syncSaves(BoolCallback callback);
+	virtual Networking::Request *syncSaves(BoolCallback callback);
 
 	/** Calls the callback when finished. */
-	virtual int32 createDirectory(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual int32 touch(Common::String path, BoolCallback callback) { return -1; } //TODO
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual int32 info(StorageInfoCallback callback) { return -1; } //TODO
+	virtual Networking::Request *info(StorageInfoCallback callback) { return nullptr; } //TODO
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index d3ec0cc..6943d89 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -38,50 +38,45 @@ OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networki
 	_parentStorage(parent),
 	_innerRequest(
 		new CurlJsonRequest(
-			new Common::Callback<OneDriveTokenRefresher, Networking::RequestJsonPair>(this, &OneDriveTokenRefresher::innerRequestCallback),
+			new Common::Callback<OneDriveTokenRefresher, Networking::JsonResponse>(this, &OneDriveTokenRefresher::innerRequestCallback),
 			url
 		)
-	), _jsonCallback(callback), _retryId(-1), _started(false) {}
+	), _jsonCallback(callback), _retryRequest(nullptr), _started(false) {}
 
 OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
 
-void OneDriveTokenRefresher::innerRequestCallback(Networking::RequestJsonPair pair) {
+void OneDriveTokenRefresher::innerRequestCallback(Networking::JsonResponse pair) {
 	if (!pair.value) {
 		//notify user of failure
 		warning("OneDriveTokenRefresher: got NULL instead of JSON");
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+		finish();
 		return;
 	}
 
 	Common::JSONObject result = pair.value->asObject();
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request		
-		ConnMan.getRequestInfo(pair.id).state = Networking::PAUSED;
-		_retryId = pair.id;	
+		if (pair.request) pair.request->pause();
+		_retryRequest = pair.request;
 		delete pair.value;
-		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::RequestBoolPair>(this, &OneDriveTokenRefresher::tokenRefreshed));
+		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
 		return;
 	}
 
 	//notify user of success	
-	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-	if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, pair.value));
+	finishJson(pair.value);
 }
 
-void OneDriveTokenRefresher::tokenRefreshed(Storage::RequestBoolPair pair) {
+void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
 	if (!pair.value) {
 		//failed to refresh token, notify user with NULL in original callback
 		warning("OneDriveTokenRefresher: failed to refresh token");
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-		if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+		finish();
 		return;
 	}
 
 	//successfully received refreshed token, can restart the original request now	
-	Networking::RequestInfo &info = ConnMan.getRequestInfo(_retryId);
-	info.state = Networking::RETRY;
-	info.retryInSeconds = 1;
+	if (_retryRequest) _retryRequest->retry(1);
 
 	//update headers: first change header with token, then pass those to request
 	for (uint32 i = 0; i < _headers.size(); ++i) {
@@ -89,7 +84,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::RequestBoolPair pair) {
 			_headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
 		}
 	}
-	CurlJsonRequest *retryRequest = (CurlJsonRequest *)info.request;
+	CurlJsonRequest *retryRequest = (CurlJsonRequest *)_retryRequest;
 	if (retryRequest) retryRequest->setHeaders(_headers);
 }
 
@@ -105,11 +100,19 @@ void OneDriveTokenRefresher::handle() {
 void OneDriveTokenRefresher::restart() {
 	//can't restart as all headers were passed to _innerRequest which is probably dead now
 	warning("OneDriveTokenRefresher: cannot be restarted");
-	ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-	if (_jsonCallback) (*_jsonCallback)(Networking::RequestJsonPair(_id, 0));
+	finish();
 }
 
-Cloud::Storage::RequestReadStreamPair OneDriveTokenRefresher::execute() {
+void OneDriveTokenRefresher::finish() {
+	finishJson(0);
+}
+
+void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
+	Request::finish();
+	if (_jsonCallback) (*_jsonCallback)(Networking::JsonResponse(this, json));
+}
+
+Networking::NetworkReadStreamResponse OneDriveTokenRefresher::execute() {
 	if (!_started) {
 		for (uint32 i = 0; i < _headers.size(); ++i)
 			_innerRequest->addHeader(_headers[i]);
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 58b7dce..c09879f 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -35,24 +35,27 @@ class OneDriveStorage;
 class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
 	OneDriveStorage *_parentStorage;
 	Common::Array<Common::String> _headers;
-	Networking::CurlJsonRequest *_innerRequest;
+	CurlJsonRequest *_innerRequest;
 	Networking::JsonCallback _jsonCallback;
-	int32 _retryId;
+	Request *_retryRequest;
 	bool _started;
 
-	void innerRequestCallback(Networking::RequestJsonPair pair);
-	void tokenRefreshed(Storage::RequestBoolPair pair);
+	void innerRequestCallback(Networking::JsonResponse pair);
+	void tokenRefreshed(Storage::BoolResponse pair);
+
+	virtual void finishJson(Common::JSONValue *json);
 public:	
 	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url);
 	virtual ~OneDriveTokenRefresher();
 
 	virtual void handle();
 	virtual void restart();
+	virtual void finish();
 
 	virtual void setHeaders(Common::Array<Common::String> &headers) { _headers = headers; }
 	virtual void addHeader(Common::String header) { _headers.push_back(header); }
 	virtual void addPostField(Common::String field) { _innerRequest->addPostField(field); }
-	virtual Cloud::Storage::RequestReadStreamPair execute();
+	virtual Networking::NetworkReadStreamResponse execute();
 };
 
 } //end of namespace OneDrive
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 0114b46..8ae5308 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -23,28 +23,26 @@
 #ifndef BACKENDS_CLOUD_STORAGE_H
 #define BACKENDS_CLOUD_STORAGE_H
 
+#include "backends/cloud/storagefile.h"
+#include "backends/cloud/storageinfo.h"
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curlrequest.h"
 #include "common/array.h"
 #include "common/stream.h"
 #include "common/str.h"
 #include "common/callback.h"
-#include "backends/cloud/storagefile.h"
-#include "backends/cloud/storageinfo.h"
-#include "backends/networking/curl/networkreadstream.h"
-#include <backends/networking/curl/request.h>
 
 namespace Cloud {
 
 class Storage {
 public:
-	typedef Networking::RequestIdPair<Common::Array<StorageFile>&> RequestFileArrayPair;
-	typedef Networking::RequestIdPair<Networking::NetworkReadStream *> RequestReadStreamPair;
-	typedef Networking::RequestIdPair<StorageInfo> RequestStorageInfoPair;
-	typedef Networking::RequestIdPair<bool> RequestBoolPair;	
+	typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse;
+	typedef Networking::Response<StorageInfo> StorageInfoResponse;
+	typedef Networking::Response<bool> BoolResponse;
 
-	typedef Common::BaseCallback<RequestFileArrayPair> *FileArrayCallback;
-	typedef Common::BaseCallback<RequestReadStreamPair> *ReadStreamCallback;
-	typedef Common::BaseCallback<RequestStorageInfoPair> *StorageInfoCallback;
-	typedef Common::BaseCallback<RequestBoolPair> *BoolCallback;
+	typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback;
+	typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback;
+	typedef Common::BaseCallback<BoolResponse> *BoolCallback;
 
 	Storage() {}
 	virtual ~Storage() {}
@@ -66,37 +64,37 @@ public:
 	/**
 	 * Public Cloud API comes down there.
 	 *
-	 * All Cloud API methods return int32 request id, which might be used to access
-	 * request through ConnectionManager. All methods also accept a callback, which
-	 * would be called, when request is complete.
+	 * All Cloud API methods return Networking::Request *, which
+	 * might be used to control request. All methods also accept
+	 * a callback, which is called, when request is complete.
 	 */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual int32 listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
+	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
+	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual int32 streamFile(Common::String path, ReadStreamCallback callback) = 0;
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 remove(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 syncSaves(BoolCallback callback) = 0;
+	virtual Networking::Request *syncSaves(BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 createDirectory(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual int32 touch(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback) = 0;
 
 	/** Returns the StorageInfo struct. */
-	virtual int32 info(StorageInfoCallback callback) = 0;
+	virtual Networking::Request *info(StorageInfoCallback callback) = 0;
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 9d88c59..ef2afc2 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -34,7 +34,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _nextId(0) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
@@ -48,16 +48,10 @@ void ConnectionManager::registerEasyHandle(CURL *easy) {
 	curl_multi_add_handle(_multi, easy);
 }
 
-int32 ConnectionManager::addRequest(Request *request) {
-	int32 newId = _nextId++;
-	_requests[newId] = RequestInfo(newId, request);
-	request->setId(newId);
+Request *ConnectionManager::addRequest(Request *request) {
+	_requests.push_back(request);
 	if (!_timerStarted) startTimer();
-	return newId;
-}
-
-RequestInfo &ConnectionManager::getRequestInfo(int32 id) {
-	return _requests[id];
+	return request;
 }
 
 //private goes here:
@@ -91,36 +85,22 @@ void ConnectionManager::handle() {
 
 void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
-	debug("handling %d request(s)", _requests.size());
-	Common::Array<int32> idsToRemove;
-	for (Common::HashMap<int32, RequestInfo>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
-		RequestInfo &info = _requests[i->_key];
+	debug("handling %d request(s)", _requests.size());	
+	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
+		Request *request = *i;
+		if (!request || request->state() == FINISHED) {
+			delete (*i);
+			_requests.erase(i);
+			continue;
+		}
 		
-		switch(info.state) {
-		case FINISHED:
-			delete info.request;
-			info.request = 0;
-			idsToRemove.push_back(info.id);
-			break;
-
-		case PROCESSING:
-			info.request->handle();
-			break;
-
-		case RETRY:
-			if (info.retryInSeconds > 0) --info.retryInSeconds;
-			else {
-				info.state = PROCESSING;
-				info.request->restart();
-				debug("request restarted");
-			}
-
-		default:
-			; //nothing to do
+		if (request) {
+			if (request->state() == PROCESSING) request->handle();
+			else if (request->state() == RETRY) request->handleRetry();
 		}
+
+		++i;		
 	}
-	for (uint32 i = 0; i < idsToRemove.size(); ++i)
-		_requests.erase(idsToRemove[i]);
 	if (_requests.empty()) stopTimer();
 }
 
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 15327a2..2d37c05 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -36,30 +36,12 @@ namespace Networking {
 
 class NetworkReadStream;
 
-enum RequestState {
-	PROCESSING,
-	PAUSED,
-	RETRY,
-	FINISHED
-};
-
-struct RequestInfo {
-	int32 id;
-	Request *request;
-	RequestState state;	
-	uint32 retryInSeconds;
-
-	RequestInfo() : id(-1), request(0), state(FINISHED), retryInSeconds(0) {}
-	RequestInfo(int32 rqId, Request *rq) : id(rqId), request(rq), state(PROCESSING), retryInSeconds(0) {}
-};
-
 class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	friend void connectionsThread(void *); //calls handle()
 
 	CURLM *_multi;	
 	bool _timerStarted;
-	Common::HashMap<int32, RequestInfo> _requests;
-	int32 _nextId;
+	Common::Array<Request *> _requests;	
 	
 	void startTimer(int interval = 1000000); //1 second is the default interval
 	void stopTimer();
@@ -81,15 +63,15 @@ public:
 	/**
 	* Use this method to add new Request into manager's queue.
 	* Manager will periodically call handle() method of these
-	* Requests until they return true.
+	* Requests until they set their state to FINISHED.
+	*
+	* If Request's state is RETRY, handleRetry() is called instead.
 	*
 	* @note This method starts the timer if it's not started yet.
 	*
-	* @return generated Request's id, which might be used to get its status
+	* @return the same Request pointer, just as a shortcut
 	*/
-	int32 addRequest(Request *request);	
-
-	RequestInfo &getRequestInfo(int32 id);
+	Request *addRequest(Request *request);
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 326d8e2..3c598d7 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -69,14 +69,11 @@ void CurlJsonRequest::handle() {
 			if (_stream->httpResponseCode() != 200)
 				warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 
-			ConnMan.getRequestInfo(_id).state = Networking::FINISHED;
-			if (_jsonCallback) {
-				char *contents = getPreparedContents();
-				if (_stream->httpResponseCode() != 200)
-					debug("%s", contents);
-				Common::JSONValue *json = Common::JSON::parse(contents);
-				(*_jsonCallback)(RequestJsonPair(_id, json)); //potential memory leak, free it in your callbacks!
-			}
+			char *contents = getPreparedContents();
+			if (_stream->httpResponseCode() != 200)
+				debug("%s", contents);
+			Common::JSONValue *json = Common::JSON::parse(contents);
+			finishJson(json);
 		}
 	}
 }
@@ -88,4 +85,14 @@ void CurlJsonRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
+void CurlJsonRequest::finishJson(Common::JSONValue *json) {
+	Request::finish();
+	if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
+	else delete json;
+}
+
+void CurlJsonRequest::finish() {
+	finishJson(0);
+}
+
 } //end of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 5e78bd1..0a560f9 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -29,10 +29,8 @@
 
 namespace Networking {
 
-class NetworkReadStream;
-
-typedef RequestIdPair<Common::JSONValue*> RequestJsonPair;
-typedef Common::BaseCallback<RequestJsonPair> *JsonCallback;
+typedef Response<Common::JSONValue *> JsonResponse;
+typedef Common::BaseCallback<JsonResponse> *JsonCallback;
 
 class CurlJsonRequest: public CurlRequest {
 	JsonCallback _jsonCallback;
@@ -41,12 +39,17 @@ class CurlJsonRequest: public CurlRequest {
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
 	char *getPreparedContents();
 
+protected:
+	/** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */
+	virtual void finishJson(Common::JSONValue *json);
+
 public:
 	CurlJsonRequest(JsonCallback cb, Common::String url);
 	virtual ~CurlJsonRequest();
 
 	virtual void handle(); 
 	virtual void restart();
+	virtual void finish();
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index f01a430..a8eb425 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -40,10 +40,10 @@ CurlRequest::~CurlRequest() {
 void CurlRequest::handle() {
 	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);	
 
-	if (_stream && _stream->eos()) {		
+	if (_stream && _stream->eos()) {
 		if (_stream->httpResponseCode() != 200)
 			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
-		ConnMan.getRequestInfo(_id).state = Networking::FINISHED;		
+		finish();
 	}
 }
 
@@ -71,13 +71,13 @@ void CurlRequest::addPostField(Common::String keyValuePair) {
 		_postFields += "&" + keyValuePair;
 }
 
-Cloud::Storage::RequestReadStreamPair CurlRequest::execute() {
+NetworkReadStreamResponse CurlRequest::execute() {
 	if (!_stream) {
 		_stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);
 		ConnMan.addRequest(this);
 	}
 
-	return Cloud::Storage::RequestReadStreamPair(_id, _stream);
+	return NetworkReadStreamResponse(this, _stream);
 }
 
 } //end of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 18a41a1..5677720 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -24,7 +24,6 @@
 #define BACKENDS_NETWORKING_CURL_CURLREQUEST_H
 
 #include "backends/networking/curl/request.h"
-#include "backends/cloud/storage.h"
 #include "common/str.h"
 #include "common/array.h"
 
@@ -34,6 +33,9 @@ namespace Networking {
 
 class NetworkReadStream;
 
+typedef Response<NetworkReadStream *> NetworkReadStreamResponse;
+typedef Common::BaseCallback<NetworkReadStreamResponse> *NetworkReadStreamCallback;
+
 class CurlRequest: public Request {
 protected:
 	Common::String _url;
@@ -48,12 +50,20 @@ public:
 	virtual void handle();
 	virtual void restart();
 
+	/** Replaces all headers with the passed array of headers. */
 	virtual void setHeaders(Common::Array<Common::String> &headers);
+
+	/** Adds a header into headers list. */
 	virtual void addHeader(Common::String header);
+
+	/** Adds a post field (key=value pair). */
 	virtual void addPostField(Common::String field);
 
-	/** Start this Request with ConnMan. Returns its ReadStream and request id. */
-	virtual Cloud::Storage::RequestReadStreamPair execute();
+	/**
+	* Starts this Request with ConnMan.
+	* @return its NetworkReadStream in NetworkReadStreamResponse.
+	*/
+	virtual NetworkReadStreamResponse execute();
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 6431a01..a0c8746 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -83,6 +83,6 @@ public:
 	long httpResponseCode();
 };
 
-} //end of namespace Cloud
+} //end of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index d81fe90..ff919e0 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -28,40 +28,124 @@
 
 namespace Networking {
 
-template<typename T> struct RequestIdPair {
-	int32 id;
+class Request;
+
+/**
+* Response<T> is a struct to be returned from Request
+* to user's callbacks. It's a type safe way to indicate
+* which "return value" Request has and user awaits.
+*
+* It just keeps a Request pointer together with
+* some T value (which might be a pointer, a reference
+* or a plain type (copied by value)).
+*
+* To make it more convenient, typedefs are used.
+* For example, Response<void *> is called DataResponse
+* and corresponding callback pointer is DataCallback.
+*/
+
+template<typename T> struct Response {
+	Request *request;
 	T value;
 
-	RequestIdPair(int32 rid, T v) : id(rid), value(v) {}
+	Response(Request *rq, T v) : request(rq), value(v) {}
 };
 
-typedef RequestIdPair<void *> RequestDataPair;
-typedef Common::BaseCallback<RequestDataPair> *DataCallback;
+typedef Response<void *> DataReponse;
+typedef Common::BaseCallback<DataReponse> *DataCallback;
+
+/**
+* RequestState is used to indicate current Request state.
+* ConnectionManager uses it to decide what to do with the Request.
+*
+* PROCESSING state indicates that Request is working.
+* ConnectionManager calls handle() method of Requests in that state.
+*
+* PAUSED state indicates that Request is not working.
+* ConnectionManager keeps Requests in that state and doesn't call any methods of those.
+*
+* RETRY state indicates that Request must restart after a few seconds.
+* ConnectionManager calls handleRetry() method of Requests in that state.
+* Default handleRetry() implementation decreases _retryInSeconds value
+* until it reaches zero. When it does, Request's restart() method is called.
+*
+* FINISHED state indicates that Request did the work and might be deleted.
+* ConnectionManager deletes Requests in that state.
+* After this state is set, but before ConnectionManager deletes the Request,
+* Request calls user's callback. User can ask Request to change its state
+* by calling retry() or pause() methods and Request won't be deleted.
+*/
+
+enum RequestState {
+	PROCESSING,
+	PAUSED,
+	RETRY,
+	FINISHED
+};
 
 class Request {
 protected:
 	/**
-	* Callback, which should be called before Request returns true in handle().
+	* Callback, which should be called when Request is finished.
 	* That's the way Requests pass the result to the code which asked to create this request.
+	*
+	* @note some Requests use their own callbacks to return something but void *.
+	* @note callback must be called in finish() or similar method.
 	*/
 
 	DataCallback _callback;
 
-	int32 _id;
+	/**
+	* Request state, which is used by ConnectionManager to determine
+	* whether request might be deleted or it's still working.
+	*
+	* State might be changed from outside with finish(), pause() or
+	* retry() methods. Override these if you want to react to these
+	* changes correctly.
+	*/
+
+	RequestState _state;
+
+	/** In RETRY state this indicates whether it's time to call restart(). */
+	uint32 _retryInSeconds;
 
 public:
-	Request(DataCallback cb): _callback(cb), _id(-1) {}
+	Request(DataCallback cb): _callback(cb), _state(PROCESSING), _retryInSeconds(0) {}
 	virtual ~Request() { delete _callback; }
 
-	/**
-	* Method, which does actual work. Depends on what this Request is doing.
-	*/
-
+	/** Method, which does actual work. Depends on what this Request is doing. */
 	virtual void handle() = 0;
 
+	/** Method, which is called by ConnectionManager when Request's state is RETRY.	 */
+	virtual void handleRetry() {
+		if (_retryInSeconds > 0) --_retryInSeconds;
+		else {
+			_state = PROCESSING;
+			restart();
+		}
+	}
+
+	/** Method, which is used to restart the Request. */
 	virtual void restart() = 0;
 
-	void setId(int32 id) { _id = id; }
+	/** Method, which is called to pause the Request. */
+	virtual void pause() { _state = PAUSED; }
+
+	/**
+	* Method, which is called to *interrupt* the Request.
+	* When it's called, Request must stop its work and
+	* call the callback to notify user of failure.
+	*/
+	virtual void finish() { _state = FINISHED; }
+
+	/** Method, which is called to retry the Request. */
+	virtual void retry(uint32 seconds) {		
+		_state = RETRY;
+		_retryInSeconds = seconds;
+	}
+
+	/** Returns Request's current state. */
+	RequestState state() const { return _state; }
 };
 
 } //end of namespace Cloud
diff --git a/local/onedrive/2/doom.jpg b/local/onedrive/2/doom.jpg
new file mode 100644
index 0000000..ef1ca01
Binary files /dev/null and b/local/onedrive/2/doom.jpg differ


Commit: baf37b9330ee96ca04fe742dc923d9fddfcf4c20
    https://github.com/scummvm/scummvm/commit/baf37b9330ee96ca04fe742dc923d9fddfcf4c20
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove DOOM picture

Accidentally commited a test file, so now I'm removing it.

Changed paths:
  R local/onedrive/2/doom.jpg



diff --git a/local/onedrive/2/doom.jpg b/local/onedrive/2/doom.jpg
deleted file mode 100644
index ef1ca01..0000000
Binary files a/local/onedrive/2/doom.jpg and /dev/null differ


Commit: 1348befe297d32de9bf40f89f37bcbae2c91d81f
    https://github.com/scummvm/scummvm/commit/1348befe297d32de9bf40f89f37bcbae2c91d81f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add access to CurlRequest's Stream

One can access CurlRequest's NetworkReadStream in order to find out HTTP
response code or some other Stream-related data.

OneDriveTokenRefresher uses it to print some info on that 404 error I'm
trying to troubleshoot.

Changed paths:
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 6943d89..6620c23 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -25,6 +25,7 @@
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
@@ -56,6 +57,17 @@ void OneDriveTokenRefresher::innerRequestCallback(Networking::JsonResponse pair)
 	Common::JSONObject result = pair.value->asObject();
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request		
+		CurlJsonRequest *streamRequest = (CurlJsonRequest *)pair.request;
+		if (streamRequest) {
+			const Networking::NetworkReadStream *stream = streamRequest->getNetworkReadStream();
+			if (stream) {
+				debug("code %ld", stream->httpResponseCode());
+			}
+		}
+
+		Common::JSONObject error = result.getVal("error")->asObject();
+		debug("code = %s", error.getVal("code")->asString().c_str());
+		debug("message = %s", error.getVal("message")->asString().c_str());
 		if (pair.request) pair.request->pause();
 		_retryRequest = pair.request;
 		delete pair.value;
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 5677720..f77dce9 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -64,6 +64,9 @@ public:
 	* @return its NetworkReadStream in NetworkReadStreamResponse.
 	*/
 	virtual NetworkReadStreamResponse execute();
+
+	/** Returns Request's NetworkReadStream. */
+	const NetworkReadStream *getNetworkReadStream() const { return _stream; }
 };
 
 }  //end of namespace Networking
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 39316de..8658fc7 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -78,7 +78,7 @@ void NetworkReadStream::finished() {
 	_requestComplete = true;
 }
 
-long NetworkReadStream::httpResponseCode() {
+long NetworkReadStream::httpResponseCode() const {
 	long responseCode = -1;
 	if (_easy)
 		curl_easy_getinfo(_easy, CURLINFO_RESPONSE_CODE, &responseCode);
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index a0c8746..14c00a4 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -80,7 +80,7 @@ public:
 	*
 	* @note This method should be called when eos() == true.
 	*/
-	long httpResponseCode();
+	long httpResponseCode() const;
 };
 
 } //end of namespace Networking


Commit: dcbaeb57c4cbe58d17fd46b52fd23ae154458398
    https://github.com/scummvm/scummvm/commit/dcbaeb57c4cbe58d17fd46b52fd23ae154458398
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix OneDriveTokenRefresher

Failed to update token in header when "Bearer" was used instead of
"bearer".

Changed paths:
    backends/cloud/onedrive/onedrivetokenrefresher.cpp



diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 6620c23..4be034b 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -92,7 +92,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
 
 	//update headers: first change header with token, then pass those to request
 	for (uint32 i = 0; i < _headers.size(); ++i) {
-		if (_headers[i].contains("Authorization: bearer ")) {			
+		if (_headers[i].contains("Authorization")) {
 			_headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
 		}
 	}


Commit: 6c01edc57a221e5c376d23f12bb2f090c73c4ffd
    https://github.com/scummvm/scummvm/commit/6c01edc57a221e5c376d23f12bb2f090c73c4ffd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Simplify OneDriveTokenRefresher

It now just extends CurlJsonRequest, not wraps one.

Changed paths:
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 4be034b..9421483 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -23,10 +23,7 @@
 
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
@@ -35,50 +32,10 @@ namespace Cloud {
 namespace OneDrive {
 
 OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url):
-	CurlJsonRequest(0, url),
-	_parentStorage(parent),
-	_innerRequest(
-		new CurlJsonRequest(
-			new Common::Callback<OneDriveTokenRefresher, Networking::JsonResponse>(this, &OneDriveTokenRefresher::innerRequestCallback),
-			url
-		)
-	), _jsonCallback(callback), _retryRequest(nullptr), _started(false) {}
+	CurlJsonRequest(callback, url), _parentStorage(parent) {}
 
 OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
 
-void OneDriveTokenRefresher::innerRequestCallback(Networking::JsonResponse pair) {
-	if (!pair.value) {
-		//notify user of failure
-		warning("OneDriveTokenRefresher: got NULL instead of JSON");
-		finish();
-		return;
-	}
-
-	Common::JSONObject result = pair.value->asObject();
-	if (result.contains("error")) {
-		//new token needed => request token & then retry original request		
-		CurlJsonRequest *streamRequest = (CurlJsonRequest *)pair.request;
-		if (streamRequest) {
-			const Networking::NetworkReadStream *stream = streamRequest->getNetworkReadStream();
-			if (stream) {
-				debug("code %ld", stream->httpResponseCode());
-			}
-		}
-
-		Common::JSONObject error = result.getVal("error")->asObject();
-		debug("code = %s", error.getVal("code")->asString().c_str());
-		debug("message = %s", error.getVal("message")->asString().c_str());
-		if (pair.request) pair.request->pause();
-		_retryRequest = pair.request;
-		delete pair.value;
-		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
-		return;
-	}
-
-	//notify user of success	
-	finishJson(pair.value);
-}
-
 void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
 	if (!pair.value) {
 		//failed to refresh token, notify user with NULL in original callback
@@ -87,53 +44,54 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
 		return;
 	}
 
-	//successfully received refreshed token, can restart the original request now	
-	if (_retryRequest) _retryRequest->retry(1);
-
 	//update headers: first change header with token, then pass those to request
 	for (uint32 i = 0; i < _headers.size(); ++i) {
 		if (_headers[i].contains("Authorization")) {
 			_headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
 		}
 	}
-	CurlJsonRequest *retryRequest = (CurlJsonRequest *)_retryRequest;
-	if (retryRequest) retryRequest->setHeaders(_headers);
+	setHeaders(_headers);
+
+	//successfully received refreshed token, can restart the original request now	
+	retry(0);
 }
 
-void OneDriveTokenRefresher::handle() {
-	if (!_started) {
-		for (uint32 i = 0; i < _headers.size(); ++i)
-			_innerRequest->addHeader(_headers[i]);
-		_started = true;
-		ConnMan.addRequest(_innerRequest);
+void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {	
+	if (!json) {
+		//notify user of failure
+		warning("OneDriveTokenRefresher: got NULL instead of JSON");
+		CurlJsonRequest::finish();
+		return;
 	}
-}
 
-void OneDriveTokenRefresher::restart() {
-	//can't restart as all headers were passed to _innerRequest which is probably dead now
-	warning("OneDriveTokenRefresher: cannot be restarted");
-	finish();
-}
+	Common::JSONObject result = json->asObject();
+	if (result.contains("error")) {
+		//new token needed => request token & then retry original request		
+		if (_stream) {
+			debug("code %ld", _stream->httpResponseCode());
+		}
 
-void OneDriveTokenRefresher::finish() {
-	finishJson(0);
-}
+		Common::JSONObject error = result.getVal("error")->asObject();
+		debug("code = %s", error.getVal("code")->asString().c_str());
+		debug("message = %s", error.getVal("message")->asString().c_str());
+		pause();		
+		delete json;
+		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
+		return;
+	}
 
-void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
-	Request::finish();
-	if (_jsonCallback) (*_jsonCallback)(Networking::JsonResponse(this, json));
+	//notify user of success
+	CurlJsonRequest::finishJson(json);
 }
 
-Networking::NetworkReadStreamResponse OneDriveTokenRefresher::execute() {
-	if (!_started) {
-		for (uint32 i = 0; i < _headers.size(); ++i)
-			_innerRequest->addHeader(_headers[i]);
-		_started = true;
-	} else {
-		warning("OneDriveTokenRefresher: inner Request is already started");
-	}
-	return _innerRequest->execute();
+void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+	_headers = headers;
+	curl_slist_free_all(_headersList);
+	_headersList = 0;
+	for (uint32 i = 0; i < headers.size(); ++i)
+		CurlJsonRequest::addHeader(headers[i]);
 }
 
+
 } //end of namespace OneDrive
 } //end of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index c09879f..08212bf 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
 
 #include "backends/cloud/storage.h"
-#include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
@@ -34,13 +33,8 @@ class OneDriveStorage;
 
 class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
 	OneDriveStorage *_parentStorage;
-	Common::Array<Common::String> _headers;
-	CurlJsonRequest *_innerRequest;
-	Networking::JsonCallback _jsonCallback;
-	Request *_retryRequest;
-	bool _started;
-
-	void innerRequestCallback(Networking::JsonResponse pair);
+	Common::Array<Common::String> _headers;	
+	
 	void tokenRefreshed(Storage::BoolResponse pair);
 
 	virtual void finishJson(Common::JSONValue *json);
@@ -48,14 +42,12 @@ public:
 	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url);
 	virtual ~OneDriveTokenRefresher();
 
-	virtual void handle();
-	virtual void restart();
-	virtual void finish();
+	virtual void setHeaders(Common::Array<Common::String> &headers);
 
-	virtual void setHeaders(Common::Array<Common::String> &headers) { _headers = headers; }
-	virtual void addHeader(Common::String header) { _headers.push_back(header); }
-	virtual void addPostField(Common::String field) { _innerRequest->addPostField(field); }
-	virtual Networking::NetworkReadStreamResponse execute();
+	virtual void addHeader(Common::String header) {
+		_headers.push_back(header);
+		CurlJsonRequest::addHeader(header);
+	}
 };
 
 } //end of namespace OneDrive
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 0a560f9..9bd12a1 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -33,13 +33,13 @@ typedef Response<Common::JSONValue *> JsonResponse;
 typedef Common::BaseCallback<JsonResponse> *JsonCallback;
 
 class CurlJsonRequest: public CurlRequest {
+protected:
 	JsonCallback _jsonCallback;
 	Common::MemoryWriteStreamDynamic _contentsStream;
 
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
 	char *getPreparedContents();
 
-protected:
 	/** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */
 	virtual void finishJson(Common::JSONValue *json);
 


Commit: cdf30e9d584f81029311f111f314fabdc1bf6d60
    https://github.com/scummvm/scummvm/commit/cdf30e9d584f81029311f111f314fabdc1bf6d60
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix posix backend compilation

Changed paths:
    backends/fs/posix/posix-fs.cpp



diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index d388f30..b38a076 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -39,6 +39,7 @@
 #include <dirent.h>
 #include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #ifdef __OS2__
 #define INCL_DOS


Commit: 0ef1cda1724e973c36c76e07763f681cb14bd597
    https://github.com/scummvm/scummvm/commit/0ef1cda1724e973c36c76e07763f681cb14bd597
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add FolderDownloadRequest

Uses Storage's listDirectory() and download() methods to download
contents.

Changed paths:
  A backends/cloud/folderdownloadrequest.cpp
  A backends/cloud/folderdownloadrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/storagefile.cpp
    backends/cloud/storagefile.h
    backends/module.mk



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index a1a97e0..379d7bb 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -24,6 +24,7 @@
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/downloadrequest.h"
+#include "backends/cloud/folderdownloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
@@ -79,8 +80,9 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
 
-void DropboxStorage::printFiles(Common::Array<StorageFile> files) {
+void DropboxStorage::printFiles(FileArrayResponse pair) {
 	debug("files:");
+	Common::Array<StorageFile> &files = pair.value;
 	for (uint32 i = 0; i < files.size(); ++i)
 		debug("\t%s", files[i].name().c_str());
 }
@@ -116,11 +118,20 @@ Networking::Request *DropboxStorage::download(Common::String remotePath, Common:
 	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
+Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) {
+	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive));
+}
+
 Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation	
 	//"" is root in Dropbox, not "/"
 	//this must create all these directories:
-	return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
+	//return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
+	return downloadFolder(
+		"/not_flat", "local/not_flat_1_level/",
+		new Common::Callback<DropboxStorage, FileArrayResponse>(this, &DropboxStorage::printFiles),
+		false
+	);
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index f95d0c9..19f2f9c 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -43,7 +43,7 @@ class DropboxStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void printFiles(Common::Array<StorageFile> files);
+	void printFiles(FileArrayResponse pair);
 
 public:	
 	virtual ~DropboxStorage();
@@ -76,6 +76,9 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
+	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
+	Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
+
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
new file mode 100644
index 0000000..f60c9f6
--- /dev/null
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -0,0 +1,111 @@
+/* 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 "backends/cloud/folderdownloadrequest.h"
+#include "common/debug.h"
+
+namespace Cloud {
+
+FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive):
+	Request(nullptr), _storage(storage), _fileArrayCallback(callback),
+	_remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+void FolderDownloadRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();	
+	_currentFile = StorageFile();
+	_files.clear();
+	_failedFiles.clear();
+	_ignoreCallback = false;
+
+	//list directory first
+	_workingRequest = _storage->listDirectory(
+		_remoteDirectoryPath,
+		new Common::Callback<FolderDownloadRequest, Storage::FileArrayResponse>(this, &FolderDownloadRequest::directoryListedCallback),
+		_recursive
+	);
+}
+
+void FolderDownloadRequest::directoryListedCallback(Storage::FileArrayResponse pair) {
+	if (_ignoreCallback) return;
+	//TODO: somehow ListDirectory requests must indicate that file array is incomplete
+	_files = pair.value;
+	downloadNextFile();
+}
+
+void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
+	if (_ignoreCallback) return;
+	if (!pair.value) _failedFiles.push_back(_currentFile);
+	downloadNextFile();
+}
+
+void FolderDownloadRequest::downloadNextFile() {
+	do {
+		if (_files.empty()) {
+			finishFiles(_failedFiles);
+			return;
+		}
+	
+		_currentFile = _files.back();
+		_files.pop_back();
+	} while (_currentFile.isDirectory()); //TODO: may be create these directories (in case those are empty)
+
+	Common::String remotePath = _currentFile.path();
+	Common::String localPath = remotePath;
+	if (_remoteDirectoryPath == "" || remotePath.hasPrefix(_remoteDirectoryPath)) {
+		localPath.erase(0, _remoteDirectoryPath.size());
+		if (_remoteDirectoryPath != "" && (_remoteDirectoryPath.lastChar() != '/' && _remoteDirectoryPath.lastChar() != '\\'))
+			localPath.erase(0, 1);
+	} else {
+		warning("Can't process the following paths:");
+		warning("remote directory: %s", _remoteDirectoryPath.c_str());
+		warning("remote file under that directory: %s", remotePath.c_str());		
+	}
+	if (_localDirectoryPath != "") {
+		if (_localDirectoryPath.lastChar() == '/' || _localDirectoryPath.lastChar() == '\\')
+			localPath = _localDirectoryPath + localPath;
+		else
+			localPath = _localDirectoryPath + "/" + localPath;
+	}
+	debug("%s -> %s", remotePath.c_str(), localPath.c_str());
+	_workingRequest = _storage->download(
+		remotePath, localPath,
+		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback)		
+	);
+}
+
+void FolderDownloadRequest::finish() {
+	//TODO: somehow indicate that request was interrupted
+	Common::Array<StorageFile> files;
+	finishFiles(files);
+}
+
+void FolderDownloadRequest::finishFiles(Common::Array<StorageFile> &files) {
+	Request::finish();
+	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
+}
+
+} //end of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
new file mode 100644
index 0000000..038dc64
--- /dev/null
+++ b/backends/cloud/folderdownloadrequest.h
@@ -0,0 +1,59 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
+#define BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
+
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "backends/cloud/storage.h"
+#include "common/file.h"
+
+namespace Cloud {
+
+class FolderDownloadRequest: public Networking::Request {
+	Storage *_storage;
+	Storage::FileArrayCallback _fileArrayCallback;
+	Common::String _remoteDirectoryPath, _localDirectoryPath;
+	bool _recursive;
+	Common::Array<StorageFile> _files, _failedFiles;
+	StorageFile _currentFile;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void directoryListedCallback(Storage::FileArrayResponse pair);
+	void fileDownloadedCallback(Storage::BoolResponse pair);
+	void downloadNextFile();
+	void finishFiles(Common::Array<StorageFile> &files);
+public:
+	FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive);
+	virtual ~FolderDownloadRequest() {}
+
+	virtual void handle() {}
+	virtual void restart() { start(); }
+	virtual void finish();
+};
+
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index 02c9cae..c0f8d3a 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -24,6 +24,14 @@
 
 namespace Cloud {
 
+StorageFile::StorageFile() {
+	_path = "";
+	_name = "";
+	_size = 0;
+	_timestamp = 0;
+	_isDirectory = false;
+}
+
 StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 	_path = pth;
 
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index 8706ba1..ced99fa 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -39,6 +39,7 @@ class StorageFile {
 	bool _isDirectory;
 
 public:
+	StorageFile(); //invalid empty file
 	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir);
 
 	Common::String path() const { return _path; }
diff --git a/backends/module.mk b/backends/module.mk
index 5ec3419..54e3817 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -25,6 +25,7 @@ MODULE_OBJS += \
 	cloud/manager.o \
 	cloud/storagefile.o \
 	cloud/downloadrequest.o \
+	cloud/folderdownloadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/onedrive/onedrivestorage.o \


Commit: bc773835453478c5e56cfc2f653e6d863bc5de95
    https://github.com/scummvm/scummvm/commit/bc773835453478c5e56cfc2f653e6d863bc5de95
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CONFIGURE: Added configure switches for enabling/desabling cloud and net libs

Changed paths:
    configure



diff --git a/configure b/configure
index de01ca7..448da8a 100755
--- a/configure
+++ b/configure
@@ -155,6 +155,7 @@ _build_hq_scalers=yes
 _enable_prof=no
 _global_constructors=no
 _bink=yes
+_cloud=auto
 # Default vkeybd/keymapper/eventrec options
 _vkeybd=no
 _keymapper=no
@@ -962,6 +963,7 @@ Optional Features:
   --disable-hq-scalers     exclude HQ2x and HQ3x scalers
   --disable-translation    don't build support for translated messages
   --disable-taskbar        don't build support for taskbar and launcher integration
+  --disable-cloud          don't build cloud support
   --enable-vkeybd          build virtual keyboard support
   --enable-keymapper       build key mapper support
   --enable-eventrecorder   enable event recording functionality
@@ -1044,6 +1046,9 @@ Optional Libraries:
   --with-sndio-prefix=DIR  Prefix where sndio is installed (optional)
   --disable-sndio          disable sndio MIDI driver [autodetect]
 
+  --disable-sdlnet         disable SDL_Net networking library [autodetect]
+  --disable-libcurl        disable libcurl networking library [autodetect]
+
 Some influential environment variables:
   LDFLAGS        linker flags, e.g. -L<lib dir> if you have libraries in a
                  nonstandard directory <lib dir>
@@ -1122,6 +1127,12 @@ for ac_option in $@; do
 	--disable-freetype2)      _freetype2=no   ;;
 	--enable-taskbar)         _taskbar=yes    ;;
 	--disable-taskbar)        _taskbar=no     ;;
+	--enable-sdlnet)          _sdlnet=yes     ;;
+	--disable-sdlnet)         _sdlnet=no      ;;
+	--enable-libcurl)         _libcurl=yes     ;;
+	--disable-libcurl)        _libcurl=no      ;;
+	--enable-cloud)           _cloud=yes      ;;
+	--disable-cloud)          _cloud=no       ;;
 	--enable-updates)         _updates=yes    ;;
 	--disable-updates)        _updates=no     ;;
 	--enable-libunity)        _libunity=yes   ;;
@@ -4795,7 +4806,11 @@ if test "$_keymapper" = yes ; then
 fi
 
 if test "$_eventrec" = yes ; then
-	echo ", event recorder"
+	echo_n ", event recorder"
+fi
+
+if test "$_cloud" = yes ; then
+	echo ", cloud"
 else
 	echo
 fi


Commit: 1dfa73b8f863972f513d9d04b995cb7316365a8c
    https://github.com/scummvm/scummvm/commit/1dfa73b8f863972f513d9d04b995cb7316365a8c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix ConnectionManager singleton warning

Changed paths:
    backends/networking/curl/connectionmanager.cpp



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index ef2afc2..337ad42 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -28,10 +28,13 @@
 #include "common/system.h"
 #include "common/timer.h"
 #include <curl/curl.h>
-using Common::Singleton;
+
+namespace Common {
 
 DECLARE_SINGLETON(Networking::ConnectionManager);
 
+}
+
 namespace Networking {
 
 ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {


Commit: 8aa87815a62117c5fd29b335c8e33b0cbea0da44
    https://github.com/scummvm/scummvm/commit/8aa87815a62117c5fd29b335c8e33b0cbea0da44
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "global destructor" warning

Plain char * is used instead of Common::String in DropboxStorage and
OneDriveStorage's KEY and SECRET.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 379d7bb..3d7eafe 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -36,8 +36,20 @@
 namespace Cloud {
 namespace Dropbox {
 
-Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
-Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow
+char *DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
+char *DropboxStorage::SECRET; //TODO: hide these secrets somehow
+
+void DropboxStorage::loadKeyAndSecret() {
+	Common::String k = ConfMan.get("DROPBOX_KEY", "cloud");
+	KEY = new char[k.size() + 1];
+	memcpy(KEY, k.c_str(), k.size());
+	KEY[k.size()] = 0;
+
+	k = ConfMan.get("DROPBOX_SECRET", "cloud");
+	SECRET = new char[k.size() + 1];
+	memcpy(SECRET, k.c_str(), k.size());
+	SECRET[k.size()] = 0;
+}
 
 static void saveAccessTokenCallback(Networking::JsonResponse pair) {
 	Common::JSONValue *json = (Common::JSONValue *)pair.value;
@@ -178,8 +190,7 @@ void DropboxStorage::infoMethodCallback(StorageInfoResponse pair) {
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
-	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
-	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
+	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
 		warning("No access_token found");
@@ -201,7 +212,7 @@ Common::String DropboxStorage::getAuthLink() {
 	url += "?response_type=code";
 	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
 	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
-	url += "&client_id=" + KEY;
+	url += "&client_id="; url += KEY;
 	return url;
 }
 
@@ -211,8 +222,7 @@ void DropboxStorage::authThroughConsole() {
 		return;
 	}
 
-	KEY = ConfMan.get("DROPBOX_KEY", "cloud");
-	SECRET = ConfMan.get("DROPBOX_SECRET", "cloud");
+	loadKeyAndSecret();
 
 	if (ConfMan.hasKey("dropbox_code", "cloud")) {
 		//phase 2: get access_token using specified code
@@ -232,8 +242,8 @@ void DropboxStorage::getAccessToken(Common::String code) {
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
-	request->addPostField("client_id=" + KEY);
-	request->addPostField("client_secret=" + SECRET);
+	request->addPostField("client_id=" + Common::String(KEY));
+	request->addPostField("client_secret=" + Common::String(SECRET));
 	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");	
 	ConnMan.addRequest(request);	
 }
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 19f2f9c..c411e25 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -31,7 +31,9 @@ namespace Cloud {
 namespace Dropbox {
 
 class DropboxStorage: public Cloud::Storage {
-	static Common::String KEY, SECRET;
+	static char *KEY, *SECRET;
+
+	static void loadKeyAndSecret();
 
 	Common::String _token, _uid;
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index e181fec..6f3a283 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -37,8 +37,20 @@
 namespace Cloud {
 namespace OneDrive {
 
-Common::String OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
-Common::String OneDriveStorage::SECRET; //TODO: hide these secrets somehow
+char *OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
+char *OneDriveStorage::SECRET; //TODO: hide these secrets somehow
+
+void OneDriveStorage::loadKeyAndSecret() {
+	Common::String k = ConfMan.get("ONEDRIVE_KEY", "cloud");
+	KEY = new char[k.size() + 1];
+	memcpy(KEY, k.c_str(), k.size());
+	KEY[k.size()] = 0;
+
+	k = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+	SECRET = new char[k.size() + 1];
+	memcpy(SECRET, k.c_str(), k.size());
+	SECRET[k.size()] = 0;
+}
 
 OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):
 	_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
@@ -67,8 +79,8 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 		request->addPostField("refresh_token=" + _refreshToken);
 		request->addPostField("grant_type=refresh_token");
 	}
-	request->addPostField("client_id=" + KEY);
-	request->addPostField("client_secret=" + SECRET);
+	request->addPostField("client_id=" + Common::String(KEY));
+	request->addPostField("client_secret=" + Common::String(SECRET));
 	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
 	ConnMan.addRequest(request);
 }
@@ -186,8 +198,7 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
-	KEY = ConfMan.get("ONEDRIVE_KEY", "cloud");
-	SECRET = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
 		warning("No access_token found");
@@ -215,7 +226,7 @@ Common::String OneDriveStorage::getAuthLink() {
 	url += "?response_type=code";
 	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
 	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
-	url += "&client_id=" + KEY;
+	url += "&client_id="; url += KEY;
 	url += "&scope=onedrive.appfolder%20offline_access"; //TODO
 	return url;
 }
@@ -226,8 +237,7 @@ void OneDriveStorage::authThroughConsole() {
 		return;
 	}
 
-	KEY = ConfMan.get("ONEDRIVE_KEY", "cloud");
-	SECRET = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+	loadKeyAndSecret();
 
 	if (ConfMan.hasKey("onedrive_code", "cloud")) {
 		//phase 2: get access_token using specified code
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 858af6c..290abd7 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -31,7 +31,9 @@ namespace Cloud {
 namespace OneDrive {
 
 class OneDriveStorage: public Cloud::Storage {
-	static Common::String KEY, SECRET;
+	static char *KEY, *SECRET;
+
+	static void loadKeyAndSecret();
 
 	Common::String _token, _uid, _refreshToken;
 


Commit: 14d60e62f8452dd433fcf275cd87fd9b8c3bd6fd
    https://github.com/scummvm/scummvm/commit/14d60e62f8452dd433fcf275cd87fd9b8c3bd6fd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix format string warnings

I get 'warning: ISO C++98 does not support the '%lg' ms_printf format'
warning though.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    common/json.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 3d7eafe..ad6702c 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -168,7 +168,7 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 	if (outerCallback) {		
 		//Dropbox documentation states there is no errors for this API method
 		Common::JSONObject info = json->asObject();
-		Common::String uid = Common::String::format("%d", info.getVal("uid")->asNumber());
+		Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asNumber());
 		Common::String name = info.getVal("display_name")->asString();
 		Common::String email = info.getVal("email")->asString();
 		Common::JSONObject quota = info.getVal("quota_info")->asObject();
diff --git a/common/json.cpp b/common/json.cpp
index 83ce0db..95c9123 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -934,7 +934,7 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
 			ret_string = "null";
 		else {
 			char str[80];
-			sprintf(str, "%.15Lf", _numberValue); //ss.precision(15);
+			sprintf(str, "%lg", _numberValue);
 			ret_string = str;
 		}
 		break;


Commit: 827c7e43da118f12ae614530340a566a23c42746
    https://github.com/scummvm/scummvm/commit/827c7e43da118f12ae614530340a566a23c42746
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveListDirectoryRequest

Works as charm.

Changed paths:
  A backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
  A backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/module.mk



diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
new file mode 100644
index 0000000..2c47852
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -0,0 +1,138 @@
+/* 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 "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::FileArrayCallback cb, bool recursive):
+	Networking::Request(0),
+	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _filesCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+void OneDriveListDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_files.clear();
+	_directoriesQueue.clear();
+	_currentDirectory = "";
+	_ignoreCallback = false;
+
+	_directoriesQueue.push_back(_requestedPath);
+	listNextDirectory();
+}
+
+void OneDriveListDirectoryRequest::listNextDirectory() {
+	if (_directoriesQueue.empty()) {
+		finishFiles(_files);
+		return;
+	}
+
+	_currentDirectory = _directoriesQueue.back();
+	_directoriesQueue.pop_back();
+
+	if (_currentDirectory != "" && _currentDirectory.lastChar() != '/' && _currentDirectory.lastChar() != '\\')
+		_currentDirectory += '/';
+
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + _currentDirectory;
+	url.deleteLastChar();
+	url += ":/children";
+	makeRequest(url);
+}
+
+void OneDriveListDirectoryRequest::makeRequest(Common::String url) {	
+	Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+
+void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse pair) {	
+	Common::JSONValue *json = pair.value;
+
+	if (_ignoreCallback) {
+		delete json;
+		return;
+	}
+
+	if (!json) {
+		finish();
+		return;
+	}
+
+	Common::JSONObject response = json->asObject();		
+	
+	//TODO: check that all keys exist to avoid segfaults
+
+	Common::JSONArray items = response.getVal("value")->asArray();
+	for (uint32 i = 0; i < items.size(); ++i) {
+		Common::JSONObject item = items[i]->asObject();	
+
+		Common::String path = _currentDirectory + item.getVal("name")->asString();
+		bool isDirectory = item.contains("folder");
+		uint32 size = 0, timestamp = 0;		
+		//if (!isDirectory) {
+		size = item.getVal("size")->asNumber();
+		timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());
+		//}
+
+		StorageFile file(path, size, timestamp, isDirectory);
+		_files.push_back(file);
+		if (_requestedRecursive && file.isDirectory()) {
+			_directoriesQueue.push_back(file.path());
+		}
+	}
+
+	bool hasMore = response.contains("@odata.nextLink");
+	if (hasMore) {
+		makeRequest(response.getVal("@odata.nextLink")->asString());
+	} else {
+		listNextDirectory();
+	}
+
+	delete json;
+}
+
+void OneDriveListDirectoryRequest::finish() {
+	//TODO: indicate it's interrupted
+	Common::Array<StorageFile> files;
+	finishFiles(files);
+}
+
+void OneDriveListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files) {
+	Request::finish();
+	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
+}
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
new file mode 100644
index 0000000..61eebb2
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -0,0 +1,64 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage;
+
+class OneDriveListDirectoryRequest: public Networking::Request {
+	Common::String _requestedPath;
+	bool _requestedRecursive;
+	OneDriveStorage *_storage;
+	Storage::FileArrayCallback _filesCallback;	
+	Common::Array<StorageFile> _files;
+	Common::Array<Common::String> _directoriesQueue;
+	Common::String _currentDirectory;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void listNextDirectory();
+	void listedDirectoryCallback(Networking::JsonResponse pair);
+	void makeRequest(Common::String url);
+	void finishFiles(Common::Array<StorageFile> &files);
+public:
+	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
+	virtual ~OneDriveListDirectoryRequest() { delete _filesCallback; }
+
+	virtual void handle() {}
+	virtual void restart() { start(); }
+	virtual void finish();
+};
+
+} //end of namespace OneDrive
+} //end of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 6f3a283..d143793 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -23,6 +23,7 @@
 
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
 #include "backends/cloud/downloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
@@ -161,6 +162,11 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 	delete pair.value;
 }
 
+Networking::Request *OneDriveStorage::listDirectory(Common::String path, FileArrayCallback callback, bool recursive) {
+	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, recursive));
+}
+
+
 Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback) {
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
@@ -186,6 +192,13 @@ void OneDriveStorage::fileDownloaded(BoolResponse pair) {
 	else debug("download failed!");
 }
 
+void OneDriveStorage::printFiles(FileArrayResponse pair) {
+	debug("files:");
+	Common::Array<StorageFile> &files = pair.value;
+	for (uint32 i = 0; i < files.size(); ++i)
+		debug("\t%s", files[i].path().c_str());
+}
+
 Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation
 	/*
@@ -194,7 +207,7 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 	*/
-	return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::fileDownloaded));
+	return listDirectory("subfolder", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), true);
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 290abd7..c9a32a8 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -51,6 +51,7 @@ class OneDriveStorage: public Cloud::Storage {
 
 	void printJson(Networking::JsonResponse pair);
 	void fileDownloaded(BoolResponse pair);
+	void printFiles(FileArrayResponse pair);
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair);
 public:	
@@ -73,7 +74,7 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns Common::Array<StorageFile>. */
-	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) { return nullptr; } //TODO
+	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
diff --git a/backends/module.mk b/backends/module.mk
index 54e3817..f5b1aa4 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -29,7 +29,8 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/onedrive/onedrivestorage.o \
-	cloud/onedrive/onedrivetokenrefresher.o
+	cloud/onedrive/onedrivetokenrefresher.o \
+	cloud/onedrive/onedrivelistdirectoryrequest.o
 endif
 
 ifdef USE_LIBCURL


Commit: b74d7a6861dbb5d0fafec0e6587deb7637b0ab12
    https://github.com/scummvm/scummvm/commit/b74d7a6861dbb5d0fafec0e6587deb7637b0ab12
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Requests destructors

I forgot to delete callbacks!

Changed paths:
    backends/cloud/downloadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curlrequest.cpp



diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 4ea8576..c9c243b 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -40,7 +40,10 @@ class DownloadRequest: public Networking::Request {
 	void finishBool(bool success);
 public:
 	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile);
-	virtual ~DownloadRequest() { delete _localFile; }
+	virtual ~DownloadRequest() {
+		delete _boolCallback;
+		delete _localFile;
+	}
 
 	virtual void handle();
 	virtual void restart();
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index f60c9f6..cbd3501 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -32,6 +32,13 @@ FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArra
 	start();
 }
 
+FolderDownloadRequest::~FolderDownloadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _fileArrayCallback;
+}
+
+
 void FolderDownloadRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 038dc64..949bcad 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -47,7 +47,7 @@ class FolderDownloadRequest: public Networking::Request {
 	void finishFiles(Common::Array<StorageFile> &files);
 public:
 	FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive);
-	virtual ~FolderDownloadRequest() {}
+	virtual ~FolderDownloadRequest();
 
 	virtual void handle() {}
 	virtual void restart() { start(); }
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 3c598d7..bc5de60 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -34,7 +34,7 @@ namespace Networking {
 CurlJsonRequest::CurlJsonRequest(JsonCallback cb, Common::String url):
 	CurlRequest(0, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
 
-CurlJsonRequest::~CurlJsonRequest() {}
+CurlJsonRequest::~CurlJsonRequest() { delete _jsonCallback; }
 
 char *CurlJsonRequest::getPreparedContents() {
 	//write one more byte in the end
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index a8eb425..61af633 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -33,9 +33,7 @@ namespace Networking {
 CurlRequest::CurlRequest(DataCallback cb, Common::String url):
 	Request(cb), _url(url), _stream(0), _headersList(0) {}
 
-CurlRequest::~CurlRequest() {
-	if (_stream) delete _stream;
-}
+CurlRequest::~CurlRequest() { delete _stream; }
 
 void CurlRequest::handle() {
 	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);	


Commit: 6f35fc42721895be70c6c21f87eb9a75693ff168
    https://github.com/scummvm/scummvm/commit/6f35fc42721895be70c6c21f87eb9a75693ff168
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveStorage::downloadFolder()

Just uses FolderDownloadRequest the way DropboxStorage does.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index c411e25..59dea9d 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -79,7 +79,7 @@ public:
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index d143793..c924dbf 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -25,6 +25,7 @@
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
 #include "backends/cloud/downloadrequest.h"
+#include "backends/cloud/folderdownloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/cloudmanager.h"
@@ -187,6 +188,11 @@ Networking::Request *OneDriveStorage::download(Common::String remotePath, Common
 	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
 }
 
+/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
+Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) {
+	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive));
+}
+
 void OneDriveStorage::fileDownloaded(BoolResponse pair) {
 	if (pair.value) debug("file downloaded!");
 	else debug("download failed!");
@@ -207,7 +213,7 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 	*/
-	return listDirectory("subfolder", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), true);
+	return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index c9a32a8..674d136 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -85,6 +85,9 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
 
+	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
+
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 8ae5308..8009075 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -81,6 +81,9 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
 
+	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false) = 0;
+
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback) = 0;
 


Commit: f4dfaed19d14c309e44d390b44bff571566779a5
    https://github.com/scummvm/scummvm/commit/f4dfaed19d14c309e44d390b44bff571566779a5
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
Replace 0 constant with nullptr in getCurrentStorage()

Changed paths:
    backends/cloud/manager.cpp



diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index a7e92df..9e11f0d 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -97,7 +97,7 @@ void Manager::addStorage(Cloud::Storage *storage, bool makeCurrent, bool saveCon
 Storage *Manager::getCurrentStorage() {
 	if (_currentStorageIndex < _storages.size())
 		return _storages[_currentStorageIndex];
-	return 0;
+	return nullptr;
 }
 
 void Manager::syncSaves(Storage::BoolCallback callback) {


Commit: 81c34adaef269524b1f53adcac722aa6a9730075
    https://github.com/scummvm/scummvm/commit/81c34adaef269524b1f53adcac722aa6a9730075
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
Fix comment formatting

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/iso8601.cpp
    backends/cloud/iso8601.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/storage.h
    backends/cloud/storagefile.cpp
    backends/cloud/storagefile.h
    backends/cloud/storageinfo.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h
    backends/networking/curl/request.h
    common/callback.h
    common/cloudmanager.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index c7f6f75..8d5e244 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -100,4 +100,4 @@ void DownloadRequest::finishBool(bool success) {
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index c9c243b..0bad5df 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -50,6 +50,6 @@ public:
 	virtual void finish();
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 04fbf46..6ea90c1 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -136,5 +136,5 @@ void DropboxListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files)
 	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
 }
 
-} //end of namespace Dropbox
-} //end of namespace Cloud
+} // End of namespace Dropbox
+} // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index e2a9ebf..8539be1 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -55,7 +55,7 @@ public:
 	virtual void finish();
 };
 
-} //end of namespace Dropbox
-} //end of namespace Cloud
+} // End of namespace Dropbox
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index ad6702c..b1dd865 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -248,5 +248,5 @@ void DropboxStorage::getAccessToken(Common::String code) {
 	ConnMan.addRequest(request);	
 }
 
-} //end of namespace Dropbox
-} //end of namespace Cloud
+} // End of namespace Dropbox
+} // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 59dea9d..da508d7 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -51,17 +51,16 @@ public:
 	virtual ~DropboxStorage();
 
 	/**
-	* Storage methods, which are used by CloudManager to save
-	* storage in configuration file.
-	*/
+	 * Storage methods, which are used by CloudManager to save
+	 * storage in configuration file.
+	 */
 
 	/**
-	* Save storage data using ConfMan.
-	* @param keyPrefix all saved keys must start with this prefix.
-	* @note every Storage must write keyPrefix + "type" key
-	*       with common value (e.g. "Dropbox").
-	*/
-
+	 * Save storage data using ConfMan.
+	 * @param keyPrefix all saved keys must start with this prefix.
+	 * @note every Storage must write keyPrefix + "type" key
+	 *       with common value (e.g. "Dropbox").
+	 */
 	virtual void saveConfig(Common::String keyPrefix);
 
 	/** Public Cloud API comes down there. */
@@ -106,23 +105,23 @@ public:
 	virtual bool isWorking() { return false; } //TODO
 
 	/**
-	* Load token and user id from configs and return DropboxStorage for those.	
-	* @return pointer to the newly created DropboxStorage or 0 if some problem occured.
-	*/
+	 * Load token and user id from configs and return DropboxStorage for those.	
+	 * @return pointer to the newly created DropboxStorage or 0 if some problem occured.
+	 */
 	static DropboxStorage *loadFromConfig(Common::String keyPrefix);
 
 	/**
-	* Returns Dropbox auth link.
-	*/
+	 * Returns Dropbox auth link.
+	 */
 	static Common::String getAuthLink();
 
 	/**
-	* Show message with Dropbox auth instructions. (Temporary)
-	*/
+	 * Show message with Dropbox auth instructions. (Temporary)
+	 */
 	static void authThroughConsole();
 };
 
-} //end of namespace Dropbox
-} //end of namespace Cloud
+} // End of namespace Dropbox
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index cbd3501..9c2e8b1 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -115,4 +115,4 @@ void FolderDownloadRequest::finishFiles(Common::Array<StorageFile> &files) {
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 949bcad..33fa599 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -54,6 +54,6 @@ public:
 	virtual void finish();
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
index 12cbb74..3bc169a 100644
--- a/backends/cloud/iso8601.cpp
+++ b/backends/cloud/iso8601.cpp
@@ -87,5 +87,5 @@ uint32 convertToTimestamp(const Common::String &iso8601Date) {
 	return minutes * 60 + s;
 }
 
-} //end of namespace ISO8601
-} //end of namespace Cloud
+} // End of namespace ISO8601
+} // End of namespace Cloud
diff --git a/backends/cloud/iso8601.h b/backends/cloud/iso8601.h
index a53b3bb..6e11322 100644
--- a/backends/cloud/iso8601.h
+++ b/backends/cloud/iso8601.h
@@ -31,7 +31,7 @@ namespace ISO8601 {
 	/** Returns timestamp corresponding to given ISO 8601 date */
 	uint32 convertToTimestamp(const Common::String &iso8601Date);
 
-} //end of namespace ISO8601
-} //end of namespace Cloud
+} // End of namespace ISO8601
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 9e11f0d..959f395 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -105,4 +105,4 @@ void Manager::syncSaves(Storage::BoolCallback callback) {
 	if (storage) storage->syncSaves(callback);
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 08106e0..6eaf17b 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -44,6 +44,6 @@ public:
 	virtual void syncSaves(Storage::BoolCallback callback);
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 2c47852..452c47a 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -134,5 +134,5 @@ void OneDriveListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files
 	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
 }
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index 61eebb2..ce407c0 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -58,7 +58,7 @@ public:
 	virtual void finish();
 };
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index c924dbf..c8b4ab1 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -271,5 +271,5 @@ void OneDriveStorage::authThroughConsole() {
 	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
 }
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 674d136..3dc5b7a 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -41,9 +41,9 @@ class OneDriveStorage: public Cloud::Storage {
 	OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
 
 	/**
-	* This private constructor is called from authThroughConsole() (phase 2).
-	* It uses OAuth code flow to get tokens.
-	*/
+	 * This private constructor is called from authThroughConsole() (phase 2).
+	 * It uses OAuth code flow to get tokens.
+	 */
 	OneDriveStorage(Common::String code);
 
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair);
@@ -58,17 +58,16 @@ public:
 	virtual ~OneDriveStorage();
 
 	/**
-	* Storage methods, which are used by CloudManager to save
-	* storage in configuration file.
-	*/
+	 * Storage methods, which are used by CloudManager to save
+	 * storage in configuration file.
+	 */
 
 	/**
-	* Save storage data using ConfMan.
-	* @param keyPrefix all saved keys must start with this prefix.
-	* @note every Storage must write keyPrefix + "type" key
-	*       with common value (e.g. "Dropbox").
-	*/
-
+	 * Save storage data using ConfMan.
+	 * @param keyPrefix all saved keys must start with this prefix.
+	 * @note every Storage must write keyPrefix + "type" key
+	 *       with common value (e.g. "Dropbox").
+	 */
 	virtual void saveConfig(Common::String keyPrefix);
 
 	/** Public Cloud API comes down there. */
@@ -110,32 +109,32 @@ public:
 	virtual bool isWorking() { return false; } //TODO
 
 	/**
-	* Load token and user id from configs and return OneDriveStorage for those.	
-	* @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
-	*/
+	 * Load token and user id from configs and return OneDriveStorage for those.	
+	 * @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
+	 */
 	static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
 
 	/**
-	* Returns OneDrive auth link.
-	*/
+	 * Returns OneDrive auth link.
+	 */
 	static Common::String getAuthLink();
 
 	/**
-	* Show message with OneDrive auth instructions. (Temporary)
-	*/
+	 * Show message with OneDrive auth instructions. (Temporary)
+	 */
 	static void authThroughConsole();
 
 	/**
-	* Gets new access_token. If <code> passed is "", refresh_token is used.
-	* Use "" in order to refresh token and pass a callback, so you could
-	* continue your work when new token is available.
-	*/
+	 * Gets new access_token. If <code> passed is "", refresh_token is used.
+	 * Use "" in order to refresh token and pass a callback, so you could
+	 * continue your work when new token is available.
+	 */
 	void getAccessToken(BoolCallback callback, Common::String code = "");
 
 	Common::String accessToken() { return _token; }
 };
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 9421483..d932f9a 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -93,5 +93,5 @@ void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers)
 }
 
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 08212bf..90ca9d6 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -50,7 +50,7 @@ public:
 	}
 };
 
-} //end of namespace OneDrive
-} //end of namespace Cloud
+} // End of namespace OneDrive
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 8009075..ac74f90 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -58,7 +58,6 @@ public:
 	 * @note every Storage must write keyPrefix + "type" key
 	 *       with common value (e.g. "Dropbox").
 	 */
-
 	virtual void saveConfig(Common::String keyPrefix) = 0;
 
 	/**
@@ -106,6 +105,6 @@ public:
 	virtual bool isWorking() = 0;
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index c0f8d3a..b37b5e0 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -53,4 +53,4 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 	_isDirectory = dir;
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index ced99fa..704a038 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -28,11 +28,10 @@
 namespace Cloud {
 
 /**
-* StorageFile represents a file storaged on remote cloud storage.
-* It contains basic information about a file, and might be used
-* when listing directories or syncing files.
-*/
-
+ * StorageFile represents a file storaged on remote cloud storage.
+ * It contains basic information about a file, and might be used
+ * when listing directories or syncing files.
+ */
 class StorageFile {
 	Common::String _path, _name;
 	uint32 _size, _timestamp;
@@ -49,6 +48,6 @@ public:
 	bool isDirectory() const { return _isDirectory; }
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
index f095635..1492898 100644
--- a/backends/cloud/storageinfo.h
+++ b/backends/cloud/storageinfo.h
@@ -48,6 +48,6 @@ public:
 
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 337ad42..949dc94 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -131,4 +131,4 @@ void ConnectionManager::processTransfers() {
 	}
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 2d37c05..c632fa5 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -54,29 +54,29 @@ public:
 	virtual ~ConnectionManager();
 
 	/**
-	* All libcurl transfers are going through this ConnectionManager.
-	* So, if you want to start any libcurl transfer, you must create
-	* an easy handle and register it using this method.
-	*/
+	 * All libcurl transfers are going through this ConnectionManager.
+	 * So, if you want to start any libcurl transfer, you must create
+	 * an easy handle and register it using this method.
+	 */
 	void registerEasyHandle(CURL *easy);
 
 	/**
-	* Use this method to add new Request into manager's queue.
-	* Manager will periodically call handle() method of these
-	* Requests until they set their state to FINISHED.
-	*
-	* If Request's state is RETRY, handleRetry() is called instead.
-	*
-	* @note This method starts the timer if it's not started yet.
-	*
-	* @return the same Request pointer, just as a shortcut
-	*/
+	 * Use this method to add new Request into manager's queue.
+	 * Manager will periodically call handle() method of these
+	 * Requests until they set their state to FINISHED.
+	 *
+	 * If Request's state is RETRY, handleRetry() is called instead.
+	 *
+	 * @note This method starts the timer if it's not started yet.
+	 *
+	 * @return the same Request pointer, just as a shortcut
+	 */
 	Request *addRequest(Request *request);
 };
 
 /** Shortcut for accessing the connection manager. */
 #define ConnMan		Networking::ConnectionManager::instance()
 
-} //end of namespace Networking
+} // End of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index bc5de60..d114d69 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -95,4 +95,4 @@ void CurlJsonRequest::finish() {
 	finishJson(0);
 }
 
-} //end of namespace Networking
+} // End of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 9bd12a1..5e08be2 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -52,6 +52,6 @@ public:
 	virtual void finish();
 };
 
-}  //end of namespace Networking
+} // End of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 61af633..a745741 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -78,4 +78,4 @@ NetworkReadStreamResponse CurlRequest::execute() {
 	return NetworkReadStreamResponse(this, _stream);
 }
 
-} //end of namespace Networking
+} // End of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index f77dce9..8623a48 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -60,15 +60,15 @@ public:
 	virtual void addPostField(Common::String field);
 
 	/**
-	* Starts this Request with ConnMan.
-	* @return its NetworkReadStream in NetworkReadStreamResponse.
-	*/
+	 * Starts this Request with ConnMan.
+	 * @return its NetworkReadStream in NetworkReadStreamResponse.
+	 */
 	virtual NetworkReadStreamResponse execute();
 
 	/** Returns Request's NetworkReadStream. */
 	const NetworkReadStream *getNetworkReadStream() const { return _stream; }
 };
 
-}  //end of namespace Networking
+} // End of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 8658fc7..997f0b2 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -85,4 +85,4 @@ long NetworkReadStream::httpResponseCode() const {
 	return responseCode;
 }
 
-} //end of namespace Cloud
+} // End of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 14c00a4..33edade 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -41,48 +41,48 @@ public:
 	virtual ~NetworkReadStream();
 
 	/**
-	* Returns true if a read failed because the stream end has been reached.
-	* This flag is cleared by clearErr().
-	* For a SeekableReadStream, it is also cleared by a successful seek.
-	*
-	* @note The semantics of any implementation of this method are
-	* supposed to match those of ISO C feof(). In particular, in a stream
-	* with N bytes, reading exactly N bytes from the start should *not*
-	* set eos; only reading *beyond* the available data should set it.
-	*/
+	 * Returns true if a read failed because the stream end has been reached.
+	 * This flag is cleared by clearErr().
+	 * For a SeekableReadStream, it is also cleared by a successful seek.
+	 *
+	 * @note The semantics of any implementation of this method are
+	 * supposed to match those of ISO C feof(). In particular, in a stream
+	 * with N bytes, reading exactly N bytes from the start should *not*
+	 * set eos; only reading *beyond* the available data should set it.
+	 */
 	virtual bool eos() const;
 
 	/**
-	* Read data from the stream. Subclasses must implement this
-	* method; all other read methods are implemented using it.
-	*
-	* @note The semantics of any implementation of this method are
-	* supposed to match those of ISO C fread(), in particular where
-	* it concerns setting error and end of file/stream flags.
-	*
-	* @param dataPtr	pointer to a buffer into which the data is read
-	* @param dataSize	number of bytes to be read
-	* @return the number of bytes which were actually read.
-	*/
+	 * Read data from the stream. Subclasses must implement this
+	 * method; all other read methods are implemented using it.
+	 *
+	 * @note The semantics of any implementation of this method are
+	 * supposed to match those of ISO C fread(), in particular where
+	 * it concerns setting error and end of file/stream flags.
+	 *
+	 * @param dataPtr	pointer to a buffer into which the data is read
+	 * @param dataSize	number of bytes to be read
+	 * @return the number of bytes which were actually read.
+	 */
 	virtual uint32 read(void *dataPtr, uint32 dataSize);
 
 	/**
-	* This method is called by ConnectionManager to indicate
-	* that transfer is finished.
-	*
-	* @note It's called on failure too.
-	*/
+	 * This method is called by ConnectionManager to indicate
+	 * that transfer is finished.
+	 *
+	 * @note It's called on failure too.
+	 */
 	void finished();
 
 	/**
-	* Returns HTTP response code from inner CURL handle.
-	* It returns -1 to indicate there is no inner handle.
-	*
-	* @note This method should be called when eos() == true.
-	*/
+	 * Returns HTTP response code from inner CURL handle.
+	 * It returns -1 to indicate there is no inner handle.
+	 *
+	 * @note This method should be called when eos() == true.
+	 */
 	long httpResponseCode() const;
 };
 
-} //end of namespace Networking
+} // End of namespace Networking
 
 #endif
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index ff919e0..a3c723d 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -31,18 +31,18 @@ namespace Networking {
 class Request;
 
 /**
-* Response<T> is a struct to be returned from Request
-* to user's callbacks. It's a type safe way to indicate
-* which "return value" Request has and user awaits.
-*
-* It just keeps a Request pointer together with
-* some T value (which might be a pointer, a reference
-* or a plain type (copied by value)).
-*
-* To make it more convenient, typedefs are used.
-* For example, Response<void *> is called DataResponse
-* and corresponding callback pointer is DataCallback.
-*/
+ * Response<T> is a struct to be returned from Request
+ * to user's callbacks. It's a type safe way to indicate
+ * which "return value" Request has and user awaits.
+ *
+ * It just keeps a Request pointer together with
+ * some T value (which might be a pointer, a reference
+ * or a plain type (copied by value)).
+ *
+ * To make it more convenient, typedefs are used.
+ * For example, Response<void *> is called DataResponse
+ * and corresponding callback pointer is DataCallback.
+ */
 
 template<typename T> struct Response {
 	Request *request;
@@ -55,27 +55,26 @@ typedef Response<void *> DataReponse;
 typedef Common::BaseCallback<DataReponse> *DataCallback;
 
 /**
-* RequestState is used to indicate current Request state.
-* ConnectionManager uses it to decide what to do with the Request.
-*
-* PROCESSING state indicates that Request is working.
-* ConnectionManager calls handle() method of Requests in that state.
-*
-* PAUSED state indicates that Request is not working.
-* ConnectionManager keeps Requests in that state and doesn't call any methods of those.
-*
-* RETRY state indicates that Request must restart after a few seconds.
-* ConnectionManager calls handleRetry() method of Requests in that state.
-* Default handleRetry() implementation decreases _retryInSeconds value
-* until it reaches zero. When it does, Request's restart() method is called.
-*
-* FINISHED state indicates that Request did the work and might be deleted.
-* ConnectionManager deletes Requests in that state.
-* After this state is set, but before ConnectionManager deletes the Request,
-* Request calls user's callback. User can ask Request to change its state
-* by calling retry() or pause() methods and Request won't be deleted.
-*/
-
+ * RequestState is used to indicate current Request state.
+ * ConnectionManager uses it to decide what to do with the Request.
+ *
+ * PROCESSING state indicates that Request is working.
+ * ConnectionManager calls handle() method of Requests in that state.
+ *
+ * PAUSED state indicates that Request is not working.
+ * ConnectionManager keeps Requests in that state and doesn't call any methods of those.
+ *
+ * RETRY state indicates that Request must restart after a few seconds.
+ * ConnectionManager calls handleRetry() method of Requests in that state.
+ * Default handleRetry() implementation decreases _retryInSeconds value
+ * until it reaches zero. When it does, Request's restart() method is called.
+ *
+ * FINISHED state indicates that Request did the work and might be deleted.
+ * ConnectionManager deletes Requests in that state.
+ * After this state is set, but before ConnectionManager deletes the Request,
+ * Request calls user's callback. User can ask Request to change its state
+ * by calling retry() or pause() methods and Request won't be deleted.
+ */
 enum RequestState {
 	PROCESSING,
 	PAUSED,
@@ -86,24 +85,22 @@ enum RequestState {
 class Request {
 protected:
 	/**
-	* Callback, which should be called when Request is finished.
-	* That's the way Requests pass the result to the code which asked to create this request.
-	*
-	* @note some Requests use their own callbacks to return something but void *.
-	* @note callback must be called in finish() or similar method.
-	*/
-
+	 * Callback, which should be called when Request is finished.
+	 * That's the way Requests pass the result to the code which asked to create this request.
+	 *
+	 * @note some Requests use their own callbacks to return something but void *.
+	 * @note callback must be called in finish() or similar method.
+	 */
 	DataCallback _callback;
 
 	/**
-	* Request state, which is used by ConnectionManager to determine
-	* whether request might be deleted or it's still working.
-	*
-	* State might be changed from outside with finish(), pause() or
-	* retry() methods. Override these if you want to react to these
-	* changes correctly.
-	*/
-
+	 * Request state, which is used by ConnectionManager to determine
+	 * whether request might be deleted or it's still working.
+	 *
+	 * State might be changed from outside with finish(), pause() or
+	 * retry() methods. Override these if you want to react to these
+	 * changes correctly.
+	 */
 	RequestState _state;
 
 	/** In RETRY state this indicates whether it's time to call restart(). */
@@ -132,10 +129,10 @@ public:
 	virtual void pause() { _state = PAUSED; }
 
 	/**
-	* Method, which is called to *interrupt* the Request.
-	* When it's called, Request must stop its work and
-	* call the callback to notify user of failure.
-	*/
+	 * Method, which is called to *interrupt* the Request.
+	 * When it's called, Request must stop its work and
+	 * call the callback to notify user of failure.
+	 */
 	virtual void finish() { _state = FINISHED; }
 
 	/** Method, which is called to retry the Request. */
@@ -148,6 +145,6 @@ public:
 	RequestState state() const { return _state; }
 };
 
-} //end of namespace Cloud
+} // End of namespace Cloud
 
 #endif
diff --git a/common/callback.h b/common/callback.h
index 4356e4b..c6c249a 100644
--- a/common/callback.h
+++ b/common/callback.h
@@ -26,20 +26,19 @@
 namespace Common {
 
 /**
-* BaseCallback<S> is a simple base class for object-oriented callbacks.
-*
-* Object-oriented callbacks are such callbacks that know exact instance
-* which method must be called.
-*
-* For backwards compatibility purposes, there is a GlobalFunctionCallback,
-* which is BaseCallback<void *>, so it can be used with global C-like
-* functions too.
-*
-* <S> is the type, which is passed to operator() of this callback.
-* This allows you to specify that you accept a callback, which wants
-* to receive an <S> object.
-*/
-
+ * BaseCallback<S> is a simple base class for object-oriented callbacks.
+ *
+ * Object-oriented callbacks are such callbacks that know exact instance
+ * which method must be called.
+ *
+ * For backwards compatibility purposes, there is a GlobalFunctionCallback,
+ * which is BaseCallback<void *>, so it can be used with global C-like
+ * functions too.
+ *
+ * <S> is the type, which is passed to operator() of this callback.
+ * This allows you to specify that you accept a callback, which wants
+ * to receive an <S> object.
+ */
 template<typename S = void *> class BaseCallback {
 public:
 	BaseCallback() {}
@@ -48,13 +47,12 @@ public:
 };
 
 /**
-* GlobalFunctionCallback<T> is a simple wrapper for global C functions.
-*
-* If there is a method, which accepts BaseCallback<T>, you can
-* easily pass your C function by passing
-*     new GlobalFunctionCallback<T>(yourFunction)
-*/
-
+ * GlobalFunctionCallback<T> is a simple wrapper for global C functions.
+ *
+ * If there is a method, which accepts BaseCallback<T>, you can
+ * easily pass your C function by passing
+ *     new GlobalFunctionCallback<T>(yourFunction)
+ */
 template<typename T> class GlobalFunctionCallback: public BaseCallback<T> {
 	typedef void(*GlobalFunction)(T result);
 	GlobalFunction _callback;
@@ -68,20 +66,19 @@ public:
 };
 
 /**
-* Callback<T, S> implements an object-oriented callback.
-*
-* <T> stands for a class which method you want to call.
-* <S>, again, is the type of an object passed to operator().
-*
-* So, if you have void MyClass::myMethod(AnotherClass) method,
-* the corresponding callback is Callback<MyClass, AnotherClass>.
-* You create it similarly to this:
-*     new Callback<MyClass, AnotherClass>(
-*         pointerToMyClassObject,
-*         &MyClass::myMethod
-*     )
-*/
-
+ * Callback<T, S> implements an object-oriented callback.
+ *
+ * <T> stands for a class which method you want to call.
+ * <S>, again, is the type of an object passed to operator().
+ *
+ * So, if you have void MyClass::myMethod(AnotherClass) method,
+ * the corresponding callback is Callback<MyClass, AnotherClass>.
+ * You create it similarly to this:
+ *     new Callback<MyClass, AnotherClass>(
+ *         pointerToMyClassObject,
+ *         &MyClass::myMethod
+ *     )
+ */
 template<class T, typename S = void *> class Callback: public BaseCallback<S> {
 protected:
 	typedef void(T::*TMethod)(S);
@@ -94,37 +91,36 @@ public:
 };
 
 /**
-* CallbackBridge<T, OS, S> helps you to chain callbacks.
-*
-* CallbackBridge keeps a pointer to BaseCallback<OS>.
-* When its operator() is called, it passes this pointer
-* along with the actual data (of type <S>) to the method
-* of <T> class.
-*
-* This is needed when you have to call a callback only
-* when your own callback is called. So, your callback
-* is "inner" and the other one is "outer".
-*
-* CallbackBridge implements the "inner" one and calls
-* the method you wanted. It passes the "outer", so you
-* can call it from your method. You can ignore it, but
-* probably there is no point in using CallbackBridge then.
-*
-* So, if you receive a BaseCallback<SomeClass> callback
-* and you want to call it from your MyClass::myMethod method,
-* you should create CallbackBridge<MyClass, SomeClass, S>,
-* where <S> is data type you want to receive in MyClass::myMethod.
-*
-* You create it similarly to this:
-*     new Callback<MyClass, SomeClass, AnotherClass>(
-*         pointerToMyClassObject,
-*         &MyClass::myMethod,
-*         outerCallback
-*     )
-* where `outerCallback` is BaseCallback<SomeClass> and `myMethod` is:
-* void MyClass::myMethod(BaseCallback<SomeClass> *, AnotherClass)
-*/
-
+ * CallbackBridge<T, OS, S> helps you to chain callbacks.
+ *
+ * CallbackBridge keeps a pointer to BaseCallback<OS>.
+ * When its operator() is called, it passes this pointer
+ * along with the actual data (of type <S>) to the method
+ * of <T> class.
+ *
+ * This is needed when you have to call a callback only
+ * when your own callback is called. So, your callback
+ * is "inner" and the other one is "outer".
+ *
+ * CallbackBridge implements the "inner" one and calls
+ * the method you wanted. It passes the "outer", so you
+ * can call it from your method. You can ignore it, but
+ * probably there is no point in using CallbackBridge then.
+ *
+ * So, if you receive a BaseCallback<SomeClass> callback
+ * and you want to call it from your MyClass::myMethod method,
+ * you should create CallbackBridge<MyClass, SomeClass, S>,
+ * where <S> is data type you want to receive in MyClass::myMethod.
+ *
+ * You create it similarly to this:
+ *     new Callback<MyClass, SomeClass, AnotherClass>(
+ *         pointerToMyClassObject,
+ *         &MyClass::myMethod,
+ *         outerCallback
+ *     )
+ * where `outerCallback` is BaseCallback<SomeClass> and `myMethod` is:
+ * void MyClass::myMethod(BaseCallback<SomeClass>  *, AnotherClass)
+ */
 template<class T, typename OS, typename S = void *> class CallbackBridge: public BaseCallback<S> {
 	typedef void(T::*TCallbackMethod)(BaseCallback<OS> *, S);
 	T *_object;
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index a480bdf..350901e 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -33,45 +33,40 @@ public:
 	virtual ~CloudManager() {}
 
 	/**
-	* Loads all information from configs and creates current Storage instance.
-	*
-	* @note It's called once on startup in scummvm_main().
-	*/
-
+	 * Loads all information from configs and creates current Storage instance.
+	 *
+	 * @note It's called once on startup in scummvm_main().
+	 */
 	virtual void init() = 0;
 
 	/**
-	* Saves all information into configuration file.
-	*/
-
+	 * Saves all information into configuration file.
+	 */
 	virtual void save() = 0;
 
 	/**
-	* Adds new Storage into list.	
-	*
-	* @param	storage Cloud::Storage to add.
-	* @param	makeCurrent whether added storage should be the new current storage.
-	* @param	saveConfig whether save() should be called to update configuration file.
-	*/
-
+	 * Adds new Storage into list.	
+	 *
+	 * @param	storage Cloud::Storage to add.
+	 * @param	makeCurrent whether added storage should be the new current storage.
+	 * @param	saveConfig whether save() should be called to update configuration file.
+	 */
 	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true) = 0;
 
 	/**
-	* Returns active Storage, which could be used to interact
-	*  with cloud storage.
-	*
-	* @return	active Cloud::Storage or null, if there is no active Storage.
-	*/
-
+	 * Returns active Storage, which could be used to interact
+	 *  with cloud storage.
+	 *
+	 * @return	active Cloud::Storage or null, if there is no active Storage.
+	 */
 	virtual Cloud::Storage *getCurrentStorage() = 0;
 
 	/**
-	* Starts saves syncing process in currently active storage if there is any.
-	*/
-
+	 * Starts saves syncing process in currently active storage if there is any.
+	 */
 	virtual void syncSaves(Cloud::Storage::BoolCallback callback = 0) = 0;
 };
 
-} //end of namespace Common
+} // End of namespace Common
 
 #endif


Commit: 86d5b1b2e13c09f52d1af21b1f9b43d4b3c712c3
    https://github.com/scummvm/scummvm/commit/86d5b1b2e13c09f52d1af21b1f9b43d4b3c712c3
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
Remove some unnecessary blank lines

Changed paths:
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 9c2e8b1..00bddb9 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -38,7 +38,6 @@ FolderDownloadRequest::~FolderDownloadRequest() {
 	delete _fileArrayCallback;
 }
 
-
 void FolderDownloadRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 452c47a..f1402c4 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -76,7 +76,6 @@ void OneDriveListDirectoryRequest::makeRequest(Common::String url) {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-
 void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse pair) {	
 	Common::JSONValue *json = pair.value;
 


Commit: 1747f8ec420edd0517e37a88e5fdd57f1b11b22f
    https://github.com/scummvm/scummvm/commit/1747f8ec420edd0517e37a88e5fdd57f1b11b22f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "device_id" into configuration file

Changed paths:
    backends/cloud/manager.cpp
    backends/cloud/manager.h



diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 959f395..13f23b8 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -22,12 +22,13 @@
 
 #include "backends/cloud/manager.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
 #include "common/config-manager.h"
-#include "onedrive/onedrivestorage.h"
+#include "common/random.h"
 
 namespace Cloud {
 
-Manager::Manager(): _currentStorageIndex(0) {}
+Manager::Manager(): _currentStorageIndex(0), _deviceId(0) {}
 
 Manager::~Manager() {
 	//TODO: do we have to save storages on manager destruction?	
@@ -40,6 +41,15 @@ void Manager::init() {
 	bool offerDropbox = false;
 	bool offerOneDrive = true;
 
+	if (!ConfMan.hasKey("device_id", "cloud")) {
+		Common::RandomSource source("Cloud Random Source");
+		_deviceId = source.getRandomNumber(UINT_MAX - 1);
+		ConfMan.setInt("device_id", _deviceId, "cloud");
+		ConfMan.flushToDisk();
+	} else {
+		_deviceId = ConfMan.getInt("device_id", "cloud");
+	}
+
 	if (ConfMan.hasKey("storages_number", "cloud")) {
 		int storages = ConfMan.getInt("storages_number", "cloud");
 		for (int i = 1; i <= storages; ++i) {
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 6eaf17b..f472b80 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -31,6 +31,7 @@ namespace Cloud {
 class Manager: public Common::CloudManager {
 	Common::Array<Storage *> _storages;
 	uint _currentStorageIndex;
+	uint _deviceId;
 
 public:
 	Manager();


Commit: c235aa29f997b9bd000be5424662b114957f1640
    https://github.com/scummvm/scummvm/commit/c235aa29f997b9bd000be5424662b114957f1640
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add SavesSyncRequest sketch

Never tested it, actually. It requires Storage to implement upload()
method and me to find some way to get saves' ReadStream.

The saveTimestamps() and loadTimestamps() part should be tested, other
parts should work fine.

Changed paths:
  A backends/cloud/savessyncrequest.cpp
  A backends/cloud/savessyncrequest.h
    backends/module.mk



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
new file mode 100644
index 0000000..d48ec6b
--- /dev/null
+++ b/backends/cloud/savessyncrequest.cpp
@@ -0,0 +1,229 @@
+/* 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 "backends/cloud/savessyncrequest.h"
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace Cloud {
+
+SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback):
+	Request(nullptr), _storage(storage), _boolCallback(callback),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+SavesSyncRequest::~SavesSyncRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void SavesSyncRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();	
+	_currentDownloadingFile = StorageFile();
+	_currentUploadingFile = "";
+	_filesToDownload.clear();
+	_filesToUpload.clear();
+	_localFilesTimestamps.clear();
+	_ignoreCallback = false;
+
+	//load timestamps
+	loadTimestamps();
+
+	//list saves directory
+	_workingRequest = _storage->listDirectory("saves", new Common::Callback<SavesSyncRequest, Storage::FileArrayResponse>(this, &SavesSyncRequest::directoryListedCallback));
+}
+
+void SavesSyncRequest::directoryListedCallback(Storage::FileArrayResponse pair) {
+	if (_ignoreCallback) return;
+	//TODO: somehow ListDirectory requests must indicate that file array is incomplete
+
+	const uint32 INVALID_TIMESTAMP = UINT_MAX;
+	
+	//determine which files to download and which files to upload
+	Common::Array<StorageFile> &remoteFiles = pair.value;
+	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
+		StorageFile &file = remoteFiles[i];
+		if (file.isDirectory()) continue;
+		Common::String name = file.name();
+		if (!_localFilesTimestamps.contains(name))
+			_filesToDownload.push_back(file);
+		else {
+			if (_localFilesTimestamps[name] != INVALID_TIMESTAMP) {
+				if (_localFilesTimestamps[name] == file.timestamp())
+					continue;
+
+				//we actually can have some files not only with timestamp < remote
+				//but also with timestamp > remote (when we have been using ANOTHER CLOUD and then switched back)
+				if (_localFilesTimestamps[name] < file.timestamp())
+					_filesToDownload.push_back(file);
+				else
+					_filesToUpload.push_back(file.name());
+			}
+		}
+	}
+
+	//upload files with invalid timestamp (the ones we've added - means they might not have any remote version)
+	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
+		if (i->_value == INVALID_TIMESTAMP)
+			_filesToUpload.push_back(i->_key);
+	}
+
+	//start downloading files
+	downloadNextFile();
+}
+
+void SavesSyncRequest::downloadNextFile() {
+	if (_filesToDownload.empty()) {
+		uploadNextFile();
+		return;
+	}
+
+	_currentDownloadingFile = _filesToDownload.back();
+	_filesToDownload.pop_back();
+
+	_workingRequest = _storage->download(_currentDownloadingFile.path(), "saves/" + _currentDownloadingFile.name(),
+		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback)
+	);
+}
+
+void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
+	if (_ignoreCallback) return;
+
+	//stop syncing if download failed
+	if (!pair.value) {
+		finish();
+		return;
+	}
+
+	//update local timestamp for downloaded file
+	_localFilesTimestamps[_currentDownloadingFile.name()] = _currentDownloadingFile.timestamp();
+
+	//continue downloading files
+	downloadNextFile();
+}
+
+void SavesSyncRequest::uploadNextFile() {
+	if (_filesToUpload.empty()) {
+		finishBool(true);
+		return;
+	}
+
+	_currentUploadingFile = _filesToUpload.back();
+	_filesToUpload.pop_back();
+
+	_workingRequest = _storage->upload("saves/" + _currentUploadingFile, nullptr, //TODO: pass save's read stream
+		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileUploadedCallback)
+	);
+}
+
+void SavesSyncRequest::fileUploadedCallback(Storage::BoolResponse pair) {
+	if (_ignoreCallback) return;
+
+	//stop syncing if upload failed
+	if (!pair.value) {
+		finish();
+		return;
+	}
+
+	//TODO: update local timestamp for the uploaded file
+	//_localFilesTimestamps[_currentUploadingFile] = pair.request.<what?>;
+
+	//continue uploading files
+	uploadNextFile();
+}
+
+void SavesSyncRequest::handle() {}
+
+void SavesSyncRequest::restart() { start(); }
+
+void SavesSyncRequest::finish() { finishBool(false); }
+
+void SavesSyncRequest::finishBool(bool success) {
+	Request::finish();
+
+	//save updated timestamps (even if Request failed, there would be only valid timestamps)
+	saveTimestamps();
+
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+void SavesSyncRequest::loadTimestamps() {
+	Common::File f;
+	if (!f.open("saves/timestamps"))
+		error("SavesSyncRequest: failed to open 'saves/timestamps' file to load timestamps");
+	
+	while (!f.eos()) {
+		//read filename into buffer (reading until the first ' ')
+		Common::String buffer;
+		while (!f.eos()) {
+			byte b = f.readByte();
+			if (b == ' ') break;
+			buffer += (char)b;
+		}
+		
+		//read timestamp info buffer (reading until ' ' or some line ending char)
+		Common::String filename = buffer;
+		bool lineEnded = false;
+		buffer = "";
+		while (!f.eos()) {
+			byte b = f.readByte();
+			if (b == ' ' || b == '\n' || b == '\r') {
+				lineEnded = (b == '\n');
+				break;
+			}
+			buffer += (char)b;
+		}
+
+		//parse timestamp
+		uint timestamp = atol(buffer.c_str());
+		_localFilesTimestamps[filename] = timestamp;
+
+		//read until the end of the line
+		if (!lineEnded) {
+			while (!f.eos()) {
+				byte b = f.readByte();
+				if (b == '\n') break;
+			}
+		}
+	}
+
+	f.close();
+}
+
+void SavesSyncRequest::saveTimestamps() {
+	Common::DumpFile f;
+	if (!f.open("saves/timestamps", true))
+		error("SavesSyncRequest: failed to open 'saves/timestamps' file to save timestamps");
+	Common::String data;
+	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i)
+		data += i->_key + Common::String::format(" %u\n", i->_value);
+	if (f.write(data.c_str(), data.size()) != data.size())
+		error("SavesSyncRequest: failed to write timestamps data into 'saves/timestamps'");
+	f.close();
+}
+
+
+} // End of namespace Cloud
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
new file mode 100644
index 0000000..a8c54d4
--- /dev/null
+++ b/backends/cloud/savessyncrequest.h
@@ -0,0 +1,63 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_SAVESSYNCREQUEST_H
+#define BACKENDS_CLOUD_SAVESSYNCREQUEST_H
+
+#include "backends/networking/curl/request.h"
+#include "backends/cloud/storage.h"
+#include "common/hashmap.h"
+
+namespace Cloud {
+
+class SavesSyncRequest: public Networking::Request {
+	Storage *_storage;
+	Storage::BoolCallback _boolCallback;
+	Common::HashMap<Common::String, uint32> _localFilesTimestamps;
+	Common::Array<StorageFile> _filesToDownload;
+	Common::Array<Common::String> _filesToUpload;
+	StorageFile _currentDownloadingFile;
+	Common::String _currentUploadingFile;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void directoryListedCallback(Storage::FileArrayResponse pair);
+	void fileDownloadedCallback(Storage::BoolResponse pair);
+	void fileUploadedCallback(Storage::BoolResponse pair);
+	void downloadNextFile();
+	void uploadNextFile();
+	void finishBool(bool success);
+	void loadTimestamps();
+	void saveTimestamps();	
+public:
+	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback);
+	virtual ~SavesSyncRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual void finish();
+};
+
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index f5b1aa4..a31704a 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -26,6 +26,7 @@ MODULE_OBJS += \
 	cloud/storagefile.o \
 	cloud/downloadrequest.o \
 	cloud/folderdownloadrequest.o \
+	cloud/savessyncrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/onedrive/onedrivestorage.o \


Commit: 701b07adfb578a6e23cedd7a310fbc1776e0a8a9
    https://github.com/scummvm/scummvm/commit/701b07adfb578a6e23cedd7a310fbc1776e0a8a9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
COMMON: Fix JSON to understand integers correctly

Changed paths:
    common/json.cpp
    common/json.h



diff --git a/common/json.cpp b/common/json.cpp
index 95c9123..779f99a 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -300,13 +300,15 @@ JSONValue *JSONValue::parse(const char **data) {
 		bool neg = **data == '-';
 		if (neg) (*data)++;
 
+		int integer = 0;
 		double number = 0.0;
+		bool onlyInteger = true;
 
 		// Parse the whole part of the number - only if it wasn't 0
 		if (**data == '0')
 			(*data)++;
 		else if (**data >= '1' && **data <= '9')
-			number = JSON::parseInt(data);
+			number = integer = JSON::parseInt(data);
 		else
 			return NULL;
 
@@ -325,6 +327,7 @@ JSONValue *JSONValue::parse(const char **data) {
 
 			// Save the number
 			number += decimal;
+			onlyInteger = false;
 		}
 
 		// Could be an exponent now...
@@ -346,11 +349,15 @@ JSONValue *JSONValue::parse(const char **data) {
 			double expo = JSON::parseInt(data);
 			for (double i = 0.0; i < expo; i++)
 				number = neg_expo ? (number / 10.0) : (number * 10.0);
+			onlyInteger = false;
 		}
 
 		// Was it neg?
 		if (neg) number *= -1;
 
+		if (onlyInteger)
+			return new JSONValue(neg ? -integer : integer);
+
 		return new JSONValue(number);
 	}
 
@@ -555,6 +562,18 @@ JSONValue::JSONValue(double numberValue) {
 }
 
 /**
+* Basic constructor for creating a JSON Value of type Number (Integer)
+*
+* @access public
+*
+* @param int numberValue The number to use as the value
+*/
+JSONValue::JSONValue(int numberValue) {
+	_type = JSONType_IntegerNumber;
+	_integerValue = numberValue;
+}
+
+/**
 * Basic constructor for creating a JSON Value of type Array
 *
 * @access public
@@ -601,6 +620,10 @@ JSONValue::JSONValue(const JSONValue &source) {
 		_numberValue = source._numberValue;
 		break;
 
+	case JSONType_IntegerNumber:
+		_integerValue = source._integerValue;
+		break;
+
 	case JSONType_Array: {
 		JSONArray source_array = *source._arrayValue;
 		JSONArray::iterator iter;
@@ -695,6 +718,17 @@ bool JSONValue::isNumber() const {
 }
 
 /**
+* Checks if the value is an Integer
+*
+* @access public
+*
+* @return bool Returns true if it is an Integer value, false otherwise
+*/
+bool JSONValue::isIntegerNumber() const {
+	return _type == JSONType_IntegerNumber;
+}
+
+/**
 * Checks if the value is an Array
 *
 * @access public
@@ -753,6 +787,18 @@ double JSONValue::asNumber() const {
 }
 
 /**
+* Retrieves the Integer value of this JSONValue
+* Use isIntegerNumber() before using this method.
+*
+* @access public
+*
+* @return int Returns the number value
+*/
+int JSONValue::asIntegerNumber() const {
+	return _integerValue;
+}
+
+/**
 * Retrieves the Array value of this JSONValue
 * Use isArray() before using this method.
 *
@@ -940,6 +986,13 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
 		break;
 	}
 
+	case JSONType_IntegerNumber: {
+		char str[80];
+		sprintf(str, "%d", _integerValue);
+		ret_string = str;		
+		break;
+	}
+
 	case JSONType_Array: {
 		ret_string = indentDepth ? "[\n" + indentStr1 : "[";
 		JSONArray::const_iterator iter = _arrayValue->begin();
diff --git a/common/json.h b/common/json.h
index c9debf0..3557193 100644
--- a/common/json.h
+++ b/common/json.h
@@ -85,7 +85,7 @@ typedef HashMap<String, JSONValue*> JSONObject;
 
 class JSON;
 
-enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object };
+enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_IntegerNumber, JSONType_Array, JSONType_Object };
 
 class JSONValue {
 	friend class JSON;
@@ -96,6 +96,7 @@ public:
 	JSONValue(const String &stringValue);
 	JSONValue(bool boolValue);
 	JSONValue(double numberValue);
+	JSONValue(int numberValue);
 	JSONValue(const JSONArray &arrayValue);
 	JSONValue(const JSONObject &objectValue);
 	JSONValue(const JSONValue &source);
@@ -105,12 +106,14 @@ public:
 	bool isString() const;
 	bool isBool() const;
 	bool isNumber() const;
+	bool isIntegerNumber() const;
 	bool isArray() const;
 	bool isObject() const;
 
 	const String &asString() const;
 	bool asBool() const;
 	double asNumber() const;
+	int asIntegerNumber() const;
 	const JSONArray &asArray() const;
 	const JSONObject &asObject() const;
 
@@ -135,6 +138,7 @@ private:
 	union {
 		bool _boolValue;
 		double _numberValue;
+		int _integerValue;
 		String *_stringValue;
 		JSONArray *_arrayValue;
 		JSONObject *_objectValue;


Commit: d917592099381402c2681b291d379bda78a1c3f7
    https://github.com/scummvm/scummvm/commit/d917592099381402c2681b291d379bda78a1c3f7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add DropboxUploadRequest

Changed paths:
  A backends/cloud/dropbox/dropboxuploadrequest.cpp
  A backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.h
    backends/module.mk
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b1dd865..1220a99 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -23,15 +23,16 @@
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
+#include "backends/cloud/dropbox/dropboxuploadrequest.h"
 #include "backends/cloud/downloadrequest.h"
 #include "backends/cloud/folderdownloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
+#include "common/file.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include <common/file.h>
 
 namespace Cloud {
 namespace Dropbox {
@@ -99,10 +100,18 @@ void DropboxStorage::printFiles(FileArrayResponse pair) {
 		debug("\t%s", files[i].name().c_str());
 }
 
+void DropboxStorage::printBool(BoolResponse pair) {
+	debug("bool: %s", (pair.value?"true":"false"));
+}
+
 Networking::Request *DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
+Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) {
+	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback));
+}
+
 Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
@@ -139,11 +148,20 @@ Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
 	//"" is root in Dropbox, not "/"
 	//this must create all these directories:
 	//return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
+	/*
 	return downloadFolder(
 		"/not_flat", "local/not_flat_1_level/",
 		new Common::Callback<DropboxStorage, FileArrayResponse>(this, &DropboxStorage::printFiles),
 		false
 	);
+	*/
+	Common::File *file = new Common::File();
+	if (!file->open("final.bmp")) {
+		warning("no such file");		
+		delete file;
+		return nullptr;
+	}
+	return upload("/remote/test3.bmp", file, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool));
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index da508d7..cc515fe 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -46,6 +46,7 @@ class DropboxStorage: public Cloud::Storage {
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
 	void printFiles(FileArrayResponse pair);
+	void printBool(BoolResponse pair);
 
 public:	
 	virtual ~DropboxStorage();
@@ -69,7 +70,7 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
new file mode 100644
index 0000000..d068078
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -0,0 +1,163 @@
+/* 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 "backends/cloud/dropbox/dropboxuploadrequest.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "common/json.h"
+#include "common/debug.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::BoolCallback callback):
+	Networking::Request(0), _token(token), _savePath(path), _contentsStream(contents), _boolCallback(callback),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+DropboxUploadRequest::~DropboxUploadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _contentsStream;
+	delete _boolCallback;
+}
+
+
+void DropboxUploadRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	if (!_contentsStream->seek(0)) {
+		warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
+		finish();
+	}
+	_ignoreCallback = false;
+
+	uploadNextPart();
+}
+
+void DropboxUploadRequest::uploadNextPart() {	
+	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
+	
+	Common::String url = "https://content.dropboxapi.com/2/files/upload_session/";
+	Common::JSONObject jsonRequestParameters;
+
+	if (_contentsStream->pos() == 0 || _sessionId == "") {
+		url += "start";		
+		jsonRequestParameters.setVal("close", new Common::JSONValue(false));
+	} else {
+		if (_contentsStream->size() - _contentsStream->pos() <= UPLOAD_PER_ONE_REQUEST) {
+			url += "finish";			
+			Common::JSONObject jsonCursor, jsonCommit;			
+			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
+			jsonCursor.setVal("offset", new Common::JSONValue(_contentsStream->pos()));
+			jsonCommit.setVal("path", new Common::JSONValue(_savePath));
+			jsonCommit.setVal("mode", new Common::JSONValue("overwrite"));
+			jsonCommit.setVal("autorename", new Common::JSONValue(false));
+			jsonCommit.setVal("mute", new Common::JSONValue(false));
+			jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
+			jsonRequestParameters.setVal("commit", new Common::JSONValue(jsonCommit));
+		} else {
+			url += "append_v2";
+			Common::JSONObject jsonCursor;
+			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
+			jsonCursor.setVal("offset", new Common::JSONValue(_contentsStream->pos()));
+			jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
+			jsonRequestParameters.setVal("close", new Common::JSONValue(false));			
+		}
+	}
+	
+	Common::JSONValue value(jsonRequestParameters);
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, url);
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/octet-stream");	
+	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));	
+
+	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
+	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
+	request->setBuffer(buffer, size);
+	
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
+	if (_ignoreCallback) return;
+	_workingRequest = nullptr;
+
+	Common::JSONValue *json = pair.value;
+	if (json) {
+		bool needsFinishRequest = false;
+
+		if (json->isObject()) {
+			Common::JSONObject response = json->asObject();
+
+			//debug("%s", json->stringify(true).c_str());
+
+			if (response.contains("error") || response.contains("error_summary")) {
+				warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
+				delete json;
+				finish();
+				return;
+			}
+
+			if (response.contains("server_modified")) {
+				//finished
+				finishBool(true);
+				return;
+			}
+
+			if (_sessionId == "") {
+				if (response.contains("session_id"))
+					_sessionId = response.getVal("session_id")->asString();
+				else
+					warning("no session_id found in Dropbox's response");
+				needsFinishRequest = true;
+			}
+		}
+
+		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1))
+			finishBool(true);
+		else
+			uploadNextPart();
+	} else {
+		warning("null, not json");		
+		finish();		
+	}
+
+	delete json;
+}
+
+void DropboxUploadRequest::handle() {}
+
+void DropboxUploadRequest::restart() { start(); }
+
+void DropboxUploadRequest::finish() { finishBool(false); }
+
+void DropboxUploadRequest::finishBool(bool success) {
+	Request::finish();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
new file mode 100644
index 0000000..a7d7a6c
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -0,0 +1,60 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXUPLOADREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_DROPBOXUPLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+class DropboxUploadRequest: public Networking::Request {
+	Common::String _token;
+	Common::String _savePath;
+	Common::SeekableReadStream *_contentsStream;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _sessionId;
+	
+	void start();
+	void uploadNextPart();
+	void partUploadedCallback(Networking::JsonResponse pair);
+	void finishBool(bool success);
+
+public:
+	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::BoolCallback callback);
+	virtual ~DropboxUploadRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual void finish();
+};
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 3dc5b7a..41d84f9 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -76,7 +76,7 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index a8c54d4..dd43cab 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -26,6 +26,7 @@
 #include "backends/networking/curl/request.h"
 #include "backends/cloud/storage.h"
 #include "common/hashmap.h"
+#include "common/hash-str.h"
 
 namespace Cloud {
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index ac74f90..dbd8628 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -72,7 +72,7 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::ReadStream *contents, BoolCallback callback) = 0;
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0;
diff --git a/backends/module.mk b/backends/module.mk
index a31704a..281c6a9 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS += \
 	cloud/savessyncrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
+	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
 	cloud/onedrive/onedrivelistdirectoryrequest.o
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index d114d69..fee0932 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -55,7 +55,7 @@ char *CurlJsonRequest::getPreparedContents() {
 }
 
 void CurlJsonRequest::handle() {
-	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);
+	if (!_stream) _stream = makeStream();
 
 	if (_stream) {
 		const int kBufSize = 16*1024;
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index a745741..64f6c26 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -31,12 +31,22 @@
 namespace Networking {
 
 CurlRequest::CurlRequest(DataCallback cb, Common::String url):
-	Request(cb), _url(url), _stream(0), _headersList(0) {}
+	Request(cb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {}
+
+CurlRequest::~CurlRequest() {
+	delete _stream;
+	delete _bytesBuffer;
+}
+
+NetworkReadStream *CurlRequest::makeStream() {
+	if (_bytesBuffer)
+		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, true);
+	return new NetworkReadStream(_url.c_str(), _headersList, _postFields);
+}
 
-CurlRequest::~CurlRequest() { delete _stream; }
 
 void CurlRequest::handle() {
-	if (!_stream) _stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);	
+	if (!_stream) _stream = makeStream();	
 
 	if (_stream && _stream->eos()) {
 		if (_stream->httpResponseCode() != 200)
@@ -47,13 +57,13 @@ void CurlRequest::handle() {
 
 void CurlRequest::restart() {
 	if (_stream) delete _stream;
-	_stream = 0;
+	_stream = nullptr;
 	//with no stream available next handle() will create another one
 }
 
 void CurlRequest::setHeaders(Common::Array<Common::String> &headers) {
 	curl_slist_free_all(_headersList);
-	_headersList = 0;
+	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
 		addHeader(headers[i]);
 }
@@ -63,15 +73,28 @@ void CurlRequest::addHeader(Common::String header) {
 }
 
 void CurlRequest::addPostField(Common::String keyValuePair) {
+	if (_bytesBuffer)
+		warning("CurlRequest: added POST fields would be ignored, because there is buffer present");
+
 	if (_postFields == "")
 		_postFields = keyValuePair;
 	else
 		_postFields += "&" + keyValuePair;
 }
 
+void CurlRequest::setBuffer(byte *buffer, uint32 size) {
+	if (_postFields != "")
+		warning("CurlRequest: added POST fields would be ignored, because buffer added");
+
+	if (_bytesBuffer) delete _bytesBuffer;
+
+	_bytesBuffer = buffer;
+	_bytesBufferSize = size;
+}
+
 NetworkReadStreamResponse CurlRequest::execute() {
 	if (!_stream) {
-		_stream = new NetworkReadStream(_url.c_str(), _headersList, _postFields);
+		_stream = makeStream();
 		ConnMan.addRequest(this);
 	}
 
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 8623a48..461f153 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -42,6 +42,10 @@ protected:
 	NetworkReadStream *_stream;
 	curl_slist *_headersList;
 	Common::String _postFields;
+	byte *_bytesBuffer;
+	uint32 _bytesBufferSize;
+
+	virtual NetworkReadStream *makeStream();
 
 public:
 	CurlRequest(DataCallback cb, Common::String url);
@@ -59,6 +63,9 @@ public:
 	/** Adds a post field (key=value pair). */
 	virtual void addPostField(Common::String field);
 
+	/** Sets bytes buffer. */
+	virtual void setBuffer(byte *buffer, uint32 size);
+
 	/**
 	 * Starts this Request with ConnMan.
 	 * @return its NetworkReadStream in NetworkReadStreamResponse.
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 997f0b2..f96a069 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -36,8 +36,10 @@ static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 }
 
 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields):
-	_easy(0), _eos(false), _requestComplete(false)
-{
+	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), false) {}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool post) :
+	_easy(0), _eos(false), _requestComplete(false) {
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
@@ -46,10 +48,10 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, C
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
-	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
-	if (postFields.size() != 0) {
-		curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size());
-		curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str());
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);	
+	if (post || bufferSize != 0) {
+		curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
+		curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
 	}
 	ConnMan.registerEasyHandle(_easy);
 }
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 33edade..f1f4126 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -38,6 +38,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 
 public:	
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields);
+	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool post = true);
 	virtual ~NetworkReadStream();
 
 	/**


Commit: b9e3730ccd9a76101ef0cb8812f41c371a24d0d6
    https://github.com/scummvm/scummvm/commit/b9e3730ccd9a76101ef0cb8812f41c371a24d0d6
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add UploadStatus struct

It contains not just "success" flag, but also "file" struct, so the
caller can find out some information about uploaded file - like
timestamp.

Changed paths:
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 6ea90c1..89e92fa 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -76,11 +76,11 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair
 		Common::JSONArray items = response.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
 			Common::JSONObject item = items[i]->asObject();
-			Common::String path = item.getVal("path_lower")->asString();			
+			Common::String path = item.getVal("path_lower")->asString();
 			bool isDirectory = (item.getVal(".tag")->asString() == "folder");
 			uint32 size = 0, timestamp = 0;
 			if (!isDirectory) {
-				size = item.getVal("size")->asNumber();
+				size = item.getVal("size")->asIntegerNumber();
 				timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
 			}
 			_files.push_back(StorageFile(path, size, timestamp, isDirectory));
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 1220a99..1f983ce 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -104,14 +104,46 @@ void DropboxStorage::printBool(BoolResponse pair) {
 	debug("bool: %s", (pair.value?"true":"false"));
 }
 
+void DropboxStorage::printUploadStatus(UploadResponse pair) {
+	UploadStatus status = pair.value;
+	if (status.interrupted) {
+		debug("upload interrupted by user");
+		return;
+	}
+	if (status.failed) {
+		debug("upload failed with following response:");
+		debug("%s", status.response.c_str());
+		return;
+	}
+	debug("upload HTTP response code = %ld", status.httpResponseCode);
+	if (!status.failed) {
+		debug("uploaded file info:");
+		debug("path: %s", status.file.path().c_str());
+		debug("size: %u", status.file.size());
+		debug("timestamp: %u", status.file.timestamp());
+	}
+}
+
 Networking::Request *DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
-Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) {
+Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) {
 	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback));
 }
 
+Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback) {
+	Common::File *f = new Common::File();
+	if (!f->open(localPath)) {
+		warning("DropboxStorage: unable to open file to upload from");
+		UploadStatus status(false, true, StorageFile(), "", -1);
+		if (callback) (*callback)(UploadResponse(nullptr, status));
+		delete f;
+		return nullptr;
+	}
+	return upload(remotePath, f, callback);
+}
+
 Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
@@ -155,13 +187,7 @@ Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
 		false
 	);
 	*/
-	Common::File *file = new Common::File();
-	if (!file->open("final.bmp")) {
-		warning("no such file");		
-		delete file;
-		return nullptr;
-	}
-	return upload("/remote/test3.bmp", file, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool));
+	return upload("/remote/test4.bmp", "final.bmp", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
@@ -186,13 +212,13 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 	if (outerCallback) {		
 		//Dropbox documentation states there is no errors for this API method
 		Common::JSONObject info = json->asObject();
-		Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asNumber());
+		Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asIntegerNumber());
 		Common::String name = info.getVal("display_name")->asString();
 		Common::String email = info.getVal("email")->asString();
 		Common::JSONObject quota = info.getVal("quota_info")->asObject();
-		uint32 quotaNormal = quota.getVal("normal")->asNumber();
-		uint32 quotaShared = quota.getVal("shared")->asNumber();
-		uint32 quotaAllocated = quota.getVal("quota")->asNumber();
+		uint32 quotaNormal = quota.getVal("normal")->asIntegerNumber();
+		uint32 quotaShared = quota.getVal("shared")->asIntegerNumber();
+		uint32 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index cc515fe..ca28580 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -47,6 +47,7 @@ class DropboxStorage: public Cloud::Storage {
 
 	void printFiles(FileArrayResponse pair);
 	void printBool(BoolResponse pair);
+	void printUploadStatus(UploadResponse pair);
 
 public:	
 	virtual ~DropboxStorage();
@@ -70,7 +71,8 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback);
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback);
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index d068078..18e1173 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -21,17 +21,19 @@
 */
 
 #include "backends/cloud/dropbox/dropboxuploadrequest.h"
+#include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
 #include "common/debug.h"
 
 namespace Cloud {
 namespace Dropbox {
 
-DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::BoolCallback callback):
-	Networking::Request(0), _token(token), _savePath(path), _contentsStream(contents), _boolCallback(callback),
+DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback):
+	Networking::Request(0), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
@@ -40,7 +42,7 @@ DropboxUploadRequest::~DropboxUploadRequest() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
 	delete _contentsStream;
-	delete _boolCallback;
+	delete _uploadCallback;
 }
 
 
@@ -105,6 +107,11 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
 	if (_ignoreCallback) return;
 	_workingRequest = nullptr;
 
+	UploadStatus status;
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	if (rq && rq->getNetworkReadStream())
+		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
 	Common::JSONValue *json = pair.value;
 	if (json) {
 		bool needsFinishRequest = false;
@@ -117,13 +124,19 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
 			if (response.contains("error") || response.contains("error_summary")) {
 				warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
 				delete json;
-				finish();
+				status.failed = true;
+				status.response = json->stringify(true);
+				finishUpload(status);
 				return;
 			}
 
 			if (response.contains("server_modified")) {
 				//finished
-				finishBool(true);
+				Common::String path = response.getVal("path_lower")->asString();				
+				uint32 size = response.getVal("size")->asIntegerNumber();
+				uint32 timestamp = ISO8601::convertToTimestamp(response.getVal("server_modified")->asString());				
+				status.file = StorageFile(path, size, timestamp, false);
+				finishUpload(status);
 				return;
 			}
 
@@ -136,13 +149,19 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
 			}
 		}
 
-		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1))
-			finishBool(true);
-		else
+		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {			
+			if (status.file.name() == "") {
+				status.file = StorageFile(_savePath, 0, 0, false);
+				warning("no file info to put into status");
+			}
+			finishUpload(status);
+		} else {
 			uploadNextPart();
+		}
 	} else {
-		warning("null, not json");		
-		finish();		
+		warning("null, not json");
+		status.failed = true;
+		finishUpload(status);
 	}
 
 	delete json;
@@ -152,11 +171,15 @@ void DropboxUploadRequest::handle() {}
 
 void DropboxUploadRequest::restart() { start(); }
 
-void DropboxUploadRequest::finish() { finishBool(false); }
+void DropboxUploadRequest::finish() {
+	UploadStatus status;
+	status.interrupted = true;
+	finishUpload(status);
+}
 
-void DropboxUploadRequest::finishBool(bool success) {
+void DropboxUploadRequest::finishUpload(UploadStatus status) {
 	Request::finish();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, status));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
index a7d7a6c..9b68995 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.h
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -35,7 +35,7 @@ class DropboxUploadRequest: public Networking::Request {
 	Common::String _token;
 	Common::String _savePath;
 	Common::SeekableReadStream *_contentsStream;
-	Storage::BoolCallback _boolCallback;
+	Storage::UploadCallback _uploadCallback;
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _sessionId;
@@ -43,10 +43,10 @@ class DropboxUploadRequest: public Networking::Request {
 	void start();
 	void uploadNextPart();
 	void partUploadedCallback(Networking::JsonResponse pair);
-	void finishBool(bool success);
+	void finishUpload(UploadStatus status);
 
 public:
-	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::BoolCallback callback);
+	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback);
 	virtual ~DropboxUploadRequest();
 
 	virtual void handle();
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 41d84f9..7028667 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -76,7 +76,8 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) { return nullptr; }
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index d48ec6b..be1075c 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -23,6 +23,8 @@
 #include "backends/cloud/savessyncrequest.h"
 #include "common/debug.h"
 #include "common/file.h"
+#include "common/system.h"
+#include "common/savefile.h"
 
 namespace Cloud {
 
@@ -85,6 +87,8 @@ void SavesSyncRequest::directoryListedCallback(Storage::FileArrayResponse pair)
 		}
 	}
 
+	//TODO: upload files which are added to local directory (not available on cloud), but have no timestamp
+
 	//upload files with invalid timestamp (the ones we've added - means they might not have any remote version)
 	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
 		if (i->_value == INVALID_TIMESTAMP)
@@ -104,7 +108,7 @@ void SavesSyncRequest::downloadNextFile() {
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
 
-	_workingRequest = _storage->download(_currentDownloadingFile.path(), "saves/" + _currentDownloadingFile.name(),
+	_workingRequest = _storage->download(_currentDownloadingFile.path(), "saves/" + _currentDownloadingFile.name(), //TODO: real saves folder here
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback)
 	);
 }
@@ -133,23 +137,24 @@ void SavesSyncRequest::uploadNextFile() {
 
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
-
-	_workingRequest = _storage->upload("saves/" + _currentUploadingFile, nullptr, //TODO: pass save's read stream
-		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileUploadedCallback)
+	
+	_workingRequest = _storage->upload("saves/" + _currentUploadingFile, g_system->getSavefileManager()->openForLoading(_currentUploadingFile),
+		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback)
 	);
 }
 
-void SavesSyncRequest::fileUploadedCallback(Storage::BoolResponse pair) {
+void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse pair) {
 	if (_ignoreCallback) return;
+	UploadStatus status = pair.value;
 
 	//stop syncing if upload failed
-	if (!pair.value) {
+	if (status.interrupted || status.failed) {
 		finish();
 		return;
 	}
 
-	//TODO: update local timestamp for the uploaded file
-	//_localFilesTimestamps[_currentUploadingFile] = pair.request.<what?>;
+	//update local timestamp for the uploaded file
+	_localFilesTimestamps[_currentUploadingFile] = status.file.timestamp();
 
 	//continue uploading files
 	uploadNextFile();
@@ -172,6 +177,7 @@ void SavesSyncRequest::finishBool(bool success) {
 
 void SavesSyncRequest::loadTimestamps() {
 	Common::File f;
+	//TODO: real saves folder here
 	if (!f.open("saves/timestamps"))
 		error("SavesSyncRequest: failed to open 'saves/timestamps' file to load timestamps");
 	
@@ -215,6 +221,7 @@ void SavesSyncRequest::loadTimestamps() {
 
 void SavesSyncRequest::saveTimestamps() {
 	Common::DumpFile f;
+	//TODO: real saves folder here
 	if (!f.open("saves/timestamps", true))
 		error("SavesSyncRequest: failed to open 'saves/timestamps' file to save timestamps");
 	Common::String data;
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index dd43cab..dca1fb7 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -44,7 +44,7 @@ class SavesSyncRequest: public Networking::Request {
 	void start();
 	void directoryListedCallback(Storage::FileArrayResponse pair);
 	void fileDownloadedCallback(Storage::BoolResponse pair);
-	void fileUploadedCallback(Storage::BoolResponse pair);
+	void fileUploadedCallback(Storage::UploadResponse pair);
 	void downloadNextFile();
 	void uploadNextFile();
 	void finishBool(bool success);
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index dbd8628..1749881 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -34,15 +34,37 @@
 
 namespace Cloud {
 
+/** Struct to represent upload() resulting status. */
+struct UploadStatus {
+	/** true if Request was interrupted (finished by user with finish()) */
+	bool interrupted;
+	/** true if Request has failed (bad server response or some other error occurred) */
+	bool failed;
+	/** Contains uploaded file description (empty if failed) */
+	StorageFile file;
+	/** Server's original response (empty if not failed) */
+	Common::String response;
+	/** Server's HTTP response code. */
+	long httpResponseCode;
+
+	UploadStatus():
+		interrupted(false), failed(false), file(), response(), httpResponseCode(-1) {}
+
+	UploadStatus(bool interrupt, bool failure, StorageFile f, Common::String resp, long code):
+		interrupted(interrupt), failed(failure), file(f), response(resp), httpResponseCode(code) {}
+};
+
 class Storage {
 public:
 	typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse;
 	typedef Networking::Response<StorageInfo> StorageInfoResponse;
 	typedef Networking::Response<bool> BoolResponse;
+	typedef Networking::Response<UploadStatus> UploadResponse;
 
 	typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback;
 	typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback;
 	typedef Common::BaseCallback<BoolResponse> *BoolCallback;
+	typedef Common::BaseCallback<UploadResponse> *UploadCallback;
 
 	Storage() {}
 	virtual ~Storage() {}
@@ -72,7 +94,8 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, BoolCallback callback) = 0;
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) = 0;
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0;


Commit: a19fc52c329fdb0611eee7aedfb20fb0d1b9269c
    https://github.com/scummvm/scummvm/commit/a19fc52c329fdb0611eee7aedfb20fb0d1b9269c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make DropboxUploadRequest use "/upload" too

If file could be uploaded in one API call, no need to create a session
(which requires at least two calls: to start and then to finish it).

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 1f983ce..ab3f5d0 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -105,6 +105,7 @@ void DropboxStorage::printBool(BoolResponse pair) {
 }
 
 void DropboxStorage::printUploadStatus(UploadResponse pair) {
+	debug(" ");
 	UploadStatus status = pair.value;
 	if (status.interrupted) {
 		debug("upload interrupted by user");
@@ -118,9 +119,9 @@ void DropboxStorage::printUploadStatus(UploadResponse pair) {
 	debug("upload HTTP response code = %ld", status.httpResponseCode);
 	if (!status.failed) {
 		debug("uploaded file info:");
-		debug("path: %s", status.file.path().c_str());
-		debug("size: %u", status.file.size());
-		debug("timestamp: %u", status.file.timestamp());
+		debug("\tpath: %s", status.file.path().c_str());
+		debug("\tsize: %u", status.file.size());
+		debug("\ttimestamp: %u", status.file.timestamp());
 	}
 }
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 18e1173..e422793 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -65,8 +65,16 @@ void DropboxUploadRequest::uploadNextPart() {
 	Common::JSONObject jsonRequestParameters;
 
 	if (_contentsStream->pos() == 0 || _sessionId == "") {
-		url += "start";		
-		jsonRequestParameters.setVal("close", new Common::JSONValue(false));
+		if (_contentsStream->size() <= UPLOAD_PER_ONE_REQUEST) {
+			url = "https://content.dropboxapi.com/2/files/upload";
+			jsonRequestParameters.setVal("path", new Common::JSONValue(_savePath));
+			jsonRequestParameters.setVal("mode", new Common::JSONValue("overwrite"));
+			jsonRequestParameters.setVal("autorename", new Common::JSONValue(false));
+			jsonRequestParameters.setVal("mute", new Common::JSONValue(false));
+		} else {
+			url += "start";
+			jsonRequestParameters.setVal("close", new Common::JSONValue(false));
+		}
 	} else {
 		if (_contentsStream->size() - _contentsStream->pos() <= UPLOAD_PER_ONE_REQUEST) {
 			url += "finish";			


Commit: aa987e5c52899bfafff4f1f84479a67761569109
    https://github.com/scummvm/scummvm/commit/aa987e5c52899bfafff4f1f84479a67761569109
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ListDirectoryStatus struct

It contains flags to indicate whether Request was interrupted or failed,
so dependent Requests may see that list is incomplete.

Changed paths:
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 89e92fa..2796a4c 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -22,23 +22,32 @@
 
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
-#include "backends/cloud/storage.h"
 
 namespace Cloud {
 namespace Dropbox {
 
-DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive):
-	Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _filesCallback(cb),
-	_token(token), _complete(false), _innerRequest(nullptr) {
-	startupWork();
+DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive):
+	Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb),
+	_token(token), _workingRequest(nullptr), _ignoreCallback(false) {
+	start();
 }
 
-void DropboxListDirectoryRequest::startupWork() {
+DropboxListDirectoryRequest::~DropboxListDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void DropboxListDirectoryRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
 	_files.clear();
-	_complete = false;
+	_ignoreCallback = false;
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
@@ -54,39 +63,50 @@ void DropboxListDirectoryRequest::startupWork() {
 	Common::JSONValue value(jsonRequestParameters);
 	request->addPostField(Common::JSON::stringify(&value));
 
-	_innerRequest = ConnMan.addRequest(request);
+	_workingRequest = ConnMan.addRequest(request);
 }
 
 
 void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	ListDirectoryStatus status(_files);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	if (rq && rq->getNetworkReadStream())
+		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
 	Common::JSONValue *json = pair.value;
 	if (json) {
 		Common::JSONObject response = json->asObject();
 		
 		if (response.contains("error") || response.contains("error_summary")) {
 			warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
-			_complete = true;
+			status.failed = true;
+			status.response = json->stringify();
+			finishStatus(status);
 			delete json;
 			return;
 		}
 
-		//TODO: check that all keys exist to avoid segfaults
-		//TODO: get more files in the folder to check "has_more" case
-
-		Common::JSONArray items = response.getVal("entries")->asArray();
-		for (uint32 i = 0; i < items.size(); ++i) {
-			Common::JSONObject item = items[i]->asObject();
-			Common::String path = item.getVal("path_lower")->asString();
-			bool isDirectory = (item.getVal(".tag")->asString() == "folder");
-			uint32 size = 0, timestamp = 0;
-			if (!isDirectory) {
-				size = item.getVal("size")->asIntegerNumber();
-				timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+
+		if (response.contains("entries")) {
+			Common::JSONArray items = response.getVal("entries")->asArray();
+			for (uint32 i = 0; i < items.size(); ++i) {
+				Common::JSONObject item = items[i]->asObject();
+				Common::String path = item.getVal("path_lower")->asString();
+				bool isDirectory = (item.getVal(".tag")->asString() == "folder");
+				uint32 size = 0, timestamp = 0;
+				if (!isDirectory) {
+					size = item.getVal("size")->asIntegerNumber();
+					timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
+				}
+				_files.push_back(StorageFile(path, size, timestamp, isDirectory));
 			}
-			_files.push_back(StorageFile(path, size, timestamp, isDirectory));
 		}
 
-		bool hasMore = response.getVal("has_more")->asBool();
+		bool hasMore = (response.contains("has_more") && response.getVal("has_more")->asBool());
 
 		if (hasMore) {
 			Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
@@ -100,40 +120,33 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair
 			Common::JSONValue value(jsonRequestParameters);
 			request->addPostField(Common::JSON::stringify(&value));
 
-			ConnMan.addRequest(request);
-		} else {
-			_complete = true;
+			_workingRequest = ConnMan.addRequest(request);
+		} else {			
+			finishStatus(status);
 		}		
 	} else {
 		warning("null, not json");
-		_complete = true;
+		status.failed = true;
+		finishStatus(status);
 	}
 
 	delete json;
 }
 
-void DropboxListDirectoryRequest::handle() {
-	if (_complete) finishFiles(_files);	
-}
+void DropboxListDirectoryRequest::handle() {}
 
-void DropboxListDirectoryRequest::restart() {
-	if (_innerRequest) {		
-		//TODO: I'm really not sure some CurlRequest would handle this (it must stop corresponding CURL transfer)
-		_innerRequest->finish(); //may be CANCELED or INTERRUPTED or something?
-		_innerRequest = nullptr;
-	}
-
-	startupWork();
-}
+void DropboxListDirectoryRequest::restart() { start(); }
 
 void DropboxListDirectoryRequest::finish() {
 	Common::Array<StorageFile> files;
-	finishFiles(files);
+	ListDirectoryStatus status(files);
+	status.interrupted = true;
+	finishStatus(status);
 }
 
-void DropboxListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files) {
+void DropboxListDirectoryRequest::finishStatus(ListDirectoryStatus status) {
 	Request::finish();
-	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 8539be1..3c7c1fd 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -35,20 +35,18 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Common::String _requestedPath;
 	bool _requestedRecursive;
 
-	Storage::FileArrayCallback _filesCallback;
+	Storage::ListDirectoryCallback _listDirectoryCallback;
 	Common::String _token;
-	bool _complete;
 	Common::Array<StorageFile> _files;
-	Request *_innerRequest;
-
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	
+	void start();
 	void responseCallback(Networking::JsonResponse pair);
-	void startupWork();
-
-	void finishFiles(Common::Array<StorageFile> &files);
-
+	void finishStatus(ListDirectoryStatus status);
 public:
-	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
-	virtual ~DropboxListDirectoryRequest() { delete _filesCallback; }
+	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false);
+	virtual ~DropboxListDirectoryRequest();
 
 	virtual void handle();
 	virtual void restart();
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index ab3f5d0..4a17afe 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -125,7 +125,7 @@ void DropboxStorage::printUploadStatus(UploadResponse pair) {
 	}
 }
 
-Networking::Request *DropboxStorage::listDirectory(Common::String path, FileArrayCallback outerCallback, bool recursive) {
+Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, bool recursive) {
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
 }
 
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index ca28580..d9967d6 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -67,10 +67,10 @@ public:
 
 	/** Public Cloud API comes down there. */
 
-	/** Returns Common::Array<StorageFile>. */
-	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
-
-	/** Calls the callback when finished. */
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false);
+	
+	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback);
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback);
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index e422793..e64a883 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -45,7 +45,6 @@ DropboxUploadRequest::~DropboxUploadRequest() {
 	delete _uploadCallback;
 }
 
-
 void DropboxUploadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 00bddb9..db132ff 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -50,15 +50,21 @@ void FolderDownloadRequest::start() {
 	//list directory first
 	_workingRequest = _storage->listDirectory(
 		_remoteDirectoryPath,
-		new Common::Callback<FolderDownloadRequest, Storage::FileArrayResponse>(this, &FolderDownloadRequest::directoryListedCallback),
+		new Common::Callback<FolderDownloadRequest, Storage::ListDirectoryResponse>(this, &FolderDownloadRequest::directoryListedCallback),
 		_recursive
 	);
 }
 
-void FolderDownloadRequest::directoryListedCallback(Storage::FileArrayResponse pair) {
+void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) {
 	if (_ignoreCallback) return;
-	//TODO: somehow ListDirectory requests must indicate that file array is incomplete
-	_files = pair.value;
+
+	ListDirectoryStatus status = pair.value;
+	if (status.failed || status.interrupted) {
+		finish();
+		return;
+	}
+	
+	_files = pair.value.files;
 	downloadNextFile();
 }
 
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 33fa599..779ea33 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -26,7 +26,6 @@
 #include "backends/networking/curl/request.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/cloud/storage.h"
-#include "common/file.h"
 
 namespace Cloud {
 
@@ -41,7 +40,7 @@ class FolderDownloadRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void directoryListedCallback(Storage::FileArrayResponse pair);
+	void directoryListedCallback(Storage::ListDirectoryResponse pair);
 	void fileDownloadedCallback(Storage::BoolResponse pair);
 	void downloadNextFile();
 	void finishFiles(Common::Array<StorageFile> &files);
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index f1402c4..dbd5e44 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -25,18 +25,25 @@
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
 
 namespace Cloud {
 namespace OneDrive {
 
-OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::FileArrayCallback cb, bool recursive):
+OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive):
 	Networking::Request(0),
-	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _filesCallback(cb),
+	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
 
+OneDriveListDirectoryRequest::~OneDriveListDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
 void OneDriveListDirectoryRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
@@ -48,12 +55,12 @@ void OneDriveListDirectoryRequest::start() {
 	_ignoreCallback = false;
 
 	_directoriesQueue.push_back(_requestedPath);
-	listNextDirectory();
+	listNextDirectory(_files);
 }
 
-void OneDriveListDirectoryRequest::listNextDirectory() {
+void OneDriveListDirectoryRequest::listNextDirectory(ListDirectoryStatus status) {
 	if (_directoriesQueue.empty()) {
-		finishFiles(_files);
+		finishStatus(status);
 		return;
 	}
 
@@ -76,7 +83,8 @@ void OneDriveListDirectoryRequest::makeRequest(Common::String url) {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse pair) {	
+void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse pair) {
+	_workingRequest = nullptr;
 	Common::JSONValue *json = pair.value;
 
 	if (_ignoreCallback) {
@@ -84,26 +92,29 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 		return;
 	}
 
+	ListDirectoryStatus status(_files);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	if (rq && rq->getNetworkReadStream())
+		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
 	if (!json) {
-		finish();
+		status.failed = true;
+		finishStatus(status);
 		return;
 	}
 
 	Common::JSONObject response = json->asObject();		
 	
-	//TODO: check that all keys exist to avoid segfaults
+	//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
 	Common::JSONArray items = response.getVal("value")->asArray();
 	for (uint32 i = 0; i < items.size(); ++i) {
 		Common::JSONObject item = items[i]->asObject();	
 
 		Common::String path = _currentDirectory + item.getVal("name")->asString();
-		bool isDirectory = item.contains("folder");
-		uint32 size = 0, timestamp = 0;		
-		//if (!isDirectory) {
-		size = item.getVal("size")->asNumber();
-		timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());
-		//}
+		bool isDirectory = item.contains("folder");		
+		uint32 size = item.getVal("size")->asIntegerNumber();
+		uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());		
 
 		StorageFile file(path, size, timestamp, isDirectory);
 		_files.push_back(file);
@@ -116,21 +127,22 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 	if (hasMore) {
 		makeRequest(response.getVal("@odata.nextLink")->asString());
 	} else {
-		listNextDirectory();
+		listNextDirectory(status);
 	}
 
 	delete json;
 }
 
-void OneDriveListDirectoryRequest::finish() {
-	//TODO: indicate it's interrupted
+void OneDriveListDirectoryRequest::finish() {	
 	Common::Array<StorageFile> files;
-	finishFiles(files);
+	ListDirectoryStatus status(files);
+	status.interrupted = true;
+	finishStatus(status);
 }
 
-void OneDriveListDirectoryRequest::finishFiles(Common::Array<StorageFile> &files) {
+void OneDriveListDirectoryRequest::finishStatus(ListDirectoryStatus status) {
 	Request::finish();
-	if (_filesCallback) (*_filesCallback)(Storage::FileArrayResponse(this, files));
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status));
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index ce407c0..a05dd87 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -37,7 +37,7 @@ class OneDriveListDirectoryRequest: public Networking::Request {
 	Common::String _requestedPath;
 	bool _requestedRecursive;
 	OneDriveStorage *_storage;
-	Storage::FileArrayCallback _filesCallback;	
+	Storage::ListDirectoryCallback _listDirectoryCallback;
 	Common::Array<StorageFile> _files;
 	Common::Array<Common::String> _directoriesQueue;
 	Common::String _currentDirectory;
@@ -45,13 +45,13 @@ class OneDriveListDirectoryRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void listNextDirectory();
+	void listNextDirectory(ListDirectoryStatus status);
 	void listedDirectoryCallback(Networking::JsonResponse pair);
 	void makeRequest(Common::String url);
-	void finishFiles(Common::Array<StorageFile> &files);
+	void finishStatus(ListDirectoryStatus status);
 public:
-	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::FileArrayCallback cb, bool recursive = false);
-	virtual ~OneDriveListDirectoryRequest() { delete _filesCallback; }
+	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false);
+	virtual ~OneDriveListDirectoryRequest();
 
 	virtual void handle() {}
 	virtual void restart() { start(); }
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index c8b4ab1..19e4972 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -163,7 +163,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 	delete pair.value;
 }
 
-Networking::Request *OneDriveStorage::listDirectory(Common::String path, FileArrayCallback callback, bool recursive) {
+Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive) {
 	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, recursive));
 }
 
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 7028667..55d0396 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -72,10 +72,10 @@ public:
 
 	/** Public Cloud API comes down there. */
 
-	/** Returns Common::Array<StorageFile>. */
-	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false);
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false);
 
-	/** Calls the callback when finished. */
+	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) { return nullptr; } //TODO
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) { return nullptr; }
 
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index be1075c..96386ee 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -55,17 +55,22 @@ void SavesSyncRequest::start() {
 	loadTimestamps();
 
 	//list saves directory
-	_workingRequest = _storage->listDirectory("saves", new Common::Callback<SavesSyncRequest, Storage::FileArrayResponse>(this, &SavesSyncRequest::directoryListedCallback));
+	_workingRequest = _storage->listDirectory("saves", new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback));
 }
 
-void SavesSyncRequest::directoryListedCallback(Storage::FileArrayResponse pair) {
+void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) {
 	if (_ignoreCallback) return;
-	//TODO: somehow ListDirectory requests must indicate that file array is incomplete
+
+	ListDirectoryStatus status = pair.value;
+	if (status.interrupted || status.failed) {
+		finishBool(false);
+		return;
+	}
 
 	const uint32 INVALID_TIMESTAMP = UINT_MAX;
 	
 	//determine which files to download and which files to upload
-	Common::Array<StorageFile> &remoteFiles = pair.value;
+	Common::Array<StorageFile> &remoteFiles = status.files;
 	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
 		StorageFile &file = remoteFiles[i];
 		if (file.isDirectory()) continue;
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index dca1fb7..f2f2aba 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -42,7 +42,7 @@ class SavesSyncRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void directoryListedCallback(Storage::FileArrayResponse pair);
+	void directoryListedCallback(Storage::ListDirectoryResponse pair);
 	void fileDownloadedCallback(Storage::BoolResponse pair);
 	void fileUploadedCallback(Storage::UploadResponse pair);
 	void downloadNextFile();
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 1749881..311b3fd 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -54,17 +54,39 @@ struct UploadStatus {
 		interrupted(interrupt), failed(failure), file(f), response(resp), httpResponseCode(code) {}
 };
 
+/** Struct to represent upload() resulting status. */
+struct ListDirectoryStatus {
+	/** true if Request was interrupted (finished by user with finish()) */
+	bool interrupted;
+	/** true if Request has failed (bad server response or some other error occurred) */
+	bool failed;
+	/** Contains listed files (might be incomplete if failed or interrupted) */
+	Common::Array<StorageFile> &files;
+	/** Server's original response (empty if not failed) */
+	Common::String response;
+	/** Server's HTTP response code. */
+	long httpResponseCode;
+
+	ListDirectoryStatus(Common::Array<StorageFile> &f) :
+		interrupted(false), failed(false), files(f), response(), httpResponseCode(-1) {}
+
+	ListDirectoryStatus(bool interrupt, bool failure, Common::Array<StorageFile> &f, Common::String resp, long code) :
+		interrupted(interrupt), failed(failure), files(f), response(resp), httpResponseCode(code) {}
+};
+
 class Storage {
 public:
 	typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse;
 	typedef Networking::Response<StorageInfo> StorageInfoResponse;
 	typedef Networking::Response<bool> BoolResponse;
 	typedef Networking::Response<UploadStatus> UploadResponse;
+	typedef Networking::Response<ListDirectoryStatus> ListDirectoryResponse;
 
 	typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback;
 	typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback;
 	typedef Common::BaseCallback<BoolResponse> *BoolCallback;
 	typedef Common::BaseCallback<UploadResponse> *UploadCallback;
+	typedef Common::BaseCallback<ListDirectoryResponse> *ListDirectoryCallback;
 
 	Storage() {}
 	virtual ~Storage() {}
@@ -90,10 +112,10 @@ public:
 	 * a callback, which is called, when request is complete.
 	 */
 
-	/** Returns Common::Array<StorageFile>. */
-	virtual Networking::Request *listDirectory(Common::String path, FileArrayCallback callback, bool recursive = false) = 0;
-
-	/** Calls the callback when finished. */
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false) = 0;
+	
+	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) = 0;
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) = 0;
 


Commit: cc4512e50b5489ec57adc05b3c2277c132bed767
    https://github.com/scummvm/scummvm/commit/cc4512e50b5489ec57adc05b3c2277c132bed767
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
COMMON: Add SaveFileManager::openRawFile()

It's needed for the cloud saves upload/sync feature.

Changed paths:
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h
    common/savefile.h



diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index daec36a..75ba50a 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -75,6 +75,22 @@ Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &
 	return results;
 }
 
+Common::InSaveFile *DefaultSaveFileManager::openRawFile(const Common::String &filename) {
+	// Assure the savefile name cache is up-to-date.
+	assureCached(getSavePath());
+	if (getError().getCode() != Common::kNoError)
+		return nullptr;
+
+	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
+	if (file == _saveFileCache.end()) {
+		return nullptr;
+	} else {
+		// Open the file for loading.
+		Common::SeekableReadStream *sf = file->_value.createReadStream();
+		return sf;
+	}
+}
+
 Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String &filename) {
 	// Assure the savefile name cache is up-to-date.
 	assureCached(getSavePath());
diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h
index bf4ca02..166e700 100644
--- a/backends/saves/default/default-saves.h
+++ b/backends/saves/default/default-saves.h
@@ -38,6 +38,7 @@ public:
 	DefaultSaveFileManager(const Common::String &defaultSavepath);
 
 	virtual Common::StringArray listSavefiles(const Common::String &pattern);
+	virtual Common::InSaveFile *openRawFile(const Common::String &filename);
 	virtual Common::InSaveFile *openForLoading(const Common::String &filename);
 	virtual Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true);
 	virtual bool removeSavefile(const Common::String &filename);
diff --git a/common/savefile.h b/common/savefile.h
index 9fca07f..d9c5512 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -137,6 +137,15 @@ public:
 	virtual InSaveFile *openForLoading(const String &name) = 0;
 
 	/**
+	* Open the file with the specified name in the given directory for loading.
+	* In contrast to openForLoading(), it returns raw file instead of unpacked.
+	*
+	* @param name  The name of the savefile.
+	* @return Pointer to an InSaveFile, or NULL if an error occurred.
+	*/
+	virtual InSaveFile *openRawFile(const String &name) = 0;
+
+	/**
 	 * Removes the given savefile from the system.
 	 *
 	 * @param name  The name of the savefile to be removed.


Commit: af37ecca3430c871ec8a03bcada90303e6bf9877
    https://github.com/scummvm/scummvm/commit/af37ecca3430c871ec8a03bcada90303e6bf9877
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make SavesSyncRequest work

It now actually read the "timestamps" file, loads and saves files as it
should, ignores Dropbox's "not_found" error.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 4a17afe..b33e2b6 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -33,6 +33,9 @@
 #include "common/file.h"
 #include "common/json.h"
 #include <curl/curl.h>
+#include "common/system.h"
+#include "common/savefile.h"
+#include "../savessyncrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -188,7 +191,18 @@ Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
 		false
 	);
 	*/
-	return upload("/remote/test4.bmp", "final.bmp", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
+	/*
+	debug("%s", ConfMan.get("savepath").c_str());
+	Common::StringArray arr = g_system->getSavefileManager()->listSavefiles("*");
+	for (uint32 i = 0; i < arr.size(); ++i) {
+		debug("%s", arr[i].c_str());
+	}
+	debug("EOL");
+	*/
+	//return upload("/remote/backslash", "C:\\Users\\Tkachov\\AppData\\Roaming\\ScummVM\\Saved games\\sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
+	//return upload("/remote/slash", "C:/Users/Tkachov/AppData/Roaming/ScummVM/Saved games/sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool)));
+	//return upload("/remote/test4.bmp", "final.bmp", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 96386ee..e632bfff 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -21,13 +21,17 @@
 */
 
 #include "backends/cloud/savessyncrequest.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
-#include "common/system.h"
 #include "common/savefile.h"
+#include "common/system.h"
+#include <common/json.h>
 
 namespace Cloud {
 
+const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
+
 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback):
 	Request(nullptr), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -55,25 +59,44 @@ void SavesSyncRequest::start() {
 	loadTimestamps();
 
 	//list saves directory
-	_workingRequest = _storage->listDirectory("saves", new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback));
+	_workingRequest = _storage->listDirectory("/saves", new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback));
 }
 
 void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) {
+	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
 	ListDirectoryStatus status = pair.value;
-	if (status.interrupted || status.failed) {
+	bool irrecoverable = status.interrupted || status.failed;
+	if (status.failed) {
+		Common::JSONValue *value = Common::JSON::parse(status.response.c_str());
+		if (value) {
+			if (value->isObject()) {
+				Common::JSONObject object = value->asObject();
+				//Dropbox-related error:
+				if (object.contains("error_summary")) {
+					Common::String summary = object.getVal("error_summary")->asString();
+					if (summary.contains("not_found")) {
+						//oh how lucky we are! It's just user don't have /cloud/ folder yet!
+						irrecoverable = false;
+					}
+				}
+			}
+			delete value;
+		}
+	}
+
+	if (irrecoverable) {
 		finishBool(false);
 		return;
 	}
-
-	const uint32 INVALID_TIMESTAMP = UINT_MAX;
 	
 	//determine which files to download and which files to upload
 	Common::Array<StorageFile> &remoteFiles = status.files;
 	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
 		StorageFile &file = remoteFiles[i];
 		if (file.isDirectory()) continue;
+		if (file.name() == TIMESTAMPS_FILENAME) continue;
 		Common::String name = file.name();
 		if (!_localFilesTimestamps.contains(name))
 			_filesToDownload.push_back(file);
@@ -92,14 +115,24 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pa
 		}
 	}
 
-	//TODO: upload files which are added to local directory (not available on cloud), but have no timestamp
-
 	//upload files with invalid timestamp (the ones we've added - means they might not have any remote version)
 	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
+		if (i->_key == TIMESTAMPS_FILENAME) continue;
 		if (i->_value == INVALID_TIMESTAMP)
 			_filesToUpload.push_back(i->_key);
 	}
 
+	///////
+	debug("\ndownload files:");
+	for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
+		debug("%s", _filesToDownload[i].name().c_str());
+	}
+	debug("\nupload files:");
+	for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
+		debug("%s", _filesToUpload[i].c_str());
+	}
+	///////
+
 	//start downloading files
 	downloadNextFile();
 }
@@ -113,12 +146,16 @@ void SavesSyncRequest::downloadNextFile() {
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
 
-	_workingRequest = _storage->download(_currentDownloadingFile.path(), "saves/" + _currentDownloadingFile.name(), //TODO: real saves folder here
+	///////
+	debug("downloading %s", _currentDownloadingFile.name().c_str());
+	///////
+	_workingRequest = _storage->download(_currentDownloadingFile.path(), concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback)
 	);
 }
 
 void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
+	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
 	//stop syncing if download failed
@@ -143,12 +180,16 @@ void SavesSyncRequest::uploadNextFile() {
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
 	
-	_workingRequest = _storage->upload("saves/" + _currentUploadingFile, g_system->getSavefileManager()->openForLoading(_currentUploadingFile),
+	///////
+	debug("uploading %s", _currentUploadingFile.c_str());
+	///////
+	_workingRequest = _storage->upload("/saves/" + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback)
 	);
 }
 
 void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse pair) {
+	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	UploadStatus status = pair.value;
 
@@ -181,16 +222,24 @@ void SavesSyncRequest::finishBool(bool success) {
 }
 
 void SavesSyncRequest::loadTimestamps() {
-	Common::File f;
-	//TODO: real saves folder here
-	if (!f.open("saves/timestamps"))
-		error("SavesSyncRequest: failed to open 'saves/timestamps' file to load timestamps");
+	//start with listing all the files in saves/ directory and setting invalid timestamp to them
+	Common::StringArray localFiles = g_system->getSavefileManager()->listSavefiles("*");
+	for (uint32 i = 0; i < localFiles.size(); ++i)
+		_localFilesTimestamps[localFiles[i]] = INVALID_TIMESTAMP;
+
+	//now actually load timestamps from file
+	Common::InSaveFile *file = g_system->getSavefileManager()->openRawFile(TIMESTAMPS_FILENAME);
+	if (!file) {
+		warning("SavesSyncRequest: failed to open '%s' file to load timestamps", TIMESTAMPS_FILENAME);
+		return;
+	}
+
 	
-	while (!f.eos()) {
+	while (!file->eos()) {
 		//read filename into buffer (reading until the first ' ')
 		Common::String buffer;
-		while (!f.eos()) {
-			byte b = f.readByte();
+		while (!file->eos()) {
+			byte b = file->readByte();
 			if (b == ' ') break;
 			buffer += (char)b;
 		}
@@ -199,8 +248,8 @@ void SavesSyncRequest::loadTimestamps() {
 		Common::String filename = buffer;
 		bool lineEnded = false;
 		buffer = "";
-		while (!f.eos()) {
-			byte b = f.readByte();
+		while (!file->eos()) {
+			byte b = file->readByte();
 			if (b == ' ' || b == '\n' || b == '\r') {
 				lineEnded = (b == '\n');
 				break;
@@ -210,32 +259,53 @@ void SavesSyncRequest::loadTimestamps() {
 
 		//parse timestamp
 		uint timestamp = atol(buffer.c_str());
+		if (buffer == "" || timestamp == 0) break;
 		_localFilesTimestamps[filename] = timestamp;
 
 		//read until the end of the line
 		if (!lineEnded) {
-			while (!f.eos()) {
-				byte b = f.readByte();
+			while (!file->eos()) {
+				byte b = file->readByte();
 				if (b == '\n') break;
 			}
 		}
 	}
-
-	f.close();
+	
+	delete file;
 }
 
 void SavesSyncRequest::saveTimestamps() {
-	Common::DumpFile f;
-	//TODO: real saves folder here
-	if (!f.open("saves/timestamps", true))
-		error("SavesSyncRequest: failed to open 'saves/timestamps' file to save timestamps");
-	Common::String data;
-	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i)
-		data += i->_key + Common::String::format(" %u\n", i->_value);
-	if (f.write(data.c_str(), data.size()) != data.size())
-		error("SavesSyncRequest: failed to write timestamps data into 'saves/timestamps'");
+	Common::DumpFile f;	
+	Common::String filename = concatWithSavesPath(TIMESTAMPS_FILENAME);
+	if (!f.open(filename, true)) {
+		warning("SavesSyncRequest: failed to open '%s' file to save timestamps", filename.c_str());
+		return;
+	}
+	
+	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
+		Common::String data = i->_key + Common::String::format(" %u\n", i->_value);
+		if (f.write(data.c_str(), data.size()) != data.size()) {
+			warning("SavesSyncRequest: failed to write timestamps data into '%s'", filename.c_str());
+			return;
+		}
+	}
+
 	f.close();
 }
 
+Common::String SavesSyncRequest::concatWithSavesPath(Common::String name) {
+	Common::String path = ConfMan.get("savepath");
+	if (path.size() > 0 && (path.lastChar() == '/' || path.lastChar() == '\\'))
+		return path + name;
+
+	//simple heuristic to determine which path separator to use
+	int backslashes = 0;
+	for (uint32 i = 0; i < path.size(); ++i)
+		if (path[i] == '/') --backslashes;
+		else if (path[i] == '\\') ++backslashes;
+
+	if (backslashes) return path + '\\' + name;
+	return path + '/' + name;
+}
 
 } // End of namespace Cloud
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index f2f2aba..da7b27e 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -31,6 +31,9 @@
 namespace Cloud {
 
 class SavesSyncRequest: public Networking::Request {
+	const uint32 INVALID_TIMESTAMP = UINT_MAX;
+	static const char *TIMESTAMPS_FILENAME;
+
 	Storage *_storage;
 	Storage::BoolCallback _boolCallback;
 	Common::HashMap<Common::String, uint32> _localFilesTimestamps;
@@ -49,7 +52,8 @@ class SavesSyncRequest: public Networking::Request {
 	void uploadNextFile();
 	void finishBool(bool success);
 	void loadTimestamps();
-	void saveTimestamps();	
+	void saveTimestamps();
+	Common::String concatWithSavesPath(Common::String name);
 public:
 	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback);
 	virtual ~SavesSyncRequest();


Commit: 001b417f33beeb3b2da11f58105b971dc7e6f600
    https://github.com/scummvm/scummvm/commit/001b417f33beeb3b2da11f58105b971dc7e6f600
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix OneDriveTokenRefresher

It was calling finish(), causing stack overflow.

Some minor changes are added also.

Changed paths:
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp



diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 19e4972..b0f4f7b 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -35,6 +35,7 @@
 #include "common/json.h"
 #include "common/system.h"
 #include <curl/curl.h>
+#include "../savessyncrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
@@ -205,6 +206,11 @@ void OneDriveStorage::printFiles(FileArrayResponse pair) {
 		debug("\t%s", files[i].path().c_str());
 }
 
+void OneDriveStorage::printBool(BoolResponse pair) {
+	debug("bool: %s", pair.value ? "true" : "false");
+}
+
+
 Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	//this is not the real syncSaves() implementation
 	/*
@@ -213,7 +219,8 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 	*/
-	return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
+	//return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool)));
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 55d0396..43675fb 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -52,6 +52,7 @@ class OneDriveStorage: public Cloud::Storage {
 	void printJson(Networking::JsonResponse pair);
 	void fileDownloaded(BoolResponse pair);
 	void printFiles(FileArrayResponse pair);
+	void printBool(BoolResponse pair);
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair);
 public:	
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index d932f9a..a0c41ff 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -60,7 +60,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (!json) {
 		//notify user of failure
 		warning("OneDriveTokenRefresher: got NULL instead of JSON");
-		CurlJsonRequest::finish();
+		CurlJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -72,8 +72,24 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
-		debug("code = %s", error.getVal("code")->asString().c_str());
-		debug("message = %s", error.getVal("message")->asString().c_str());
+		bool irrecoverable = true;
+
+		if (error.contains("code")) {
+			Common::String code = error.getVal("code")->asString();
+			debug("code = %s", code.c_str());
+			//if (code == "itemNotFound") irrecoverable = true;
+		}
+
+		if (error.contains("message")) {
+			Common::String message = error.getVal("message")->asString();
+			debug("message = %s", message.c_str());
+		}
+
+		if (irrecoverable) {
+			CurlJsonRequest::finishJson(nullptr);
+			return;
+		}
+
 		pause();		
 		delete json;
 		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));


Commit: eb63b50b7f0841e40365f3fbafa9810e8b190872
    https://github.com/scummvm/scummvm/commit/eb63b50b7f0841e40365f3fbafa9810e8b190872
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Refactor Request

Added ErrorResponse and ErrorCallback. Each Request now has an
ErrorCallback, which should be called instead of usual callback in case
of failure.

Changed paths:
  A backends/networking/curl/request.cpp
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.h
    backends/module.mk
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/request.h
    common/cloudmanager.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 8d5e244..7dde74f 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -27,31 +27,55 @@
 
 namespace Cloud {
 
-DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile):
-	Request(0), _boolCallback(callback), _remoteFileStream(0), _localFile(dumpFile) {
-	storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback));
+DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile):
+	Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileName(remoteFile), _storage(storage),
+	_remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false) {
+	start();
 }
 
-void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse pair) {
-	if (!pair.value) {
-		warning("DownloadRequest: no ReadStream passed");
-		finish();
-		return;
-	}
+DownloadRequest::~DownloadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+	delete _localFile;
+}
+
+void DownloadRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_remoteFileStream = nullptr;
+	//TODO: reopen DumpFile
+	_ignoreCallback = false;
+
+	_workingRequest = _storage->streamFile(
+		_remoteFileName,
+		new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback),
+		new Common::Callback<DownloadRequest, Networking::ErrorResponse>(this, &DownloadRequest::streamErrorCallback)
+	);
+}
 
-	_remoteFileStream = (Networking::NetworkReadStream *)pair.value;
+void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	_remoteFileStream = (Networking::NetworkReadStream *)response.value;
+}
+
+void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
 }
 
 void DownloadRequest::handle() {	
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
-		finish();
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 		return;
 	}
 
 	if (!_localFile->isOpen()) {
 		warning("DownloadRequest: failed to open file to write");
-		finish();
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 		return;
 	}
 
@@ -67,7 +91,7 @@ void DownloadRequest::handle() {
 	if (readBytes != 0)
 		if (_localFile->write(buf, readBytes) != readBytes) {
 			warning("DownloadRequest: unable to write all received bytes into output file");
-			finish();
+			finishError(Networking::ErrorResponse(this, false, true, "", -1));
 			return;
 		}
 
@@ -77,26 +101,20 @@ void DownloadRequest::handle() {
 			//TODO: do something about it actually			
 		}
 
-		finishBool(_remoteFileStream->httpResponseCode() == 200);
+		finishSuccess(_remoteFileStream->httpResponseCode() == 200);
 
 		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()		
 	}
 }
 
 void DownloadRequest::restart() {
-	//this request doesn't know anything about the _remoteFileStream it's reading
-	//thus, it can't restart it
-	warning("DownloadRequest: cannot be restarted");
-	finish();
-	//TODO: fix that
-}
-
-void DownloadRequest::finish() {
-	finishBool(false);
+	warning("DownloadRequest: can't restart as there are no means to reopen DumpFile");
+	finishError(Networking::ErrorResponse(this, false, true, "", -1));
+	//start();
 }
 
-void DownloadRequest::finishBool(bool success) {
-	Request::finish();
+void DownloadRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 0bad5df..9e3421d 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -31,23 +31,24 @@
 namespace Cloud {
 
 class DownloadRequest: public Networking::Request {	
-	Storage::BoolCallback _boolCallback;
+	Storage::BoolCallback _boolCallback;	
+	Common::DumpFile *_localFile;
+	Common::String _remoteFileName;
+	Storage *_storage;
 	Networking::NetworkReadStream *_remoteFileStream;
-	Common::DumpFile *_localFile;	
+	Request *_workingRequest;
+	bool _ignoreCallback;
 
-	void streamCallback(Networking::NetworkReadStreamResponse pair);
-
-	void finishBool(bool success);
+	void start();
+	void streamCallback(Networking::NetworkReadStreamResponse response);
+	void streamErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
 public:
-	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile);
-	virtual ~DownloadRequest() {
-		delete _boolCallback;
-		delete _localFile;
-	}
+	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile);
+	virtual ~DownloadRequest();
 
 	virtual void handle();
 	virtual void restart();
-	virtual void finish();
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 2796a4c..d782f81 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -31,8 +31,8 @@
 namespace Cloud {
 namespace Dropbox {
 
-DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive):
-	Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb),
+DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb),
 	_token(token), _workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
@@ -49,8 +49,9 @@ void DropboxListDirectoryRequest::start() {
 	_files.clear();
 	_ignoreCallback = false;
 
-	Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder");
+	Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder");
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 
@@ -66,33 +67,32 @@ void DropboxListDirectoryRequest::start() {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-
-void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair) {
+void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
-	ListDirectoryStatus status(_files);
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
-		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	Common::JSONValue *json = pair.value;
+	Common::JSONValue *json = response.value;
 	if (json) {
-		Common::JSONObject response = json->asObject();
+		Common::JSONObject responseObjecct = json->asObject();
 		
-		if (response.contains("error") || response.contains("error_summary")) {
-			warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
-			status.failed = true;
-			status.response = json->stringify();
-			finishStatus(status);
+		if (responseObjecct.contains("error") || responseObjecct.contains("error_summary")) {
+			warning("Dropbox returned error: %s", responseObjecct.getVal("error_summary")->asString().c_str());
+			error.failed = true;
+			error.response = json->stringify();
+			finishError(error);
 			delete json;
 			return;
 		}
 
 		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
 
-		if (response.contains("entries")) {
-			Common::JSONArray items = response.getVal("entries")->asArray();
+		if (responseObjecct.contains("entries")) {
+			Common::JSONArray items = responseObjecct.getVal("entries")->asArray();
 			for (uint32 i = 0; i < items.size(); ++i) {
 				Common::JSONObject item = items[i]->asObject();
 				Common::String path = item.getVal("path_lower")->asString();
@@ -106,47 +106,47 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair
 			}
 		}
 
-		bool hasMore = (response.contains("has_more") && response.getVal("has_more")->asBool());
+		bool hasMore = (responseObjecct.contains("has_more") && responseObjecct.getVal("has_more")->asBool());
 
-		if (hasMore) {
-			Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
-			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
+		if (hasMore) {			
+			Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
+			Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
+			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
 			request->addHeader("Authorization: Bearer " + _token);
 			request->addHeader("Content-Type: application/json");
 
 			Common::JSONObject jsonRequestParameters;
-			jsonRequestParameters.setVal("cursor", new Common::JSONValue(response.getVal("cursor")->asString()));
+			jsonRequestParameters.setVal("cursor", new Common::JSONValue(responseObjecct.getVal("cursor")->asString()));
 
 			Common::JSONValue value(jsonRequestParameters);
 			request->addPostField(Common::JSON::stringify(&value));
 
 			_workingRequest = ConnMan.addRequest(request);
 		} else {			
-			finishStatus(status);
+			finishSuccess(_files);
 		}		
 	} else {
 		warning("null, not json");
-		status.failed = true;
-		finishStatus(status);
+		error.failed = true;
+		finishError(error);
 	}
 
 	delete json;
 }
 
+void DropboxListDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
 void DropboxListDirectoryRequest::handle() {}
 
 void DropboxListDirectoryRequest::restart() { start(); }
 
-void DropboxListDirectoryRequest::finish() {
-	Common::Array<StorageFile> files;
-	ListDirectoryStatus status(files);
-	status.interrupted = true;
-	finishStatus(status);
-}
-
-void DropboxListDirectoryRequest::finishStatus(ListDirectoryStatus status) {
-	Request::finish();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status));
+void DropboxListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {	
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 3c7c1fd..3a83af0 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -42,15 +42,15 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	bool _ignoreCallback;
 	
 	void start();
-	void responseCallback(Networking::JsonResponse pair);
-	void finishStatus(ListDirectoryStatus status);
+	void responseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishSuccess(Common::Array<StorageFile> &files);
 public:
-	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false);
+	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
 	virtual ~DropboxListDirectoryRequest();
 
 	virtual void handle();
 	virtual void restart();
-	virtual void finish();
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b33e2b6..d22e0ab 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -96,118 +96,93 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
 
-void DropboxStorage::printFiles(FileArrayResponse pair) {
+void DropboxStorage::printFiles(FileArrayResponse response) {
 	debug("files:");
-	Common::Array<StorageFile> &files = pair.value;
+	Common::Array<StorageFile> &files = response.value;
 	for (uint32 i = 0; i < files.size(); ++i)
 		debug("\t%s", files[i].name().c_str());
 }
 
-void DropboxStorage::printBool(BoolResponse pair) {
-	debug("bool: %s", (pair.value?"true":"false"));
+void DropboxStorage::printBool(BoolResponse response) {
+	debug("bool: %s", (response.value?"true":"false"));
 }
 
-void DropboxStorage::printUploadStatus(UploadResponse pair) {
-	debug(" ");
-	UploadStatus status = pair.value;
-	if (status.interrupted) {
-		debug("upload interrupted by user");
-		return;
-	}
-	if (status.failed) {
-		debug("upload failed with following response:");
-		debug("%s", status.response.c_str());
-		return;
-	}
-	debug("upload HTTP response code = %ld", status.httpResponseCode);
-	if (!status.failed) {
-		debug("uploaded file info:");
-		debug("\tpath: %s", status.file.path().c_str());
-		debug("\tsize: %u", status.file.size());
-		debug("\ttimestamp: %u", status.file.timestamp());
-	}
+void DropboxStorage::printStorageFile(UploadResponse response) {	
+	debug("\nuploaded file info:");
+	debug("\tpath: %s", response.value.path().c_str());
+	debug("\tsize: %u", response.value.size());
+	debug("\ttimestamp: %u", response.value.timestamp());
 }
 
-Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, bool recursive) {
-	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive));
+void DropboxStorage::printErrorResponse(Networking::ErrorResponse error) {
+	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
 }
 
-Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) {
-	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback));
+Networking::ErrorCallback DropboxStorage::getErrorPrintingCallback() {
+	return new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::printErrorResponse);
 }
 
-Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback) {
+Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) {
+	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive));
+}
+
+Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback));
+}
+
+Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::File *f = new Common::File();
 	if (!f->open(localPath)) {
 		warning("DropboxStorage: unable to open file to upload from");
-		UploadStatus status(false, true, StorageFile(), "", -1);
-		if (callback) (*callback)(UploadResponse(nullptr, status));
+		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
+		delete errorCallback;
+		delete callback;
 		delete f;
 		return nullptr;
 	}
-	return upload(remotePath, f, callback);
+	return upload(remotePath, f, callback, errorCallback);
 }
 
-Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) {
+Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
 
-	Networking::CurlRequest *request = new Networking::CurlRequest(0, "https://content.dropboxapi.com/2/files/download");
+	Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, "https://content.dropboxapi.com/2/files/download"); //TODO: is it right?
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
 
-	Networking::NetworkReadStreamResponse pair = request->execute();
-	if (callback) (*callback)(pair);
-	return pair.request;
+	Networking::NetworkReadStreamResponse response = request->execute();
+	if (callback) (*callback)(response);
+	return response.request;
 }
 
-Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("DropboxStorage: unable to open file to download into");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
 		delete f;
 		return nullptr;
 	}
 
-	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
+	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
 }
 
-Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) {
-	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive));
+Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
-Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) {
-	//this is not the real syncSaves() implementation	
-	//"" is root in Dropbox, not "/"
-	//this must create all these directories:
-	//return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
-	/*
-	return downloadFolder(
-		"/not_flat", "local/not_flat_1_level/",
-		new Common::Callback<DropboxStorage, FileArrayResponse>(this, &DropboxStorage::printFiles),
-		false
-	);
-	*/
-	/*
-	debug("%s", ConfMan.get("savepath").c_str());
-	Common::StringArray arr = g_system->getSavefileManager()->listSavefiles("*");
-	for (uint32 i = 0; i < arr.size(); ++i) {
-		debug("%s", arr[i].c_str());
-	}
-	debug("EOL");
-	*/
-	//return upload("/remote/backslash", "C:\\Users\\Tkachov\\AppData\\Roaming\\ScummVM\\Saved games\\sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
-	//return upload("/remote/slash", "C:/Users/Tkachov/AppData/Roaming/ScummVM/Saved games/sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool)));
-	//return upload("/remote/test4.bmp", "final.bmp", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus));
+Networking::Request *DropboxStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	//this might be the real syncSaves() implementation
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool), getErrorPrintingCallback())); //TODO	
 }
 
-Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
+Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
 	//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
@@ -216,8 +191,8 @@ Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) {
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
-void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse pair) {
-	Common::JSONValue *json = pair.value;
+void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		delete outerCallback;
@@ -241,11 +216,11 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 	delete json;
 }
 
-void DropboxStorage::infoMethodCallback(StorageInfoResponse pair) {
+void DropboxStorage::infoMethodCallback(StorageInfoResponse response) {
 	debug("\nStorage info:");
-	debug("User name: %s", pair.value.name().c_str());
-	debug("Email: %s", pair.value.email().c_str());
-	debug("Disk usage: %u/%u", pair.value.used(), pair.value.available());
+	debug("User name: %s", response.value.name().c_str());
+	debug("Email: %s", response.value.email().c_str());
+	debug("Disk usage: %u/%u", response.value.used(), response.value.available());
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
@@ -298,7 +273,7 @@ void DropboxStorage::authThroughConsole() {
 
 void DropboxStorage::getAccessToken(Common::String code) {
 	Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::JsonResponse>(saveAccessTokenCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token"); //TODO
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index d9967d6..0082e5c 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -45,9 +45,12 @@ class DropboxStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void printFiles(FileArrayResponse pair);
-	void printBool(BoolResponse pair);
-	void printUploadStatus(UploadResponse pair);
+	void printFiles(FileArrayResponse response);
+	void printBool(BoolResponse response);
+	void printStorageFile(UploadResponse response);
+	void printErrorResponse(Networking::ErrorResponse error);
+
+	Networking::ErrorCallback getErrorPrintingCallback();
 
 public:	
 	virtual ~DropboxStorage();
@@ -68,38 +71,38 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns ListDirectoryStatus struct with list of files. */
-	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false);
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 	
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback);
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback);
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback);
+	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual Networking::Request *info(StorageInfoCallback callback);
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** This method is passed into info(). (Temporary) */
-	void infoMethodCallback(StorageInfoResponse pair);
+	void infoMethodCallback(StorageInfoResponse response);
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index e64a883..50a1b8a 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -32,8 +32,8 @@
 namespace Cloud {
 namespace Dropbox {
 
-DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback):
-	Networking::Request(0), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
+DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
@@ -50,7 +50,7 @@ void DropboxUploadRequest::start() {
 	if (_workingRequest) _workingRequest->finish();
 	if (!_contentsStream->seek(0)) {
 		warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
-		finish();
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 	}
 	_ignoreCallback = false;
 
@@ -97,8 +97,9 @@ void DropboxUploadRequest::uploadNextPart() {
 	}
 	
 	Common::JSONValue value(jsonRequestParameters);
-	Networking::JsonCallback innerCallback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, url);
+	Networking::JsonCallback callback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxUploadRequest, Networking::ErrorResponse>(this, &DropboxUploadRequest::partUploadedErrorCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, url);
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/octet-stream");	
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));	
@@ -110,46 +111,45 @@ void DropboxUploadRequest::uploadNextPart() {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
-	if (_ignoreCallback) return;
+void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
+	debug("partUploadedCallback");
 	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
 
-	UploadStatus status;
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	Networking::ErrorResponse error(this, false, true, "", -1);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
-		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	Common::JSONValue *json = pair.value;
+	Common::JSONValue *json = response.value;
 	if (json) {
 		bool needsFinishRequest = false;
 
 		if (json->isObject()) {
-			Common::JSONObject response = json->asObject();
+			Common::JSONObject object = json->asObject();
 
 			//debug("%s", json->stringify(true).c_str());
 
-			if (response.contains("error") || response.contains("error_summary")) {
-				warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str());
+			if (object.contains("error") || object.contains("error_summary")) {
+				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
 				delete json;
-				status.failed = true;
-				status.response = json->stringify(true);
-				finishUpload(status);
+				error.response = json->stringify(true);
+				finishError(error);
 				return;
 			}
 
-			if (response.contains("server_modified")) {
+			if (object.contains("server_modified")) {
 				//finished
-				Common::String path = response.getVal("path_lower")->asString();				
-				uint32 size = response.getVal("size")->asIntegerNumber();
-				uint32 timestamp = ISO8601::convertToTimestamp(response.getVal("server_modified")->asString());				
-				status.file = StorageFile(path, size, timestamp, false);
-				finishUpload(status);
+				Common::String path = object.getVal("path_lower")->asString();
+				uint32 size = object.getVal("size")->asIntegerNumber();
+				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString());
+				finishSuccess(StorageFile(path, size, timestamp, false));
 				return;
 			}
 
 			if (_sessionId == "") {
-				if (response.contains("session_id"))
-					_sessionId = response.getVal("session_id")->asString();
+				if (object.contains("session_id"))
+					_sessionId = object.getVal("session_id")->asString();
 				else
 					warning("no session_id found in Dropbox's response");
 				needsFinishRequest = true;
@@ -157,36 +157,33 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) {
 		}
 
 		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {			
-			if (status.file.name() == "") {
-				status.file = StorageFile(_savePath, 0, 0, false);
-				warning("no file info to put into status");
-			}
-			finishUpload(status);
+			warning("no file info to return");
+			finishSuccess(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");
-		status.failed = true;
-		finishUpload(status);
+		warning("null, not json");		
+		finishError(error);
 	}
 
 	delete json;
 }
 
+void DropboxUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
+	debug("partUploadedErrorCallback");
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
 void DropboxUploadRequest::handle() {}
 
 void DropboxUploadRequest::restart() { start(); }
 
-void DropboxUploadRequest::finish() {
-	UploadStatus status;
-	status.interrupted = true;
-	finishUpload(status);
-}
-
-void DropboxUploadRequest::finishUpload(UploadStatus status) {
-	Request::finish();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, status));
+void DropboxUploadRequest::finishSuccess(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
index 9b68995..a85d7ef 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.h
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -42,16 +42,16 @@ class DropboxUploadRequest: public Networking::Request {
 	
 	void start();
 	void uploadNextPart();
-	void partUploadedCallback(Networking::JsonResponse pair);
-	void finishUpload(UploadStatus status);
+	void partUploadedCallback(Networking::JsonResponse response);
+	void partUploadedErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(StorageFile status);
 
 public:
-	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback);
+	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
 	virtual ~DropboxUploadRequest();
 
 	virtual void handle();
 	virtual void restart();
-	virtual void finish();
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index db132ff..19f6c6c 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -25,8 +25,8 @@
 
 namespace Cloud {
 
-FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive):
-	Request(nullptr), _storage(storage), _fileArrayCallback(callback),
+FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive):
+	Request(nullptr, ecb), _storage(storage), _fileArrayCallback(callback),
 	_remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
@@ -51,33 +51,39 @@ void FolderDownloadRequest::start() {
 	_workingRequest = _storage->listDirectory(
 		_remoteDirectoryPath,
 		new Common::Callback<FolderDownloadRequest, Storage::ListDirectoryResponse>(this, &FolderDownloadRequest::directoryListedCallback),
+		new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::directoryListedErrorCallback),
 		_recursive
 	);
 }
 
-void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) {
+void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
+	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-
-	ListDirectoryStatus status = pair.value;
-	if (status.failed || status.interrupted) {
-		finish();
-		return;
-	}
-	
-	_files = pair.value.files;
+	_files = response.value;
 	downloadNextFile();
 }
 
-void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
+void FolderDownloadRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	if (!pair.value) _failedFiles.push_back(_currentFile);
+	if (!response.value) _failedFiles.push_back(_currentFile);
 	downloadNextFile();
 }
 
+void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
+	fileDownloadedCallback(Storage::BoolResponse(error.request, false));
+}
+
 void FolderDownloadRequest::downloadNextFile() {
 	do {
 		if (_files.empty()) {
-			finishFiles(_failedFiles);
+			finishSuccess(_failedFiles);
 			return;
 		}
 	
@@ -105,18 +111,17 @@ void FolderDownloadRequest::downloadNextFile() {
 	debug("%s -> %s", remotePath.c_str(), localPath.c_str());
 	_workingRequest = _storage->download(
 		remotePath, localPath,
-		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback)		
+		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback),
+		new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::fileDownloadedErrorCallback)
 	);
 }
 
-void FolderDownloadRequest::finish() {
-	//TODO: somehow indicate that request was interrupted
-	Common::Array<StorageFile> files;
-	finishFiles(files);
-}
+void FolderDownloadRequest::handle() {}
+
+void FolderDownloadRequest::restart() { start(); }
 
-void FolderDownloadRequest::finishFiles(Common::Array<StorageFile> &files) {
-	Request::finish();
+void FolderDownloadRequest::finishSuccess(Common::Array<StorageFile> &files) {
+	Request::finishSuccess();
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 779ea33..8fa3b11 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -40,17 +40,18 @@ class FolderDownloadRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void directoryListedCallback(Storage::ListDirectoryResponse pair);
-	void fileDownloadedCallback(Storage::BoolResponse pair);
+	void directoryListedCallback(Storage::ListDirectoryResponse response);
+	void directoryListedErrorCallback(Networking::ErrorResponse error);
+	void fileDownloadedCallback(Storage::BoolResponse response);
+	void fileDownloadedErrorCallback(Networking::ErrorResponse error);
 	void downloadNextFile();
-	void finishFiles(Common::Array<StorageFile> &files);
+	void finishSuccess(Common::Array<StorageFile> &files);
 public:
-	FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive);
+	FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive);
 	virtual ~FolderDownloadRequest();
 
-	virtual void handle() {}
-	virtual void restart() { start(); }
-	virtual void finish();
+	virtual void handle();
+	virtual void restart();	
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 13f23b8..2f1533c 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -110,9 +110,9 @@ Storage *Manager::getCurrentStorage() {
 	return nullptr;
 }
 
-void Manager::syncSaves(Storage::BoolCallback callback) {
+void Manager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->syncSaves(callback);
+	if (storage) storage->syncSaves(callback, errorCallback);
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index f472b80..013e117 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -42,7 +42,7 @@ public:
 	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true);
 
 	virtual Storage *getCurrentStorage();
-	virtual void syncSaves(Storage::BoolCallback callback);
+	virtual void syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback);
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index dbd5e44..e362600 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -31,8 +31,8 @@
 namespace Cloud {
 namespace OneDrive {
 
-OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive):
-	Networking::Request(0),
+OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
 	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
@@ -55,12 +55,12 @@ void OneDriveListDirectoryRequest::start() {
 	_ignoreCallback = false;
 
 	_directoriesQueue.push_back(_requestedPath);
-	listNextDirectory(_files);
+	listNextDirectory();
 }
 
-void OneDriveListDirectoryRequest::listNextDirectory(ListDirectoryStatus status) {
+void OneDriveListDirectoryRequest::listNextDirectory() {
 	if (_directoriesQueue.empty()) {
-		finishStatus(status);
+		finishSuccess(_files);
 		return;
 	}
 
@@ -78,36 +78,37 @@ void OneDriveListDirectoryRequest::listNextDirectory(ListDirectoryStatus status)
 
 void OneDriveListDirectoryRequest::makeRequest(Common::String url) {	
 	Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, url.c_str());
+	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse pair) {
+void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	Common::JSONValue *json = pair.value;
+	Common::JSONValue *json = response.value;
 
 	if (_ignoreCallback) {
 		delete json;
 		return;
 	}
 
-	ListDirectoryStatus status(_files);
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request;
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
-		status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	if (!json) {
-		status.failed = true;
-		finishStatus(status);
+		error.failed = true;
+		finishError(error);
 		return;
 	}
 
-	Common::JSONObject response = json->asObject();		
+	Common::JSONObject object = json->asObject();		
 	
 	//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
-	Common::JSONArray items = response.getVal("value")->asArray();
+	Common::JSONArray items = object.getVal("value")->asArray();
 	for (uint32 i = 0; i < items.size(); ++i) {
 		Common::JSONObject item = items[i]->asObject();	
 
@@ -123,26 +124,29 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 		}
 	}
 
-	bool hasMore = response.contains("@odata.nextLink");
+	bool hasMore = object.contains("@odata.nextLink");
 	if (hasMore) {
-		makeRequest(response.getVal("@odata.nextLink")->asString());
+		makeRequest(object.getVal("@odata.nextLink")->asString());
 	} else {
-		listNextDirectory(status);
+		listNextDirectory();
 	}
 
 	delete json;
 }
 
-void OneDriveListDirectoryRequest::finish() {	
-	Common::Array<StorageFile> files;
-	ListDirectoryStatus status(files);
-	status.interrupted = true;
-	finishStatus(status);
+void OneDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
 }
 
-void OneDriveListDirectoryRequest::finishStatus(ListDirectoryStatus status) {
-	Request::finish();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status));
+void OneDriveListDirectoryRequest::handle() {}
+
+void OneDriveListDirectoryRequest::restart() { start(); }
+
+void OneDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index a05dd87..b8adfe7 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -45,17 +45,17 @@ class OneDriveListDirectoryRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void listNextDirectory(ListDirectoryStatus status);
-	void listedDirectoryCallback(Networking::JsonResponse pair);
+	void listNextDirectory();
+	void listedDirectoryCallback(Networking::JsonResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
 	void makeRequest(Common::String url);
-	void finishStatus(ListDirectoryStatus status);
+	void finishSuccess(Common::Array<StorageFile> &files);
 public:
-	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false);
+	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
 	virtual ~OneDriveListDirectoryRequest();
 
-	virtual void handle() {}
-	virtual void restart() { start(); }
-	virtual void finish();
+	virtual void handle();
+	virtual void restart();
 };
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index b0f4f7b..8746b7a 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -74,7 +74,7 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://login.live.com/oauth20_token.srf"); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
@@ -88,8 +88,8 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	ConnMan.addRequest(request);
 }
 
-void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair) {
-	Common::JSONValue *json = pair.value;
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("OneDriveStorage: got NULL instead of JSON");
 		if (callback) (*callback)(BoolResponse(nullptr, false));
@@ -111,8 +111,8 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 	delete json;
 }
 
-void OneDriveStorage::codeFlowComplete(BoolResponse pair) {
-	if (!pair.value) {
+void OneDriveStorage::codeFlowComplete(BoolResponse response) {
+	if (!response.value) {
 		warning("OneDriveStorage: failed to get access token through code flow");
 		return;
 	}
@@ -130,8 +130,8 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
-void OneDriveStorage::printJson(Networking::JsonResponse pair) {
-	Common::JSONValue *json = pair.value;
+void OneDriveStorage::printJson(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("printJson: NULL");
 		return;
@@ -141,77 +141,85 @@ void OneDriveStorage::printJson(Networking::JsonResponse pair) {
 	delete json;
 }
 
-void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair) {
-	if (!pair.value) {
+void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
+	if (!response.value) {
 		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0));
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
 		return;
 	}
 
-	Common::JSONObject result = pair.value->asObject();
+	Common::JSONObject result = response.value->asObject();
 	if (result.contains("@content.downloadUrl")) {
 		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(
-				pair.request,
+				response.request,
 				new Networking::NetworkReadStream(url, 0, "")
 			));
 	} else {
 		warning("downloadUrl not found in passed JSON");
-		debug("%s", pair.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0));
+		debug("%s", response.value->stringify().c_str());
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
 	}
-	delete pair.value;
+	delete response.value;
 }
 
-Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive) {
-	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, recursive));
+Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
 
-Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback) {
+Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, url.c_str());
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
 	return ConnMan.addRequest(request);
 }
 
-Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) {
+Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(localPath, true)) {
 		warning("OneDriveStorage: unable to open file to download into");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
 		delete f;
 		return nullptr;
 	}
 
-	return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f));
+	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
 }
 
 /** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) {
-	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive));
+Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
-void OneDriveStorage::fileDownloaded(BoolResponse pair) {
-	if (pair.value) debug("file downloaded!");
+void OneDriveStorage::fileDownloaded(BoolResponse response) {
+	if (response.value) debug("file downloaded!");
 	else debug("download failed!");
 }
 
-void OneDriveStorage::printFiles(FileArrayResponse pair) {
+void OneDriveStorage::printFiles(FileArrayResponse response) {
 	debug("files:");
-	Common::Array<StorageFile> &files = pair.value;
+	Common::Array<StorageFile> &files = response.value;
 	for (uint32 i = 0; i < files.size(); ++i)
 		debug("\t%s", files[i].path().c_str());
 }
 
-void OneDriveStorage::printBool(BoolResponse pair) {
-	debug("bool: %s", pair.value ? "true" : "false");
+void OneDriveStorage::printBool(BoolResponse response) {
+	debug("bool: %s", response.value ? "true" : "false");
+}
+
+void OneDriveStorage::printErrorResponse(Networking::ErrorResponse error) {
+	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
 }
 
+Networking::ErrorCallback OneDriveStorage::getErrorPrintingCallback() {
+	return new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::printErrorResponse);
+}
 
-Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
+Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	//this is not the real syncSaves() implementation
 	/*
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
@@ -220,7 +228,7 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
 	return ConnMan.addRequest(request);
 	*/
 	//return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool)));
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 43675fb..f95ea89 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -46,15 +46,18 @@ class OneDriveStorage: public Cloud::Storage {
 	 */
 	OneDriveStorage(Common::String code);
 
-	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair);
-	void codeFlowComplete(BoolResponse pair);
+	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
+	void codeFlowComplete(BoolResponse response);
 
-	void printJson(Networking::JsonResponse pair);
-	void fileDownloaded(BoolResponse pair);
-	void printFiles(FileArrayResponse pair);
-	void printBool(BoolResponse pair);
+	void printJson(Networking::JsonResponse response);
+	void fileDownloaded(BoolResponse response);
+	void printFiles(FileArrayResponse response);
+	void printBool(BoolResponse response);
+	void printErrorResponse(Networking::ErrorResponse error);
 
-	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair);
+	Networking::ErrorCallback getErrorPrintingCallback();
+
+	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 public:	
 	virtual ~OneDriveStorage();
 
@@ -74,35 +77,35 @@ public:
 	/** Public Cloud API comes down there. */
 
 	/** Returns ListDirectoryStatus struct with list of files. */
-	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false);
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) { return nullptr; } //TODO
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) { return nullptr; }
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback);
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback);
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false);
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback);
+	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual Networking::Request *info(StorageInfoCallback callback) { return nullptr; } //TODO
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index a0c41ff..bc7bd74 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -31,16 +31,16 @@
 namespace Cloud {
 namespace OneDrive {
 
-OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url):
-	CurlJsonRequest(callback, url), _parentStorage(parent) {}
+OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
+	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
 
 OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
 
-void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
-	if (!pair.value) {
+void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
+	if (!response.value) {
 		//failed to refresh token, notify user with NULL in original callback
 		warning("OneDriveTokenRefresher: failed to refresh token");
-		finish();
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 		return;
 	}
 
@@ -56,11 +56,10 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) {
 	retry(0);
 }
 
-void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {	
+void OneDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
 	if (!json) {
-		//notify user of failure
-		warning("OneDriveTokenRefresher: got NULL instead of JSON");
-		CurlJsonRequest::finishJson(nullptr);
+		//that's probably not an error (200 OK)
+		CurlJsonRequest::finishSuccess(nullptr);
 		return;
 	}
 
@@ -74,19 +73,26 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::JSONObject error = result.getVal("error")->asObject();
 		bool irrecoverable = true;
 
+		Common::String code, message;
 		if (error.contains("code")) {
-			Common::String code = error.getVal("code")->asString();
-			debug("code = %s", code.c_str());
-			//if (code == "itemNotFound") irrecoverable = true;
+			code = error.getVal("code")->asString();
+			debug("code = %s", code.c_str());			
 		}
 
 		if (error.contains("message")) {
-			Common::String message = error.getVal("message")->asString();
+			message = error.getVal("message")->asString();
 			debug("message = %s", message.c_str());
 		}
 
-		if (irrecoverable) {
-			CurlJsonRequest::finishJson(nullptr);
+		//determine whether token refreshing would help in this situation
+		if (code == "itemNotFound") {
+			if (message.contains("application ID"))
+				irrecoverable = false;
+		}
+
+		if (irrecoverable) {			
+			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
+			delete json;
 			return;
 		}
 
@@ -97,7 +103,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishJson(json);
+	CurlJsonRequest::finishSuccess(json);
 }
 
 void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
@@ -108,6 +114,10 @@ void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers)
 		CurlJsonRequest::addHeader(headers[i]);
 }
 
+void OneDriveTokenRefresher::addHeader(Common::String header) {
+	_headers.push_back(header);
+	CurlJsonRequest::addHeader(header);
+}
 
 } // End of namespace OneDrive
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 90ca9d6..04b0bf2 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -35,19 +35,15 @@ class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
 	OneDriveStorage *_parentStorage;
 	Common::Array<Common::String> _headers;	
 	
-	void tokenRefreshed(Storage::BoolResponse pair);
+	void tokenRefreshed(Storage::BoolResponse response);
 
-	virtual void finishJson(Common::JSONValue *json);
+	virtual void finishSuccess(Common::JSONValue *json);
 public:	
-	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url);
+	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~OneDriveTokenRefresher();
 
 	virtual void setHeaders(Common::Array<Common::String> &headers);
-
-	virtual void addHeader(Common::String header) {
-		_headers.push_back(header);
-		CurlJsonRequest::addHeader(header);
-	}
+	virtual void addHeader(Common::String header);
 };
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index e632bfff..b48a99a 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -32,8 +32,8 @@ namespace Cloud {
 
 const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
 
-SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback):
-	Request(nullptr), _storage(storage), _boolCallback(callback),
+SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
+	Request(nullptr, ecb), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
@@ -59,48 +59,35 @@ void SavesSyncRequest::start() {
 	loadTimestamps();
 
 	//list saves directory
-	_workingRequest = _storage->listDirectory("/saves", new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback));
+	_workingRequest = _storage->listDirectory(
+		"/saves",
+		new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
+		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
+	);
 }
 
-void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) {
+void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
-	ListDirectoryStatus status = pair.value;
-	bool irrecoverable = status.interrupted || status.failed;
-	if (status.failed) {
-		Common::JSONValue *value = Common::JSON::parse(status.response.c_str());
-		if (value) {
-			if (value->isObject()) {
-				Common::JSONObject object = value->asObject();
-				//Dropbox-related error:
-				if (object.contains("error_summary")) {
-					Common::String summary = object.getVal("error_summary")->asString();
-					if (summary.contains("not_found")) {
-						//oh how lucky we are! It's just user don't have /cloud/ folder yet!
-						irrecoverable = false;
-					}
-				}
-			}
-			delete value;
-		}
+	Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
+	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {		
+		localFileNotAvailableInCloud[i->_key] = true;
 	}
 
-	if (irrecoverable) {
-		finishBool(false);
-		return;
-	}
-	
 	//determine which files to download and which files to upload
-	Common::Array<StorageFile> &remoteFiles = status.files;
+	Common::Array<StorageFile> &remoteFiles = response.value;
 	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
 		StorageFile &file = remoteFiles[i];
 		if (file.isDirectory()) continue;
 		if (file.name() == TIMESTAMPS_FILENAME) continue;
+
 		Common::String name = file.name();
 		if (!_localFilesTimestamps.contains(name))
 			_filesToDownload.push_back(file);
 		else {
+			localFileNotAvailableInCloud[name] = false;
+
 			if (_localFilesTimestamps[name] != INVALID_TIMESTAMP) {
 				if (_localFilesTimestamps[name] == file.timestamp())
 					continue;
@@ -115,11 +102,10 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pa
 		}
 	}
 
-	//upload files with invalid timestamp (the ones we've added - means they might not have any remote version)
-	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
+	//upload files which are unavailable in cloud
+	for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
 		if (i->_key == TIMESTAMPS_FILENAME) continue;
-		if (i->_value == INVALID_TIMESTAMP)
-			_filesToUpload.push_back(i->_key);
+		if (i->_value) _filesToUpload.push_back(i->_key);
 	}
 
 	///////
@@ -137,6 +123,50 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pa
 	downloadNextFile();
 }
 
+void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	bool irrecoverable = error.interrupted || error.failed;
+	if (error.failed) {
+		Common::JSONValue *value = Common::JSON::parse(error.response.c_str());
+		if (value) {
+			if (value->isObject()) {
+				Common::JSONObject object = value->asObject();
+
+				//Dropbox-related error:
+				if (object.contains("error_summary")) {
+					Common::String summary = object.getVal("error_summary")->asString();
+					if (summary.contains("not_found")) {						
+						irrecoverable = false;
+					}
+				}
+
+				//OneDrive-related error:
+				if (object.contains("error") && object.getVal("error")->isObject()) {
+					Common::JSONObject errorNode = object.getVal("error")->asObject();
+					if (errorNode.contains("code") && errorNode.contains("message")) {
+						Common::String code = errorNode.getVal("code")->asString();
+						if (code == "itemNotFound") {
+							irrecoverable = false;
+						}
+					}
+				}
+			}
+			delete value;
+		}
+	}
+
+	if (irrecoverable) {
+		finishError(error);
+		return;
+	}
+
+	//we're lucky - user just lacks his "/cloud/" folder
+	Common::Array<StorageFile> files;
+	directoryListedCallback(Storage::ListDirectoryResponse(error.request, files));
+}
+
 void SavesSyncRequest::downloadNextFile() {
 	if (_filesToDownload.empty()) {
 		uploadNextFile();
@@ -150,17 +180,18 @@ void SavesSyncRequest::downloadNextFile() {
 	debug("downloading %s", _currentDownloadingFile.name().c_str());
 	///////
 	_workingRequest = _storage->download(_currentDownloadingFile.path(), concatWithSavesPath(_currentDownloadingFile.name()),
-		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback)
+		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
+		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
 }
 
-void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
+void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
 	//stop syncing if download failed
-	if (!pair.value) {
-		finish();
+	if (!response.value) {
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 		return;
 	}
 
@@ -171,9 +202,17 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) {
 	downloadNextFile();
 }
 
+void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//stop syncing if download failed
+	finishError(error);	
+}
+
 void SavesSyncRequest::uploadNextFile() {
 	if (_filesToUpload.empty()) {
-		finishBool(true);
+		finishSuccess(true);
 		return;
 	}
 
@@ -184,36 +223,45 @@ void SavesSyncRequest::uploadNextFile() {
 	debug("uploading %s", _currentUploadingFile.c_str());
 	///////
 	_workingRequest = _storage->upload("/saves/" + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
-		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback)
+		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
+		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
 	);
 }
 
-void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse pair) {
+void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	UploadStatus status = pair.value;
-
-	//stop syncing if upload failed
-	if (status.interrupted || status.failed) {
-		finish();
-		return;
-	}
-
+	
 	//update local timestamp for the uploaded file
-	_localFilesTimestamps[_currentUploadingFile] = status.file.timestamp();
+	_localFilesTimestamps[_currentUploadingFile] = response.value.timestamp();
 
 	//continue uploading files
 	uploadNextFile();
 }
 
+void SavesSyncRequest::fileUploadedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//stop syncing if upload failed
+	finishError(error);
+}
+
 void SavesSyncRequest::handle() {}
 
 void SavesSyncRequest::restart() { start(); }
 
-void SavesSyncRequest::finish() { finishBool(false); }
+void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
+	debug("SavesSync::finishError");
+
+	//save updated timestamps (even if Request failed, there would be only valid timestamps)
+	saveTimestamps();
+
+	Request::finishError(error);
+}
 
-void SavesSyncRequest::finishBool(bool success) {
-	Request::finish();
+void SavesSyncRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
 
 	//save updated timestamps (even if Request failed, there would be only valid timestamps)
 	saveTimestamps();
@@ -233,7 +281,6 @@ void SavesSyncRequest::loadTimestamps() {
 		warning("SavesSyncRequest: failed to open '%s' file to load timestamps", TIMESTAMPS_FILENAME);
 		return;
 	}
-
 	
 	while (!file->eos()) {
 		//read filename into buffer (reading until the first ' ')
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index da7b27e..bf44b70 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -45,22 +45,25 @@ class SavesSyncRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
-	void directoryListedCallback(Storage::ListDirectoryResponse pair);
-	void fileDownloadedCallback(Storage::BoolResponse pair);
-	void fileUploadedCallback(Storage::UploadResponse pair);
+	void directoryListedCallback(Storage::ListDirectoryResponse response);
+	void directoryListedErrorCallback(Networking::ErrorResponse error);
+	void fileDownloadedCallback(Storage::BoolResponse response);
+	void fileDownloadedErrorCallback(Networking::ErrorResponse error);
+	void fileUploadedCallback(Storage::UploadResponse response);
+	void fileUploadedErrorCallback(Networking::ErrorResponse error);
 	void downloadNextFile();
 	void uploadNextFile();
-	void finishBool(bool success);
+	virtual void finishError(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
 	void loadTimestamps();
 	void saveTimestamps();
 	Common::String concatWithSavesPath(Common::String name);
 public:
-	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback);
+	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb);
 	virtual ~SavesSyncRequest();
 
 	virtual void handle();
-	virtual void restart();
-	virtual void finish();
+	virtual void restart();	
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 311b3fd..32c4378 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -34,53 +34,13 @@
 
 namespace Cloud {
 
-/** Struct to represent upload() resulting status. */
-struct UploadStatus {
-	/** true if Request was interrupted (finished by user with finish()) */
-	bool interrupted;
-	/** true if Request has failed (bad server response or some other error occurred) */
-	bool failed;
-	/** Contains uploaded file description (empty if failed) */
-	StorageFile file;
-	/** Server's original response (empty if not failed) */
-	Common::String response;
-	/** Server's HTTP response code. */
-	long httpResponseCode;
-
-	UploadStatus():
-		interrupted(false), failed(false), file(), response(), httpResponseCode(-1) {}
-
-	UploadStatus(bool interrupt, bool failure, StorageFile f, Common::String resp, long code):
-		interrupted(interrupt), failed(failure), file(f), response(resp), httpResponseCode(code) {}
-};
-
-/** Struct to represent upload() resulting status. */
-struct ListDirectoryStatus {
-	/** true if Request was interrupted (finished by user with finish()) */
-	bool interrupted;
-	/** true if Request has failed (bad server response or some other error occurred) */
-	bool failed;
-	/** Contains listed files (might be incomplete if failed or interrupted) */
-	Common::Array<StorageFile> &files;
-	/** Server's original response (empty if not failed) */
-	Common::String response;
-	/** Server's HTTP response code. */
-	long httpResponseCode;
-
-	ListDirectoryStatus(Common::Array<StorageFile> &f) :
-		interrupted(false), failed(false), files(f), response(), httpResponseCode(-1) {}
-
-	ListDirectoryStatus(bool interrupt, bool failure, Common::Array<StorageFile> &f, Common::String resp, long code) :
-		interrupted(interrupt), failed(failure), files(f), response(resp), httpResponseCode(code) {}
-};
-
 class Storage {
 public:
 	typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse;
 	typedef Networking::Response<StorageInfo> StorageInfoResponse;
 	typedef Networking::Response<bool> BoolResponse;
-	typedef Networking::Response<UploadStatus> UploadResponse;
-	typedef Networking::Response<ListDirectoryStatus> ListDirectoryResponse;
+	typedef Networking::Response<StorageFile> UploadResponse;
+	typedef Networking::Response<Common::Array<StorageFile> &> ListDirectoryResponse;
 
 	typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback;
 	typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback;
@@ -113,35 +73,35 @@ public:
 	 */
 
 	/** Returns ListDirectoryStatus struct with list of files. */
-	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false) = 0;
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
 	
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) = 0;
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) = 0;
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0;
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0;
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false) = 0;
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback) = 0;
+	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback) = 0;
+	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns the StorageInfo struct. */
-	virtual Networking::Request *info(StorageInfoCallback callback) = 0;
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
diff --git a/backends/module.mk b/backends/module.mk
index 281c6a9..c40781d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -40,7 +40,8 @@ MODULE_OBJS += \
 	networking/curl/connectionmanager.o \
 	networking/curl/networkreadstream.o \
 	networking/curl/curlrequest.o \
-	networking/curl/curljsonrequest.o
+	networking/curl/curljsonrequest.o \
+	networking/curl/request.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index fee0932..df982bc 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,8 +31,8 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(JsonCallback cb, Common::String url):
-	CurlRequest(0, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
+CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url):
+	CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
 
 CurlJsonRequest::~CurlJsonRequest() { delete _jsonCallback; }
 
@@ -65,34 +65,32 @@ void CurlJsonRequest::handle() {
 			if (_contentsStream.write(buf, readBytes) != readBytes)
 				warning("MemoryWriteStreamDynamic was unable to write all the bytes");
 
-		if (_stream->eos()) {			
-			if (_stream->httpResponseCode() != 200)
-				warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
-
+		if (_stream->eos()) {
 			char *contents = getPreparedContents();
-			if (_stream->httpResponseCode() != 200)
-				debug("%s", contents);
 			Common::JSONValue *json = Common::JSON::parse(contents);
-			finishJson(json);
+			if (json) {
+				finishSuccess(json); //it's JSON even if's not 200 OK? That's fine!..
+			} else {
+				if (_stream->httpResponseCode() == 200) //no JSON, but 200 OK? That's fine!..
+					finishSuccess(nullptr);
+				else
+					finishError(ErrorResponse(this, false, true, contents, _stream->httpResponseCode()));
+			}
 		}
 	}
 }
 
 void CurlJsonRequest::restart() {
 	if (_stream) delete _stream;
-	_stream = 0;
+	_stream = nullptr;
 	_contentsStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
 	//with no stream available next handle() will create another one
 }
 
-void CurlJsonRequest::finishJson(Common::JSONValue *json) {
-	Request::finish();
+void CurlJsonRequest::finishSuccess(Common::JSONValue *json) {
+	Request::finishSuccess();
 	if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
 	else delete json;
 }
 
-void CurlJsonRequest::finish() {
-	finishJson(0);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 5e08be2..83005a7 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -41,15 +41,14 @@ protected:
 	char *getPreparedContents();
 
 	/** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */
-	virtual void finishJson(Common::JSONValue *json);
+	virtual void finishSuccess(Common::JSONValue *json);
 
 public:
-	CurlJsonRequest(JsonCallback cb, Common::String url);
+	CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url);
 	virtual ~CurlJsonRequest();
 
 	virtual void handle(); 
 	virtual void restart();
-	virtual void finish();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 64f6c26..861546b 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -30,8 +30,8 @@
 
 namespace Networking {
 
-CurlRequest::CurlRequest(DataCallback cb, Common::String url):
-	Request(cb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {}
+CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url):
+	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {}
 
 CurlRequest::~CurlRequest() {
 	delete _stream;
@@ -49,9 +49,14 @@ void CurlRequest::handle() {
 	if (!_stream) _stream = makeStream();	
 
 	if (_stream && _stream->eos()) {
-		if (_stream->httpResponseCode() != 200)
+		if (_stream->httpResponseCode() != 200) {
 			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
-		finish();
+			ErrorResponse error(this, false, true, "", _stream->httpResponseCode());
+			finishError(error);
+			return;
+		}
+
+		finishSuccess(); //note that this Request doesn't call its callback on success (that's because it has nothing to return)
 	}
 }
 
@@ -101,4 +106,6 @@ NetworkReadStreamResponse CurlRequest::execute() {
 	return NetworkReadStreamResponse(this, _stream);
 }
 
+const NetworkReadStream *CurlRequest::getNetworkReadStream() const { return _stream; }
+
 } // End of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 461f153..660479e 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -48,7 +48,7 @@ protected:
 	virtual NetworkReadStream *makeStream();
 
 public:
-	CurlRequest(DataCallback cb, Common::String url);
+	CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url);
 	virtual ~CurlRequest();
 
 	virtual void handle();
@@ -73,7 +73,7 @@ public:
 	virtual NetworkReadStreamResponse execute();
 
 	/** Returns Request's NetworkReadStream. */
-	const NetworkReadStream *getNetworkReadStream() const { return _stream; }
+	const NetworkReadStream *getNetworkReadStream() const;
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp
new file mode 100644
index 0000000..d2f9158
--- /dev/null
+++ b/backends/networking/curl/request.cpp
@@ -0,0 +1,70 @@
+/* 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 "backends/networking/curl/request.h"
+
+namespace Networking {
+
+ErrorResponse::ErrorResponse(Request *rq):
+	request(rq), interrupted(false), failed(true), response(""), httpResponseCode(-1) {}
+
+ErrorResponse::ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode):
+	request(rq), interrupted(interrupt), failed(failure), response(resp), httpResponseCode(httpCode) {}
+
+Request::Request(DataCallback cb, ErrorCallback ecb):
+	_callback(cb), _errorCallback(ecb), _state(PROCESSING), _retryInSeconds(0) {}
+
+Request::~Request() {
+	delete _callback;
+	delete _errorCallback;
+}
+
+void Request::handleRetry() {
+	if (_retryInSeconds > 0) --_retryInSeconds;
+	else {
+		_state = PROCESSING;
+		restart();
+	}
+}
+
+void Request::pause() { _state = PAUSED; }
+
+void Request::finish() {
+	ErrorResponse error(this, true, false, "", -1);
+	finishError(error);
+}
+
+void Request::retry(uint32 seconds) {
+	_state = RETRY;
+	_retryInSeconds = seconds;
+}
+
+RequestState Request::state() const { return _state; }
+
+void Request::finishError(ErrorResponse error) {
+	_state = FINISHED;
+	if (_errorCallback) (*_errorCallback)(error);
+}
+
+void Request::finishSuccess() { _state = FINISHED; }
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index a3c723d..de5308f 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -25,6 +25,7 @@
 
 #include "common/callback.h"
 #include "common/scummsys.h"
+#include "common/str.h"
 
 namespace Networking {
 
@@ -51,8 +52,39 @@ template<typename T> struct Response {
 	Response(Request *rq, T v) : request(rq), value(v) {}
 };
 
+/**
+ * ErrorResponse is a struct to be returned from Request
+ * to user's failure callbacks.
+ *
+ * It keeps a Request pointer together with some useful
+ * information fields, which would explain why failure
+ * callback was called.
+ *
+ * <interrupted> flag is set when Request was interrupted,
+ * i.e. finished by user with finish() call.
+ *
+ * <failed> flag is set when Request has failed because of
+ * some error (bad server response, for example).
+ *
+ * <response> contains server's original response.
+ *
+ * <httpResponseCode> contains server's HTTP response code.
+ */
+
+struct ErrorResponse {
+	Request *request;
+	bool interrupted;
+	bool failed;
+	Common::String response;
+	long httpResponseCode;
+
+	ErrorResponse(Request *rq);
+	ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode);
+};
+
 typedef Response<void *> DataReponse;
 typedef Common::BaseCallback<DataReponse> *DataCallback;
+typedef Common::BaseCallback<ErrorResponse> *ErrorCallback;
 
 /**
  * RequestState is used to indicate current Request state.
@@ -74,6 +106,9 @@ typedef Common::BaseCallback<DataReponse> *DataCallback;
  * After this state is set, but before ConnectionManager deletes the Request,
  * Request calls user's callback. User can ask Request to change its state
  * by calling retry() or pause() methods and Request won't be deleted.
+ *
+ * Request get a success and failure callbacks. Request must call one
+ * (and only one!) of these callbacks when it sets FINISHED state.
  */
 enum RequestState {
 	PROCESSING,
@@ -94,6 +129,13 @@ protected:
 	DataCallback _callback;
 
 	/**
+	* Callback, which should be called when Request is failed/interrupted.
+	* That's the way Requests pass error information to the code which asked to create this request.
+	* @note callback must be called in finish() or similar method.
+	*/
+	ErrorCallback _errorCallback;
+
+	/**
 	 * Request state, which is used by ConnectionManager to determine
 	 * whether request might be deleted or it's still working.
 	 *
@@ -106,45 +148,42 @@ protected:
 	/** In RETRY state this indicates whether it's time to call restart(). */
 	uint32 _retryInSeconds;
 
+	/** Sets FINISHED state and calls the _errorCallback with given error. */
+	virtual void finishError(ErrorResponse error);
+
+	/** Sets FINISHED state. Implementations might extend it if needed. */
+	virtual void finishSuccess();
+
 public:
-	Request(DataCallback cb): _callback(cb), _state(PROCESSING), _retryInSeconds(0) {}
-	virtual ~Request() { delete _callback; }
+	Request(DataCallback cb, ErrorCallback ecb);
+	virtual ~Request();
 
 	/** Method, which does actual work. Depends on what this Request is doing. */
 	virtual void handle() = 0;
 
 	/** Method, which is called by ConnectionManager when Request's state is RETRY.	 */
-	virtual void handleRetry() {
-		if (_retryInSeconds > 0) --_retryInSeconds;
-		else {
-			_state = PROCESSING;
-			restart();
-		}
-	}
+	virtual void handleRetry();
 
 	/** Method, which is used to restart the Request. */
 	virtual void restart() = 0;
 
 	/** Method, which is called to pause the Request. */
-	virtual void pause() { _state = PAUSED; }
+	virtual void pause();
 
 	/**
 	 * Method, which is called to *interrupt* the Request.
 	 * When it's called, Request must stop its work and
-	 * call the callback to notify user of failure.
+	 * call the failure callback to notify user.
 	 */
-	virtual void finish() { _state = FINISHED; }
+	virtual void finish();
 
 	/** Method, which is called to retry the Request. */
-	virtual void retry(uint32 seconds) {		
-		_state = RETRY;
-		_retryInSeconds = seconds;
-	}
+	virtual void retry(uint32 seconds);
 
 	/** Returns Request's current state. */
-	RequestState state() const { return _state; }
+	RequestState state() const;
 };
 
-} // End of namespace Cloud
+} // End of namespace Networking
 
 #endif
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 350901e..51c98e7 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -64,7 +64,7 @@ public:
 	/**
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
-	virtual void syncSaves(Cloud::Storage::BoolCallback callback = 0) = 0;
+	virtual void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr) = 0;
 };
 
 } // End of namespace Common


Commit: b39f46788a70a6c72d5ca678c79c0b53ebde9b68
    https://github.com/scummvm/scummvm/commit/b39f46788a70a6c72d5ca678c79c0b53ebde9b68
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveUploadRequest

Doesn't support server's requested ranges yet.

Commit also adds some PUT-related code in NetworkReadStream and
CurlRequest.

Changed paths:
  A backends/cloud/onedrive/onedriveuploadrequest.cpp
  A backends/cloud/onedrive/onedriveuploadrequest.h
  A backends/cloud/storage.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/storage.h
    backends/module.mk
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d22e0ab..7ba072d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -131,19 +131,6 @@ Networking::Request *DropboxStorage::upload(Common::String path, Common::Seekabl
 	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback));
 }
 
-Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	Common::File *f = new Common::File();
-	if (!f->open(localPath)) {
-		warning("DropboxStorage: unable to open file to upload from");
-		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
-		delete errorCallback;
-		delete callback;
-		delete f;
-		return nullptr;
-	}
-	return upload(remotePath, f, callback, errorCallback);
-}
-
 Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 0082e5c..a5ef76a 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -74,8 +74,7 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 	
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);	
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 8746b7a..fe10580 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -24,8 +24,10 @@
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
+#include "backends/cloud/onedrive/onedriveuploadrequest.h"
 #include "backends/cloud/downloadrequest.h"
 #include "backends/cloud/folderdownloadrequest.h"
+#include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/cloudmanager.h"
@@ -35,7 +37,6 @@
 #include "common/json.h"
 #include "common/system.h"
 #include <curl/curl.h>
-#include "../savessyncrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
@@ -168,6 +169,9 @@ Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDir
 	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
+Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	return ConnMan.addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
+}
 
 Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
@@ -210,6 +214,13 @@ void OneDriveStorage::printBool(BoolResponse response) {
 	debug("bool: %s", response.value ? "true" : "false");
 }
 
+void OneDriveStorage::printFile(UploadResponse response) {
+	debug("\nuploaded file info:");
+	debug("\tpath: %s", response.value.path().c_str());
+	debug("\tsize: %u", response.value.size());
+	debug("\ttimestamp: %u", response.value.timestamp());
+}
+
 void OneDriveStorage::printErrorResponse(Networking::ErrorResponse error) {
 	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
 	debug("%s", error.response.c_str());
@@ -228,7 +239,11 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networkin
 	return ConnMan.addRequest(request);
 	*/
 	//return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
+	return Storage::upload(
+		"uploads/test.jpg", "test.jpg",
+		new Common::Callback<OneDriveStorage, UploadResponse>(this, &OneDriveStorage::printFile), getErrorPrintingCallback()
+	);
+	//return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
 }
 
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index f95ea89..0ced98c 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -53,6 +53,7 @@ class OneDriveStorage: public Cloud::Storage {
 	void fileDownloaded(BoolResponse response);
 	void printFiles(FileArrayResponse response);
 	void printBool(BoolResponse response);
+	void printFile(UploadResponse response);
 	void printErrorResponse(Networking::ErrorResponse error);
 
 	Networking::ErrorCallback getErrorPrintingCallback();
@@ -80,8 +81,7 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index bc7bd74..bf849f7 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -90,6 +90,8 @@ void OneDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
 				irrecoverable = false;
 		}
 
+		if (code == "unauthenticated") irrecoverable = false;
+
 		if (irrecoverable) {			
 			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
 			delete json;
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
new file mode 100644
index 0000000..752907f
--- /dev/null
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -0,0 +1,171 @@
+/* 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 "backends/cloud/onedrive/onedriveuploadrequest.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+#include "common/debug.h"
+#include "onedrivetokenrefresher.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+OneDriveUploadRequest::~OneDriveUploadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _contentsStream;
+	delete _uploadCallback;
+}
+
+void OneDriveUploadRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	if (!_contentsStream->seek(0)) {
+		warning("OneDriveUploadRequest: cannot restart because stream couldn't seek(0)");
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+	}
+	_ignoreCallback = false;
+
+	uploadNextPart();
+}
+
+void OneDriveUploadRequest::uploadNextPart() {	
+	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
+
+	if (_uploadUrl == "" && _contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
+		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+_savePath+":/upload.createSession"; //folder must exist
+		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
+		Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+		request->addHeader("Authorization: Bearer " + _storage->accessToken());
+		request->setBuffer(new byte[1], 0); //use POST
+		_workingRequest = ConnMan.addRequest(request);
+		return;
+	}
+
+	Common::String url;
+	if (_uploadUrl == "") {		
+		url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+_savePath+":/content";
+	} else {		
+		url = _uploadUrl;
+	}
+	
+	Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());	
+	request->usePut();
+
+	uint32 oldPos = _contentsStream->pos();
+
+	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
+	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
+	request->setBuffer(buffer, size);
+
+	//request->addHeader(Common::String::format("Content-Length: %u", size));
+	if (_uploadUrl != "")
+		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));	;
+	
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	Networking::ErrorResponse error(this, false, true, "", -1);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();		
+
+	Common::JSONValue *json = response.value;
+	if (json) {
+		if (json->isObject()) {
+			Common::JSONObject object = json->asObject();
+
+			if (object.contains("error")) {
+				warning("OneDrive returned error: %s", json->stringify(true).c_str());
+				delete json;
+				error.response = json->stringify(true);
+				finishError(error);
+				return;
+			}
+
+			if (object.contains("id") && object.contains("name")) {
+				//finished				
+				Common::String path = _savePath; //object.getVal("name")->asString();; //object.getVal("id")->asString();
+				uint32 size = object.getVal("size")->asIntegerNumber();
+				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
+				finishSuccess(StorageFile(path, size, timestamp, false));
+				return;
+			}
+
+			if (_uploadUrl == "") {
+				if (object.contains("uploadUrl"))
+					_uploadUrl = object.getVal("uploadUrl")->asString();
+				else
+					warning("no uploadUrl found in OneDrive's response");				
+			}
+		}
+
+		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
+			warning("no file info to return");
+			finishSuccess(StorageFile(_savePath, 0, 0, false));
+		} else {
+			uploadNextPart();
+		}
+	} else {
+		warning("null, not json");		
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void OneDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void OneDriveUploadRequest::handle() {}
+
+void OneDriveUploadRequest::restart() { start(); }
+
+void OneDriveUploadRequest::finishSuccess(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.h b/backends/cloud/onedrive/onedriveuploadrequest.h
new file mode 100644
index 0000000..09419d8
--- /dev/null
+++ b/backends/cloud/onedrive/onedriveuploadrequest.h
@@ -0,0 +1,61 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace OneDrive {
+class OneDriveStorage;
+
+class OneDriveUploadRequest: public Networking::Request {
+	OneDriveStorage *_storage;
+	Common::String _savePath;
+	Common::SeekableReadStream *_contentsStream;
+	Storage::UploadCallback _uploadCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _uploadUrl;
+	
+	void start();
+	void uploadNextPart();
+	void partUploadedCallback(Networking::JsonResponse response);
+	void partUploadedErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(StorageFile status);
+
+public:
+	OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
+	virtual ~OneDriveUploadRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
new file mode 100644
index 0000000..6588b05
--- /dev/null
+++ b/backends/cloud/storage.cpp
@@ -0,0 +1,42 @@
+/* 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 "backends/cloud/storage.h"
+#include "common/file.h"
+
+namespace Cloud {
+
+Networking::Request *Storage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	Common::File *f = new Common::File();
+	if (!f->open(localPath)) {
+		warning("Storage: unable to open file to upload from");
+		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
+		delete errorCallback;
+		delete callback;
+		delete f;
+		return nullptr;
+	}
+	return upload(remotePath, f, callback, errorCallback);
+}
+
+} // End of namespace Cloud
+
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 32c4378..1d92189 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -77,7 +77,7 @@ public:
 	
 	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
-	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
diff --git a/backends/module.mk b/backends/module.mk
index c40781d..fd06e52 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -23,6 +23,7 @@ ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloud/iso8601.o \
 	cloud/manager.o \
+	cloud/storage.o \
 	cloud/storagefile.o \
 	cloud/downloadrequest.o \
 	cloud/folderdownloadrequest.o \
@@ -32,7 +33,8 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
-	cloud/onedrive/onedrivelistdirectoryrequest.o
+	cloud/onedrive/onedrivelistdirectoryrequest.o \
+	cloud/onedrive/onedriveuploadrequest.o
 endif
 
 ifdef USE_LIBCURL
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 861546b..a3f997a 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -31,7 +31,7 @@
 namespace Networking {
 
 CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url):
-	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {}
+	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0), _uploading(false) {}
 
 CurlRequest::~CurlRequest() {
 	delete _stream;
@@ -40,8 +40,8 @@ CurlRequest::~CurlRequest() {
 
 NetworkReadStream *CurlRequest::makeStream() {
 	if (_bytesBuffer)
-		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, true);
-	return new NetworkReadStream(_url.c_str(), _headersList, _postFields);
+		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, true);
+	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading);
 }
 
 
@@ -97,6 +97,8 @@ void CurlRequest::setBuffer(byte *buffer, uint32 size) {
 	_bytesBufferSize = size;
 }
 
+void CurlRequest::usePut() { _uploading = true; }
+
 NetworkReadStreamResponse CurlRequest::execute() {
 	if (!_stream) {
 		_stream = makeStream();
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 660479e..5737078 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -44,6 +44,7 @@ protected:
 	Common::String _postFields;
 	byte *_bytesBuffer;
 	uint32 _bytesBufferSize;
+	bool _uploading; //using PUT method
 
 	virtual NetworkReadStream *makeStream();
 
@@ -66,6 +67,9 @@ public:
 	/** Sets bytes buffer. */
 	virtual void setBuffer(byte *buffer, uint32 size);
 
+	/** Remembers to use PUT method when it would create NetworkReadStream. */
+	virtual void usePut();
+
 	/**
 	 * Starts this Request with ConnMan.
 	 * @return its NetworkReadStream in NetworkReadStreamResponse.
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index f96a069..283e5e6 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -35,11 +35,17 @@ static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields):
-	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), false) {}
+static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {	
+	NetworkReadStream *stream = (NetworkReadStream *)p;
+	if (stream) return stream->fillWithSendingContents(d, n*l);
+	return 0;
+}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading):
+	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, false) {}
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool post) :
-	_easy(0), _eos(false), _requestComplete(false) {
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool post):
+	_easy(0), _eos(false), _requestComplete(false), _sendingContentsBuffer(nullptr), _sendingContentsSize(0), _sendingContentsPos(0) {
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
@@ -49,9 +55,17 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, b
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);	
-	if (post || bufferSize != 0) {
-		curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
-		curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
+	if (uploading) {
+		curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
+		curl_easy_setopt(_easy, CURLOPT_READDATA, this);
+		curl_easy_setopt(_easy, CURLOPT_READFUNCTION, curlReadDataCallback);
+		_sendingContentsBuffer = buffer;
+		_sendingContentsSize = bufferSize;
+	} else {
+		if (post || bufferSize != 0) {
+			curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
+			curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
+		}
 	}
 	ConnMan.registerEasyHandle(_easy);
 }
@@ -87,4 +101,14 @@ long NetworkReadStream::httpResponseCode() const {
 	return responseCode;
 }
 
+uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
+	uint32 size = _sendingContentsSize - _sendingContentsPos;
+	if (size > maxSize) size = maxSize;
+	for (uint32 i = 0; i < size; ++i) {
+		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
+	}
+	_sendingContentsPos += size;
+	return size;
+}
+
 } // End of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index f1f4126..d48d01b 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -35,10 +35,13 @@ namespace Networking {
 class NetworkReadStream: public Common::MemoryReadWriteStream {	
 	CURL *_easy;
 	bool _eos, _requestComplete;
+	byte *_sendingContentsBuffer;
+	uint32 _sendingContentsSize;
+	uint32 _sendingContentsPos;
 
 public:	
-	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields);
-	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool post = true);
+	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false);
+	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading = false, bool post = true);
 	virtual ~NetworkReadStream();
 
 	/**
@@ -82,6 +85,15 @@ public:
 	 * @note This method should be called when eos() == true.
 	 */
 	long httpResponseCode() const;
+
+	/**
+	 * Fills the passed buffer with _sendingContentsBuffer contents.
+	 * It works similarly to read(), expect it's not for reading
+	 * Stream's contents, but for sending our own data to the server.
+	 *
+	 * @returns how many bytes were actually read (filled in)
+	 */
+	uint32 fillWithSendingContents(char *bufferToFill, uint32 maxSize);
 };
 
 } // End of namespace Networking


Commit: 3638c8348d98273da5c4e2c5bd1afa8a985a2d0c
    https://github.com/scummvm/scummvm/commit/3638c8348d98273da5c4e2c5bd1afa8a985a2d0c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make SavesSyncRequest work with OneDrive

It actually works fine, but small Storage::savesDirectoryPath() was
added, because Dropbox's directories must start with a slash, and
OneDrive's directories must not.

Saves sync tested and it works fine with OneDrive.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 7ba072d..beb5108 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -178,6 +178,8 @@ Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Net
 	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
 }
 
+Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; }
+
 void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index a5ef76a..90a1d27 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -103,6 +103,9 @@ public:
 	/** This method is passed into info(). (Temporary) */
 	void infoMethodCallback(StorageInfoResponse response);
 
+	/** Returns storage's saves directory path with the trailing slash. */
+	virtual Common::String savesDirectoryPath();
+
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index fe10580..1e9a641 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -238,14 +238,11 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networkin
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
 	*/
-	//return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false);
-	return Storage::upload(
-		"uploads/test.jpg", "test.jpg",
-		new Common::Callback<OneDriveStorage, UploadResponse>(this, &OneDriveStorage::printFile), getErrorPrintingCallback()
-	);
-	//return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
 }
 
+Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
+
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 0ced98c..45a8dca 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -107,6 +107,9 @@ public:
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
+	/** Returns storage's saves directory path with the trailing slash. */
+	virtual Common::String savesDirectoryPath();
+
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() { return false; } //TODO
 
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index b48a99a..9727738 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -60,7 +60,7 @@ void SavesSyncRequest::start() {
 
 	//list saves directory
 	_workingRequest = _storage->listDirectory(
-		"/saves",
+		_storage->savesDirectoryPath(),
 		new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
 	);
@@ -165,6 +165,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 	//we're lucky - user just lacks his "/cloud/" folder
 	Common::Array<StorageFile> files;
 	directoryListedCallback(Storage::ListDirectoryResponse(error.request, files));
+	//TODO: create it before uploading stuff
 }
 
 void SavesSyncRequest::downloadNextFile() {
@@ -222,7 +223,7 @@ void SavesSyncRequest::uploadNextFile() {
 	///////
 	debug("uploading %s", _currentUploadingFile.c_str());
 	///////
-	_workingRequest = _storage->upload("/saves/" + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
+	_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
 	);
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 1d92189..4bd2aa3 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -103,6 +103,9 @@ public:
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
+	/** Returns storage's saves directory path with the trailing slash. */
+	virtual Common::String savesDirectoryPath() = 0;
+
 	/** Returns whether saves sync process is running. */
 	virtual bool isSyncing() = 0;
 


Commit: 13351a730d79cc2f0d5b964226c69bb04e2c93c1
    https://github.com/scummvm/scummvm/commit/13351a730d79cc2f0d5b964226c69bb04e2c93c1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDrive::info()

Unfortunately, OneDrive doesn't share quota information anymore because
of some reason. I had to get as much information as I could.

Changed paths:
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h



diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 1e9a641..a16a351 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -131,6 +131,40 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
+void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (outerCallback) {
+		Common::JSONObject info = json->asObject();
+
+		Common::String uid, name, email;
+		uint32 quotaUsed = 0, quotaAllocated = 25 * 1024 * 1024 * 1024; // 25 GB, because I actually don't know any way to find out the real one
+
+		if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
+			Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
+			if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
+				Common::JSONObject user = createdBy.getVal("user")->asObject();
+				uid = user.getVal("id")->asString();
+				name = user.getVal("displayName")->asString();
+			}
+		}
+
+		if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
+			quotaUsed = info.getVal("size")->asIntegerNumber();
+		}
+
+		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+		delete outerCallback;
+	}
+
+	delete json;
+}
+
 void OneDriveStorage::printJson(Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
@@ -232,13 +266,14 @@ Networking::ErrorCallback OneDriveStorage::getErrorPrintingCallback() {
 
 Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	//this is not the real syncSaves() implementation
-	/*
-	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
+	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO	
+}
+
+Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
 	request->addHeader("Authorization: bearer " + _token);
 	return ConnMan.addRequest(request);
-	*/
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO
 }
 
 Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 45a8dca..5741f8e 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -49,6 +49,9 @@ class OneDriveStorage: public Cloud::Storage {
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
 
+	/** Constructs StorageInfo based on JSON response from cloud. */
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
+
 	void printJson(Networking::JsonResponse response);
 	void fileDownloaded(BoolResponse response);
 	void printFiles(FileArrayResponse response);
@@ -105,7 +108,7 @@ public:
 	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Returns the StorageInfo struct. */
-	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();


Commit: 0d0033fb6ad00e3081bc2854ce5972746b603105
    https://github.com/scummvm/scummvm/commit/0d0033fb6ad00e3081bc2854ce5972746b603105
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make syncSaves() common for all Storages

As it uses SavesSyncRequest and this request is using Storage's
upload(), download() and listDirectory(), there is no need to make
storage-dependent version of that request and so method could be
implemented in base Storage.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index beb5108..1aae73e 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -114,15 +114,6 @@ void DropboxStorage::printStorageFile(UploadResponse response) {
 	debug("\ttimestamp: %u", response.value.timestamp());
 }
 
-void DropboxStorage::printErrorResponse(Networking::ErrorResponse error) {
-	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
-}
-
-Networking::ErrorCallback DropboxStorage::getErrorPrintingCallback() {
-	return new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::printErrorResponse);
-}
-
 Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) {
 	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive));
 }
@@ -162,11 +153,6 @@ Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, C
 	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
-Networking::Request *DropboxStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	//this might be the real syncSaves() implementation
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool), getErrorPrintingCallback())); //TODO	
-}
-
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 90a1d27..7752ddd 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -48,9 +48,6 @@ class DropboxStorage: public Cloud::Storage {
 	void printFiles(FileArrayResponse response);
 	void printBool(BoolResponse response);
 	void printStorageFile(UploadResponse response);
-	void printErrorResponse(Networking::ErrorResponse error);
-
-	Networking::ErrorCallback getErrorPrintingCallback();
 
 public:	
 	virtual ~DropboxStorage();
@@ -89,9 +86,6 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index a16a351..fb2ead3 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -255,20 +255,6 @@ void OneDriveStorage::printFile(UploadResponse response) {
 	debug("\ttimestamp: %u", response.value.timestamp());
 }
 
-void OneDriveStorage::printErrorResponse(Networking::ErrorResponse error) {
-	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
-}
-
-Networking::ErrorCallback OneDriveStorage::getErrorPrintingCallback() {
-	return new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::printErrorResponse);
-}
-
-Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	//this is not the real syncSaves() implementation
-	return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO	
-}
-
 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 5741f8e..241b6a8 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -57,9 +57,6 @@ class OneDriveStorage: public Cloud::Storage {
 	void printFiles(FileArrayResponse response);
 	void printBool(BoolResponse response);
 	void printFile(UploadResponse response);
-	void printErrorResponse(Networking::ErrorResponse error);
-
-	Networking::ErrorCallback getErrorPrintingCallback();
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 public:	
@@ -99,9 +96,6 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 6588b05..2e13376 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -21,11 +21,25 @@
 */
 
 #include "backends/cloud/storage.h"
+#include "backends/cloud/savessyncrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "common/debug.h"
 #include "common/file.h"
 
 namespace Cloud {
 
+Networking::ErrorCallback Storage::getErrorPrintingCallback() {
+	return new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::printErrorResponse);
+}
+
+void Storage::printErrorResponse(Networking::ErrorResponse error) {
+	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
+}
+
 Networking::Request *Storage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+
 	Common::File *f = new Common::File();
 	if (!f->open(localPath)) {
 		warning("Storage: unable to open file to upload from");
@@ -35,8 +49,15 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 		delete f;
 		return nullptr;
 	}
+
 	return upload(remotePath, f, callback, errorCallback);
 }
 
+Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return ConnMan.addRequest(new SavesSyncRequest(this, callback, errorCallback));
+}
+
+
 } // End of namespace Cloud
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 4bd2aa3..b4d2680 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -48,6 +48,15 @@ public:
 	typedef Common::BaseCallback<UploadResponse> *UploadCallback;
 	typedef Common::BaseCallback<ListDirectoryResponse> *ListDirectoryCallback;
 
+protected:	
+
+	/** Returns default error callback (printErrorResponse). */
+	virtual Networking::ErrorCallback getErrorPrintingCallback();
+
+	/** Prints ErrorResponse contents with debug(). */
+	virtual void printErrorResponse(Networking::ErrorResponse error);
+
+public:
 	Storage() {}
 	virtual ~Storage() {}
 
@@ -92,7 +101,7 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
+	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;


Commit: ca85d4482af0b2e570565d5aa6d562ec86b10100
    https://github.com/scummvm/scummvm/commit/ca85d4482af0b2e570565d5aa6d562ec86b10100
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use uint64 in StorageInfo

There was a warning regarding 25 GB constant.

By the way, I'm not sure how to print uint64 (%llu is available in C99
only, and gcc produces a warning about that).

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/storageinfo.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 1aae73e..263c2c8 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -181,9 +181,9 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 		Common::String name = info.getVal("display_name")->asString();
 		Common::String email = info.getVal("email")->asString();
 		Common::JSONObject quota = info.getVal("quota_info")->asObject();
-		uint32 quotaNormal = quota.getVal("normal")->asIntegerNumber();
-		uint32 quotaShared = quota.getVal("shared")->asIntegerNumber();
-		uint32 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
+		uint64 quotaNormal = quota.getVal("normal")->asIntegerNumber();
+		uint64 quotaShared = quota.getVal("shared")->asIntegerNumber();
+		uint64 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
@@ -195,7 +195,7 @@ void DropboxStorage::infoMethodCallback(StorageInfoResponse response) {
 	debug("\nStorage info:");
 	debug("User name: %s", response.value.name().c_str());
 	debug("Email: %s", response.value.email().c_str());
-	debug("Disk usage: %u/%u", response.value.used(), response.value.available());
+	debug("Disk usage: %u/%u", (uint32)response.value.used(), (uint32)response.value.available());
 }
 
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index fb2ead3..c64ed6b 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -143,7 +143,7 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 		Common::JSONObject info = json->asObject();
 
 		Common::String uid, name, email;
-		uint32 quotaUsed = 0, quotaAllocated = 25 * 1024 * 1024 * 1024; // 25 GB, because I actually don't know any way to find out the real one
+		uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
 
 		if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
 			Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
index 1492898..e0666bc 100644
--- a/backends/cloud/storageinfo.h
+++ b/backends/cloud/storageinfo.h
@@ -34,17 +34,17 @@ namespace Cloud {
 
 class StorageInfo {	
 	Common::String _uid, _name, _email;
-	uint32 _usedBytes, _allocatedBytes;
+	uint64 _usedBytes, _allocatedBytes;
 
 public:
-	StorageInfo(Common::String uid, Common::String name, Common::String email, uint32 used, uint32 allocated):
+	StorageInfo(Common::String uid, Common::String name, Common::String email, uint64 used, uint64 allocated):
 		_uid(uid), _name(name), _email(email), _usedBytes(used), _allocatedBytes(allocated) {}
 
 	Common::String uid() const { return _uid; }
 	Common::String name() const { return _name; }
 	Common::String email() const { return _email; }
-	uint32 used() const { return _usedBytes; }
-	uint32 available() const { return _allocatedBytes; }
+	uint64 used() const { return _usedBytes; }
+	uint64 available() const { return _allocatedBytes; }
 
 };
 


Commit: 675e7a6ed1d19e64e2f80a4dd1f454e646cb52f5
    https://github.com/scummvm/scummvm/commit/675e7a6ed1d19e64e2f80a4dd1f454e646cb52f5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Move download methods into Storage

DownloadRequest and FolderDownloadRequest are using other Storage's
methods. Thus, download() and downloadFolder() could be implemented in
base Storage class.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 263c2c8..acc96f9 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -137,22 +137,6 @@ Networking::Request *DropboxStorage::streamFile(Common::String path, Networking:
 	return response.request;
 }
 
-Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	Common::DumpFile *f = new Common::DumpFile();
-	if (!f->open(localPath, true)) {
-		warning("DropboxStorage: unable to open file to download into");
-		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
-		delete f;
-		return nullptr;
-	}
-
-	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
-}
-
-Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
-}
-
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 7752ddd..5db2bb7 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -77,12 +77,6 @@ public:
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index c64ed6b..e1c6861 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -215,23 +215,6 @@ Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking
 	return ConnMan.addRequest(request);
 }
 
-Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	Common::DumpFile *f = new Common::DumpFile();
-	if (!f->open(localPath, true)) {
-		warning("OneDriveStorage: unable to open file to download into");
-		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
-		delete f;
-		return nullptr;
-	}
-
-	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
-}
-
-/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
-}
-
 void OneDriveStorage::fileDownloaded(BoolResponse response) {
 	if (response.value) debug("file downloaded!");
 	else debug("download failed!");
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 241b6a8..49d63b9 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -87,12 +87,6 @@ public:
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 2e13376..90e095a 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -21,6 +21,8 @@
 */
 
 #include "backends/cloud/storage.h"
+#include "backends/cloud/downloadrequest.h"
+#include "backends/cloud/folderdownloadrequest.h"
 #include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "common/debug.h"
@@ -53,6 +55,27 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 	return upload(remotePath, f, callback, errorCallback);
 }
 
+Networking::Request *Storage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+
+	Common::DumpFile *f = new Common::DumpFile();
+	if (!f->open(localPath, true)) {
+		warning("Storage: unable to open file to download into");
+		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
+		delete errorCallback;
+		delete callback;
+		delete f;
+		return nullptr;
+	}
+
+	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
+}
+
+Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
+}
+
 Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	return ConnMan.addRequest(new SavesSyncRequest(this, callback, errorCallback));
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index b4d2680..4956d5f 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -92,10 +92,10 @@ public:
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
-	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
+	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;


Commit: 4e7dec550077bc37cf254311aefb621cdeebbdfe
    https://github.com/scummvm/scummvm/commit/4e7dec550077bc37cf254311aefb621cdeebbdfe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add DropboxCreateDirectoryRequest

Also add CloudManager::testFeature(), because syncSaves() now works fine
and I don't want to break it again and again with my testing requests.

Changed paths:
  A backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
  A backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/manager.cpp
    backends/cloud/manager.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/savessyncrequest.cpp
    backends/module.mk
    base/main.cpp
    common/cloudmanager.h



diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
new file mode 100644
index 0000000..c077d8d
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -0,0 +1,109 @@
+/* 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 "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+DropboxCreateDirectoryRequest::DropboxCreateDirectoryRequest(Common::String token, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _token(token), _path(path), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+DropboxCreateDirectoryRequest::~DropboxCreateDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void DropboxCreateDirectoryRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();	
+	_ignoreCallback = false;
+
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback);
+	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::ErrorResponse>(this, &DropboxCreateDirectoryRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/files/create_folder");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/json");
+
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("path", new Common::JSONValue(_path));
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	_workingRequest = nullptr;
+	if (_ignoreCallback) {
+		delete json;
+		return;
+	}
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+	
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		finishError(error);
+		return;
+	}
+	
+	Common::JSONObject info = json->asObject();
+	if (info.contains("id")) finishSuccess(true);
+	else {
+		error.response = json->stringify(true);
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void DropboxCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void DropboxCreateDirectoryRequest::handle() {}
+
+void DropboxCreateDirectoryRequest::restart() { start(); }
+
+void DropboxCreateDirectoryRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
new file mode 100644
index 0000000..ea3175b
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
@@ -0,0 +1,55 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXCREATEDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_DROPBOXCREATEDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+class DropboxCreateDirectoryRequest: public Networking::Request {
+	Common::String _token;
+	Common::String _path;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	
+	void start();
+	void responseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
+public:
+	DropboxCreateDirectoryRequest(Common::String token, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~DropboxCreateDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index acc96f9..f51819c 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -22,20 +22,15 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxuploadrequest.h"
-#include "backends/cloud/downloadrequest.h"
-#include "backends/cloud/folderdownloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
-#include "common/file.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "common/system.h"
-#include "common/savefile.h"
-#include "../savessyncrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -137,6 +132,11 @@ Networking::Request *DropboxStorage::streamFile(Common::String path, Networking:
 	return response.request;
 }
 
+Networking::Request *DropboxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return ConnMan.addRequest(new DropboxCreateDirectoryRequest(_token, path, callback, errorCallback));
+}
+
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 5db2bb7..2f57b05 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -80,7 +80,7 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 2f1533c..70f2c3a 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -25,6 +25,7 @@
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "common/config-manager.h"
 #include "common/random.h"
+#include "common/debug.h"
 
 namespace Cloud {
 
@@ -110,9 +111,19 @@ Storage *Manager::getCurrentStorage() {
 	return nullptr;
 }
 
+void Manager::printBool(Storage::BoolResponse response) {
+	debug("bool = %s", (response.value ? "true" : "false"));
+}
+
 void Manager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->syncSaves(callback, errorCallback);
 }
 
+void Manager::testFeature() {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->createDirectory("/remote/sub2/dir",
+		new Common::Callback<Manager, Storage::BoolResponse>(this, &Manager::printBool), nullptr);
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
index 013e117..f68b335 100644
--- a/backends/cloud/manager.h
+++ b/backends/cloud/manager.h
@@ -33,6 +33,8 @@ class Manager: public Common::CloudManager {
 	uint _currentStorageIndex;
 	uint _deviceId;
 
+	void printBool(Storage::BoolResponse response);
+
 public:
 	Manager();
 	virtual ~Manager();
@@ -43,6 +45,7 @@ public:
 
 	virtual Storage *getCurrentStorage();
 	virtual void syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback);
+	virtual void testFeature();
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index e1c6861..2adf5ff 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -25,15 +25,12 @@
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
 #include "backends/cloud/onedrive/onedriveuploadrequest.h"
-#include "backends/cloud/downloadrequest.h"
-#include "backends/cloud/folderdownloadrequest.h"
-#include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
 #include "common/cloudmanager.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
-#include "common/file.h"
 #include "common/json.h"
 #include "common/system.h"
 #include <curl/curl.h>
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 9727738..cf0c427 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -59,8 +59,10 @@ void SavesSyncRequest::start() {
 	loadTimestamps();
 
 	//list saves directory
+	Common::String dir = _storage->savesDirectoryPath();
+	if (dir.lastChar() == '/') dir.deleteLastChar();
 	_workingRequest = _storage->listDirectory(
-		_storage->savesDirectoryPath(),
+		dir,
 		new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
 	);
diff --git a/backends/module.mk b/backends/module.mk
index fd06e52..96dfa22 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS += \
 	cloud/folderdownloadrequest.o \
 	cloud/savessyncrequest.o \
 	cloud/dropbox/dropboxstorage.o \
+	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/onedrive/onedrivestorage.o \
diff --git a/base/main.cpp b/base/main.cpp
index f629eb9..36dd8c6 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -480,6 +480,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #ifdef USE_CLOUD
 	system.getCloudManager()->init();
 	system.getCloudManager()->syncSaves();
+	system.getCloudManager()->testFeature(); //TODO: remove later
 #endif
 
 	// Unless a game was specified, show the launcher dialog
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
index 51c98e7..936f0e0 100644
--- a/common/cloudmanager.h
+++ b/common/cloudmanager.h
@@ -65,6 +65,11 @@ public:
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
 	virtual void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr) = 0;
+
+	/**
+	 * Starts feature testing (the one I'm working on currently). (Temporary)
+	 */
+	virtual void testFeature() = 0;
 };
 
 } // End of namespace Common


Commit: 8cdde307f7b1d3eff71050817921ea0aa8c318fe
    https://github.com/scummvm/scummvm/commit/8cdde307f7b1d3eff71050817921ea0aa8c318fe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OneDriveCreateDirectoryRequest

Changed paths:
  A backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
  A backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
    backends/cloud/manager.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/module.mk



diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
index 70f2c3a..13424ba 100644
--- a/backends/cloud/manager.cpp
+++ b/backends/cloud/manager.cpp
@@ -122,7 +122,7 @@ void Manager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallbac
 
 void Manager::testFeature() {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->createDirectory("/remote/sub2/dir",
+	if (storage) storage->createDirectory("base/belong_to_us",
 		new Common::Callback<Manager, Storage::BoolResponse>(this, &Manager::printBool), nullptr);
 }
 
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
new file mode 100644
index 0000000..c48ae1d
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -0,0 +1,128 @@
+/* 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 "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+OneDriveCreateDirectoryRequest::~OneDriveCreateDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void OneDriveCreateDirectoryRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();	
+	_ignoreCallback = false;
+
+	Common::String name = _path, parent = _path;
+	if (name.size() != 0) {
+		uint32 i = name.size() - 1;
+		while (true) {
+			parent.deleteLastChar();
+			if (name[i] == '/' || name[i] == '\\') {
+				name.erase(0, i + 1);				
+				break;
+			}
+			if (i == 0) break;
+			--i;
+		}
+	}
+
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";	
+	if (parent != "") url += ":/" + parent + ":";
+	url += "/children";	
+	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
+	Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+	request->addHeader("Content-Type: application/json");
+
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+	jsonRequestParameters.setVal("folder", new Common::JSONValue(Common::JSONObject()));
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	_workingRequest = nullptr;
+	if (_ignoreCallback) {
+		delete json;
+		return;
+	}
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+	
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		finishError(error);
+		return;
+	}
+	
+	Common::JSONObject info = json->asObject();
+	if (info.contains("id")) finishSuccess(true);
+	else {
+		error.response = json->stringify(true);
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void OneDriveCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void OneDriveCreateDirectoryRequest::handle() {}
+
+void OneDriveCreateDirectoryRequest::restart() { start(); }
+
+void OneDriveCreateDirectoryRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
new file mode 100644
index 0000000..4bf0d9e
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -0,0 +1,57 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage;
+
+class OneDriveCreateDirectoryRequest: public Networking::Request {
+	OneDriveStorage *_storage;
+	Common::String _path;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	
+	void start();
+	void responseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
+public:
+	OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~OneDriveCreateDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 2adf5ff..190a7ab 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
 #include "backends/cloud/onedrive/onedriveuploadrequest.h"
@@ -235,6 +236,11 @@ void OneDriveStorage::printFile(UploadResponse response) {
 	debug("\ttimestamp: %u", response.value.timestamp());
 }
 
+Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return ConnMan.addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
+}
+
 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 49d63b9..8fc7b2a 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -90,7 +90,7 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
diff --git a/backends/module.mk b/backends/module.mk
index 96dfa22..4733509 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -33,6 +33,7 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/onedrive/onedrivestorage.o \
+	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
 	cloud/onedrive/onedrivelistdirectoryrequest.o \
 	cloud/onedrive/onedriveuploadrequest.o


Commit: 06163cb8b907e30f8463b2b9700d136c73b19a33
    https://github.com/scummvm/scummvm/commit/06163cb8b907e30f8463b2b9700d136c73b19a33
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix SavesSyncRequest to create saves folder

Changed paths:
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index cf0c427..d5cb6f6 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -164,10 +164,37 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 		return;
 	}
 
-	//we're lucky - user just lacks his "/cloud/" folder
+	//we're lucky - user just lacks his "/cloud/" folder - let's create one
+	Common::String dir = _storage->savesDirectoryPath();
+	if (dir.lastChar() == '/') dir.deleteLastChar();
+	debug("creating %s", dir.c_str());
+	_workingRequest = _storage->createDirectory(dir,
+		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
+		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
+	);
+}
+
+void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//stop syncing if failed to create saves directory
+	if (!response.value) {
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
+	}
+
+	//continue with empty files list
 	Common::Array<StorageFile> files;
-	directoryListedCallback(Storage::ListDirectoryResponse(error.request, files));
-	//TODO: create it before uploading stuff
+	directoryListedCallback(Storage::ListDirectoryResponse(response.request, files));
+}
+
+void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//stop syncing if failed to create saves directory
+	finishError(error);
 }
 
 void SavesSyncRequest::downloadNextFile() {
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index bf44b70..0e20159 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -47,6 +47,8 @@ class SavesSyncRequest: public Networking::Request {
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
 	void directoryListedErrorCallback(Networking::ErrorResponse error);
+	void directoryCreatedCallback(Storage::BoolResponse response);
+	void directoryCreatedErrorCallback(Networking::ErrorResponse error);
 	void fileDownloadedCallback(Storage::BoolResponse response);
 	void fileDownloadedErrorCallback(Networking::ErrorResponse error);
 	void fileUploadedCallback(Storage::UploadResponse response);


Commit: 4b3a8be0b9db448971e6095a24501c66714c484f
    https://github.com/scummvm/scummvm/commit/4b3a8be0b9db448971e6095a24501c66714c484f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Shorten Cloud API

touch() and isSyncing() are not needed.

remove() is not needed too, but it could be used in the future.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 2f57b05..2c60097 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -82,9 +82,6 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
-	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
@@ -94,9 +91,6 @@ public:
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();
 
-	/** Returns whether saves sync process is running. */
-	virtual bool isSyncing() { return false; } //TODO
-
 	/** Returns whether there are any requests running. */
 	virtual bool isWorking() { return false; } //TODO
 
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 8fc7b2a..5edd96e 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -92,18 +92,12 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
-	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();
 
-	/** Returns whether saves sync process is running. */
-	virtual bool isSyncing() { return false; } //TODO
-
 	/** Returns whether there are any requests running. */
 	virtual bool isWorking() { return false; } //TODO
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 4956d5f..1efee85 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -106,18 +106,12 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
-	/** Calls the callback when finished. */
-	virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
-
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath() = 0;
 
-	/** Returns whether saves sync process is running. */
-	virtual bool isSyncing() = 0;
-
 	/** Returns whether there are any requests running. */
 	virtual bool isWorking() = 0;
 };


Commit: a66322408f95ff7b29cf6967eaecaac06dfe5b31
    https://github.com/scummvm/scummvm/commit/a66322408f95ff7b29cf6967eaecaac06dfe5b31
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Implement Storage's isWorking()

It now keeps track of how many Requests are running.

To achieve that, we had to pass a callback to ConnectionManager, so each
Request has a callback paired with it. If that's one of Storage's
Requests, it has a callback, which would decrease a counter. When
Storage adds a Request, it also increases a counter and passes that
callback. Callback is called by ConnMan when Request is deleted.

isWorking() returns true if there is at least one Request running.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index f51819c..861a58d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -110,11 +110,11 @@ void DropboxStorage::printStorageFile(UploadResponse response) {
 }
 
 Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) {
-	return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive));
+	return addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive));
 }
 
 Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback));
+	return addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback));
 }
 
 Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
@@ -134,14 +134,14 @@ Networking::Request *DropboxStorage::streamFile(Common::String path, Networking:
 
 Networking::Request *DropboxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	return ConnMan.addRequest(new DropboxCreateDirectoryRequest(_token, path, callback, errorCallback));
+	return addRequest(new DropboxCreateDirectoryRequest(_token, path, callback, errorCallback));
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
 	request->addHeader("Authorization: Bearer " + _token);
-	return ConnMan.addRequest(request);
+	return addRequest(request);
 	//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
 	//so, when CurlJsonRequest is finished, it calls the innerCallback
 	//innerCallback (which is DropboxStorage::infoInnerCallback in this case) processes the void *ptr
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 2c60097..c186d1e 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -91,9 +91,6 @@ public:
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();
 
-	/** Returns whether there are any requests running. */
-	virtual bool isWorking() { return false; } //TODO
-
 	/**
 	 * Load token and user id from configs and return DropboxStorage for those.	
 	 * @return pointer to the newly created DropboxStorage or 0 if some problem occured.
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 190a7ab..98f0ac5 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -84,7 +84,7 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
 	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
-	ConnMan.addRequest(request);
+	addRequest(request);
 }
 
 void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
@@ -198,11 +198,11 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 }
 
 Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
+	return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
 Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	return ConnMan.addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
+	return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
 }
 
 Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
@@ -210,7 +210,7 @@ Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
-	return ConnMan.addRequest(request);
+	return addRequest(request);
 }
 
 void OneDriveStorage::fileDownloaded(BoolResponse response) {
@@ -238,14 +238,14 @@ void OneDriveStorage::printFile(UploadResponse response) {
 
 Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	return ConnMan.addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
+	return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
 }
 
 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
 	request->addHeader("Authorization: bearer " + _token);
-	return ConnMan.addRequest(request);
+	return addRequest(request);
 }
 
 Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 5edd96e..a09d68b 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -98,9 +98,6 @@ public:
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();
 
-	/** Returns whether there are any requests running. */
-	virtual bool isWorking() { return false; } //TODO
-
 	/**
 	 * Load token and user id from configs and return OneDriveStorage for those.	
 	 * @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 90e095a..0f9a2a1 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -30,6 +30,10 @@
 
 namespace Cloud {
 
+Storage::Storage(): _runningRequestsCount(0) {}
+
+Storage::~Storage() {}
+
 Networking::ErrorCallback Storage::getErrorPrintingCallback() {
 	return new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::printErrorResponse);
 }
@@ -39,6 +43,17 @@ void Storage::printErrorResponse(Networking::ErrorResponse error) {
 	debug("%s", error.response.c_str());
 }
 
+Networking::Request *Storage::addRequest(Networking::Request *request) {
+	++_runningRequestsCount;
+	if (_runningRequestsCount == 1) debug("Storage is working now");
+	return ConnMan.addRequest(request, new Common::Callback<Storage, Networking::Request *>(this, &Storage::requestFinishedCallback));
+}
+
+void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer) {
+	--_runningRequestsCount;
+	if (_runningRequestsCount == 0) debug("Storage is not working now");
+}
+
 Networking::Request *Storage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 
@@ -68,19 +83,22 @@ Networking::Request *Storage::download(Common::String remotePath, Common::String
 		return nullptr;
 	}
 
-	return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
+	return addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
 }
 
 Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
+	return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
 Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	return ConnMan.addRequest(new SavesSyncRequest(this, callback, errorCallback));
+	return addRequest(new SavesSyncRequest(this, callback, errorCallback));
 }
 
+bool Storage::isWorking() {
+	return _runningRequestsCount > 0;
+}
 
 } // End of namespace Cloud
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 1efee85..b1c62ba 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -48,7 +48,9 @@ public:
 	typedef Common::BaseCallback<UploadResponse> *UploadCallback;
 	typedef Common::BaseCallback<ListDirectoryResponse> *ListDirectoryCallback;
 
-protected:	
+protected:
+	/** Keeps track of running requests. */
+	uint32 _runningRequestsCount;
 
 	/** Returns default error callback (printErrorResponse). */
 	virtual Networking::ErrorCallback getErrorPrintingCallback();
@@ -56,9 +58,25 @@ protected:
 	/** Prints ErrorResponse contents with debug(). */
 	virtual void printErrorResponse(Networking::ErrorResponse error);
 
+	/**
+	 * Adds request to the ConnMan, but also increases _runningRequestsCount.
+	 * This method should be used by Storage implementations instead of
+	 * direct ConnMan.addRequest() call.
+	 *
+	 * @return the same Request pointer, just as a shortcut
+	 */
+	virtual Networking::Request *addRequest(Networking::Request *request);
+
+	/**
+	 * Decreases _runningRequestCount. It's called from ConnMan automatically.
+	 * Passed pointer is dangling, but one can use the address to determine
+	 * some special Requests (which addresses were remembered somewhere).
+	 */
+	virtual void requestFinishedCallback(Networking::Request *invalidRequestPointer);
+
 public:
-	Storage() {}
-	virtual ~Storage() {}
+	Storage();
+	virtual ~Storage();
 
 	/**
 	 * Storage methods, which are used by CloudManager to save
@@ -113,7 +131,7 @@ public:
 	virtual Common::String savesDirectoryPath() = 0;
 
 	/** Returns whether there are any requests running. */
-	virtual bool isWorking() = 0;
+	virtual bool isWorking();
 };
 
 } // End of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 949dc94..ef3c858 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -51,8 +51,8 @@ void ConnectionManager::registerEasyHandle(CURL *easy) {
 	curl_multi_add_handle(_multi, easy);
 }
 
-Request *ConnectionManager::addRequest(Request *request) {
-	_requests.push_back(request);
+Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
+	_requests.push_back(RequestWithCallback(request, callback));
 	if (!_timerStarted) startTimer();
 	return request;
 }
@@ -89,10 +89,11 @@ void ConnectionManager::handle() {
 void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
 	debug("handling %d request(s)", _requests.size());	
-	for (Common::Array<Request *>::iterator i = _requests.begin(); i != _requests.end();) {
-		Request *request = *i;
+	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
+		Request *request = i->request;
 		if (!request || request->state() == FINISHED) {
-			delete (*i);
+			delete (i->request);
+			if (i->callback) (*i->callback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
 			_requests.erase(i);
 			continue;
 		}
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index c632fa5..b39a779 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -39,9 +39,18 @@ class NetworkReadStream;
 class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	friend void connectionsThread(void *); //calls handle()
 
+	typedef Common::BaseCallback<Request *> *RequestCallback;
+
+	struct RequestWithCallback { //I'm completely out of ideas
+		Request *request;
+		RequestCallback callback;
+		
+		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), callback(cb) {}
+	};
+
 	CURLM *_multi;	
 	bool _timerStarted;
-	Common::Array<Request *> _requests;	
+	Common::Array<RequestWithCallback> _requests;
 	
 	void startTimer(int interval = 1000000); //1 second is the default interval
 	void stopTimer();
@@ -71,7 +80,7 @@ public:
 	 *
 	 * @return the same Request pointer, just as a shortcut
 	 */
-	Request *addRequest(Request *request);
+	Request *addRequest(Request *request, RequestCallback callback = nullptr);
 };
 
 /** Shortcut for accessing the connection manager. */


Commit: 1f974a7a2a2073074391fbf090d2bf909006e773
    https://github.com/scummvm/scummvm/commit/1f974a7a2a2073074391fbf090d2bf909006e773
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix ConnectionManager's destructor

It now terminates active Requests.

Changed paths:
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    base/main.cpp



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index ef3c858..a3cad0d 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -43,6 +43,19 @@ ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
 }
 
 ConnectionManager::~ConnectionManager() {
+	//terminate all requests
+	_handleMutex.lock();
+	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
+		Request *request = i->request;
+		RequestCallback callback = i->callback;
+		if (request) request->finish();
+		delete request;
+		if (callback) (*callback)(request);
+	}
+	_requests.clear();
+	_handleMutex.unlock();
+
+	//cleanup
 	curl_multi_cleanup(_multi);
 	curl_global_cleanup();
 }
@@ -80,10 +93,11 @@ void ConnectionManager::stopTimer() {
 }
 
 void ConnectionManager::handle() {
-	//TODO: lock mutex here (in case another handle() would be called before this one ends)
+	//lock mutex here (in case another handle() would be called before this one ends)
+	_handleMutex.lock();
 	interateRequests();
 	processTransfers();
-	//TODO: unlock mutex here
+	_handleMutex.unlock();
 }
 
 void ConnectionManager::interateRequests() {
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index b39a779..75cff05 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -27,6 +27,7 @@
 #include "common/str.h"
 #include "common/singleton.h"
 #include "common/hashmap.h"
+#include "common/mutex.h"
 
 typedef void CURL;
 typedef void CURLM;
@@ -51,6 +52,7 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	CURLM *_multi;	
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests;
+	Common::Mutex _handleMutex;
 	
 	void startTimer(int interval = 1000000); //1 second is the default interval
 	void stopTimer();
diff --git a/base/main.cpp b/base/main.cpp
index 36dd8c6..aede617 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -67,6 +67,9 @@
 
 #include "backends/keymapper/keymapper.h"
 #include "common/cloudmanager.h"
+#ifdef USE_LIBCURL
+#include "backends/networking/curl/connectionmanager.h"
+#endif
 
 #if defined(_WIN32_WCE)
 #include "backends/platform/wince/CELauncherDialog.h"
@@ -591,6 +594,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			launcherDialog();
 		}
 	}
+#ifdef USE_LIBCURL
+	Networking::ConnectionManager::destroy();
+#endif
 	PluginManager::instance().unloadAllPlugins();
 	PluginManager::destroy();
 	GUI::GuiManager::destroy();


Commit: b3bf5322117d03c003011839ea1e7897c48183fa
    https://github.com/scummvm/scummvm/commit/b3bf5322117d03c003011839ea1e7897c48183fa
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make CloudManager singleton

It's needed to ::destroy() it in main().

Changed paths:
  A backends/cloud/cloudmanager.cpp
  A backends/cloud/cloudmanager.h
  R backends/cloud/manager.cpp
  R backends/cloud/manager.h
  R common/cloudmanager.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/module.mk
    backends/platform/sdl/sdl.cpp
    base/main.cpp
    common/system.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
new file mode 100644
index 0000000..d18bb6f
--- /dev/null
+++ b/backends/cloud/cloudmanager.cpp
@@ -0,0 +1,122 @@
+/* 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 "backends/cloud/cloudmanager.h"
+#include "backends/cloud/dropbox/dropboxstorage.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+
+namespace Common {
+
+DECLARE_SINGLETON(Cloud::CloudManager);
+
+}
+
+namespace Cloud {
+
+CloudManager::CloudManager() : _currentStorageIndex(0) {}
+
+CloudManager::~CloudManager() {
+	//TODO: do we have to save storages on manager destruction?	
+	for (uint32 i = 0; i < _storages.size(); ++i)
+		delete _storages[i];
+	_storages.clear();
+}
+
+void CloudManager::init() {
+	bool offerDropbox = false;
+	bool offerOneDrive = true;
+	
+	if (ConfMan.hasKey("storages_number", "cloud")) {
+		int storages = ConfMan.getInt("storages_number", "cloud");
+		for (int i = 1; i <= storages; ++i) {
+			Storage *loaded = 0;
+			Common::String keyPrefix = Common::String::format("storage%d_", i);
+			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
+				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
+				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
+				else if (storageType == "OneDrive") {
+					loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
+					offerOneDrive = false;
+				} else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
+			} else {
+				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
+			}
+			if (loaded) _storages.push_back(loaded);
+		}
+
+		uint32 index = 0;
+		if (ConfMan.hasKey("current_storage", "cloud")) {
+			index = ConfMan.getInt("current_storage", "cloud") - 1; //count from 1, all for UX
+		}
+		if (index >= _storages.size()) index = 0;
+		_currentStorageIndex = index;
+
+		if (_storages.size() == 0) offerDropbox = true;
+	} else {
+		offerDropbox = true;
+	}
+	if (offerDropbox) {
+		//this is temporary console offer to auth with Dropbox
+		Dropbox::DropboxStorage::authThroughConsole();
+	} else if (offerOneDrive) {
+		//OneDrive time
+		OneDrive::OneDriveStorage::authThroughConsole();
+	}
+}
+
+void CloudManager::save() {
+	ConfMan.set("storages_number", Common::String::format("%d", _storages.size()), "cloud");
+	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex + 1), "cloud");
+	for (uint32 i = 0; i < _storages.size(); ++i)
+		_storages[i]->saveConfig(Common::String::format("storage%d_", i + 1));
+	ConfMan.flushToDisk();
+}
+
+void CloudManager::addStorage(Storage *storage, bool makeCurrent, bool saveConfig) {
+	if (!storage) error("Cloud::CloudManager: NULL storage passed");
+	_storages.push_back(storage);
+	if (makeCurrent) _currentStorageIndex = _storages.size() - 1;
+	if (saveConfig) save();
+}
+
+Storage *CloudManager::getCurrentStorage() {
+	if (_currentStorageIndex < _storages.size())
+		return _storages[_currentStorageIndex];
+	return nullptr;
+}
+
+void CloudManager::printBool(Storage::BoolResponse response) const {
+	debug("bool = %s", (response.value ? "true" : "false"));
+}
+
+void CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->syncSaves(callback, errorCallback);
+}
+
+void CloudManager::testFeature() {
+	Storage *storage = getCurrentStorage();
+}
+
+} // End of namespace Common
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
new file mode 100644
index 0000000..a13eeeb
--- /dev/null
+++ b/backends/cloud/cloudmanager.h
@@ -0,0 +1,87 @@
+/* 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.
+*
+*/
+
+#ifndef CLOUD_CLOUDMANAGER_H
+#define CLOUD_CLOUDMANAGER_H
+
+#include "backends/cloud/storage.h"
+#include "common/array.h"
+#include "common/singleton.h"
+
+namespace Cloud {
+
+class CloudManager : public Common::Singleton<CloudManager> {
+	Common::Array<Cloud::Storage *> _storages;
+	uint _currentStorageIndex;	
+
+	void printBool(Cloud::Storage::BoolResponse response) const;
+
+public:
+	CloudManager();
+	virtual ~CloudManager();
+
+	/**
+	 * Loads all information from configs and creates current Storage instance.
+	 *
+	 * @note It's called once on startup in scummvm_main().
+	 */
+	void init();
+
+	/**
+	 * Saves all information into configuration file.
+	 */
+	void save();
+
+	/**
+	 * Adds new Storage into list.	
+	 *
+	 * @param	storage Cloud::Storage to add.
+	 * @param	makeCurrent whether added storage should be the new current storage.
+	 * @param	saveConfig whether save() should be called to update configuration file.
+	 */
+	void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true);
+
+	/**
+	 * Returns active Storage, which could be used to interact
+	 *  with cloud storage.
+	 *
+	 * @return	active Cloud::Storage or null, if there is no active Storage.
+	 */
+	Cloud::Storage *getCurrentStorage();
+
+	/**
+	 * Starts saves syncing process in currently active storage if there is any.
+	 */
+	void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
+
+	/**
+	 * Starts feature testing (the one I'm working on currently). (Temporary)
+	 */
+	void testFeature();
+};
+
+/** Shortcut for accessing the connection manager. */
+#define CloudMan		Cloud::CloudManager::instance()
+
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp
deleted file mode 100644
index 13424ba..0000000
--- a/backends/cloud/manager.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/* 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 "backends/cloud/manager.h"
-#include "backends/cloud/dropbox/dropboxstorage.h"
-#include "backends/cloud/onedrive/onedrivestorage.h"
-#include "common/config-manager.h"
-#include "common/random.h"
-#include "common/debug.h"
-
-namespace Cloud {
-
-Manager::Manager(): _currentStorageIndex(0), _deviceId(0) {}
-
-Manager::~Manager() {
-	//TODO: do we have to save storages on manager destruction?	
-	for (uint32 i = 0; i < _storages.size(); ++i)
-		delete _storages[i];
-	_storages.clear();	
-}
-
-void Manager::init() {
-	bool offerDropbox = false;
-	bool offerOneDrive = true;
-
-	if (!ConfMan.hasKey("device_id", "cloud")) {
-		Common::RandomSource source("Cloud Random Source");
-		_deviceId = source.getRandomNumber(UINT_MAX - 1);
-		ConfMan.setInt("device_id", _deviceId, "cloud");
-		ConfMan.flushToDisk();
-	} else {
-		_deviceId = ConfMan.getInt("device_id", "cloud");
-	}
-
-	if (ConfMan.hasKey("storages_number", "cloud")) {
-		int storages = ConfMan.getInt("storages_number", "cloud");
-		for (int i = 1; i <= storages; ++i) {
-			Storage *loaded = 0;
-			Common::String keyPrefix = Common::String::format("storage%d_", i);
-			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
-				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
-				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
-				else if (storageType == "OneDrive") {
-					loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
-					offerOneDrive = false;
-				} else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
-			} else {
-				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
-			}
-			if (loaded) _storages.push_back(loaded);
-		}
-
-		uint32 index = 0;
-		if (ConfMan.hasKey("current_storage", "cloud")) {
-			index = ConfMan.getInt("current_storage", "cloud") - 1; //count from 1, all for UX
-		}
-		if (index >= _storages.size()) index = 0;
-		_currentStorageIndex = index;
-
-		if (_storages.size() == 0) offerDropbox = true;
-	} else {
-		offerDropbox = true;
-	}
-
-	if (offerDropbox) {
-		//this is temporary console offer to auth with Dropbox
-		Dropbox::DropboxStorage::authThroughConsole();
-	} else if(offerOneDrive) {
-		//OneDrive time
-		OneDrive::OneDriveStorage::authThroughConsole();
-	}
-}
-
-void Manager::save() {
-	ConfMan.set("storages_number", Common::String::format("%d", _storages.size()), "cloud");
-	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex + 1), "cloud");
-	for (uint32 i = 0; i < _storages.size(); ++i)
-		_storages[i]->saveConfig(Common::String::format("storage%d_", i+1));
-	ConfMan.flushToDisk();
-}
-
-void Manager::addStorage(Cloud::Storage *storage, bool makeCurrent, bool saveConfig) {
-	if (!storage) error("Cloud::Manager: NULL storage passed");
-	_storages.push_back(storage);
-	if (makeCurrent) _currentStorageIndex = _storages.size() - 1;
-	if (saveConfig) save();
-}
-
-Storage *Manager::getCurrentStorage() {
-	if (_currentStorageIndex < _storages.size())
-		return _storages[_currentStorageIndex];
-	return nullptr;
-}
-
-void Manager::printBool(Storage::BoolResponse response) {
-	debug("bool = %s", (response.value ? "true" : "false"));
-}
-
-void Manager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	Storage *storage = getCurrentStorage();
-	if (storage) storage->syncSaves(callback, errorCallback);
-}
-
-void Manager::testFeature() {
-	Storage *storage = getCurrentStorage();
-	if (storage) storage->createDirectory("base/belong_to_us",
-		new Common::Callback<Manager, Storage::BoolResponse>(this, &Manager::printBool), nullptr);
-}
-
-} // End of namespace Cloud
diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h
deleted file mode 100644
index f68b335..0000000
--- a/backends/cloud/manager.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_MANAGER_H
-#define BACKENDS_CLOUD_MANAGER_H
-
-#include "common/cloudmanager.h"
-#include "common/str.h"
-
-namespace Cloud {
-
-class Manager: public Common::CloudManager {
-	Common::Array<Storage *> _storages;
-	uint _currentStorageIndex;
-	uint _deviceId;
-
-	void printBool(Storage::BoolResponse response);
-
-public:
-	Manager();
-	virtual ~Manager();
-
-	virtual void init();
-	virtual void save();
-	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true);
-
-	virtual Storage *getCurrentStorage();
-	virtual void syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback);
-	virtual void testFeature();
-};
-
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 98f0ac5..ca8a234 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
@@ -29,11 +30,9 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
-#include "common/cloudmanager.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
-#include "common/system.h"
 #include <curl/curl.h>
 
 namespace Cloud {
@@ -104,7 +103,7 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 		_token = result.getVal("access_token")->asString();
 		_uid = result.getVal("user_id")->asString();
 		_refreshToken = result.getVal("refresh_token")->asString();
-		g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken
+		CloudMan.save(); //ask CloudManager to save our new refreshToken
 		if (callback) (*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
@@ -116,10 +115,10 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	g_system->getCloudManager()->addStorage(this);
+	CloudMan.addStorage(this);
 	ConfMan.removeKey("onedrive_code", "cloud");
 	debug("Done! You can use OneDrive now! Look:");
-	g_system->getCloudManager()->syncSaves();
+	CloudMan.syncSaves();
 }
 
 void OneDriveStorage::saveConfig(Common::String keyPrefix) {
diff --git a/backends/module.mk b/backends/module.mk
index 4733509..40ccd17 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,8 +21,8 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
+	cloud/cloudmanager.o \
 	cloud/iso8601.o \
-	cloud/manager.o \
 	cloud/storage.o \
 	cloud/storagefile.o \
 	cloud/downloadrequest.o \
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index acb4d99..dca6891 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -33,7 +33,6 @@
 #include "gui/EventRecorder.h"
 #include "common/taskbar.h"
 #include "common/textconsole.h"
-#include "backends/cloud/manager.h"
 
 #include "backends/saves/default/default-saves.h"
 
@@ -159,11 +158,6 @@ void OSystem_SDL::init() {
 		_taskbarManager = new Common::TaskbarManager();
 #endif
 
-#if defined(USE_CLOUD)
-	if (_cloudManager == 0)
-		_cloudManager = new Cloud::Manager();
-#endif
-
 }
 
 void OSystem_SDL::initBackend() {
diff --git a/base/main.cpp b/base/main.cpp
index aede617..4edc7a9 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -66,7 +66,9 @@
 #endif
 
 #include "backends/keymapper/keymapper.h"
-#include "common/cloudmanager.h"
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#endif
 #ifdef USE_LIBCURL
 #include "backends/networking/curl/connectionmanager.h"
 #endif
@@ -481,9 +483,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #endif
 		
 #ifdef USE_CLOUD
-	system.getCloudManager()->init();
-	system.getCloudManager()->syncSaves();
-	system.getCloudManager()->testFeature(); //TODO: remove later
+	CloudMan.init();
+	CloudMan.syncSaves();
+	CloudMan.testFeature(); //TODO: remove later
 #endif
 
 	// Unless a game was specified, show the launcher dialog
@@ -597,6 +599,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #ifdef USE_LIBCURL
 	Networking::ConnectionManager::destroy();
 #endif
+#ifdef USE_CLOUD
+	//I think it's important to destroy it after ConnectionManager
+	Cloud::CloudManager::destroy();
+#endif
 	PluginManager::instance().unloadAllPlugins();
 	PluginManager::destroy();
 	GUI::GuiManager::destroy();
diff --git a/common/cloudmanager.h b/common/cloudmanager.h
deleted file mode 100644
index 936f0e0..0000000
--- a/common/cloudmanager.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef COMMON_CLOUDMANAGER_H
-#define COMMON_CLOUDMANAGER_H
-
-#include "backends/cloud/storage.h"
-
-namespace Common {
-
-class CloudManager {
-public:
-	CloudManager() {}
-	virtual ~CloudManager() {}
-
-	/**
-	 * Loads all information from configs and creates current Storage instance.
-	 *
-	 * @note It's called once on startup in scummvm_main().
-	 */
-	virtual void init() = 0;
-
-	/**
-	 * Saves all information into configuration file.
-	 */
-	virtual void save() = 0;
-
-	/**
-	 * Adds new Storage into list.	
-	 *
-	 * @param	storage Cloud::Storage to add.
-	 * @param	makeCurrent whether added storage should be the new current storage.
-	 * @param	saveConfig whether save() should be called to update configuration file.
-	 */
-	virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true) = 0;
-
-	/**
-	 * Returns active Storage, which could be used to interact
-	 *  with cloud storage.
-	 *
-	 * @return	active Cloud::Storage or null, if there is no active Storage.
-	 */
-	virtual Cloud::Storage *getCurrentStorage() = 0;
-
-	/**
-	 * Starts saves syncing process in currently active storage if there is any.
-	 */
-	virtual void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr) = 0;
-
-	/**
-	 * Starts feature testing (the one I'm working on currently). (Temporary)
-	 */
-	virtual void testFeature() = 0;
-};
-
-} // End of namespace Common
-
-#endif
diff --git a/common/system.h b/common/system.h
index 815fa9d..6d185d3 100644
--- a/common/system.h
+++ b/common/system.h
@@ -56,7 +56,6 @@ class HardwareInputSet;
 class Keymap;
 class KeymapperDefaultBindings;
 #endif
-class CloudManager;
 }
 
 class AudioCDManager;
@@ -179,15 +178,6 @@ protected:
 	Common::UpdateManager *_updateManager;
 #endif
 
-#if defined(USE_CLOUD)
-	/**
-	* No default value is provided for _cloudThread by OSystem.
-	*
-	* @note _cloudThread is deleted by the OSystem destructor.
-	*/
-	Common::CloudManager *_cloudManager;
-#endif
-
 	/**
 	 * No default value is provided for _fsFactory by OSystem.
 	 *
@@ -1126,18 +1116,6 @@ public:
 	}
 #endif
 
-#if defined(USE_CLOUD)
-	/**
-	* Returns the CloudManager, used to sync save games and
-	* upload/download files from user's cloud storage.
-	*
-	* @return the CloudManager for the current architecture
-	*/
-	virtual Common::CloudManager *getCloudManager() {
-		return _cloudManager;
-	}
-#endif
-
 	/**
 	 * Returns the FilesystemFactory object, depending on the current architecture.
 	 *


Commit: 00d8d232366f418d3a25046786ab9ab5c90a52c2
    https://github.com/scummvm/scummvm/commit/00d8d232366f418d3a25046786ab9ab5c90a52c2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add mutexes in Storage

Changed paths:
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 0f9a2a1..f035c93 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -44,14 +44,18 @@ void Storage::printErrorResponse(Networking::ErrorResponse error) {
 }
 
 Networking::Request *Storage::addRequest(Networking::Request *request) {
+	_runningRequestsMutex.lock();
 	++_runningRequestsCount;
 	if (_runningRequestsCount == 1) debug("Storage is working now");
+	_runningRequestsMutex.unlock();
 	return ConnMan.addRequest(request, new Common::Callback<Storage, Networking::Request *>(this, &Storage::requestFinishedCallback));
 }
 
 void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer) {
+	_runningRequestsMutex.lock();
 	--_runningRequestsCount;
 	if (_runningRequestsCount == 0) debug("Storage is not working now");
+	_runningRequestsMutex.unlock();
 }
 
 Networking::Request *Storage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
@@ -97,7 +101,10 @@ Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::Error
 }
 
 bool Storage::isWorking() {
-	return _runningRequestsCount > 0;
+	_runningRequestsMutex.lock();
+	bool working = _runningRequestsCount > 0;
+	_runningRequestsMutex.unlock();
+	return working;
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index b1c62ba..1276b81 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -28,9 +28,10 @@
 #include "backends/networking/curl/request.h"
 #include "backends/networking/curl/curlrequest.h"
 #include "common/array.h"
+#include "common/callback.h"
+#include "common/mutex.h"
 #include "common/stream.h"
 #include "common/str.h"
-#include "common/callback.h"
 
 namespace Cloud {
 
@@ -51,6 +52,7 @@ public:
 protected:
 	/** Keeps track of running requests. */
 	uint32 _runningRequestsCount;
+	Common::Mutex _runningRequestsMutex;
 
 	/** Returns default error callback (printErrorResponse). */
 	virtual Networking::ErrorCallback getErrorPrintingCallback();


Commit: 2a9cf65eeb305525c55c4d157ef8b1a104b8f7d7
    https://github.com/scummvm/scummvm/commit/2a9cf65eeb305525c55c4d157ef8b1a104b8f7d7
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add mode to skip drawing of button for PicButton

Changed paths:
    gui/widget.cpp
    gui/widget.h



diff --git a/gui/widget.cpp b/gui/widget.cpp
index f2a29c3..1d02478 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -398,7 +398,7 @@ void ButtonWidget::setUnpressedState() {
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey),
-	  _gfx(), _alpha(256), _transparency(false) {
+	  _gfx(), _alpha(256), _transparency(false), _showButton(true) {
 
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
@@ -406,7 +406,7 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, name, "", tooltip, cmd, hotkey),
-	  _gfx(), _alpha(256), _transparency(false) {
+	  _gfx(), _alpha(256), _transparency(false), _showButton(true) {
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
 }
@@ -449,7 +449,8 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
 }
 
 void PicButtonWidget::drawWidget() {
-	g_gui.theme()->drawButtonClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), "", _state, getFlags());
+	if (_showButton)
+		g_gui.theme()->drawButtonClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), "", _state, getFlags());
 
 	if (_gfx.getPixels()) {
 		// Check whether the set up surface needs to be converted to the GUI
diff --git a/gui/widget.h b/gui/widget.h
index 0f4b300..28d7a5b 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -226,6 +226,7 @@ public:
 
 	void useAlpha(int alpha) { _alpha = alpha; }
 	void useThemeTransparency(bool enable) { _transparency = enable; }
+	void setButtonDisplay(bool enable) {_showButton = enable; }
 
 protected:
 	void drawWidget();
@@ -233,6 +234,7 @@ protected:
 	Graphics::Surface _gfx;
 	int _alpha;
 	bool _transparency;
+	bool _showButton;
 };
 
 /* CheckboxWidget */


Commit: 0281b1eba84127bef927d70245739e4e55ad5be2
    https://github.com/scummvm/scummvm/commit/0281b1eba84127bef927d70245739e4e55ad5be2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added support for PNG images

Changed paths:
    gui/ThemeEngine.cpp
    gui/themes/scummtheme.py



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index c850a6a..70ce07b 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -36,6 +36,7 @@
 #include "graphics/fonts/ttf.h"
 
 #include "image/bmp.h"
+#include "image/png.h"
 
 #include "gui/widget.h"
 #include "gui/ThemeEngine.h"
@@ -706,24 +707,51 @@ bool ThemeEngine::addBitmap(const Common::String &filename) {
 	if (surf)
 		return true;
 
-	// If not, try to load the bitmap via the BitmapDecoder class.
-	Image::BitmapDecoder bitmapDecoder;
 	const Graphics::Surface *srcSurface = 0;
-	Common::ArchiveMemberList members;
-	_themeFiles.listMatchingMembers(members, filename);
-	for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
-		Common::SeekableReadStream *stream = (*i)->createReadStream();
-		if (stream) {
-			bitmapDecoder.loadStream(*stream);
-			srcSurface = bitmapDecoder.getSurface();
-			delete stream;
-			if (srcSurface)
-				break;
+
+	if (filename.hasSuffix(".png")) {
+		// Maybe it is PNG?
+#ifdef USE_PNG
+		Image::PNGDecoder decoder;
+		Common::ArchiveMemberList members;
+		_themeFiles.listMatchingMembers(members, filename);
+		for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
+			Common::SeekableReadStream *stream = (*i)->createReadStream();
+			if (stream) {
+				if (!decoder.loadStream(*stream))
+					error("Error decoding PNG");
+
+				srcSurface = decoder.getSurface();
+				delete stream;
+				if (srcSurface)
+					break;
+			}
+		}
+
+		if (srcSurface && srcSurface->format.bytesPerPixel != 1)
+			surf = srcSurface->convertTo(_overlayFormat);
+#else
+		error("No PNG support compiled in");
+#endif
+	} else {
+		// If not, try to load the bitmap via the BitmapDecoder class.
+		Image::BitmapDecoder bitmapDecoder;
+		Common::ArchiveMemberList members;
+		_themeFiles.listMatchingMembers(members, filename);
+		for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
+			Common::SeekableReadStream *stream = (*i)->createReadStream();
+			if (stream) {
+				bitmapDecoder.loadStream(*stream);
+				srcSurface = bitmapDecoder.getSurface();
+				delete stream;
+				if (srcSurface)
+					break;
+			}
 		}
-	}
 
-	if (srcSurface && srcSurface->format.bytesPerPixel != 1)
-		surf = srcSurface->convertTo(_overlayFormat);
+		if (srcSurface && srcSurface->format.bytesPerPixel != 1)
+			surf = srcSurface->convertTo(_overlayFormat);
+	}
 
 	// Store the surface into our hashmap (attention, may store NULL entries!)
 	_bitmaps[filename] = surf;
diff --git a/gui/themes/scummtheme.py b/gui/themes/scummtheme.py
index d5fa4df..3657872 100755
--- a/gui/themes/scummtheme.py
+++ b/gui/themes/scummtheme.py
@@ -5,7 +5,7 @@ import re
 import os
 import zipfile
 
-THEME_FILE_EXTENSIONS = ('.stx', '.bmp', '.fcc', '.ttf')
+THEME_FILE_EXTENSIONS = ('.stx', '.bmp', '.fcc', '.ttf', '.png')
 
 def buildTheme(themeName):
 	if not os.path.isdir(themeName) or not os.path.isfile(os.path.join(themeName, "THEMERC")):


Commit: 762671ccd841c5f54e4cf241a5372c26210b5865
    https://github.com/scummvm/scummvm/commit/762671ccd841c5f54e4cf241a5372c26210b5865
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added possibility to specify several state images for PicButtonWidget

Changed paths:
    gui/widget.cpp
    gui/widget.h



diff --git a/gui/widget.cpp b/gui/widget.cpp
index 1d02478..dfc0a5b 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -398,7 +398,7 @@ void ButtonWidget::setUnpressedState() {
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey),
-	  _gfx(), _alpha(256), _transparency(false), _showButton(true) {
+	  _alpha(256), _transparency(false), _showButton(true) {
 
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
@@ -406,17 +406,18 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, name, "", tooltip, cmd, hotkey),
-	  _gfx(), _alpha(256), _transparency(false), _showButton(true) {
+	  _alpha(256), _transparency(false), _showButton(true) {
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
 }
 
 PicButtonWidget::~PicButtonWidget() {
-	_gfx.free();
+	for (int i = 0; i < kPicButtonStateMax + 1; i++)
+		_gfx[i].free();
 }
 
-void PicButtonWidget::setGfx(const Graphics::Surface *gfx) {
-	_gfx.free();
+void PicButtonWidget::setGfx(const Graphics::Surface *gfx, int statenum) {
+	_gfx[statenum].free();
 
 	if (!gfx || !gfx->getPixels())
 		return;
@@ -432,10 +433,10 @@ void PicButtonWidget::setGfx(const Graphics::Surface *gfx) {
 		return;
 	}
 
-	_gfx.copyFrom(*gfx);
+	_gfx[statenum].copyFrom(*gfx);
 }
 
-void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
+void PicButtonWidget::setGfx(int w, int h, int r, int g, int b, int statenum) {
 	if (w == -1)
 		w = _w;
 	if (h == -1)
@@ -443,27 +444,41 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) {
 
 	const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
 
-	_gfx.free();
-	_gfx.create(w, h, requiredFormat);
-	_gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b));
+	_gfx[statenum].free();
+	_gfx[statenum].create(w, h, requiredFormat);
+	_gfx[statenum].fillRect(Common::Rect(0, 0, w, h), _gfx[statenum].format.RGBToColor(r, g, b));
 }
 
 void PicButtonWidget::drawWidget() {
 	if (_showButton)
 		g_gui.theme()->drawButtonClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), "", _state, getFlags());
 
-	if (_gfx.getPixels()) {
+	Graphics::Surface *gfx;
+
+	if (_state == ThemeEngine::kStateHighlight)
+		gfx = &_gfx[kPicButtonHighlight];
+	else if (_state == ThemeEngine::kStateDisabled)
+		gfx = &_gfx[kPicButtonStateDisabled];
+	else if (_state == ThemeEngine::kStatePressed)
+		gfx = &_gfx[kPicButtonStatePressed];
+	else
+		gfx = &_gfx[kPicButtonStateEnabled];
+
+	if (!gfx)
+		gfx = &_gfx[kPicButtonStateEnabled];
+
+	if (gfx->getPixels()) {
 		// Check whether the set up surface needs to be converted to the GUI
 		// color format.
 		const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
-		if (_gfx.format != requiredFormat) {
-			_gfx.convertToInPlace(requiredFormat);
+		if (gfx->format != requiredFormat) {
+			gfx->convertToInPlace(requiredFormat);
 		}
 
-		const int x = _x + (_w - _gfx.w) / 2;
-		const int y = _y + (_h - _gfx.h) / 2;
+		const int x = _x + (_w - gfx->w) / 2;
+		const int y = _y + (_h - gfx->h) / 2;
 
-		g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + _gfx.w,  y + _gfx.h), getBossClipRect(), _gfx, _state, _alpha, _transparency);
+		g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + gfx->w,  y + gfx->h), getBossClipRect(), *gfx, _state, _alpha, _transparency);
 	}
 }
 
diff --git a/gui/widget.h b/gui/widget.h
index 28d7a5b..d7b4c7b 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -80,6 +80,15 @@ enum {
 	kPressedButtonTime = 200
 };
 
+enum {
+	kPicButtonStateEnabled = 0,
+	kPicButtonHighlight = 1,
+	kPicButtonStateDisabled = 2,
+	kPicButtonStatePressed = 3,
+
+	kPicButtonStateMax = 3
+};
+
 /* Widget */
 class Widget : public GuiObject {
 	friend class Dialog;
@@ -221,8 +230,8 @@ public:
 	PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip = 0, uint32 cmd = 0, uint8 hotkey = 0);
 	~PicButtonWidget();
 
-	void setGfx(const Graphics::Surface *gfx);
-	void setGfx(int w, int h, int r, int g, int b);
+	void setGfx(const Graphics::Surface *gfx, int statenum = kPicButtonStateEnabled);
+	void setGfx(int w, int h, int r, int g, int b, int statenum = kPicButtonStateEnabled);
 
 	void useAlpha(int alpha) { _alpha = alpha; }
 	void useThemeTransparency(bool enable) { _transparency = enable; }
@@ -231,7 +240,7 @@ public:
 protected:
 	void drawWidget();
 
-	Graphics::Surface _gfx;
+	Graphics::Surface _gfx[kPicButtonStateMax + 1];
 	int _alpha;
 	bool _transparency;
 	bool _showButton;


Commit: 53a42ececfff48b93cef414bc2762806786da8d5
    https://github.com/scummvm/scummvm/commit/53a42ececfff48b93cef414bc2762806786da8d5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added new alphabitmap image type

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    graphics/transparent_surface.cpp
    graphics/transparent_surface.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/ThemeParser.cpp
    gui/ThemeParser.h



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 0352808..3c042a3 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -28,6 +28,7 @@
 #include "common/str.h"
 
 #include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
 
 #include "gui/ThemeEngine.h"
 
@@ -81,6 +82,7 @@ struct DrawStep {
 
 	DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
 	Graphics::Surface *blitSrc;
+	Graphics::TransparentSurface *blitAlphaSrc;
 };
 
 VectorRenderer *createRenderer(int mode);
@@ -420,7 +422,13 @@ public:
 	void drawCallback_BITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
 		uint16 x, y, w, h;
 		stepGetPositions(step, area, x, y, w, h);
-		blitAlphaBitmapClip(step.blitSrc, Common::Rect(x, y, x + w, y + h), clip);
+		blitKeyBitmapClip(step.blitSrc, Common::Rect(x, y, x + w, y + h), clip);
+	}
+
+	void drawCallback_ALPHABITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
+		uint16 x, y, w, h;
+		stepGetPositions(step, area, x, y, w, h);
+		blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h)); //TODO
 	}
 
 	void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
@@ -482,8 +490,10 @@ public:
 	virtual void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r) = 0;
 	virtual void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
 
-	virtual void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
-	virtual void blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
+	virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
+	virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
+
+	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index fc741f6..a50bb27 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -25,6 +25,7 @@
 #include "common/frac.h"
 
 #include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
 #include "graphics/colormasks.h"
 
 #include "gui/ThemeEngine.h"
@@ -848,8 +849,8 @@ blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const
 }
 
 template<typename PixelType>
-void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
+void VectorRendererSpec<PixelType>:: 
+blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 	int16 x = r.left;
 	int16 y = r.top;
 
@@ -885,7 +886,7 @@ blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
-blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
+blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
 	if (clipping.isEmpty() || clipping.contains(r)) {
 		blitAlphaBitmap(source, r);
 		return;
@@ -944,6 +945,12 @@ blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, cons
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) {
+	source->blit(*_activeSurface, r.left, r.top);
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
 applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
 	int pixels = _activeSurface->w * _activeSurface->h;
 	PixelType *ptr = (PixelType *)_activeSurface->getPixels();
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index bee6d4c..658c709 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -93,8 +93,9 @@ public:
 	void blitSurface(const Graphics::Surface *source, const Common::Rect &r);
 	void blitSubSurface(const Graphics::Surface *source, const Common::Rect &r);
 	void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
-	void blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r);
-	void blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
+	void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
+	void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
+	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/graphics/transparent_surface.cpp b/graphics/transparent_surface.cpp
index c2903d4..81b7f3d 100644
--- a/graphics/transparent_surface.cpp
+++ b/graphics/transparent_surface.cpp
@@ -346,7 +346,7 @@ Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int p
 	TransparentSurface srcImage(*this, false);
 	// TODO: Is the data really in the screen format?
 	if (format.bytesPerPixel != 4) {
-		warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
+		warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8); 
 		return retSize;
 	}
 
@@ -979,4 +979,82 @@ TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight)
 
 }
 
+TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
+	assert(pixels);
+
+	TransparentSurface *surface = new TransparentSurface();
+
+	// If the target format is the same, just copy
+	if (format == dstFormat) {
+		surface->copyFrom(*this);
+		return surface;
+	}
+
+	if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
+		error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
+
+	if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
+		error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
+
+	surface->create(w, h, dstFormat);
+
+	if (format.bytesPerPixel == 1) {
+		// Converting from paletted to high color
+		assert(palette);
+
+		for (int y = 0; y < h; y++) {
+			const byte *srcRow = (const byte *)getBasePtr(0, y);
+			byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+			for (int x = 0; x < w; x++) {
+				byte index = *srcRow++;
+				byte r = palette[index * 3];
+				byte g = palette[index * 3 + 1];
+				byte b = palette[index * 3 + 2];
+
+				uint32 color = dstFormat.RGBToColor(r, g, b);
+
+				if (dstFormat.bytesPerPixel == 2)
+					*((uint16 *)dstRow) = color;
+				else
+					*((uint32 *)dstRow) = color;
+
+				dstRow += dstFormat.bytesPerPixel;
+			}
+		}
+	} else {
+		// Converting from high color to high color
+		for (int y = 0; y < h; y++) {
+			const byte *srcRow = (const byte *)getBasePtr(0, y);
+			byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+			for (int x = 0; x < w; x++) {
+				uint32 srcColor;
+				if (format.bytesPerPixel == 2)
+					srcColor = READ_UINT16(srcRow);
+				else if (format.bytesPerPixel == 3)
+					srcColor = READ_UINT24(srcRow);
+				else
+					srcColor = READ_UINT32(srcRow);
+
+				srcRow += format.bytesPerPixel;
+
+				// Convert that color to the new format
+				byte r, g, b, a;
+				format.colorToARGB(srcColor, a, r, g, b);
+				uint32 color = dstFormat.ARGBToColor(a, r, g, b);
+
+				if (dstFormat.bytesPerPixel == 2)
+					*((uint16 *)dstRow) = color;
+				else
+					*((uint32 *)dstRow) = color;
+
+				dstRow += dstFormat.bytesPerPixel;
+			}
+		}
+	}
+
+	return surface;
+}
+
 } // End of namespace Graphics
diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h
index c0d3d26..461d7a6 100644
--- a/graphics/transparent_surface.h
+++ b/graphics/transparent_surface.h
@@ -151,6 +151,9 @@ struct TransparentSurface : public Graphics::Surface {
 	 *
 	 */
 	TransparentSurface *rotoscale(const TransformStruct &transform) const;
+
+	TransparentSurface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
+
 	AlphaType getAlphaMode() const;
 	void setAlphaMode(AlphaType);
 private:
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 70ce07b..1a648b4 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -31,6 +31,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/fontman.h"
 #include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
 #include "graphics/VectorRenderer.h"
 #include "graphics/fonts/bdf.h"
 #include "graphics/fonts/ttf.h"
@@ -309,7 +310,7 @@ void ThemeItemBitmap::drawSelf(bool draw, bool restore) {
 
 	if (draw) {
 		if (_alpha)
-			_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
+			_engine->renderer()->blitKeyBitmap(_bitmap, _area);
 		else
 			_engine->renderer()->blitSubSurface(_bitmap, _area);
 	}
@@ -323,7 +324,7 @@ void ThemeItemBitmapClip::drawSelf(bool draw, bool restore) {
 
 	if (draw) {
 		if (_alpha)
-			_engine->renderer()->blitAlphaBitmapClip(_bitmap, _area, _clip);
+			_engine->renderer()->blitKeyBitmapClip(_bitmap, _area, _clip);
 		else
 			_engine->renderer()->blitSubSurfaceClip(_bitmap, _area, _clip);
 	}
@@ -402,6 +403,15 @@ ThemeEngine::~ThemeEngine() {
 	}
 	_bitmaps.clear();
 
+	for (AImagesMap::iterator i = _abitmaps.begin(); i != _abitmaps.end(); ++i) {
+		Graphics::TransparentSurface *surf = i->_value;
+		if (surf) {
+			surf->free();
+			delete surf;
+		}
+	}
+	_abitmaps.clear();
+
 	delete _parser;
 	delete _themeEval;
 	delete[] _cursor;
@@ -526,6 +536,15 @@ void ThemeEngine::refresh() {
 			}
 		}
 		_bitmaps.clear();
+
+		for (AImagesMap::iterator i = _abitmaps.begin(); i != _abitmaps.end(); ++i) {
+			Graphics::TransparentSurface *surf = i->_value;
+			if (surf) {
+				surf->free();
+				delete surf;
+			}
+		}
+		_abitmaps.clear();
 	}
 
 	init();
@@ -759,6 +778,48 @@ bool ThemeEngine::addBitmap(const Common::String &filename) {
 	return surf != 0;
 }
 
+bool ThemeEngine::addAlphaBitmap(const Common::String &filename) {
+	// Nothing has to be done if the bitmap already has been loaded.
+	Graphics::TransparentSurface *surf = _abitmaps[filename];
+	if (surf)
+		return true;
+
+	const Graphics::TransparentSurface *srcSurface = 0;
+
+	if (filename.hasSuffix(".png")) {
+		// Maybe it is PNG?
+#ifdef USE_PNG
+		Image::PNGDecoder decoder;
+		Common::ArchiveMemberList members;
+		_themeFiles.listMatchingMembers(members, filename);
+		for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
+			Common::SeekableReadStream *stream = (*i)->createReadStream();
+			if (stream) {
+				if (!decoder.loadStream(*stream))
+					error("Error decoding PNG");
+
+				srcSurface = new Graphics::TransparentSurface(*decoder.getSurface(), true);
+				delete stream;
+				if (srcSurface)
+					break;
+			}
+		}
+
+		if (srcSurface && srcSurface->format.bytesPerPixel != 1)
+			surf = srcSurface->convertTo(_overlayFormat);
+#else
+		error("No PNG support compiled in");
+#endif
+	} else {
+		error("Only PNG is supported as alphabitmap");
+	}
+
+	// Store the surface into our hashmap (attention, may store NULL entries!)
+	_abitmaps[filename] = surf;
+
+	return surf != 0;
+}
+
 bool ThemeEngine::addDrawData(const Common::String &data, bool cached) {
 	DrawData id = parseDrawDataId(data);
 
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 3c259b4..92aafc8 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -32,6 +32,7 @@
 #include "common/rect.h"
 
 #include "graphics/surface.h"
+#include "graphics/transparent_surface.h"
 #include "graphics/font.h"
 #include "graphics/pixelformat.h"
 
@@ -140,6 +141,7 @@ enum TextColor {
 class ThemeEngine {
 protected:
 	typedef Common::HashMap<Common::String, Graphics::Surface *> ImagesMap;
+	typedef Common::HashMap<Common::String, Graphics::TransparentSurface *> AImagesMap;
 
 	friend class GUI::Dialog;
 	friend class GUI::GuiObject;
@@ -483,6 +485,14 @@ public:
 	bool addBitmap(const Common::String &filename);
 
 	/**
+	 * Interface for the ThemeParser class: Loads a bitmap with transparency file to use on the GUI.
+	 * The filename is also used as its identifier.
+	 *
+	 * @param filename Name of the bitmap file.
+	 */
+	bool addAlphaBitmap(const Common::String &filename);
+
+	/**
 	 * Adds a new TextStep from the ThemeParser. This will be deprecated/removed once the
 	 * new Font API is in place. FIXME: Is that so ???
 	 */
@@ -526,6 +536,10 @@ public:
 		return _bitmaps.contains(name) ? _bitmaps[name] : 0;
 	}
 
+	Graphics::TransparentSurface *getAlphaBitmap(const Common::String &name) {
+		return _abitmaps.contains(name) ? _abitmaps[name] : 0;
+	}
+
 	const Graphics::Surface *getImageSurface(const Common::String &name) const {
 		return _bitmaps.contains(name) ? _bitmaps[name] : 0;
 	}
@@ -688,6 +702,7 @@ protected:
 	TextColorData *_textColors[kTextColorMAX];
 
 	ImagesMap _bitmaps;
+	AImagesMap _abitmaps;
 	Graphics::PixelFormat _overlayFormat;
 #ifdef USE_RGB_COLOR
 	Graphics::PixelFormat _cursorFormat;
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index bd5b406..8d917f2 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -241,6 +241,18 @@ bool ThemeParser::parserCallback_bitmap(ParserNode *node) {
 	return true;
 }
 
+bool ThemeParser::parserCallback_alphabitmap(ParserNode *node) {
+	if (resolutionCheck(node->values["resolution"]) == false) {
+		node->ignore = true;
+		return true;
+	}
+
+	if (!_theme->addAlphaBitmap(node->values["filename"]))
+		return parserError("Error loading Bitmap file '" + node->values["filename"] + "'");
+
+	return true;
+}
+
 bool ThemeParser::parserCallback_text(ParserNode *node) {
 	Graphics::TextAlign alignH;
 	GUI::ThemeEngine::TextAlignVertical alignV;
@@ -323,6 +335,8 @@ static Graphics::DrawingFunctionCallback getDrawingFunctionCallback(const Common
 		return &Graphics::VectorRenderer::drawCallback_BITMAP;
 	if (name == "cross")
 		return &Graphics::VectorRenderer::drawCallback_CROSS;
+	if (name == "alphabitmap")
+		return &Graphics::VectorRenderer::drawCallback_ALPHABITMAP;
 
 	return 0;
 }
@@ -448,6 +462,16 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 				return parserError("The given filename hasn't been loaded into the GUI.");
 		}
 
+		if (functionName == "alphabitmap") {
+			if (!stepNode->values.contains("file"))
+				return parserError("Need to specify a filename for AlphaBitmap blitting.");
+
+			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
+
+			if (!drawstep->blitAlphaSrc)
+				return parserError("The given filename hasn't been loaded into the GUI.");
+		}
+
 		if (functionName == "roundedsq" || functionName == "circle" || functionName == "tab") {
 			if (stepNode->values.contains("radius") && stepNode->values["radius"] == "auto") {
 				drawstep->radius = 0xFF;
diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h
index 360e3da..14305af 100644
--- a/gui/ThemeParser.h
+++ b/gui/ThemeParser.h
@@ -80,6 +80,10 @@ protected:
 					XML_PROP(filename, true)
 					XML_PROP(resolution, false)
 				KEY_END()
+				XML_KEY(alphabitmap)
+					XML_PROP(filename, true)
+					XML_PROP(resolution, false)
+				KEY_END()
 			KEY_END()
 
 			XML_KEY(cursor)
@@ -224,6 +228,7 @@ protected:
 	bool parserCallback_drawdata(ParserNode *node);
 	bool parserCallback_bitmaps(ParserNode *node) { return true; }
 	bool parserCallback_bitmap(ParserNode *node);
+	bool parserCallback_alphabitmap(ParserNode *node);
 	bool parserCallback_cursor(ParserNode *node);
 
 


Commit: f0c52096f3e9215cb584b3862f2cd4b9632761d5
    https://github.com/scummvm/scummvm/commit/f0c52096f3e9215cb584b3862f2cd4b9632761d5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Implemented possibility to use alphabitmaps in GraphicsWidget

Changed paths:
    graphics/VectorRendererSpec.cpp
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/widget.cpp
    gui/widget.h



diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index a50bb27..e87397b 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -888,7 +888,7 @@ template<typename PixelType>
 void VectorRendererSpec<PixelType>::
 blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
 	if (clipping.isEmpty() || clipping.contains(r)) {
-		blitAlphaBitmap(source, r);
+		blitKeyBitmap(source, r);
 		return;
 	}
 
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 1a648b4..26729b9 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -170,11 +170,22 @@ protected:
 	bool _alpha;
 };
 
+class ThemeItemABitmap : public ThemeItem {
+public:
+	ThemeItemABitmap(ThemeEngine *engine, const Common::Rect &area, Graphics::TransparentSurface *bitmap, bool alpha) :
+		ThemeItem(engine, area), _bitmap(bitmap), _alpha(alpha) {}
+
+	void drawSelf(bool draw, bool restore);
+
+protected:
+	Graphics::TransparentSurface *_bitmap;
+	bool _alpha;
+};
+
 class ThemeItemBitmapClip : public ThemeItem {
 public:
 	ThemeItemBitmapClip(ThemeEngine *engine, const Common::Rect &area, const Common::Rect &clip, const Graphics::Surface *bitmap, bool alpha) :
 		ThemeItem(engine, area), _bitmap(bitmap), _alpha(alpha), _clip(clip) {}
-
 	void drawSelf(bool draw, bool restore);
 
 protected:
@@ -318,10 +329,20 @@ void ThemeItemBitmap::drawSelf(bool draw, bool restore) {
 	_engine->addDirtyRect(_area);
 }
 
-void ThemeItemBitmapClip::drawSelf(bool draw, bool restore) {
+void ThemeItemABitmap::drawSelf(bool draw, bool restore) {
 	if (restore)
 		_engine->restoreBackground(_area);
 
+	if (draw)
+		_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
+
+	_engine->addDirtyRect(_area);
+}
+
+void ThemeItemBitmapClip::drawSelf(bool draw, bool restore) {
+	if (restore)
+		 _engine->restoreBackground(_area);
+
 	if (draw) {
 		if (_alpha)
 			_engine->renderer()->blitKeyBitmapClip(_bitmap, _area, _clip);
@@ -1093,6 +1114,21 @@ void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rec
 	}
 }
 
+void ThemeEngine::queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, bool alpha) {
+
+	Common::Rect area = r;
+	area.clip(_screen.w, _screen.h);
+
+	ThemeItemABitmap *q = new ThemeItemABitmap(this, area, bitmap, alpha);
+
+	if (_buffering) {
+		_screenQueue.push_back(q);
+	} else {
+		q->drawSelf(true, false);
+		delete q;
+	}
+}
+
 void ThemeEngine::queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &r, const Common::Rect &clip, bool alpha) {
 
 	Common::Rect area = r;
@@ -1481,6 +1517,13 @@ void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &su
 	queueBitmap(&surface, r, themeTrans);
 }
 
+void ThemeEngine::drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
+	if (!ready())
+		return;
+
+	queueABitmap(&surface, r, themeTrans);
+}
+
 void ThemeEngine::drawSurfaceClip(const Common::Rect &r, const Common::Rect &clip, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
 	if (!ready())
 		return;
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 92aafc8..ccc5e03 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -355,6 +355,9 @@ public:
 	void drawSurfaceClip(const Common::Rect &r, const Common::Rect &clippingRect, const Graphics::Surface &surface,
 		WidgetStateInfo state = kStateEnabled, int alpha = 255, bool themeTrans = false);
 
+	void drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface,
+	                 WidgetStateInfo state = kStateEnabled, int alpha = 256, bool themeTrans = false);
+
 	void drawSlider(const Common::Rect &r, int width,
 	                WidgetStateInfo state = kStateEnabled);
 	void drawSliderClip(const Common::Rect &r, const Common::Rect &clippingRect, int width,
@@ -544,6 +547,10 @@ public:
 		return _bitmaps.contains(name) ? _bitmaps[name] : 0;
 	}
 
+	const Graphics::TransparentSurface *getAImageSurface(const Common::String &name) const {
+		return _abitmaps.contains(name) ? _abitmaps[name] : 0;
+	}
+
 	/**
 	 * Interface for the Theme Parser: Creates a new cursor by loading the given
 	 * bitmap and sets it as the active cursor.
@@ -630,6 +637,7 @@ protected:
 					 bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
 	void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
 	void queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r, bool alpha);
+	void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, bool alpha);
 
 	/**
 	 * DEBUG: Draws a white square and writes some text next to it.
diff --git a/gui/widget.cpp b/gui/widget.cpp
index dfc0a5b..62a69d9 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -702,6 +702,25 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) {
 	_gfx.copyFrom(*gfx);
 }
 
+void GraphicsWidget::setAGfx(const Graphics::TransparentSurface *gfx) {
+	_agfx.free();
+
+	if (!gfx || !gfx->getPixels())
+		return;
+
+	if (gfx->format.bytesPerPixel == 1) {
+		warning("GraphicsWidget::setGfx got paletted surface passed");
+		return;
+	}
+
+	if (gfx->w > _w || gfx->h > _h) {
+		warning("GraphicsWidget has size %dx%d, but a surface with %dx%d is to be set", _w, _h, gfx->w, gfx->h);
+		return;
+	}
+
+	_agfx.copyFrom(*gfx);
+}
+
 void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
 	if (w == -1)
 		w = _w;
@@ -728,6 +747,18 @@ void GraphicsWidget::drawWidget() {
 		const int y = _y + (_h - _gfx.h) / 2;
 
 		g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + _gfx.w,  y + _gfx.h), getBossClipRect(), _gfx, _state, _alpha, _transparency);
+	} else if (_agfx.getPixels()) {
+		// Check whether the set up surface needs to be converted to the GUI
+		// color format.
+		const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
+		if (_agfx.format != requiredFormat) {
+			_agfx.convertToInPlace(requiredFormat);
+		}
+
+		const int x = _x + (_w - _agfx.w) / 2;
+		const int y = _y + (_h - _agfx.h) / 2;
+
+		g_gui.theme()->drawASurface(Common::Rect(x, y, x + _agfx.w,  y + _agfx.h), _agfx, _state, _alpha, _transparency);
 	}
 }
 
diff --git a/gui/widget.h b/gui/widget.h
index d7b4c7b..5e3eea7 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -362,6 +362,7 @@ public:
 
 	void setGfx(const Graphics::Surface *gfx);
 	void setGfx(int w, int h, int r, int g, int b);
+	void setAGfx(const Graphics::TransparentSurface *gfx);
 
 	void useAlpha(int alpha) { _alpha = alpha; }
 	void useThemeTransparency(bool enable) { _transparency = enable; }
@@ -370,6 +371,7 @@ protected:
 	void drawWidget();
 
 	Graphics::Surface _gfx;
+	Graphics::TransparentSurface _agfx;
 	int _alpha;
 	bool _transparency;
 };


Commit: 4474ccf8144563c4bacbb060c943c0f68e317cea
    https://github.com/scummvm/scummvm/commit/4474ccf8144563c4bacbb060c943c0f68e317cea
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Implemented alphabitmap autoscale

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    gui/ThemeEngine.cpp
    gui/ThemeParser.cpp
    gui/ThemeParser.h



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 3c042a3..2f609ea 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -80,6 +80,8 @@ struct DrawStep {
 
 	uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */
 
+	bool autoscale; /**< scale alphaimage if present */
+
 	DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
 	Graphics::Surface *blitSrc;
 	Graphics::TransparentSurface *blitAlphaSrc;
@@ -428,7 +430,7 @@ public:
 	void drawCallback_ALPHABITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
 		uint16 x, y, w, h;
 		stepGetPositions(step, area, x, y, w, h);
-		blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h)); //TODO
+		blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h), step.autoscale); //TODO
 	}
 
 	void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
@@ -493,7 +495,7 @@ public:
 	virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
 	virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
 
-	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) = 0;
+	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index e87397b..1fdd0fc 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -886,6 +886,17 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale) {
+	if (autoscale)
+		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
+			nullptr, TS_ARGB(255, 255, 255, 255),
+			  r.width(), r.height());
+	else
+		source->blit(*_activeSurface, r.left, r.top);
+}
+
+template<typename PixelType>
+void VectorRendererSpec<PixelType>::
 blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) {
 	if (clipping.isEmpty() || clipping.contains(r)) {
 		blitKeyBitmap(source, r);
@@ -945,12 +956,6 @@ blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) {
-	source->blit(*_activeSurface, r.left, r.top);
-}
-
-template<typename PixelType>
-void VectorRendererSpec<PixelType>::
 applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) {
 	int pixels = _activeSurface->w * _activeSurface->h;
 	PixelType *ptr = (PixelType *)_activeSurface->getPixels();
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index 658c709..308fdc5 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -95,7 +95,7 @@ public:
 	void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
 	void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
 	void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
-	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r);
+	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 26729b9..545b9b5 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -334,7 +334,7 @@ void ThemeItemABitmap::drawSelf(bool draw, bool restore) {
 		_engine->restoreBackground(_area);
 
 	if (draw)
-		_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
+		_engine->renderer()->blitAlphaBitmap(_bitmap, _area, false);
 
 	_engine->addDirtyRect(_area);
 }
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 8d917f2..ca8b263 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -468,6 +468,11 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 
 			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
 
+			if (stepNode->values.contains("autoscale") && stepNode->values["autoscale"] == "true")
+				drawstep->autoscale = true;
+			else
+				drawstep->autoscale = false;
+
 			if (!drawstep->blitAlphaSrc)
 				return parserError("The given filename hasn't been loaded into the GUI.");
 		}
diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h
index 14305af..1557314 100644
--- a/gui/ThemeParser.h
+++ b/gui/ThemeParser.h
@@ -146,6 +146,7 @@ protected:
 					XML_PROP(padding, false)
 					XML_PROP(orientation, false)
 					XML_PROP(file, false)
+					XML_PROP(autoscale, false)
 				KEY_END()
 
 				XML_KEY(text)


Commit: ec7312ac13ce19f64b2b453b43e2c37235dcbe7a
    https://github.com/scummvm/scummvm/commit/ec7312ac13ce19f64b2b453b43e2c37235dcbe7a
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Implemented more modes to autoscale

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    gui/ThemeEngine.cpp
    gui/ThemeParser.cpp



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 2f609ea..fb19fa3 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -68,6 +68,12 @@ struct DrawStep {
 		kVectorAlignCenter
 	};
 
+	enum AutoScaleMode {
+		kAutoScaleNone = 0,
+		kAutoScaleStretch = 1,
+		kAutoScaleFit = 2
+	};
+
 	VectorAlignment xAlign;
 	VectorAlignment yAlign;
 
@@ -80,7 +86,7 @@ struct DrawStep {
 
 	uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */
 
-	bool autoscale; /**< scale alphaimage if present */
+	Graphics::DrawStep::AutoScaleMode autoscale; /**< scale alphaimage if present */
 
 	DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
 	Graphics::Surface *blitSrc;
@@ -495,7 +501,7 @@ public:
 	virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
 	virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
 
-	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale) = 0;
+	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale = Graphics::DrawStep::kAutoScaleNone) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 1fdd0fc..68b77d2 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -886,13 +886,25 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale) {
-	if (autoscale)
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale) {
+	if (autoscale == Graphics::DrawStep::kAutoScaleStretch) {
 		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
 			nullptr, TS_ARGB(255, 255, 255, 255),
 			  r.width(), r.height());
-	else
+	} else if (autoscale == Graphics::DrawStep::kAutoScaleFit) {
+		double ratio = (double)r.width() / source->w;
+		double ratio2 = (double)r.height() / source->h;
+
+		if (ratio2 < ratio)
+			ratio = ratio2;
+
+		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
+			nullptr, TS_ARGB(255, 255, 255, 255),
+			  (int)(source->w * ratio), (int)(source->h * ratio));
+
+	} else {
 		source->blit(*_activeSurface, r.left, r.top);
+	}
 }
 
 template<typename PixelType>
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index 308fdc5..50ee268 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -95,7 +95,7 @@ public:
 	void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
 	void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
 	void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
-	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, bool autoscale);
+	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale = Graphics::DrawStep::kAutoScaleNone);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 545b9b5..26729b9 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -334,7 +334,7 @@ void ThemeItemABitmap::drawSelf(bool draw, bool restore) {
 		_engine->restoreBackground(_area);
 
 	if (draw)
-		_engine->renderer()->blitAlphaBitmap(_bitmap, _area, false);
+		_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
 
 	_engine->addDirtyRect(_area);
 }
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index ca8b263..2262811 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -468,10 +468,13 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 
 			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
 
-			if (stepNode->values.contains("autoscale") && stepNode->values["autoscale"] == "true")
-				drawstep->autoscale = true;
-			else
-				drawstep->autoscale = false;
+			if (stepNode->values.contains("autoscale"))
+				if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch")
+					drawstep->autoscale = Graphics::DrawStep::kAutoScaleStretch;
+				else if (stepNode->values["autoscale"] == "fit")
+					drawstep->autoscale = Graphics::DrawStep::kAutoScaleFit;
+				else
+					drawstep->autoscale = Graphics::DrawStep::kAutoScaleNone;
 
 			if (!drawstep->blitAlphaSrc)
 				return parserError("The given filename hasn't been loaded into the GUI.");


Commit: 38114eb760f842eb3145c9d1af62366cf2fab8ca
    https://github.com/scummvm/scummvm/commit/38114eb760f842eb3145c9d1af62366cf2fab8ca
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Plug NinePatch bitmaps into parser

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    gui/ThemeParser.cpp



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index fb19fa3..0a83dc5 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -71,7 +71,8 @@ struct DrawStep {
 	enum AutoScaleMode {
 		kAutoScaleNone = 0,
 		kAutoScaleStretch = 1,
-		kAutoScaleFit = 2
+		kAutoScaleFit = 2,
+		kAutoScaleNinePatch = 3
 	};
 
 	VectorAlignment xAlign;
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 68b77d2..f3e496a 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -26,6 +26,7 @@
 
 #include "graphics/surface.h"
 #include "graphics/transparent_surface.h"
+#include "graphics/nine_patch.h"
 #include "graphics/colormasks.h"
 
 #include "gui/ThemeEngine.h"
@@ -902,6 +903,9 @@ blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Gra
 			nullptr, TS_ARGB(255, 255, 255, 255),
 			  (int)(source->w * ratio), (int)(source->h * ratio));
 
+	} else if (autoscale == Graphics::DrawStep::kAutoScaleNinePatch) {
+		Graphics::NinePatchBitmap nine(source, false);
+		nine.blit(*_activeSurface, r.left, r.top, r.width(), r.height());
 	} else {
 		source->blit(*_activeSurface, r.left, r.top);
 	}
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 2262811..d4701c4 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -469,12 +469,15 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
 
 			if (stepNode->values.contains("autoscale"))
-				if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch")
+				if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch") {
 					drawstep->autoscale = Graphics::DrawStep::kAutoScaleStretch;
-				else if (stepNode->values["autoscale"] == "fit")
+				} else if (stepNode->values["autoscale"] == "fit") {
 					drawstep->autoscale = Graphics::DrawStep::kAutoScaleFit;
-				else
+				} else if (stepNode->values["autoscale"] == "9patch") {
+					drawstep->autoscale = Graphics::DrawStep::kAutoScaleNinePatch;
+				} else {
 					drawstep->autoscale = Graphics::DrawStep::kAutoScaleNone;
+				}
 
 			if (!drawstep->blitAlphaSrc)
 				return parserError("The given filename hasn't been loaded into the GUI.");


Commit: 75f9b099dc3e198c7a1750a6e26ac4945719492f
    https://github.com/scummvm/scummvm/commit/75f9b099dc3e198c7a1750a6e26ac4945719492f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added possibility to specify scale mode for AlphaBitmaps

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/ThemeParser.cpp
    gui/widget.cpp
    gui/widget.h



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 0a83dc5..b791729 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -68,13 +68,6 @@ struct DrawStep {
 		kVectorAlignCenter
 	};
 
-	enum AutoScaleMode {
-		kAutoScaleNone = 0,
-		kAutoScaleStretch = 1,
-		kAutoScaleFit = 2,
-		kAutoScaleNinePatch = 3
-	};
-
 	VectorAlignment xAlign;
 	VectorAlignment yAlign;
 
@@ -87,7 +80,7 @@ struct DrawStep {
 
 	uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */
 
-	Graphics::DrawStep::AutoScaleMode autoscale; /**< scale alphaimage if present */
+	GUI::ThemeEngine::AutoScaleMode autoscale; /**< scale alphaimage if present */
 
 	DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */
 	Graphics::Surface *blitSrc;
@@ -502,7 +495,7 @@ public:
 	virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
 	virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
 
-	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale = Graphics::DrawStep::kAutoScaleNone) = 0;
+	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index f3e496a..0b947e4 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -887,12 +887,12 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale) {
-	if (autoscale == Graphics::DrawStep::kAutoScaleStretch) {
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale) {
+	if (autoscale == GUI::ThemeEngine::kAutoScaleStretch) {
 		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
 			nullptr, TS_ARGB(255, 255, 255, 255),
-			  r.width(), r.height());
-	} else if (autoscale == Graphics::DrawStep::kAutoScaleFit) {
+	                  r.width(), r.height());
+	} else if (autoscale == GUI::ThemeEngine::kAutoScaleFit) {
 		double ratio = (double)r.width() / source->w;
 		double ratio2 = (double)r.height() / source->h;
 
@@ -903,7 +903,7 @@ blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Gra
 			nullptr, TS_ARGB(255, 255, 255, 255),
 			  (int)(source->w * ratio), (int)(source->h * ratio));
 
-	} else if (autoscale == Graphics::DrawStep::kAutoScaleNinePatch) {
+	} else if (autoscale == GUI::ThemeEngine::kAutoScaleNinePatch) {
 		Graphics::NinePatchBitmap nine(source, false);
 		nine.blit(*_activeSurface, r.left, r.top, r.width(), r.height());
 	} else {
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index 50ee268..f8c1e73 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -95,7 +95,7 @@ public:
 	void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
 	void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
 	void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
-	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, Graphics::DrawStep::AutoScaleMode autoscale = Graphics::DrawStep::kAutoScaleNone);
+	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 26729b9..57c2e5d 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -172,14 +172,14 @@ protected:
 
 class ThemeItemABitmap : public ThemeItem {
 public:
-	ThemeItemABitmap(ThemeEngine *engine, const Common::Rect &area, Graphics::TransparentSurface *bitmap, bool alpha) :
-		ThemeItem(engine, area), _bitmap(bitmap), _alpha(alpha) {}
+	ThemeItemABitmap(ThemeEngine *engine, const Common::Rect &area, Graphics::TransparentSurface *bitmap, ThemeEngine::AutoScaleMode autoscale) :
+		ThemeItem(engine, area), _bitmap(bitmap), _autoscale(autoscale) {}
 
 	void drawSelf(bool draw, bool restore);
 
 protected:
 	Graphics::TransparentSurface *_bitmap;
-	bool _alpha;
+	ThemeEngine::AutoScaleMode _autoscale;
 };
 
 class ThemeItemBitmapClip : public ThemeItem {
@@ -334,7 +334,7 @@ void ThemeItemABitmap::drawSelf(bool draw, bool restore) {
 		_engine->restoreBackground(_area);
 
 	if (draw)
-		_engine->renderer()->blitAlphaBitmap(_bitmap, _area);
+		_engine->renderer()->blitAlphaBitmap(_bitmap, _area, _autoscale);
 
 	_engine->addDirtyRect(_area);
 }
@@ -1114,12 +1114,12 @@ void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rec
 	}
 }
 
-void ThemeEngine::queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, bool alpha) {
+void ThemeEngine::queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale) {
 
 	Common::Rect area = r;
 	area.clip(_screen.w, _screen.h);
 
-	ThemeItemABitmap *q = new ThemeItemABitmap(this, area, bitmap, alpha);
+	ThemeItemABitmap *q = new ThemeItemABitmap(this, area, bitmap, autoscale);
 
 	if (_buffering) {
 		_screenQueue.push_back(q);
@@ -1517,11 +1517,11 @@ void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &su
 	queueBitmap(&surface, r, themeTrans);
 }
 
-void ThemeEngine::drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
+void ThemeEngine::drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale) {
 	if (!ready())
 		return;
 
-	queueABitmap(&surface, r, themeTrans);
+	queueABitmap(&surface, r, autoscale);
 }
 
 void ThemeEngine::drawSurfaceClip(const Common::Rect &r, const Common::Rect &clip, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index ccc5e03..625bab7 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -225,6 +225,14 @@ public:
 		kShadingLuminance   ///< Converting colors to luminance for unused areas
 	};
 
+	/// AlphaBitmap scale mode selector
+	enum AutoScaleMode {
+		kAutoScaleNone = 0,		///< Use image dimensions
+		kAutoScaleStretch = 1,	///< Stretch image to full widget size
+		kAutoScaleFit = 2,		///< Scale image to widget size but keep aspect ratio
+		kAutoScaleNinePatch = 3 ///< 9-patch image
+	};
+
 	// Special image ids for images used in the GUI
 	static const char *const kImageLogo;      ///< ScummVM logo used in the launcher
 	static const char *const kImageLogoSmall; ///< ScummVM logo used in the GMM
@@ -355,8 +363,7 @@ public:
 	void drawSurfaceClip(const Common::Rect &r, const Common::Rect &clippingRect, const Graphics::Surface &surface,
 		WidgetStateInfo state = kStateEnabled, int alpha = 255, bool themeTrans = false);
 
-	void drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface,
-	                 WidgetStateInfo state = kStateEnabled, int alpha = 256, bool themeTrans = false);
+	void drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale);
 
 	void drawSlider(const Common::Rect &r, int width,
 	                WidgetStateInfo state = kStateEnabled);
@@ -637,7 +644,7 @@ protected:
 					 bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
 	void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
 	void queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r, bool alpha);
-	void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, bool alpha);
+	void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale);
 
 	/**
 	 * DEBUG: Draws a white square and writes some text next to it.
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index d4701c4..72a4d77 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -468,16 +468,17 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 
 			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
 
-			if (stepNode->values.contains("autoscale"))
+			if (stepNode->values.contains("autoscale")) {
 				if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch") {
-					drawstep->autoscale = Graphics::DrawStep::kAutoScaleStretch;
+					drawstep->autoscale = ThemeEngine::kAutoScaleStretch;
 				} else if (stepNode->values["autoscale"] == "fit") {
-					drawstep->autoscale = Graphics::DrawStep::kAutoScaleFit;
+					drawstep->autoscale = ThemeEngine::kAutoScaleFit;
 				} else if (stepNode->values["autoscale"] == "9patch") {
-					drawstep->autoscale = Graphics::DrawStep::kAutoScaleNinePatch;
+					drawstep->autoscale = ThemeEngine::kAutoScaleNinePatch;
 				} else {
-					drawstep->autoscale = Graphics::DrawStep::kAutoScaleNone;
+					drawstep->autoscale = ThemeEngine::kAutoScaleNone;
 				}
+			}
 
 			if (!drawstep->blitAlphaSrc)
 				return parserError("The given filename hasn't been loaded into the GUI.");
diff --git a/gui/widget.cpp b/gui/widget.cpp
index 62a69d9..d3de824 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -702,7 +702,7 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) {
 	_gfx.copyFrom(*gfx);
 }
 
-void GraphicsWidget::setAGfx(const Graphics::TransparentSurface *gfx) {
+void GraphicsWidget::setAGfx(const Graphics::TransparentSurface *gfx, ThemeEngine::AutoScaleMode mode) {
 	_agfx.free();
 
 	if (!gfx || !gfx->getPixels())
@@ -713,12 +713,13 @@ void GraphicsWidget::setAGfx(const Graphics::TransparentSurface *gfx) {
 		return;
 	}
 
-	if (gfx->w > _w || gfx->h > _h) {
+	if ((gfx->w > _w || gfx->h > _h) && mode == ThemeEngine::kAutoScaleNone) {
 		warning("GraphicsWidget has size %dx%d, but a surface with %dx%d is to be set", _w, _h, gfx->w, gfx->h);
 		return;
 	}
 
 	_agfx.copyFrom(*gfx);
+	_mode = mode;
 }
 
 void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) {
@@ -755,10 +756,15 @@ void GraphicsWidget::drawWidget() {
 			_agfx.convertToInPlace(requiredFormat);
 		}
 
-		const int x = _x + (_w - _agfx.w) / 2;
-		const int y = _y + (_h - _agfx.h) / 2;
+		if (_mode == GUI::ThemeEngine::kAutoScaleNone) {
+			const int x = _x + (_w - _agfx.w) / 2;
+			const int y = _y + (_h - _agfx.h) / 2;
+
+			g_gui.theme()->drawASurface(Common::Rect(x, y, x + _agfx.w,  y + _agfx.h), _agfx, _mode);
 
-		g_gui.theme()->drawASurface(Common::Rect(x, y, x + _agfx.w,  y + _agfx.h), _agfx, _state, _alpha, _transparency);
+		} else {
+			g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), _agfx, _mode);
+		}
 	}
 }
 
diff --git a/gui/widget.h b/gui/widget.h
index 5e3eea7..e2e8bff 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -362,7 +362,7 @@ public:
 
 	void setGfx(const Graphics::Surface *gfx);
 	void setGfx(int w, int h, int r, int g, int b);
-	void setAGfx(const Graphics::TransparentSurface *gfx);
+	void setAGfx(const Graphics::TransparentSurface *gfx, ThemeEngine::AutoScaleMode mode = ThemeEngine::kAutoScaleNone);
 
 	void useAlpha(int alpha) { _alpha = alpha; }
 	void useThemeTransparency(bool enable) { _transparency = enable; }
@@ -374,6 +374,7 @@ protected:
 	Graphics::TransparentSurface _agfx;
 	int _alpha;
 	bool _transparency;
+	ThemeEngine::AutoScaleMode _mode;
 };
 
 /* ContainerWidget */


Commit: 94bc75ae464dc37d2a4dee0ac6fb69e75b265413
    https://github.com/scummvm/scummvm/commit/94bc75ae464dc37d2a4dee0ac6fb69e75b265413
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Implemented centering of dialog background

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    gui/ThemeParser.cpp



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index b791729..c21f780 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -430,7 +430,7 @@ public:
 	void drawCallback_ALPHABITMAP(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
 		uint16 x, y, w, h;
 		stepGetPositions(step, area, x, y, w, h);
-		blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h), step.autoscale); //TODO
+		blitAlphaBitmap(step.blitAlphaSrc, Common::Rect(x, y, x + w, y + h), step.autoscale, step.xAlign, step.yAlign); //TODO
 	}
 
 	void drawCallback_CROSS(const Common::Rect &area, const DrawStep &step, const Common::Rect &clip) {
@@ -495,7 +495,10 @@ public:
 	virtual void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) = 0;
 	virtual void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) = 0;
 
-	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone) = 0;
+	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
+			GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
+			Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
+			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 0b947e4..8af6594 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -887,7 +887,8 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
-blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale) {
+blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale,
+			Graphics::DrawStep::VectorAlignment xAlign, Graphics::DrawStep::VectorAlignment yAlign) {
 	if (autoscale == GUI::ThemeEngine::kAutoScaleStretch) {
 		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
 			nullptr, TS_ARGB(255, 255, 255, 255),
@@ -899,9 +900,16 @@ blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI
 		if (ratio2 < ratio)
 			ratio = ratio2;
 
-		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
+		int offx = 0, offy = 0;
+		if (xAlign == Graphics::DrawStep::kVectorAlignCenter)
+			offx = (r.width() - (int)(source->w * ratio)) >> 1;
+
+		if (yAlign == Graphics::DrawStep::kVectorAlignCenter)
+			offy = (r.height() - (int)(source->h * ratio)) >> 1;
+
+		source->blit(*_activeSurface, r.left + offx, r.top + offy, Graphics::FLIP_NONE,
 			nullptr, TS_ARGB(255, 255, 255, 255),
-			  (int)(source->w * ratio), (int)(source->h * ratio));
+	                  (int)(source->w * ratio), (int)(source->h * ratio));
 
 	} else if (autoscale == GUI::ThemeEngine::kAutoScaleNinePatch) {
 		Graphics::NinePatchBitmap nine(source, false);
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index f8c1e73..b681d7e 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -95,7 +95,10 @@ public:
 	void blitSubSurfaceClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
 	void blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r);
 	void blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping);
-	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone);
+	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
+			GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
+			Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
+			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index 72a4d77..bd0d2c4 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -468,6 +468,9 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 
 			drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
 
+			if (!drawstep->blitAlphaSrc)
+				return parserError("The given filename hasn't been loaded into the GUI.");
+
 			if (stepNode->values.contains("autoscale")) {
 				if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch") {
 					drawstep->autoscale = ThemeEngine::kAutoScaleStretch;
@@ -480,8 +483,35 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 				}
 			}
 
-			if (!drawstep->blitAlphaSrc)
-				return parserError("The given filename hasn't been loaded into the GUI.");
+			if (stepNode->values.contains("xpos")) {
+				val = stepNode->values["xpos"];
+
+				if (parseIntegerKey(val, 1, &x))
+					drawstep->x = x;
+				else if (val == "center")
+					drawstep->xAlign = Graphics::DrawStep::kVectorAlignCenter;
+				else if (val == "left")
+					drawstep->xAlign = Graphics::DrawStep::kVectorAlignLeft;
+				else if (val == "right")
+					drawstep->xAlign = Graphics::DrawStep::kVectorAlignRight;
+				else
+					return parserError("Invalid value for X Position");
+			}
+
+			if (stepNode->values.contains("ypos")) {
+				val = stepNode->values["ypos"];
+
+				if (parseIntegerKey(val, 1, &x))
+					drawstep->y = x;
+				else if (val == "center")
+					drawstep->yAlign = Graphics::DrawStep::kVectorAlignCenter;
+				else if (val == "top")
+					drawstep->yAlign = Graphics::DrawStep::kVectorAlignTop;
+				else if (val == "bottom")
+					drawstep->yAlign = Graphics::DrawStep::kVectorAlignBottom;
+				else
+					return parserError("Invalid value for Y Position");
+			}
 		}
 
 		if (functionName == "roundedsq" || functionName == "circle" || functionName == "tab") {


Commit: 1359255ea83f596fbce3332d7d54f9acbc4daf4d
    https://github.com/scummvm/scummvm/commit/1359255ea83f596fbce3332d7d54f9acbc4daf4d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added empty dialog background

Changed paths:
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 57c2e5d..ee860ff 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -1416,6 +1416,8 @@ void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground b
 	case kDialogBackgroundDefault:
 		queueDD(kDDDefaultBackground, r);
 		break;
+	case kDialogBackgroundNone:
+		break;
 	}
 }
 
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 625bab7..42e5dbf 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -171,7 +171,8 @@ public:
 		kDialogBackgroundSpecial,
 		kDialogBackgroundPlain,
 		kDialogBackgroundTooltip,
-		kDialogBackgroundDefault
+		kDialogBackgroundDefault,
+		kDialogBackgroundNone
 	};
 
 	/// State of the widget to be drawn


Commit: c6e04845cc753fa219ed5c66a330eabccf2839c7
    https://github.com/scummvm/scummvm/commit/c6e04845cc753fa219ed5c66a330eabccf2839c7
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Switched GUI to draw on TransparentSurface

Changed paths:
    graphics/VectorRenderer.h
    gui/ThemeEngine.h



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index c21f780..2c32761 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -285,7 +285,7 @@ public:
 	 *
 	 * @param surface Pointer to a Surface object.
 	 */
-	virtual void setSurface(Surface *surface) {
+	virtual void setSurface(TransparentSurface *surface) {
 		_activeSurface = surface;
 	}
 
@@ -522,7 +522,7 @@ public:
 	virtual void applyScreenShading(GUI::ThemeEngine::ShadingStyle) = 0;
 
 protected:
-	Surface *_activeSurface; /**< Pointer to the surface currently being drawn */
+	TransparentSurface *_activeSurface; /**< Pointer to the surface currently being drawn */
 
 	FillMode _fillMode; /**< Defines in which way (if any) are filled the drawn shapes */
 	ShadowFillMode _shadowFillMode;
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 42e5dbf..f4a8b1e 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -686,10 +686,10 @@ protected:
 	GUI::ThemeEval *_themeEval;
 
 	/** Main screen surface. This is blitted straight into the overlay. */
-	Graphics::Surface _screen;
+	Graphics::TransparentSurface _screen;
 
 	/** Backbuffer surface. Stores previous states of the screen to blit back */
-	Graphics::Surface _backBuffer;
+	Graphics::TransparentSurface _backBuffer;
 
 	/** Sets whether the current drawing is being buffered (stored for later
 	    processing) or drawn directly to the screen. */


Commit: 30f7556beed2de07caa338650a89f18b0b71cae5
    https://github.com/scummvm/scummvm/commit/30f7556beed2de07caa338650a89f18b0b71cae5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added support for alphabitmaps in picbuttons

Changed paths:
    gui/widget.cpp
    gui/widget.h



diff --git a/gui/widget.cpp b/gui/widget.cpp
index d3de824..ffdca73 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -402,13 +402,15 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co
 
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
+	_mode = ThemeEngine::kAutoScaleNone;
 }
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, name, "", tooltip, cmd, hotkey),
-	  _alpha(256), _transparency(false), _showButton(true) {
+	  _alpha(256), _transparency(false), _showButton(true), _isAlpha(false) {
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
+	_mode = ThemeEngine::kAutoScaleNone;
 }
 
 PicButtonWidget::~PicButtonWidget() {
@@ -436,6 +438,23 @@ void PicButtonWidget::setGfx(const Graphics::Surface *gfx, int statenum) {
 	_gfx[statenum].copyFrom(*gfx);
 }
 
+void PicButtonWidget::setAGfx(const Graphics::TransparentSurface *gfx, int statenum, ThemeEngine::AutoScaleMode mode) {
+	_agfx[statenum].free();
+
+	if (!gfx || !gfx->getPixels())
+		return;
+
+	if (gfx->format.bytesPerPixel == 1) {
+		warning("PicButtonWidget::setGfx got paletted surface passed");
+		return;
+	}
+
+	_agfx[statenum].copyFrom(*gfx);
+
+	_isAlpha = true;
+	_mode = mode;
+}
+
 void PicButtonWidget::setGfx(int w, int h, int r, int g, int b, int statenum) {
 	if (w == -1)
 		w = _w;
@@ -453,32 +472,60 @@ void PicButtonWidget::drawWidget() {
 	if (_showButton)
 		g_gui.theme()->drawButtonClip(Common::Rect(_x, _y, _x + _w, _y + _h), getBossClipRect(), "", _state, getFlags());
 
-	Graphics::Surface *gfx;
+	if (!_isAlpha) {
+		Graphics::Surface *gfx;
 
-	if (_state == ThemeEngine::kStateHighlight)
-		gfx = &_gfx[kPicButtonHighlight];
-	else if (_state == ThemeEngine::kStateDisabled)
-		gfx = &_gfx[kPicButtonStateDisabled];
-	else if (_state == ThemeEngine::kStatePressed)
-		gfx = &_gfx[kPicButtonStatePressed];
-	else
-		gfx = &_gfx[kPicButtonStateEnabled];
+		if (_state == ThemeEngine::kStateHighlight)
+			gfx = &_gfx[kPicButtonHighlight];
+		else if (_state == ThemeEngine::kStateDisabled)
+			gfx = &_gfx[kPicButtonStateDisabled];
+		else if (_state == ThemeEngine::kStatePressed)
+			gfx = &_gfx[kPicButtonStatePressed];
+		else
+			gfx = &_gfx[kPicButtonStateEnabled];
 
-	if (!gfx)
-		gfx = &_gfx[kPicButtonStateEnabled];
+		if (!gfx->getPixels())
+			gfx = &_gfx[kPicButtonStateEnabled];
 
-	if (gfx->getPixels()) {
+		if (gfx->getPixels()) {
 		// Check whether the set up surface needs to be converted to the GUI
 		// color format.
-		const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
-		if (gfx->format != requiredFormat) {
-			gfx->convertToInPlace(requiredFormat);
+			const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat();
+			if (gfx->format != requiredFormat) {
+				gfx->convertToInPlace(requiredFormat);
+			}
+
+			const int x = _x + (_w - gfx->w) / 2;
+			const int y = _y + (_h - gfx->h) / 2;
+
+			g_gui.theme()->drawSurface(Common::Rect(x, y, x + gfx->w,  y + gfx->h), *gfx, _state, _alpha, _transparency);
 		}
+	} else {
+		Graphics::TransparentSurface *gfx;
+
+		if (_state == ThemeEngine::kStateHighlight)
+			gfx = &_agfx[kPicButtonHighlight];
+		else if (_state == ThemeEngine::kStateDisabled)
+			gfx = &_agfx[kPicButtonStateDisabled];
+		else if (_state == ThemeEngine::kStatePressed)
+			gfx = &_agfx[kPicButtonStatePressed];
+		else
+			gfx = &_agfx[kPicButtonStateEnabled];
 
-		const int x = _x + (_w - gfx->w) / 2;
-		const int y = _y + (_h - gfx->h) / 2;
+		if (!gfx->getPixels())
+			gfx = &_agfx[kPicButtonStateEnabled];
 
-		g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + gfx->w,  y + gfx->h), getBossClipRect(), *gfx, _state, _alpha, _transparency);
+		if (gfx->getPixels()) {
+			if (_mode == GUI::ThemeEngine::kAutoScaleNone) {
+				const int x = _x + (_w - gfx->w) / 2;
+				const int y = _y + (_h - gfx->h) / 2;
+
+				g_gui.theme()->drawASurface(Common::Rect(x, y, x + gfx->w,  y + gfx->h), *gfx, _mode);
+
+			} else {
+				g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), *gfx, _mode);
+			}
+		}
 	}
 }
 
diff --git a/gui/widget.h b/gui/widget.h
index e2e8bff..e9343f2 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -231,6 +231,7 @@ public:
 	~PicButtonWidget();
 
 	void setGfx(const Graphics::Surface *gfx, int statenum = kPicButtonStateEnabled);
+	void setAGfx(const Graphics::TransparentSurface *gfx, int statenum = kPicButtonStateEnabled, ThemeEngine::AutoScaleMode mode = ThemeEngine::kAutoScaleNone);
 	void setGfx(int w, int h, int r, int g, int b, int statenum = kPicButtonStateEnabled);
 
 	void useAlpha(int alpha) { _alpha = alpha; }
@@ -241,9 +242,12 @@ protected:
 	void drawWidget();
 
 	Graphics::Surface _gfx[kPicButtonStateMax + 1];
+	Graphics::TransparentSurface _agfx[kPicButtonStateMax + 1];
 	int _alpha;
 	bool _transparency;
 	bool _showButton;
+	bool _isAlpha;
+	ThemeEngine::AutoScaleMode _mode;
 };
 
 /* CheckboxWidget */


Commit: 8c7a8116be3f54e03644876c8d41daf1bf2e835a
    https://github.com/scummvm/scummvm/commit/8c7a8116be3f54e03644876c8d41daf1bf2e835a
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Implemented test point method to GuiObject

Changed paths:
    gui/object.h



diff --git a/gui/object.h b/gui/object.h
index 219bf77..776c941 100644
--- a/gui/object.h
+++ b/gui/object.h
@@ -91,6 +91,10 @@ public:
 
 	virtual void	removeWidget(Widget *widget);
 
+	virtual bool	isPointIn(int x, int y) {
+		return (x >= _x && x < (_x + _w) && (y >= _y) && (y < _y + _h));
+	}
+
 protected:
 	virtual void	releaseFocus() = 0;
 };


Commit: 6524a8d103c87def348b9560418850cb48d24ce4
    https://github.com/scummvm/scummvm/commit/6524a8d103c87def348b9560418850cb48d24ce4
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added transparency to PicWidgets

Changed paths:
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/widget.cpp



diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 2c32761..5f7b6e6 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -498,7 +498,8 @@ public:
 	virtual void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
 			GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
 			Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
-			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual) = 0;
+			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual,
+			int alpha = 255) = 0;
 
 	/**
 	 * Draws a string into the screen. Wrapper for the Graphics::Font string drawing
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 8af6594..9aed330 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -888,11 +888,11 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) {
 template<typename PixelType>
 void VectorRendererSpec<PixelType>::
 blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI::ThemeEngine::AutoScaleMode autoscale,
-			Graphics::DrawStep::VectorAlignment xAlign, Graphics::DrawStep::VectorAlignment yAlign) {
+			Graphics::DrawStep::VectorAlignment xAlign, Graphics::DrawStep::VectorAlignment yAlign, int alpha) {
 	if (autoscale == GUI::ThemeEngine::kAutoScaleStretch) {
 		source->blit(*_activeSurface, r.left, r.top, Graphics::FLIP_NONE,
-			nullptr, TS_ARGB(255, 255, 255, 255),
-	                  r.width(), r.height());
+			nullptr, TS_ARGB(alpha, 255, 255, 255),
+			  r.width(), r.height());
 	} else if (autoscale == GUI::ThemeEngine::kAutoScaleFit) {
 		double ratio = (double)r.width() / source->w;
 		double ratio2 = (double)r.height() / source->h;
@@ -908,7 +908,7 @@ blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r, GUI
 			offy = (r.height() - (int)(source->h * ratio)) >> 1;
 
 		source->blit(*_activeSurface, r.left + offx, r.top + offy, Graphics::FLIP_NONE,
-			nullptr, TS_ARGB(255, 255, 255, 255),
+			nullptr, TS_ARGB(alpha, 255, 255, 255),
 	                  (int)(source->w * ratio), (int)(source->h * ratio));
 
 	} else if (autoscale == GUI::ThemeEngine::kAutoScaleNinePatch) {
diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h
index b681d7e..84c802f 100644
--- a/graphics/VectorRendererSpec.h
+++ b/graphics/VectorRendererSpec.h
@@ -98,7 +98,8 @@ public:
 	void blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r,
 			GUI::ThemeEngine::AutoScaleMode autoscale = GUI::ThemeEngine::kAutoScaleNone,
 			Graphics::DrawStep::VectorAlignment xAlign = Graphics::DrawStep::kVectorAlignManual,
-			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual);
+			Graphics::DrawStep::VectorAlignment yAlign = Graphics::DrawStep::kVectorAlignManual,
+			int alpha = 255);
 
 	void applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle);
 
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index ee860ff..e0563da 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -172,14 +172,15 @@ protected:
 
 class ThemeItemABitmap : public ThemeItem {
 public:
-	ThemeItemABitmap(ThemeEngine *engine, const Common::Rect &area, Graphics::TransparentSurface *bitmap, ThemeEngine::AutoScaleMode autoscale) :
-		ThemeItem(engine, area), _bitmap(bitmap), _autoscale(autoscale) {}
+	ThemeItemABitmap(ThemeEngine *engine, const Common::Rect &area, Graphics::TransparentSurface *bitmap, ThemeEngine::AutoScaleMode autoscale, int alpha) :
+		ThemeItem(engine, area), _bitmap(bitmap), _autoscale(autoscale), _alpha(alpha) {}
 
 	void drawSelf(bool draw, bool restore);
 
 protected:
 	Graphics::TransparentSurface *_bitmap;
 	ThemeEngine::AutoScaleMode _autoscale;
+	int _alpha;
 };
 
 class ThemeItemBitmapClip : public ThemeItem {
@@ -334,7 +335,7 @@ void ThemeItemABitmap::drawSelf(bool draw, bool restore) {
 		_engine->restoreBackground(_area);
 
 	if (draw)
-		_engine->renderer()->blitAlphaBitmap(_bitmap, _area, _autoscale);
+		_engine->renderer()->blitAlphaBitmap(_bitmap, _area, _autoscale, Graphics::DrawStep::kVectorAlignManual, Graphics::DrawStep::kVectorAlignManual, _alpha);
 
 	_engine->addDirtyRect(_area);
 }
@@ -1114,12 +1115,12 @@ void ThemeEngine::queueBitmap(const Graphics::Surface *bitmap, const Common::Rec
 	}
 }
 
-void ThemeEngine::queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale) {
+void ThemeEngine::queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale, int alpha) {
 
 	Common::Rect area = r;
 	area.clip(_screen.w, _screen.h);
 
-	ThemeItemABitmap *q = new ThemeItemABitmap(this, area, bitmap, autoscale);
+	ThemeItemABitmap *q = new ThemeItemABitmap(this, area, bitmap, autoscale, alpha);
 
 	if (_buffering) {
 		_screenQueue.push_back(q);
@@ -1519,11 +1520,11 @@ void ThemeEngine::drawSurface(const Common::Rect &r, const Graphics::Surface &su
 	queueBitmap(&surface, r, themeTrans);
 }
 
-void ThemeEngine::drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale) {
+void ThemeEngine::drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale, int alpha) {
 	if (!ready())
 		return;
 
-	queueABitmap(&surface, r, autoscale);
+	queueABitmap(&surface, r, autoscale, alpha);
 }
 
 void ThemeEngine::drawSurfaceClip(const Common::Rect &r, const Common::Rect &clip, const Graphics::Surface &surface, WidgetStateInfo state, int alpha, bool themeTrans) {
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index f4a8b1e..91f82b1 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -364,7 +364,7 @@ public:
 	void drawSurfaceClip(const Common::Rect &r, const Common::Rect &clippingRect, const Graphics::Surface &surface,
 		WidgetStateInfo state = kStateEnabled, int alpha = 255, bool themeTrans = false);
 
-	void drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale);
+	void drawASurface(const Common::Rect &r, Graphics::TransparentSurface &surface, AutoScaleMode autoscale, int alpha);
 
 	void drawSlider(const Common::Rect &r, int width,
 	                WidgetStateInfo state = kStateEnabled);
@@ -645,7 +645,7 @@ protected:
 					 bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
 	void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
 	void queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r, bool alpha);
-	void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale);
+	void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale, int alpha);
 
 	/**
 	 * DEBUG: Draws a white square and writes some text next to it.
diff --git a/gui/widget.cpp b/gui/widget.cpp
index ffdca73..fcfd0f8 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -398,7 +398,7 @@ void ButtonWidget::setUnpressedState() {
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey),
-	  _alpha(256), _transparency(false), _showButton(true) {
+	  _alpha(255), _transparency(false), _showButton(true) {
 
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
@@ -407,7 +407,7 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co
 
 PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, name, "", tooltip, cmd, hotkey),
-	  _alpha(256), _transparency(false), _showButton(true), _isAlpha(false) {
+	  _alpha(255), _transparency(false), _showButton(true), _isAlpha(false) {
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
 	_type = kButtonWidget;
 	_mode = ThemeEngine::kAutoScaleNone;
@@ -520,10 +520,10 @@ void PicButtonWidget::drawWidget() {
 				const int x = _x + (_w - gfx->w) / 2;
 				const int y = _y + (_h - gfx->h) / 2;
 
-				g_gui.theme()->drawASurface(Common::Rect(x, y, x + gfx->w,  y + gfx->h), *gfx, _mode);
+				g_gui.theme()->drawASurface(Common::Rect(x, y, x + gfx->w,  y + gfx->h), *gfx, _mode, _alpha);
 
 			} else {
-				g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), *gfx, _mode);
+				g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), *gfx, _mode, _alpha);
 			}
 		}
 	}
@@ -715,13 +715,13 @@ int SliderWidget::posToValue(int pos) {
 #pragma mark -
 
 GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip)
-	: Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(256), _transparency(false) {
+	: Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(255), _transparency(false) {
 	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
 	_type = kGraphicsWidget;
 }
 
 GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip)
-	: Widget(boss, name, tooltip), _gfx(), _alpha(256), _transparency(false) {
+	: Widget(boss, name, tooltip), _gfx(), _alpha(255), _transparency(false) {
 	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG);
 	_type = kGraphicsWidget;
 }
@@ -807,10 +807,10 @@ void GraphicsWidget::drawWidget() {
 			const int x = _x + (_w - _agfx.w) / 2;
 			const int y = _y + (_h - _agfx.h) / 2;
 
-			g_gui.theme()->drawASurface(Common::Rect(x, y, x + _agfx.w,  y + _agfx.h), _agfx, _mode);
+			g_gui.theme()->drawASurface(Common::Rect(x, y, x + _agfx.w,  y + _agfx.h), _agfx, _mode, _alpha);
 
 		} else {
-			g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), _agfx, _mode);
+			g_gui.theme()->drawASurface(Common::Rect(_x, _y, _x + _w,  _y + _h), _agfx, _mode, _alpha);
 		}
 	}
 }


Commit: ea80e24481f12c117d6aecf6e5260b08423454ec
    https://github.com/scummvm/scummvm/commit/ea80e24481f12c117d6aecf6e5260b08423454ec
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Added animation classes

Changed paths:
  A gui/animation/AccelerateInterpolator.h
  A gui/animation/AlphaAnimation.h
  A gui/animation/Animation.cpp
  A gui/animation/Animation.h
  A gui/animation/DeccelerateInterpolator.h
  A gui/animation/Drawable.h
  A gui/animation/Interpolator.h
  A gui/animation/ParallelAnimation.h
  A gui/animation/RepeatAnimationWrapper.cpp
  A gui/animation/RepeatAnimationWrapper.h
  A gui/animation/ScaleAnimation.h
  A gui/animation/SequenceAnimationComposite.cpp
  A gui/animation/SequenceAnimationComposite.h
  A gui/animation/WaitForConditionAnimation.h
    graphics/transparent_surface.h
    gui/module.mk



diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h
index 461d7a6..8654183 100644
--- a/graphics/transparent_surface.h
+++ b/graphics/transparent_surface.h
@@ -154,6 +154,13 @@ struct TransparentSurface : public Graphics::Surface {
 
 	TransparentSurface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
 
+	float getRatio() {
+		if (!w)
+			return 0;
+
+		return h / (float)w;
+	}
+
 	AlphaType getAlphaMode() const;
 	void setAlphaMode(AlphaType);
 private:
diff --git a/gui/animation/AccelerateInterpolator.h b/gui/animation/AccelerateInterpolator.h
new file mode 100644
index 0000000..31494d3
--- /dev/null
+++ b/gui/animation/AccelerateInterpolator.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_ACCELERATEINTERPOLATOR_H
+#define GUI_ANIMATION_ACCELERATEINTERPOLATOR_H
+
+#include "gui/animation/Interpolator.h"
+
+namespace GUI {
+
+class AccelerateInterpolator: public Interpolator {
+public:
+	AccelerateInterpolator() {}
+	virtual ~AccelerateInterpolator() {}
+
+	virtual float interpolate(float linearValue) {
+		return pow(linearValue, 2);
+	}
+
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_ACCELERATEINTERPOLATOR_H */
diff --git a/gui/animation/AlphaAnimation.h b/gui/animation/AlphaAnimation.h
new file mode 100644
index 0000000..82cf3d4
--- /dev/null
+++ b/gui/animation/AlphaAnimation.h
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_ANIMATION_H
+#define GUI_ANIMATION_ANIMATION_H
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+class AlphaAnimation: public Animation {
+public:
+	AlphaAnimation() {}
+	virtual ~AlphaAnimation() {}
+	float getEndAlpha() const { return _endAlpha; }
+	void setEndAlpha(float endAlpha) { _endAlpha = endAlpha; }
+	float getStartAlpha() const { return _startAlpha; }
+	void setStartAlpha(float startAlpha) { _startAlpha = startAlpha; }
+
+protected:
+	virtual void updateInternal(Drawable* drawable, float interpolation) {
+		// Calculate alpha value based on properties and interpolation
+		drawable->setAlpha(_startAlpha * (1 - interpolation) + _endAlpha * interpolation);
+	}
+
+	float _startAlpha;
+	float _endAlpha;
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_ANIMATION_H */
diff --git a/gui/animation/Animation.cpp b/gui/animation/Animation.cpp
new file mode 100644
index 0000000..ee4d900
--- /dev/null
+++ b/gui/animation/Animation.cpp
@@ -0,0 +1,98 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+Animation::Animation()
+		: _startTime(0), _duration(0), _finished(false), _finishOnEnd(true) {
+}
+
+Animation::~Animation() {
+}
+
+void Animation::start(long currentTime) {
+	_finished = false;
+	_startTime = currentTime;
+}
+
+void Animation::setDuration(long duration) {
+	_duration = duration;
+}
+
+void Animation::update(Drawable *drawable, long currentTime) {
+	float interpolation;
+
+	if (currentTime < _startTime) {
+		// If the start time is in the future, nothing changes - the interpolated value is 0
+		interpolation = 0;
+	} else if (currentTime > _startTime + _duration) {
+		// If the animation is finished, the interpolated value is 1 and the animation is marked as finished
+		interpolation = 1;
+		finishAnimation();
+	} else {
+		// Calculate the interpolated value
+		interpolation = (currentTime - _startTime) / (float) (_duration);
+	}
+
+	// Activate the interpolator if present
+	if (_interpolator.get() != NULL) {
+		interpolation = _interpolator->interpolate(interpolation);
+	}
+
+	updateInternal(drawable, interpolation);
+}
+
+void Animation::finishAnimation() {
+	if (_finishOnEnd) {
+		_finished = true;
+	}
+}
+
+void Animation::updateInternal(Drawable *drawable, float interpolation) {
+	// Default implementation
+}
+
+bool Animation::isFinished() const {
+	return _finished;
+}
+
+bool Animation::isFinishOnEnd() const {
+	return _finishOnEnd;
+}
+
+void Animation::setFinishOnEnd(bool finishOnEnd) {
+	_finishOnEnd = finishOnEnd;
+}
+
+InterpolatorPtr Animation::getInterpolator() const {
+	return _interpolator;
+}
+
+void Animation::setInterpolator(InterpolatorPtr interpolator) {
+	_interpolator = interpolator;
+}
+
+} // End of namespace GUI
diff --git a/gui/animation/Animation.h b/gui/animation/Animation.h
new file mode 100644
index 0000000..300720b
--- /dev/null
+++ b/gui/animation/Animation.h
@@ -0,0 +1,76 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_ANIMATION_H
+#define GUI_ANIMATION_ANIMATION_H
+
+#include "gui/animation/Interpolator.h"
+
+namespace GUI {
+
+class Drawable;
+
+class Animation {
+public:
+	Animation();
+	virtual ~Animation() = 0;
+
+	virtual void update(Drawable *drawable, long currentTime);
+
+	/**
+	 * Set start time in millis
+	 */
+	virtual void start(long currentTime);
+
+	/**
+	 * Set duration in millis
+	 */
+	virtual void setDuration(long duration);
+
+	virtual bool isFinished() const;
+
+	bool isFinishOnEnd() const;
+
+	void setFinishOnEnd(bool finishOnEnd);
+
+	InterpolatorPtr getInterpolator() const;
+	void setInterpolator(InterpolatorPtr interpolator);
+
+protected:
+	void finishAnimation();
+
+	virtual void updateInternal(Drawable *drawable, float interpolation);
+
+	long _startTime;
+	long _duration;
+	bool _finished;
+	bool _finishOnEnd;
+	InterpolatorPtr _interpolator;
+};
+
+typedef Common::SharedPtr<Animation> AnimationPtr;
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_ANIMATION_H */
diff --git a/gui/animation/DeccelerateInterpolator.h b/gui/animation/DeccelerateInterpolator.h
new file mode 100644
index 0000000..e25ff6a
--- /dev/null
+++ b/gui/animation/DeccelerateInterpolator.h
@@ -0,0 +1,41 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_DECCELERATEINTERPOLATOR_H
+#define GUI_ANIMATION_DECCELERATEINTERPOLATOR_H
+
+#include "gui/animation/Interpolator.h"
+
+namespace GUI {
+
+class DeccelerateInterpolator: public Interpolator {
+public:
+	DeccelerateInterpolator() {}
+	virtual ~DeccelerateInterpolator() {}
+	virtual float interpolate(float linearValue) { return sqrt(linearValue); }
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_DECCELERATEINTERPOLATOR_H */
diff --git a/gui/animation/Drawable.h b/gui/animation/Drawable.h
new file mode 100644
index 0000000..cf60427
--- /dev/null
+++ b/gui/animation/Drawable.h
@@ -0,0 +1,109 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_DRAWABLE_H
+#define GUI_ANIMATION_DRAWABLE_H
+
+#include "common/ptr.h"
+#include "graphics/transparent_surface.h"
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+class Animation;
+typedef Common::SharedPtr<Animation> AnimationPtr;
+
+class Drawable {
+public:
+	Drawable() :
+		_bitmap(NULL), _positionX(0), _positionY(0), _width(0), _height(0), _alpha(1),
+		_usingSnapshot(false), _shouldCenter(false) {
+		_displayRatio = 1.0;
+	}
+
+	virtual ~Drawable() {
+		if (_usingSnapshot)
+			delete _bitmap;
+	}
+
+	void updateAnimation(long currentTime) {
+		if (_animation.get() != NULL) {
+			_animation->update(this, currentTime);
+		}
+	}
+
+	bool isAnimationFinished() {
+		if (_animation.get() != NULL)
+			return _animation->isFinished();
+
+		return false;
+	}
+
+	float getAlpha() const { return _alpha; }
+	void setAlpha(float alpha) { _alpha = alpha; }
+	AnimationPtr getAnimation() const { return _animation; }
+	void setAnimation(AnimationPtr animation) { _animation = animation; }
+	Graphics::TransparentSurface *getBitmap() const { return _bitmap; }
+	void setBitmap(Graphics::TransparentSurface *bitmap) { _bitmap = bitmap; }
+	float getPositionX() const { return _positionX; }
+	void setPositionX(float positionX) { _positionX = positionX; }
+	float getPositionY() const { return _positionY; }
+	void setPositionY(float positionY) { _positionY = positionY; }
+	virtual float getWidth() const { return _width; }
+	void setWidth(float width) { _width = width; }
+
+	virtual float getHeight() const {
+		if (_height == 0)
+			return getWidth() * _bitmap->getRatio() * _displayRatio;
+
+		return _height;
+	}
+
+	void setHeight(float height) { _height = height; }
+	void setDisplayRatio(float ratio) { _displayRatio = ratio; }
+	inline bool shouldCenter() const { return _shouldCenter; }
+	void setShouldCenter(bool shouldCenter) { _shouldCenter = shouldCenter; }
+
+protected:
+	bool _usingSnapshot;
+
+private:
+	Graphics::TransparentSurface *_bitmap;
+	float _positionX;
+	float _positionY;
+	float _width;
+	float _height;
+	float _alpha;
+	bool _shouldCenter;
+	AnimationPtr _animation;
+
+	float _displayRatio;
+};
+
+typedef Common::SharedPtr<Drawable> DrawablePtr;
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_DRAWABLE_H */
diff --git a/gui/animation/Interpolator.h b/gui/animation/Interpolator.h
new file mode 100644
index 0000000..72d7acb
--- /dev/null
+++ b/gui/animation/Interpolator.h
@@ -0,0 +1,44 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_INTERPOLATOR_H
+#define GUI_ANIMATION_INTERPOLATOR_H
+
+#include "common/ptr.h"
+
+namespace GUI {
+
+class Interpolator {
+public:
+	Interpolator() {}
+	virtual ~Interpolator() {}
+
+	virtual float interpolate(float linearValue) = 0;
+};
+
+typedef Common::SharedPtr<Interpolator> InterpolatorPtr;
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_INTERPOLATOR_H */
diff --git a/gui/animation/ParallelAnimation.h b/gui/animation/ParallelAnimation.h
new file mode 100644
index 0000000..ce1f599
--- /dev/null
+++ b/gui/animation/ParallelAnimation.h
@@ -0,0 +1,72 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_PARALLELANIMATION_H
+#define GUI_ANIMATION_PARALLELANIMATION_H
+
+#include "gui/animation/Animation.h"
+#include "common/array.h"
+
+namespace GUI {
+
+class ParallelAnimation: public Animation {
+public:
+	ParallelAnimation() {}
+	virtual ~ParallelAnimation() {}
+
+	virtual void addAnimation(AnimationPtr animation) {
+		_animations.push_back(animation);
+	}
+
+	virtual void update(Drawable *drawable, long currentTime) {
+		for (AnimationPtr anim : _animations) {
+			anim->update(drawable, currentTime);
+			if (anim->isFinished()) {
+				finishAnimation();
+			}
+		}
+	}
+
+	virtual void start(long currentTime) {
+		Animation::start(currentTime);
+
+		for (AnimationPtr anim : _animations)
+			anim->start(currentTime);
+	}
+
+	virtual void setDuration(long duration) {
+		Animation::setDuration(duration);
+
+		for (AnimationPtr anim : _animations)
+			anim->setDuration(duration);
+	}
+
+private:
+
+	Common::Array<AnimationPtr> _animations;
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_PARALLELANIMATION_H */
diff --git a/gui/animation/RepeatAnimationWrapper.cpp b/gui/animation/RepeatAnimationWrapper.cpp
new file mode 100644
index 0000000..a7e1413
--- /dev/null
+++ b/gui/animation/RepeatAnimationWrapper.cpp
@@ -0,0 +1,52 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#include "gui/animation/RepeatAnimationWrapper.h"
+
+namespace GUI {
+
+void RepeatAnimationWrapper::update(Drawable* drawable, long currentTime) {
+	// Update wrapped animation
+	_animation->update(drawable, currentTime);
+
+	// If the animation is finished, increase the repeat count and restart it if needed
+	if (_animation->isFinished()) {
+		++_repeatCount;
+		if (_timesToRepeat > 0 && _repeatCount >= _timesToRepeat) {
+			finishAnimation();
+		} else {
+			_animation->start(currentTime);
+		}
+	}
+}
+
+void RepeatAnimationWrapper::start(long currentTime) {
+	Animation::start(currentTime);
+	_repeatCount = 0;
+
+	// Start wrapped animation
+	_animation->start(currentTime);
+}
+
+} // End of namespace GUI
diff --git a/gui/animation/RepeatAnimationWrapper.h b/gui/animation/RepeatAnimationWrapper.h
new file mode 100644
index 0000000..3d766dd
--- /dev/null
+++ b/gui/animation/RepeatAnimationWrapper.h
@@ -0,0 +1,61 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_REPEATANIMATIONWRAPPER_H
+#define GUI_ANIMATION_REPEATANIMATIONWRAPPER_H
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+class RepeatAnimationWrapper: public Animation {
+public:
+	/**
+	 * Animation - animation to repeat
+	 *
+	 * timesToRepeat - 0 means infinite
+	 */
+	RepeatAnimationWrapper(AnimationPtr animation, uint16 timesToRepeat) :
+			_animation(animation), _timesToRepeat(timesToRepeat) {}
+
+	virtual ~RepeatAnimationWrapper() {}
+
+	virtual void update(Drawable* drawable, long currentTime);
+
+	/**
+	 * Set start time in millis
+	 */
+	virtual void start(long currentTime);
+
+private:
+	uint16 _timesToRepeat;
+	uint16 _repeatCount;
+
+	AnimationPtr _animation;
+
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_REPEATANIMATIONWRAPPER_H */
diff --git a/gui/animation/ScaleAnimation.h b/gui/animation/ScaleAnimation.h
new file mode 100644
index 0000000..80a4ae6
--- /dev/null
+++ b/gui/animation/ScaleAnimation.h
@@ -0,0 +1,69 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_SCALEANIMATION_H
+#define GUI_ANIMATION_SCALEANIMATION_H
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+class ScaleAnimation: public Animation {
+public:
+	ScaleAnimation() : _endWidth(0), _endWidthFactor(0) {}
+
+	virtual ~ScaleAnimation() {}
+
+	float getEndWidth() const { return _endWidth; }
+	void setEndWidth(float endWidth) { _endWidth = endWidth; }
+	float getEndWidthFactor() const { return _endWidthFactor; }
+	void setEndWidthFactor(float endWidthFactor) { _endWidthFactor = endWidthFactor; }
+	float getStartWidth() const { return _startWidth; }
+	void setStartWidth(float startWidth) { _startWidth = startWidth; }
+
+	void updateInternal(Drawable *drawable, float interpolation) {
+		// If start width was set as 0 -> use the current width as the start dimension
+		if (_startWidth == 0)
+			_startWidth = drawable->getWidth();
+
+		// If end width was set as 0 - multiply the start width by the given factor
+		if (_endWidth == 0)
+			_endWidth = _startWidth * _endWidthFactor;
+
+		// Calculate width based on interpolation
+		float width = _startWidth * (1 - interpolation) + _endWidth * interpolation;
+		drawable->setWidth(width);
+	}
+
+private:
+	virtual void updateInternal(Drawable *drawable, float interpolation);
+	float _startWidth;
+	float _endWidth;
+	float _endWidthFactor;
+};
+
+} // End of namespace GUI
+
+
+#endif /* GUI_ANIMATION_SCALEANIMATION_H */
diff --git a/gui/animation/SequenceAnimationComposite.cpp b/gui/animation/SequenceAnimationComposite.cpp
new file mode 100644
index 0000000..9ecfeeb
--- /dev/null
+++ b/gui/animation/SequenceAnimationComposite.cpp
@@ -0,0 +1,72 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#include "gui/animation/SequenceAnimationComposite.h"
+
+namespace GUI {
+
+void SequenceAnimationComposite::start(long currentTime) {
+	Animation::start(currentTime);
+
+	// The first animation in the sequence should a start time equal to this sequence
+	if (_sequence.size() >= 1)
+		_sequence[0]->start(currentTime);
+
+	// Set the index to 0
+	_index = 0;
+}
+
+void SequenceAnimationComposite::addAnimation(AnimationPtr animation) {
+	_sequence.push_back(animation);
+}
+
+void SequenceAnimationComposite::update(Drawable *drawable, long currentTime) {
+	uint16 sequenceSize = _sequence.size();
+
+	// Check index bounds
+	if (_index >= sequenceSize)
+		return;
+
+	// Get the current animation in the sequence
+	AnimationPtr anim = _sequence[_index];
+
+	// Update the drawable
+	anim->update(drawable, currentTime);
+
+	// Check if the current animation is finished
+	if (anim->isFinished()) {
+		// Increase the index - move to the next animation
+		++_index;
+
+		if (_index >= sequenceSize) {
+			// Finished the sequence
+			finishAnimation();
+		} else {
+			// Set the start time for the next animation
+			_sequence[_index]->start(currentTime);
+		}
+	}
+}
+
+} // End of namespace GUI
diff --git a/gui/animation/SequenceAnimationComposite.h b/gui/animation/SequenceAnimationComposite.h
new file mode 100644
index 0000000..4ec0331
--- /dev/null
+++ b/gui/animation/SequenceAnimationComposite.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_SEQUENCEANIMATION_H
+#define GUI_ANIMATION_SEQUENCEANIMATION_H
+
+#include "gui/animation/Animation.h"
+#include "common/array.h"
+
+namespace GUI {
+
+class SequenceAnimationComposite: public Animation {
+public:
+	SequenceAnimationComposite() {}
+	virtual ~SequenceAnimationComposite() {}
+
+	virtual void addAnimation(AnimationPtr animation);
+
+	virtual void update(Drawable* drawable, long currentTime);
+
+	virtual void start(long currentTime);
+
+private:
+	uint16 _index;
+	Common::Array<AnimationPtr> _sequence;
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_SEQUENCEANIMATION_H */
diff --git a/gui/animation/WaitForConditionAnimation.h b/gui/animation/WaitForConditionAnimation.h
new file mode 100644
index 0000000..5a67a37
--- /dev/null
+++ b/gui/animation/WaitForConditionAnimation.h
@@ -0,0 +1,71 @@
+/* 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.
+ *
+ */
+
+// Based on code by omergilad.
+
+#ifndef GUI_ANIMATION_WAITFORCONDITIONANIMATION_H
+#define GUI_ANIMATION_WAITFORCONDITIONANIMATION_H
+
+#include "gui/animation/Animation.h"
+
+namespace GUI {
+
+class Condition {
+
+public:
+	virtual ~Condition() {}
+
+	virtual bool evaluate() = 0;
+};
+
+typedef Common::SharedPtr<Condition> ConditionPtr;
+
+/**
+ * Used for delaying the animation sequence until a certain condition has been met
+ */
+class WaitForConditionAnimation: public Animation {
+public:
+	WaitForConditionAnimation() {}
+	virtual ~WaitForConditionAnimation() {}
+
+	virtual void update(Drawable *drawable, long currentTime) {
+		// Check the condition - if it has been met, finish.
+		if (_condition.get() != NULL && _condition->evaluate()) {
+			finishAnimation();
+		}
+	}
+
+	ConditionPtr getCondition() const {
+		return _condition;
+	}
+
+	void setCondition(ConditionPtr condition) {
+		_condition = condition;
+	}
+
+private:
+	ConditionPtr _condition;
+};
+
+} // End of namespace GUI
+
+#endif /* GUI_ANIMATION_WAITFORCONDITIONANIMATION_H */
diff --git a/gui/module.mk b/gui/module.mk
index 6cbc63d..2ffea5a 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -24,6 +24,9 @@ MODULE_OBJS := \
 	ThemeLayout.o \
 	ThemeParser.o \
 	Tooltip.o \
+	animation/Animation.o \
+	animation/RepeatAnimationWrapper.o \
+	animation/SequenceAnimationComposite.o \
 	widget.o \
 	widgets/editable.o \
 	widgets/edittext.o \


Commit: a686049c46387be53010357050bd252845a48d48
    https://github.com/scummvm/scummvm/commit/a686049c46387be53010357050bd252845a48d48
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix widget clipping

Changed paths:
    gui/widget.cpp



diff --git a/gui/widget.cpp b/gui/widget.cpp
index fcfd0f8..f6e6d09 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -498,7 +498,7 @@ void PicButtonWidget::drawWidget() {
 			const int x = _x + (_w - gfx->w) / 2;
 			const int y = _y + (_h - gfx->h) / 2;
 
-			g_gui.theme()->drawSurface(Common::Rect(x, y, x + gfx->w,  y + gfx->h), *gfx, _state, _alpha, _transparency);
+			g_gui.theme()->drawSurfaceClip(Common::Rect(x, y, x + gfx->w,  y + gfx->h), getBossClipRect(), *gfx, _state, _alpha, _transparency);
 		}
 	} else {
 		Graphics::TransparentSurface *gfx;


Commit: b02b16ab98330f3471919a57ec0d4e087e2fa924
    https://github.com/scummvm/scummvm/commit/b02b16ab98330f3471919a57ec0d4e087e2fa924
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Document ConnectionManager's onDeleteCallback

Changed paths:
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index a3cad0d..44511fd 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -47,7 +47,7 @@ ConnectionManager::~ConnectionManager() {
 	_handleMutex.lock();
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
 		Request *request = i->request;
-		RequestCallback callback = i->callback;
+		RequestCallback callback = i->onDeleteCallback;
 		if (request) request->finish();
 		delete request;
 		if (callback) (*callback)(request);
@@ -107,7 +107,7 @@ void ConnectionManager::interateRequests() {
 		Request *request = i->request;
 		if (!request || request->state() == FINISHED) {
 			delete (i->request);
-			if (i->callback) (*i->callback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
+			if (i->onDeleteCallback) (*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
 			_requests.erase(i);
 			continue;
 		}
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 75cff05..a91bc01 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -42,11 +42,31 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 
 	typedef Common::BaseCallback<Request *> *RequestCallback;
 
-	struct RequestWithCallback { //I'm completely out of ideas
+	/**
+	 * RequestWithCallback is used by ConnectionManager to
+	 * storage the Request and a callback which should be
+	 * called on Request delete.
+	 *
+	 * Usually one won't need to pass such callback, but
+	 * in some cases you'd like to know whether Request is
+	 * still running.
+	 *
+	 * For example, Cloud::Storage is keeping track of how
+	 * many Requests are running, and thus it needs to know
+	 * that Request was destroyed to decrease its counter.
+	 *
+	 * onDeleteCallback is called with *invalid* pointer.
+	 * ConnectionManager deletes Request first and then passes
+	 * the pointer to the callback. One may use the address
+	 * to find it in own HashMap or Array and remove it.
+	 * So, again, this pointer is for information only. One
+	 * cannot use it.
+	 */
+	struct RequestWithCallback {
 		Request *request;
-		RequestCallback callback;
+		RequestCallback onDeleteCallback;
 		
-		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), callback(cb) {}
+		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), onDeleteCallback(cb) {}
 	};
 
 	CURLM *_multi;	
@@ -78,6 +98,8 @@ public:
 	 *
 	 * If Request's state is RETRY, handleRetry() is called instead.
 	 *
+	 * The passed callback would be called after Request is deleted.
+	 *
 	 * @note This method starts the timer if it's not started yet.
 	 *
 	 * @return the same Request pointer, just as a shortcut


Commit: da3b7bd8d9f3d3828b8cea6dff60e5f43e7ad4b1
    https://github.com/scummvm/scummvm/commit/da3b7bd8d9f3d3828b8cea6dff60e5f43e7ad4b1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDriveStorage

It has its own GoogleDriveTokenRefresher and knows how to do info().

This commit also contains JSON int -> long long int fix and
CurlJsonRequest '\n' -> ' ' fix.

Changed paths:
  A backends/cloud/googledrive/googledrivestorage.cpp
  A backends/cloud/googledrive/googledrivestorage.h
  A backends/cloud/googledrive/googledrivetokenrefresher.cpp
  A backends/cloud/googledrive/googledrivetokenrefresher.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/module.mk
    backends/networking/curl/curljsonrequest.cpp
    common/json.cpp
    common/json.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index d18bb6f..92e45e8 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -23,6 +23,7 @@
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 
@@ -45,7 +46,8 @@ CloudManager::~CloudManager() {
 
 void CloudManager::init() {
 	bool offerDropbox = false;
-	bool offerOneDrive = true;
+	bool offerOneDrive = false;
+	bool offerGoogleDrive = true;
 	
 	if (ConfMan.hasKey("storages_number", "cloud")) {
 		int storages = ConfMan.getInt("storages_number", "cloud");
@@ -55,9 +57,10 @@ void CloudManager::init() {
 			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
 				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
 				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
-				else if (storageType == "OneDrive") {
-					loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
-					offerOneDrive = false;
+				else if (storageType == "OneDrive") loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
+				else if (storageType == "Google Drive") {
+					loaded = GoogleDrive::GoogleDriveStorage::loadFromConfig(keyPrefix);
+					offerGoogleDrive = false;
 				} else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
 			} else {
 				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
@@ -82,6 +85,9 @@ void CloudManager::init() {
 	} else if (offerOneDrive) {
 		//OneDrive time
 		OneDrive::OneDriveStorage::authThroughConsole();
+	} else if (offerGoogleDrive) {		
+		GoogleDrive::GoogleDriveStorage::authThroughConsole();
+		_currentStorageIndex = 100;
 	}
 }
 
@@ -117,6 +123,7 @@ void CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCa
 
 void CloudManager::testFeature() {
 	Storage *storage = getCurrentStorage();
+	if (storage) storage->info(nullptr, nullptr);
 }
 
 } // End of namespace Common
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 50a1b8a..bf8e43d 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -79,7 +79,7 @@ void DropboxUploadRequest::uploadNextPart() {
 			url += "finish";			
 			Common::JSONObject jsonCursor, jsonCommit;			
 			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
-			jsonCursor.setVal("offset", new Common::JSONValue(_contentsStream->pos()));
+			jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
 			jsonCommit.setVal("path", new Common::JSONValue(_savePath));
 			jsonCommit.setVal("mode", new Common::JSONValue("overwrite"));
 			jsonCommit.setVal("autorename", new Common::JSONValue(false));
@@ -90,7 +90,7 @@ void DropboxUploadRequest::uploadNextPart() {
 			url += "append_v2";
 			Common::JSONObject jsonCursor;
 			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
-			jsonCursor.setVal("offset", new Common::JSONValue(_contentsStream->pos()));
+			jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
 			jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
 			jsonRequestParameters.setVal("close", new Common::JSONValue(false));			
 		}
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
new file mode 100644
index 0000000..eef7f1f
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -0,0 +1,331 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "backends/cloud/cloudmanager.h"
+#include "backends/cloud/googledrive/googledrivetokenrefresher.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace GoogleDrive {
+
+char *GoogleDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
+char *GoogleDriveStorage::SECRET; //TODO: hide these secrets somehow
+
+void GoogleDriveStorage::loadKeyAndSecret() {
+	Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", "cloud");
+	KEY = new char[k.size() + 1];
+	memcpy(KEY, k.c_str(), k.size());
+	KEY[k.size()] = 0;
+
+	k = ConfMan.get("GOOGLE_DRIVE_SECRET", "cloud");
+	SECRET = new char[k.size() + 1];
+	memcpy(SECRET, k.c_str(), k.size());
+	SECRET[k.size()] = 0;
+}
+
+GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::String refreshToken):
+	_token(accessToken), _refreshToken(refreshToken) {}
+
+GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
+	getAccessToken(new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete), code);
+}
+
+GoogleDriveStorage::~GoogleDriveStorage() {}
+
+void GoogleDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+	bool codeFlow = (code != "");
+
+	if (!codeFlow && _refreshToken == "") {
+		warning("GoogleDriveStorage: no refresh token available to get new access token.");
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+		return;
+	}
+
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://accounts.google.com/o/oauth2/token"); //TODO
+	if (codeFlow) {
+		request->addPostField("code=" + code);
+		request->addPostField("grant_type=authorization_code");
+	} else {
+		request->addPostField("refresh_token=" + _refreshToken);
+		request->addPostField("grant_type=refresh_token");
+	}
+	request->addPostField("client_id=" + Common::String(KEY));
+	request->addPostField("client_secret=" + Common::String(SECRET));
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost");
+	addRequest(request);
+}
+
+void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("GoogleDriveStorage: got NULL instead of JSON");
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (!result.contains("access_token")) {
+		warning("Bad response, no token passed");
+		debug("%s", json->stringify().c_str());
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+	} else {
+		_token = result.getVal("access_token")->asString();
+		if (!result.contains("refresh_token"))
+			warning("No refresh_token passed");
+		else
+			_refreshToken = result.getVal("refresh_token")->asString();
+		CloudMan.save(); //ask CloudManager to save our new refreshToken
+		if (callback) (*callback)(BoolResponse(nullptr, true));
+	}
+	delete json;
+}
+
+void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
+	if (!response.value) {
+		warning("GoogleDriveStorage: failed to get access token through code flow");
+		return;
+	}
+
+	ConfMan.removeKey("googledrive_code", "cloud");
+	CloudMan.addStorage(this);
+	ConfMan.flushToDisk();
+	debug("Done! You can use Google Drive now! Look:");
+	CloudMan.testFeature();
+}
+
+void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "type", "Google Drive", "cloud");
+	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
+	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
+}
+
+namespace {
+uint64 atoull(Common::String s) {
+	uint64 result = 0;
+	for (uint32 i = 0; i < s.size(); ++i) {
+		if (s[i] < '0' || s[i] > '9') break;
+		result = result * 10L + (s[i] - '0');
+	}
+	return result;
+}
+}
+
+void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (outerCallback) {
+		Common::JSONObject info = json->asObject();
+
+		Common::String uid, name, email;
+		uint64 quotaUsed = 0, quotaAllocated = 0;
+
+		if (info.contains("user") && info.getVal("user")->isObject()) {
+			//"me":true, "kind":"drive#user","photoLink": "",
+			//"displayName":"Alexander Tkachev","emailAddress":"alexander at tkachov.ru","permissionId":""
+			Common::JSONObject user = info.getVal("user")->asObject();
+			uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
+			name = user.getVal("displayName")->asString();
+			email = user.getVal("emailAddress")->asString();
+		}
+
+		if (info.contains("storageQuota") && info.getVal("storageQuota")->isObject()) {
+			//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
+			Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
+			Common::String usage = storageQuota.getVal("usage")->asString();
+			Common::String limit = storageQuota.getVal("limit")->asString();			
+			quotaUsed = atoull(usage);
+			quotaAllocated = atoull(limit);
+		}
+
+		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+		delete outerCallback;
+	}
+
+	delete json;
+}
+
+void GoogleDriveStorage::printJson(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("printJson: NULL");
+		return;
+	}
+
+	debug("%s", json->stringify().c_str());
+	delete json;
+}
+
+void GoogleDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
+	if (!response.value) {
+		warning("fileInfoCallback: NULL");
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+		return;
+	}
+
+	Common::JSONObject result = response.value->asObject();
+	if (result.contains("@content.downloadUrl")) {
+		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
+		if (outerCallback)
+			(*outerCallback)(Networking::NetworkReadStreamResponse(
+				response.request,
+				new Networking::NetworkReadStream(url, 0, "")
+			));
+	} else {
+		warning("downloadUrl not found in passed JSON");
+		debug("%s", response.value->stringify().c_str());
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+	}
+	delete response.value;
+}
+
+Networking::Request *GoogleDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	//return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
+	return nullptr; //TODO
+}
+
+Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	//return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));
+	return nullptr; //TODO
+}
+
+Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
+	/*
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::fileInfoCallback, outerCallback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _token);
+	return addRequest(request);
+	*/
+	return nullptr; //TODO
+}
+
+void GoogleDriveStorage::fileDownloaded(BoolResponse response) {
+	if (response.value) debug("file downloaded!");
+	else debug("download failed!");
+}
+
+void GoogleDriveStorage::printFiles(FileArrayResponse response) {
+	debug("files:");
+	Common::Array<StorageFile> &files = response.value;
+	for (uint32 i = 0; i < files.size(); ++i)
+		debug("\t%s", files[i].path().c_str());
+}
+
+void GoogleDriveStorage::printBool(BoolResponse response) {
+	debug("bool: %s", response.value ? "true" : "false");
+}
+
+void GoogleDriveStorage::printFile(UploadResponse response) {
+	debug("\nuploaded file info:");
+	debug("\tpath: %s", response.value.path().c_str());
+	debug("\tsize: %u", response.value.size());
+	debug("\ttimestamp: %u", response.value.timestamp());
+}
+
+void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
+	debug("\nuser info:");
+	debug("\tname: %s", response.value.name().c_str());
+	debug("\temail: %s", response.value.email().c_str());
+	debug("\tdisk usage: %llu/%llu", response.value.used(), response.value.available());
+}
+
+Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	//return addRequest(new GoogleDriveCreateDirectoryRequest(this, path, callback, errorCallback));
+	return nullptr; //TODO
+}
+
+Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!callback) callback = new Common::Callback<GoogleDriveStorage, StorageInfoResponse>(this, &GoogleDriveStorage::printInfo);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::infoInnerCallback, callback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user");
+	request->addHeader("Authorization: Bearer " + _token);
+	return addRequest(request);	
+}
+
+Common::String GoogleDriveStorage::savesDirectoryPath() { return "saves/"; }
+
+GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
+	loadKeyAndSecret();
+
+	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
+		warning("No access_token found");
+		return 0;
+	}
+
+	if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) {
+		warning("No refresh_token found");
+		return 0;
+	}
+
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");	
+	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud");
+	return new GoogleDriveStorage(accessToken, refreshToken);
+}
+
+Common::String GoogleDriveStorage::getAuthLink() {
+	Common::String url = "https://accounts.google.com/o/oauth2/auth";
+	url += "?response_type=code";
+	url += "&redirect_uri=http://localhost"; //that's for copy-pasting
+	//url += "&redirect_uri=http%3A%2F%2Flocalhost"; //that's "http://localhost" for automatic opening
+	url += "&client_id="; url += KEY;	
+	url += "&scope=https://www.googleapis.com/auth/drive.appfolder"; //for copy-pasting
+	return url;
+}
+
+void GoogleDriveStorage::authThroughConsole() {
+	if (!ConfMan.hasKey("GOOGLE_DRIVE_KEY", "cloud") || !ConfMan.hasKey("GOOGLE_DRIVE_SECRET", "cloud")) {
+		warning("No Google Drive keys available, cannot do auth");
+		return;
+	}
+
+	loadKeyAndSecret();
+
+	if (ConfMan.hasKey("googledrive_code", "cloud")) {
+		//phase 2: get access_token using specified code
+		new GoogleDriveStorage(ConfMan.get("googledrive_code", "cloud"));
+		return;
+	}
+
+	debug("Navigate to this URL and press \"Allow\":");
+	debug("%s\n", getAuthLink().c_str());
+	debug("Then, add googledrive_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
+	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
+	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
new file mode 100644
index 0000000..8a82a54
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -0,0 +1,131 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
+
+#include "backends/cloud/storage.h"
+#include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage: public Cloud::Storage {
+	static char *KEY, *SECRET;
+
+	static void loadKeyAndSecret();
+
+	Common::String _token, _refreshToken;
+
+	/** This private constructor is called from loadFromConfig(). */
+	GoogleDriveStorage(Common::String token, Common::String refreshToken);
+
+	/**
+	 * This private constructor is called from authThroughConsole() (phase 2).
+	 * It uses OAuth code flow to get tokens.
+	 */
+	GoogleDriveStorage(Common::String code);
+
+	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
+	void codeFlowComplete(BoolResponse response);
+
+	/** Constructs StorageInfo based on JSON response from cloud. */
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
+
+	void printJson(Networking::JsonResponse response);
+	void fileDownloaded(BoolResponse response);
+	void printFiles(FileArrayResponse response);
+	void printBool(BoolResponse response);
+	void printFile(UploadResponse response);
+	void printInfo(StorageInfoResponse response);
+
+	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
+public:	
+	virtual ~GoogleDriveStorage();
+
+	/**
+	 * Storage methods, which are used by CloudManager to save
+	 * storage in configuration file.
+	 */
+
+	/**
+	 * Save storage data using ConfMan.
+	 * @param keyPrefix all saved keys must start with this prefix.
+	 * @note every Storage must write keyPrefix + "type" key
+	 *       with common value (e.g. "Dropbox").
+	 */
+	virtual void saveConfig(Common::String keyPrefix);
+
+	/** Public Cloud API comes down there. */
+
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+
+	/** Returns UploadStatus struct with info about uploaded file. */
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns the StorageInfo struct. */
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns storage's saves directory path with the trailing slash. */
+	virtual Common::String savesDirectoryPath();
+
+	/**
+	 * Load token and user id from configs and return GoogleDriveStorage for those.	
+	 * @return pointer to the newly created GoogleDriveStorage or 0 if some problem occured.
+	 */
+	static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix);
+
+	/**
+	 * Returns GoogleDrive auth link.
+	 */
+	static Common::String getAuthLink();
+
+	/**
+	 * Show message with GoogleDrive auth instructions. (Temporary)
+	 */
+	static void authThroughConsole();
+
+	/**
+	 * Gets new access_token. If <code> passed is "", refresh_token is used.
+	 * Use "" in order to refresh token and pass a callback, so you could
+	 * continue your work when new token is available.
+	 */
+	void getAccessToken(BoolCallback callback, Common::String code = "");
+
+	Common::String accessToken() { return _token; }
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
new file mode 100644
index 0000000..3fae830
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -0,0 +1,121 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/googledrive/googledrivetokenrefresher.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveTokenRefresher::GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
+	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+
+GoogleDriveTokenRefresher::~GoogleDriveTokenRefresher() {}
+
+void GoogleDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
+	if (!response.value) {
+		//failed to refresh token, notify user with NULL in original callback
+		warning("GoogleDriveTokenRefresher: failed to refresh token");
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
+	}
+
+	//update headers: first change header with token, then pass those to request
+	for (uint32 i = 0; i < _headers.size(); ++i) {
+		if (_headers[i].contains("Authorization")) {
+			_headers[i] = "Authorization: Bearer " + _parentStorage->accessToken();
+		}
+	}
+	setHeaders(_headers);
+
+	//successfully received refreshed token, can restart the original request now	
+	retry(0);
+}
+
+void GoogleDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
+	if (!json) {
+		//that's probably not an error (200 OK)
+		CurlJsonRequest::finishSuccess(nullptr);
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (result.contains("error")) {
+		//new token needed => request token & then retry original request		
+		if (_stream) {
+			debug("code %ld", _stream->httpResponseCode());
+		}
+
+		Common::JSONObject error = result.getVal("error")->asObject();
+		bool irrecoverable = true;		
+
+		uint32 code = -1;
+		Common::String message;
+		if (error.contains("code") && error.getVal("code")->isIntegerNumber()) {
+			code = error.getVal("code")->asIntegerNumber();
+			debug("code = %u", code);
+		}
+
+		if (error.contains("message")) {
+			message = error.getVal("message")->asString();
+			debug("message = %s", message.c_str());
+		}
+
+		if (code == 401 || message == "Invalid Credentials")
+			irrecoverable = false;
+
+		if (irrecoverable) {			
+			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
+			delete json;
+			return;
+		}
+
+		pause();		
+		delete json;
+		_parentStorage->getAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
+		return;
+	}
+
+	//notify user of success
+	CurlJsonRequest::finishSuccess(json);
+}
+
+void GoogleDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+	_headers = headers;
+	curl_slist_free_all(_headersList);
+	_headersList = 0;
+	for (uint32 i = 0; i < headers.size(); ++i)
+		CurlJsonRequest::addHeader(headers[i]);
+}
+
+void GoogleDriveTokenRefresher::addHeader(Common::String header) {
+	_headers.push_back(header);
+	CurlJsonRequest::addHeader(header);
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h
new file mode 100644
index 0000000..3dcb56b
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.h
@@ -0,0 +1,52 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVETOKENREFRESHER_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVETOKENREFRESHER_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveTokenRefresher: public Networking::CurlJsonRequest {
+	GoogleDriveStorage *_parentStorage;
+	Common::Array<Common::String> _headers;	
+	
+	void tokenRefreshed(Storage::BoolResponse response);
+
+	virtual void finishSuccess(Common::JSONValue *json);
+public:	
+	GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
+	virtual ~GoogleDriveTokenRefresher();
+
+	virtual void setHeaders(Common::Array<Common::String> &headers);
+	virtual void addHeader(Common::String header);
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 40ccd17..8833edc 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -32,6 +32,8 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
+	cloud/googledrive/googledrivestorage.o \
+	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index df982bc..46d8865 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -44,9 +44,11 @@ char *CurlJsonRequest::getPreparedContents() {
 	//replace all "bad" bytes with '.' character
 	byte *result = _contentsStream.getData();
 	uint32 size = _contentsStream.size();
-	for (uint32 i = 0; i < size; ++i)
-		if (result[i] < 0x20 || result[i] > 0x7f)
+	for (uint32 i = 0; i < size; ++i) {
+		if (result[i] == '\n') result[i] = ' '; //yeah, kinda stupid
+		else if (result[i] < 0x20 || result[i] > 0x7f)
 			result[i] = '.';
+	}
 
 	//make it zero-terminated string
 	result[size - 1] = '\0';
diff --git a/common/json.cpp b/common/json.cpp
index 779f99a..5f63d3f 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -300,7 +300,7 @@ JSONValue *JSONValue::parse(const char **data) {
 		bool neg = **data == '-';
 		if (neg) (*data)++;
 
-		int integer = 0;
+		long long int integer = 0;
 		double number = 0.0;
 		bool onlyInteger = true;
 
@@ -568,7 +568,7 @@ JSONValue::JSONValue(double numberValue) {
 *
 * @param int numberValue The number to use as the value
 */
-JSONValue::JSONValue(int numberValue) {
+JSONValue::JSONValue(long long int numberValue) {
 	_type = JSONType_IntegerNumber;
 	_integerValue = numberValue;
 }
@@ -794,7 +794,7 @@ double JSONValue::asNumber() const {
 *
 * @return int Returns the number value
 */
-int JSONValue::asIntegerNumber() const {
+long long int JSONValue::asIntegerNumber() const {
 	return _integerValue;
 }
 
diff --git a/common/json.h b/common/json.h
index 3557193..d9bbbdb 100644
--- a/common/json.h
+++ b/common/json.h
@@ -96,7 +96,7 @@ public:
 	JSONValue(const String &stringValue);
 	JSONValue(bool boolValue);
 	JSONValue(double numberValue);
-	JSONValue(int numberValue);
+	JSONValue(long long int numberValue);
 	JSONValue(const JSONArray &arrayValue);
 	JSONValue(const JSONObject &objectValue);
 	JSONValue(const JSONValue &source);
@@ -113,7 +113,7 @@ public:
 	const String &asString() const;
 	bool asBool() const;
 	double asNumber() const;
-	int asIntegerNumber() const;
+	long long int asIntegerNumber() const;
 	const JSONArray &asArray() const;
 	const JSONObject &asObject() const;
 
@@ -138,7 +138,7 @@ private:
 	union {
 		bool _boolValue;
 		double _numberValue;
-		int _integerValue;
+		long long int _integerValue;
 		String *_stringValue;
 		JSONArray *_arrayValue;
 		JSONObject *_objectValue;


Commit: 1c823b6c1d465bd894f7f37a963165ee009f35ea
    https://github.com/scummvm/scummvm/commit/1c823b6c1d465bd894f7f37a963165ee009f35ea
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix SavesSyncRequest

Now it would finish with error if spawned Request is nullptr.

Changed paths:
    backends/cloud/savessyncrequest.cpp



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index d5cb6f6..86dc83a 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -66,6 +66,7 @@ void SavesSyncRequest::start() {
 		new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
 	);
+	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
@@ -172,6 +173,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
 	);
+	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
@@ -213,6 +215,7 @@ void SavesSyncRequest::downloadNextFile() {
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
+	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
@@ -256,6 +259,7 @@ void SavesSyncRequest::uploadNextFile() {
 		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
 	);
+	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {


Commit: 7ff1f918084b8d41826ad869d8c9865e1d53e082
    https://github.com/scummvm/scummvm/commit/7ff1f918084b8d41826ad869d8c9865e1d53e082
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add copyRectToOSD()

I was lazy to implement that in OpenGLGraphicsManager and I'm not sure
it's implemented correctly in SurfaceSdlGraphicsManager, but it works
for me.

Changed paths:
    backends/base-backend.cpp
    backends/base-backend.h
    backends/graphics/graphics.h
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/modular-backend.h
    common/system.h



diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp
index 3e95c3e..cf6fdfc 100644
--- a/backends/base-backend.cpp
+++ b/backends/base-backend.cpp
@@ -39,6 +39,10 @@ void BaseBackend::displayMessageOnOSD(const char *msg) {
 	dialog.runModal();
 }
 
+void BaseBackend::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {
+	warning("BaseBackend::copyRectToOSD not implemented"); //TODO
+}
+
 void BaseBackend::initBackend() {
 	// Init Event manager
 #ifndef DISABLE_DEFAULT_EVENT_MANAGER
diff --git a/backends/base-backend.h b/backends/base-backend.h
index 598f682..7000d3b 100644
--- a/backends/base-backend.h
+++ b/backends/base-backend.h
@@ -33,6 +33,7 @@ public:
 	virtual void initBackend();
 
 	virtual void displayMessageOnOSD(const char *msg);
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 	virtual void fillScreen(uint32 col);
 };
 
diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h
index 3671b9f..8b0ac19 100644
--- a/backends/graphics/graphics.h
+++ b/backends/graphics/graphics.h
@@ -84,6 +84,7 @@ public:
 	virtual void setCursorPalette(const byte *colors, uint start, uint num) = 0;
 
 	virtual void displayMessageOnOSD(const char *msg) {}
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {}
 
 	// Graphics::PaletteManager interface
 	//virtual void setPalette(const byte *colors, uint start, uint num) = 0;
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 4d6a00a..e9f26bc 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -751,6 +751,12 @@ void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) {
 #endif
 }
 
+void OpenGLGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {
+#ifdef USE_OSD
+	warning("implement copyRectToOSD"); //TODO
+#endif
+}
+
 void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
 	assert(_gameScreen->hasPalette());
 
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index 35435c1..e0d1664 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -115,6 +115,7 @@ public:
 	virtual void setCursorPalette(const byte *colors, uint start, uint num);
 
 	virtual void displayMessageOnOSD(const char *msg);
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 
 	// PaletteManager interface
 	virtual void setPalette(const byte *colors, uint start, uint num);
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 5b591e7..88fdc09 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -2197,6 +2197,54 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const char *msg) {
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
 }
+
+void SurfaceSdlGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {
+	assert(_transactionMode == kTransactionNone);
+	assert(buf);
+
+	Common::StackLock lock(_graphicsMutex);	// Lock the mutex until this function ends
+
+	// Lock the OSD surface for drawing
+	if (SDL_LockSurface(_osdSurface))
+		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
+
+#ifdef USE_RGB_COLOR
+	byte *dst = (byte *)_osdSurface->pixels + y * _osdSurface->pitch + x * _osdSurface->format->BytesPerPixel;
+	if (_videoMode.screenWidth == w && pitch == _osdSurface->pitch) {
+		memcpy(dst, buf, h*pitch);
+	} else {
+		const byte *src = (const byte *)buf;
+		do {
+			memcpy(dst, src, w * _osdSurface->format->BytesPerPixel);
+			src += pitch;
+			dst += _osdSurface->pitch;
+		} while (--h);
+	}
+#else
+	byte *dst = (byte *)_osdSurface->pixels + y * _osdSurface->pitch + x;
+	if (_osdSurface->pitch == pitch && pitch == w) {
+		memcpy(dst, buf, h*w);
+	} else {
+		const byte *src = (const byte *)buf;
+		do {
+			memcpy(dst, src, w);
+			src += pitch;
+			dst += _osdSurface->pitch;
+		} while (--h);
+	}
+#endif
+
+	// Finished drawing, so unlock the OSD surface again
+	SDL_UnlockSurface(_osdSurface);
+
+	// Init the OSD display parameters, and the fade out
+	_osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
+	_osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
+	SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
+
+	// Ensure a full redraw takes place next time the screen is updated
+	_forceFull = true;
+}
 #endif
 
 bool SurfaceSdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) {
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 25d6ff0..a8bafd0 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -145,6 +145,7 @@ public:
 
 #ifdef USE_OSD
 	virtual void displayMessageOnOSD(const char *msg);
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 #endif
 
 	// Override from Common::EventObserver
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index d8be9ca..08565dc 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -241,6 +241,10 @@ void ModularBackend::displayMessageOnOSD(const char *msg) {
 	_graphicsManager->displayMessageOnOSD(msg);
 }
 
+void ModularBackend::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {
+	_graphicsManager->copyRectToOSD(buf, pitch, x, y, w, h);
+}
+
 void ModularBackend::quit() {
 	exit(0);
 }
diff --git a/backends/modular-backend.h b/backends/modular-backend.h
index 20e8b73..cf3babf 100644
--- a/backends/modular-backend.h
+++ b/backends/modular-backend.h
@@ -127,6 +127,7 @@ public:
 
 	virtual void quit();
 	virtual void displayMessageOnOSD(const char *msg);
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 
 	//@}
 
diff --git a/common/system.h b/common/system.h
index 6d185d3..64e4b92 100644
--- a/common/system.h
+++ b/common/system.h
@@ -1086,6 +1086,33 @@ public:
 	virtual void displayMessageOnOSD(const char *msg) = 0;
 
 	/**
+	* Blit a bitmap to the 'on screen display'.
+	*
+	* If the current pixel format has one byte per pixel, the graphics data
+	* uses 8 bits per pixel, using the palette specified via setPalette.
+	* If more than one byte per pixel is in use, the graphics data uses the
+	* pixel format returned by getScreenFormat.
+	*
+	* @param buf		the buffer containing the graphics data source
+	* @param pitch		the pitch of the buffer (number of bytes in a scanline)
+	* @param x			the x coordinate of the destination rectangle
+	* @param y			the y coordinate of the destination rectangle
+	* @param w			the width of the destination rectangle
+	* @param h			the height of the destination rectangle
+	*
+	* @note The specified destination rectangle must be completly contained
+	*       in the visible screen space, and must be non-empty. If not, a
+	*       backend may or may not perform clipping, trigger an assert or
+	*       silently corrupt memory.
+	*
+	* @see updateScreen
+	* @see getScreenFormat
+	* @see copyRectToScreen
+	*/
+
+	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) = 0;
+
+	/**
 	 * Return the SaveFileManager, used to store and load savestates
 	 * and other modifiable persistent game data. For more information,
 	 * refer to the SaveFileManager documentation.


Commit: 9d186929e16ce023222028143a280aca03605fe9
    https://github.com/scummvm/scummvm/commit/9d186929e16ce023222028143a280aca03605fe9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CloudIcon

To achieve smoother animation, ConnectionManager's timer now is 20 times
more frequent.

I'm encountering some strange libcurl.dll segfault problem when I close
the application while some Requests are active. It's not
CloudIcon-related, so it's more likely related to this 20 FPS timer.
This problem shows up only in Visual Studio for me.

Changed paths:
  A backends/networking/curl/cloudicon.cpp
  A backends/networking/curl/cloudicon.h
    backends/module.mk
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/module.mk b/backends/module.mk
index 8833edc..f1ba8b8 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -45,6 +45,7 @@ ifdef USE_LIBCURL
 MODULE_OBJS += \
 	networking/curl/connectionmanager.o \
 	networking/curl/networkreadstream.o \
+	networking/curl/cloudicon.o \
 	networking/curl/curlrequest.o \
 	networking/curl/curljsonrequest.o \
 	networking/curl/request.o
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
new file mode 100644
index 0000000..477e864
--- /dev/null
+++ b/backends/networking/curl/cloudicon.cpp
@@ -0,0 +1,50 @@
+/* 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 "backends/networking/curl/cloudicon.h"
+#include "backends/cloud/cloudmanager.h"
+#include "gui/ThemeEngine.h"
+#include "gui/gui-manager.h"
+
+namespace Networking {
+
+CloudIcon::CloudIcon(): _frame(0) {}
+
+CloudIcon::~CloudIcon() {}
+
+void CloudIcon::draw() {	 
+	Cloud::Storage *storage = CloudMan.getCurrentStorage();
+	bool working = (storage && storage->isWorking());
+	if (working) {
+		//buf = animation frame;
+		if (g_system) {
+			const Graphics::Surface *s = g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall);
+			int x = 10, y = 10;
+			g_system->copyRectToOSD(s->getPixels(), s->pitch, x, y, s->w, s->h);
+		}
+	} else {
+		//buf = empty;
+		//TODO: put empty piece to OSD?
+	}
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
new file mode 100644
index 0000000..dc28c82
--- /dev/null
+++ b/backends/networking/curl/cloudicon.h
@@ -0,0 +1,40 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
+#define BACKENDS_NETWORKING_CURL_CLOUDICON_H
+
+namespace Networking {
+
+class CloudIcon {
+	int _frame;
+
+public:
+	CloudIcon();
+	~CloudIcon();
+
+	void draw();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 44511fd..c044a15 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -37,7 +37,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
@@ -52,12 +52,13 @@ ConnectionManager::~ConnectionManager() {
 		delete request;
 		if (callback) (*callback)(request);
 	}
-	_requests.clear();
-	_handleMutex.unlock();
+	_requests.clear();	
 
 	//cleanup
 	curl_multi_cleanup(_multi);
 	curl_global_cleanup();
+	_multi = nullptr;
+	_handleMutex.unlock();
 }
 
 void ConnectionManager::registerEasyHandle(CURL *easy) {
@@ -95,9 +96,13 @@ void ConnectionManager::stopTimer() {
 void ConnectionManager::handle() {
 	//lock mutex here (in case another handle() would be called before this one ends)
 	_handleMutex.lock();
-	interateRequests();
-	processTransfers();
+	++_frame;
+	if (_frame % CLOUD_PERIOD == 0) interateRequests();
+	if (_frame % CURL_PERIOD == 0) processTransfers();
 	_handleMutex.unlock();
+
+	//icon redrawing is doesn't require any mutex, but must be done after requests are iterated
+	_icon.draw();
 }
 
 void ConnectionManager::interateRequests() {
@@ -123,6 +128,8 @@ void ConnectionManager::interateRequests() {
 }
 
 void ConnectionManager::processTransfers() {
+	if (!_multi) return;
+
 	//check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
 	int transfersRunning;
 	curl_multi_perform(_multi, &transfersRunning);
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index a91bc01..3a2e974 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -23,6 +23,7 @@
 #ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
 #define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
 
+#include "backends/networking/curl/cloudicon.h"
 #include "backends/networking/curl/request.h"
 #include "common/str.h"
 #include "common/singleton.h"
@@ -38,6 +39,11 @@ namespace Networking {
 class NetworkReadStream;
 
 class ConnectionManager : public Common::Singleton<ConnectionManager> {
+	static const uint32 FRAMES_PER_SECOND = 20;
+	static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
+	static const uint32 CLOUD_PERIOD = 20; //every 20th frame
+	static const uint32 CURL_PERIOD = 1; //every frame
+
 	friend void connectionsThread(void *); //calls handle()
 
 	typedef Common::BaseCallback<Request *> *RequestCallback;
@@ -73,8 +79,10 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests;
 	Common::Mutex _handleMutex;
+	CloudIcon _icon;
+	uint32 _frame;
 	
-	void startTimer(int interval = 1000000); //1 second is the default interval
+	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
 	void handle();
 	void interateRequests();


Commit: 2a15b8b280af4d4beddb142e468f511a65552e2d
    https://github.com/scummvm/scummvm/commit/2a15b8b280af4d4beddb142e468f511a65552e2d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add clearOSD() method

So one can erase everything from OSD and then blit something on it.

Changed paths:
    backends/base-backend.cpp
    backends/base-backend.h
    backends/graphics/graphics.h
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/modular-backend.h
    common/system.h



diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp
index cf6fdfc..59d6744 100644
--- a/backends/base-backend.cpp
+++ b/backends/base-backend.cpp
@@ -43,6 +43,11 @@ void BaseBackend::copyRectToOSD(const void *buf, int pitch, int x, int y, int w,
 	warning("BaseBackend::copyRectToOSD not implemented"); //TODO
 }
 
+void BaseBackend::clearOSD() {
+	warning("BaseBackend::clearOSD not implemented"); //TODO
+	//what should I do? Remove all TimedMessageDialogs?
+}
+
 void BaseBackend::initBackend() {
 	// Init Event manager
 #ifndef DISABLE_DEFAULT_EVENT_MANAGER
diff --git a/backends/base-backend.h b/backends/base-backend.h
index 7000d3b..edee427 100644
--- a/backends/base-backend.h
+++ b/backends/base-backend.h
@@ -34,6 +34,7 @@ public:
 
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
+	virtual void clearOSD();
 	virtual void fillScreen(uint32 col);
 };
 
diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h
index 8b0ac19..1063a10 100644
--- a/backends/graphics/graphics.h
+++ b/backends/graphics/graphics.h
@@ -85,6 +85,7 @@ public:
 
 	virtual void displayMessageOnOSD(const char *msg) {}
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {}
+	virtual void clearOSD() {}
 
 	// Graphics::PaletteManager interface
 	//virtual void setPalette(const byte *colors, uint start, uint num) = 0;
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index e9f26bc..97788be 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -757,6 +757,23 @@ void OpenGLGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x, int
 #endif
 }
 
+void OpenGLGraphicsManager::clearOSD() {
+#ifdef USE_OSD
+	// HACK: Actually no client code should use graphics functions from
+	// another thread. But the MT-32 emulator still does, thus we need to
+	// make sure this doesn't happen while a updateScreen call is done.
+	Common::StackLock lock(_osdMutex);
+
+	Graphics::Surface *dst = _osd->getSurface();
+	_osd->fill(0);
+	_osd->flagDirty();
+
+	// Init the OSD display parameters.
+	_osdAlpha = kOSDInitialAlpha;
+	_osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay;
+#endif
+}
+
 void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
 	assert(_gameScreen->hasPalette());
 
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index e0d1664..f36d5d1 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -116,6 +116,7 @@ public:
 
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
+	virtual void clearOSD();
 
 	// PaletteManager interface
 	virtual void setPalette(const byte *colors, uint start, uint num);
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 88fdc09..4a33890 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -2245,6 +2245,38 @@ void SurfaceSdlGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x,
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
 }
+
+void SurfaceSdlGraphicsManager::clearOSD() {
+	assert(_transactionMode == kTransactionNone);	
+
+	Common::StackLock lock(_graphicsMutex);	// Lock the mutex until this function ends
+
+	// Lock the OSD surface for drawing
+	if (SDL_LockSurface(_osdSurface))
+		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
+
+	Graphics::Surface dst;
+	dst.init(_osdSurface->w, _osdSurface->h, _osdSurface->pitch, _osdSurface->pixels,
+		Graphics::PixelFormat(_osdSurface->format->BytesPerPixel,
+			8 - _osdSurface->format->Rloss, 8 - _osdSurface->format->Gloss,
+			8 - _osdSurface->format->Bloss, 8 - _osdSurface->format->Aloss,
+			_osdSurface->format->Rshift, _osdSurface->format->Gshift,
+			_osdSurface->format->Bshift, _osdSurface->format->Ashift));
+
+	// Clear everything with the "transparent" color, i.e. the colorkey
+	SDL_FillRect(_osdSurface, 0, kOSDColorKey);
+
+	// Finished drawing, so unlock the OSD surface again
+	SDL_UnlockSurface(_osdSurface);
+
+	// Init the OSD display parameters, and the fade out
+	_osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
+	_osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
+	SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
+
+	// Ensure a full redraw takes place next time the screen is updated
+	_forceFull = true;
+}
 #endif
 
 bool SurfaceSdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) {
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index a8bafd0..01974cf 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -146,6 +146,7 @@ public:
 #ifdef USE_OSD
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
+	virtual void clearOSD();
 #endif
 
 	// Override from Common::EventObserver
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index 08565dc..6ad80ec 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -245,6 +245,10 @@ void ModularBackend::copyRectToOSD(const void *buf, int pitch, int x, int y, int
 	_graphicsManager->copyRectToOSD(buf, pitch, x, y, w, h);
 }
 
+void ModularBackend::clearOSD() {
+	_graphicsManager->clearOSD();
+}
+
 void ModularBackend::quit() {
 	exit(0);
 }
diff --git a/backends/modular-backend.h b/backends/modular-backend.h
index cf3babf..06c69b5 100644
--- a/backends/modular-backend.h
+++ b/backends/modular-backend.h
@@ -128,6 +128,7 @@ public:
 	virtual void quit();
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
+	virtual void clearOSD();
 
 	//@}
 
diff --git a/common/system.h b/common/system.h
index 64e4b92..6071e45 100644
--- a/common/system.h
+++ b/common/system.h
@@ -1113,6 +1113,12 @@ public:
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) = 0;
 
 	/**
+	* Clears 'on screen display' from everything drawn on it.
+	*/
+
+	virtual void clearOSD() = 0;
+
+	/**
 	 * Return the SaveFileManager, used to store and load savestates
 	 * and other modifiable persistent game data. For more information,
 	 * refer to the SaveFileManager documentation.


Commit: bcb2a5bd8d8f7fc3141f5e8219c47e0342c4692e
    https://github.com/scummvm/scummvm/commit/bcb2a5bd8d8f7fc3141f5e8219c47e0342c4692e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CloudIcon to use clearOSD()

Now only icon is shown.

Changed paths:
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 477e864..e8c9259 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -27,23 +27,27 @@
 
 namespace Networking {
 
-CloudIcon::CloudIcon(): _frame(0) {}
+CloudIcon::CloudIcon(): _frame(0), _wasVisible(false) {}
 
 CloudIcon::~CloudIcon() {}
 
 void CloudIcon::draw() {	 
-	Cloud::Storage *storage = CloudMan.getCurrentStorage();
-	bool working = (storage && storage->isWorking());
-	if (working) {
-		//buf = animation frame;
+	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
+	if (storage && storage->isWorking()) {
 		if (g_system) {
+			if (!_wasVisible) {
+				g_system->clearOSD();
+				_wasVisible = true;
+			}
+
 			const Graphics::Surface *s = g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall);
-			int x = 10, y = 10;
+			int x = g_system->getOverlayWidth() - s->w - 10, y = 10;
 			g_system->copyRectToOSD(s->getPixels(), s->pitch, x, y, s->w, s->h);
+		} else {
+			_wasVisible = false;
 		}
 	} else {
-		//buf = empty;
-		//TODO: put empty piece to OSD?
+		_wasVisible = false;
 	}
 }
 
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index dc28c82..24c1fd6 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -27,6 +27,7 @@ namespace Networking {
 
 class CloudIcon {
 	int _frame;
+	bool _wasVisible;
 
 public:
 	CloudIcon();


Commit: b32c2be78dfa88cdb8bb90174fe6fef8757ae85a
    https://github.com/scummvm/scummvm/commit/b32c2be78dfa88cdb8bb90174fe6fef8757ae85a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix ConnectionManager a little

I didn't like how FINISHED Requests were waiting until the next
interateRequests() call to be removed when we could easily remove those
after they changed their state in their handle().

Changed paths:
    backends/networking/curl/connectionmanager.cpp



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index c044a15..57ea2ce 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -109,18 +109,18 @@ void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
 	debug("handling %d request(s)", _requests.size());	
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
-		Request *request = i->request;
+		Request *request = i->request;		
+		if (request) {
+			if (request->state() == PROCESSING) request->handle();
+			else if (request->state() == RETRY) request->handleRetry();
+		}
+
 		if (!request || request->state() == FINISHED) {
 			delete (i->request);
 			if (i->onDeleteCallback) (*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
 			_requests.erase(i);
 			continue;
 		}
-		
-		if (request) {
-			if (request->state() == PROCESSING) request->handle();
-			else if (request->state() == RETRY) request->handleRetry();
-		}
 
 		++i;		
 	}


Commit: 1b9987ddc9ff7a69d0de5bbbbd4aee42fff08b42
    https://github.com/scummvm/scummvm/commit/1b9987ddc9ff7a69d0de5bbbbd4aee42fff08b42
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add getOSDFormat() and make OSD 32 bpp

Changed paths:
    backends/base-backend.cpp
    backends/base-backend.h
    backends/graphics/graphics.h
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/modular-backend.h
    common/system.h



diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp
index 59d6744..dfb9e28 100644
--- a/backends/base-backend.cpp
+++ b/backends/base-backend.cpp
@@ -48,6 +48,11 @@ void BaseBackend::clearOSD() {
 	//what should I do? Remove all TimedMessageDialogs?
 }
 
+Graphics::PixelFormat BaseBackend::getOSDFormat() {
+	warning("BaseBackend::getOSDFormat not implemented");
+	return Graphics::PixelFormat();
+}
+
 void BaseBackend::initBackend() {
 	// Init Event manager
 #ifndef DISABLE_DEFAULT_EVENT_MANAGER
diff --git a/backends/base-backend.h b/backends/base-backend.h
index edee427..2394eda 100644
--- a/backends/base-backend.h
+++ b/backends/base-backend.h
@@ -35,6 +35,7 @@ public:
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 	virtual void clearOSD();
+	virtual Graphics::PixelFormat getOSDFormat();
 	virtual void fillScreen(uint32 col);
 };
 
diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h
index 1063a10..921dfca 100644
--- a/backends/graphics/graphics.h
+++ b/backends/graphics/graphics.h
@@ -86,6 +86,8 @@ public:
 	virtual void displayMessageOnOSD(const char *msg) {}
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) {}
 	virtual void clearOSD() {}
+	virtual Graphics::PixelFormat getOSDFormat() { return Graphics::PixelFormat(); }
+
 
 	// Graphics::PaletteManager interface
 	//virtual void setPalette(const byte *colors, uint start, uint num) = 0;
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 97788be..c491b03 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -774,6 +774,8 @@ void OpenGLGraphicsManager::clearOSD() {
 #endif
 }
 
+Graphics::PixelFormat OpenGLGraphicsManager::getOSDFormat() { return Graphics::PixelFormat(); } //TODO
+
 void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
 	assert(_gameScreen->hasPalette());
 
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index f36d5d1..55d2c5c 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -117,6 +117,7 @@ public:
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 	virtual void clearOSD();
+	virtual Graphics::PixelFormat getOSDFormat();
 
 	// PaletteManager interface
 	virtual void setPalette(const byte *colors, uint start, uint num);
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 4a33890..85e3983 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -883,13 +883,26 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 	_osdSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA,
 						_hwscreen->w,
 						_hwscreen->h,
-						16,
-						_hwscreen->format->Rmask,
-						_hwscreen->format->Gmask,
-						_hwscreen->format->Bmask,
-						_hwscreen->format->Amask);
+						32,
+						0xFF000000,
+						0x00FF0000,
+						0x0000FF00,
+						0x000000FF
+	);
 	if (_osdSurface == NULL)
 		error("allocating _osdSurface failed");
+
+	_osdFormat.bytesPerPixel = _osdSurface->format->BytesPerPixel;
+
+	_osdFormat.rLoss = _osdSurface->format->Rloss;
+	_osdFormat.gLoss = _osdSurface->format->Gloss;
+	_osdFormat.bLoss = _osdSurface->format->Bloss;
+	_osdFormat.aLoss = _osdSurface->format->Aloss;
+
+	_osdFormat.rShift = _osdSurface->format->Rshift;
+	_osdFormat.gShift = _osdSurface->format->Gshift;
+	_osdFormat.bShift = _osdSurface->format->Bshift;
+	_osdFormat.aShift = _osdSurface->format->Ashift;
 	SDL_SetColorKey(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kOSDColorKey);
 #endif
 
@@ -2277,6 +2290,10 @@ void SurfaceSdlGraphicsManager::clearOSD() {
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
 }
+
+Graphics::PixelFormat SurfaceSdlGraphicsManager::getOSDFormat() {
+	return _osdFormat;
+}
 #endif
 
 bool SurfaceSdlGraphicsManager::handleScalerHotkeys(Common::KeyCode key) {
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 01974cf..ff721ea 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -147,6 +147,7 @@ public:
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 	virtual void clearOSD();
+	virtual Graphics::PixelFormat getOSDFormat();	
 #endif
 
 	// Override from Common::EventObserver
@@ -175,6 +176,8 @@ protected:
 		kOSDColorKey = 1,				/** < Transparent color key */
 		kOSDInitialAlpha = 80			/** < Initial alpha level, in percent */
 	};
+	/** OSD pixel format */
+	Graphics::PixelFormat _osdFormat;
 #endif
 
 	/** Hardware screen */
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index 6ad80ec..e1bdf15 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -249,6 +249,10 @@ void ModularBackend::clearOSD() {
 	_graphicsManager->clearOSD();
 }
 
+Graphics::PixelFormat ModularBackend::getOSDFormat() {
+	return _graphicsManager->getOSDFormat();
+}
+
 void ModularBackend::quit() {
 	exit(0);
 }
diff --git a/backends/modular-backend.h b/backends/modular-backend.h
index 06c69b5..9cde279 100644
--- a/backends/modular-backend.h
+++ b/backends/modular-backend.h
@@ -129,6 +129,7 @@ public:
 	virtual void displayMessageOnOSD(const char *msg);
 	virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h);
 	virtual void clearOSD();
+	virtual Graphics::PixelFormat getOSDFormat();
 
 	//@}
 
diff --git a/common/system.h b/common/system.h
index 6071e45..3cbeee7 100644
--- a/common/system.h
+++ b/common/system.h
@@ -1119,6 +1119,12 @@ public:
 	virtual void clearOSD() = 0;
 
 	/**
+	* Returns 'on screen display' pixel format.
+	*/
+
+	virtual Graphics::PixelFormat getOSDFormat() = 0;
+
+	/**
 	 * Return the SaveFileManager, used to store and load savestates
 	 * and other modifiable persistent game data. For more information,
 	 * refer to the SaveFileManager documentation.


Commit: 4874fafb153a21de5e6ab573c50de7fbf3c46729
    https://github.com/scummvm/scummvm/commit/4874fafb153a21de5e6ab573c50de7fbf3c46729
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CloudIcon

Now it loads the surface once.

Changed paths:
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index e8c9259..ec89c12 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -24,14 +24,48 @@
 #include "backends/cloud/cloudmanager.h"
 #include "gui/ThemeEngine.h"
 #include "gui/gui-manager.h"
+#include "image/png.h"
+#include <common/file.h>
+#include <backends/graphics/surfacesdl/surfacesdl-graphics.h>
 
 namespace Networking {
 
-CloudIcon::CloudIcon(): _frame(0), _wasVisible(false) {}
+CloudIcon::CloudIcon(): _frame(0), _wasVisible(false), _iconsInited(false) {
+	initIcons();
+}
 
 CloudIcon::~CloudIcon() {}
 
-void CloudIcon::draw() {	 
+void CloudIcon::initIcons() {
+	if (_iconsInited) return;
+
+	Image::PNGDecoder decoder;
+	Common::ArchiveMemberList members;
+	Common::File file;
+	if (!file.open("cloudicon.png")) warning("failed");
+	Common::SeekableReadStream *stream = &file;	
+	if (stream) {			
+		if (!decoder.loadStream(*stream))
+			error("Error decoding PNG");
+
+		Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
+		if (s) {
+			Graphics::PixelFormat f = g_system->getOSDFormat();
+			//f.bytesPerPixel = 4;
+			debug("%d in osd vs %d in s", f.bytesPerPixel, s->format.bytesPerPixel);
+			Graphics::TransparentSurface *s2 = s->convertTo(f);
+			if (s2) _icon.copyFrom(*s2);
+			else warning("failed converting");
+		}
+		else warning("failed reading");
+	}
+	_iconsInited = true;
+}
+
+void CloudIcon::draw() {
+	initIcons();
+	_frame++;
+
 	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
 	if (storage && storage->isWorking()) {
 		if (g_system) {
@@ -39,16 +73,19 @@ void CloudIcon::draw() {
 				g_system->clearOSD();
 				_wasVisible = true;
 			}
-
-			const Graphics::Surface *s = g_gui.theme()->getImageSurface(GUI::ThemeEngine::kImageLogoSmall);
-			int x = g_system->getOverlayWidth() - s->w - 10, y = 10;
-			g_system->copyRectToOSD(s->getPixels(), s->pitch, x, y, s->w, s->h);
 		} else {
 			_wasVisible = false;
 		}
 	} else {
 		_wasVisible = false;
 	}
+
+	if (g_system) {
+		if (_icon.getPixels()) {
+			int x = g_system->getOverlayWidth() - _icon.w - 10, y = 10;
+			g_system->copyRectToOSD(_icon.getPixels(), _icon.pitch, x, y, _icon.w, _icon.h);
+		}
+	}
 }
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index 24c1fd6..7cecf3a 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -23,11 +23,16 @@
 #ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
 #define BACKENDS_NETWORKING_CURL_CLOUDICON_H
 
+#include "graphics/transparent_surface.h"
+
 namespace Networking {
 
 class CloudIcon {
 	int _frame;
-	bool _wasVisible;
+	bool _wasVisible, _iconsInited;
+	Graphics::TransparentSurface _icon;
+
+	void initIcons();
 
 public:
 	CloudIcon();


Commit: de84701aead489de944db078e6b61c2584708c53
    https://github.com/scummvm/scummvm/commit/de84701aead489de944db078e6b61c2584708c53
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Separate OSD message alpha from OSD surface

Now OSD is always drawn.

Changed paths:
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h



diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 85e3983..b76b2c7 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -121,7 +121,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
 	:
 	SdlGraphicsManager(sdlEventSource, window),
 #ifdef USE_OSD
-	_osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0),
+	_osdSurface(0), _osdMessageSurface(nullptr), _osdMessageAlpha(SDL_ALPHA_TRANSPARENT), _osdMessageFadeStartTime(0),
 #endif
 	_hwscreen(0),
 #if SDL_VERSION_ATLEAST(2, 0, 0)
@@ -892,17 +892,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 	if (_osdSurface == NULL)
 		error("allocating _osdSurface failed");
 
-	_osdFormat.bytesPerPixel = _osdSurface->format->BytesPerPixel;
-
-	_osdFormat.rLoss = _osdSurface->format->Rloss;
-	_osdFormat.gLoss = _osdSurface->format->Gloss;
-	_osdFormat.bLoss = _osdSurface->format->Bloss;
-	_osdFormat.aLoss = _osdSurface->format->Aloss;
-
-	_osdFormat.rShift = _osdSurface->format->Rshift;
-	_osdFormat.gShift = _osdSurface->format->Gshift;
-	_osdFormat.bShift = _osdSurface->format->Bshift;
-	_osdFormat.aShift = _osdSurface->format->Ashift;
+	_osdFormat = getSurfaceFormat(_osdSurface);
 	SDL_SetColorKey(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, kOSDColorKey);
 #endif
 
@@ -959,6 +949,11 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
 		SDL_FreeSurface(_osdSurface);
 		_osdSurface = NULL;
 	}
+
+	if (_osdMessageSurface) {
+		SDL_FreeSurface(_osdMessageSurface);
+		_osdMessageSurface = NULL;
+	}
 #endif
 	DestroyScalers();
 
@@ -1072,21 +1067,33 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 
 #ifdef USE_OSD
 	// OSD visible (i.e. non-transparent)?
-	if (_osdAlpha != SDL_ALPHA_TRANSPARENT) {
+	if (_osdMessageAlpha != SDL_ALPHA_TRANSPARENT) {
 		// Updated alpha value
-		const int diff = SDL_GetTicks() - _osdFadeStartTime;
+		const int diff = SDL_GetTicks() - _osdMessageFadeStartTime;
 		if (diff > 0) {
 			if (diff >= kOSDFadeOutDuration) {
 				// Back to full transparency
-				_osdAlpha = SDL_ALPHA_TRANSPARENT;
+				_osdMessageAlpha = SDL_ALPHA_TRANSPARENT;
 			} else {
 				// Do a linear fade out...
 				const int startAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
-				_osdAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration;
+				_osdMessageAlpha = startAlpha + diff * (SDL_ALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration;
 			}
-			SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
 			_forceFull = true;
 		}
+
+		if (_osdMessageAlpha == SDL_ALPHA_TRANSPARENT) {
+			removeOSDMessage();
+		} else {
+			if (_osdMessageSurface && _osdSurface) {
+				SDL_Rect dstRect;
+				dstRect.x = (_osdSurface->w - _osdMessageSurface->w) / 2;
+				dstRect.y = (_osdSurface->h - _osdMessageSurface->h) / 2;
+				dstRect.w = _osdMessageSurface->w;
+				dstRect.h = _osdMessageSurface->h;
+				blitOSDMessage(dstRect);				
+			}
+		}
 	}
 #endif
 
@@ -1192,9 +1199,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 		drawMouse();
 
 #ifdef USE_OSD
-		if (_osdAlpha != SDL_ALPHA_TRANSPARENT) {
-			SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0);
-		}
+		SDL_BlitSurface(_osdSurface, 0, _hwscreen, 0);
 #endif
 
 #ifdef USE_SDL_DEBUG_FOCUSRECT
@@ -2134,26 +2139,11 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const char *msg) {
 
 	Common::StackLock lock(_graphicsMutex);	// Lock the mutex until this function ends
 
-	uint i;
-
-	// Lock the OSD surface for drawing
-	if (SDL_LockSurface(_osdSurface))
-		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
-
-	Graphics::Surface dst;
-	dst.init(_osdSurface->w, _osdSurface->h, _osdSurface->pitch, _osdSurface->pixels,
-	         Graphics::PixelFormat(_osdSurface->format->BytesPerPixel,
-	                               8 - _osdSurface->format->Rloss, 8 - _osdSurface->format->Gloss,
-	                               8 - _osdSurface->format->Bloss, 8 - _osdSurface->format->Aloss,
-	                               _osdSurface->format->Rshift, _osdSurface->format->Gshift,
-	                               _osdSurface->format->Bshift, _osdSurface->format->Ashift));
+	removeOSDMessage();
 
 	// The font we are going to use:
 	const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
 
-	// Clear everything with the "transparent" color, i.e. the colorkey
-	SDL_FillRect(_osdSurface, 0, kOSDColorKey);
-
 	// Split the message into separate lines.
 	Common::Array<Common::String> lines;
 	const char *ptr;
@@ -2172,40 +2162,52 @@ void SurfaceSdlGraphicsManager::displayMessageOnOSD(const char *msg) {
 	const int lineHeight = font->getFontHeight() + 2 * lineSpacing;
 	int width = 0;
 	int height = lineHeight * lines.size() + 2 * vOffset;
+	uint i;
 	for (i = 0; i < lines.size(); i++) {
 		width = MAX(width, font->getStringWidth(lines[i]) + 14);
 	}
 
 	// Clip the rect
-	if (width > dst.w)
-		width = dst.w;
-	if (height > dst.h)
-		height = dst.h;
+	if (width > _osdSurface->w)
+		width = _osdSurface->w;
+	if (height > _osdSurface->h)
+		height = _osdSurface->h;
+
+	_osdMessageSurface = SDL_CreateRGBSurface(
+		SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA,
+		width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
+	);
+	
+	// Lock the surface
+	if (SDL_LockSurface(_osdMessageSurface))
+		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
 
 	// Draw a dark gray rect
-	// TODO: Rounded corners ? Border?
-	SDL_Rect osdRect;
-	osdRect.x = (dst.w - width) / 2;
-	osdRect.y = (dst.h - height) / 2;
-	osdRect.w = width;
-	osdRect.h = height;
-	SDL_FillRect(_osdSurface, &osdRect, SDL_MapRGB(_osdSurface->format, 64, 64, 64));
+	// TODO: Rounded corners ? Border?	
+	SDL_FillRect(_osdMessageSurface, nullptr, SDL_MapRGB(_osdMessageSurface->format, 64, 64, 64));
+
+	Graphics::Surface dst;
+	dst.init(_osdMessageSurface->w, _osdMessageSurface->h, _osdMessageSurface->pitch, _osdMessageSurface->pixels,
+		Graphics::PixelFormat(_osdMessageSurface->format->BytesPerPixel,
+			8 - _osdMessageSurface->format->Rloss, 8 - _osdMessageSurface->format->Gloss,
+			8 - _osdMessageSurface->format->Bloss, 8 - _osdMessageSurface->format->Aloss,
+			_osdMessageSurface->format->Rshift, _osdMessageSurface->format->Gshift,
+			_osdMessageSurface->format->Bshift, _osdMessageSurface->format->Ashift));
 
 	// Render the message, centered, and in white
 	for (i = 0; i < lines.size(); i++) {
 		font->drawString(&dst, lines[i],
-							osdRect.x, osdRect.y + i * lineHeight + vOffset + lineSpacing, osdRect.w,
-							SDL_MapRGB(_osdSurface->format, 255, 255, 255),
-							Graphics::kTextAlignCenter);
+			0, 0 + i * lineHeight + vOffset + lineSpacing, width,
+			SDL_MapRGB(_osdMessageSurface->format, 255, 255, 255),
+			Graphics::kTextAlignCenter);
 	}
 
-	// Finished drawing, so unlock the OSD surface again
-	SDL_UnlockSurface(_osdSurface);
+	// Finished drawing, so unlock the OSD message surface
+	SDL_UnlockSurface(_osdMessageSurface);
 
 	// Init the OSD display parameters, and the fade out
-	_osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
-	_osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
-	SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
+	_osdMessageAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
+	_osdMessageFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
 
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
@@ -2250,11 +2252,6 @@ void SurfaceSdlGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x,
 	// Finished drawing, so unlock the OSD surface again
 	SDL_UnlockSurface(_osdSurface);
 
-	// Init the OSD display parameters, and the fade out
-	_osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
-	_osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
-	SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
-
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
 }
@@ -2268,29 +2265,78 @@ void SurfaceSdlGraphicsManager::clearOSD() {
 	if (SDL_LockSurface(_osdSurface))
 		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
 
-	Graphics::Surface dst;
-	dst.init(_osdSurface->w, _osdSurface->h, _osdSurface->pitch, _osdSurface->pixels,
-		Graphics::PixelFormat(_osdSurface->format->BytesPerPixel,
-			8 - _osdSurface->format->Rloss, 8 - _osdSurface->format->Gloss,
-			8 - _osdSurface->format->Bloss, 8 - _osdSurface->format->Aloss,
-			_osdSurface->format->Rshift, _osdSurface->format->Gshift,
-			_osdSurface->format->Bshift, _osdSurface->format->Ashift));
-
 	// Clear everything with the "transparent" color, i.e. the colorkey
 	SDL_FillRect(_osdSurface, 0, kOSDColorKey);
 
 	// Finished drawing, so unlock the OSD surface again
 	SDL_UnlockSurface(_osdSurface);
 
-	// Init the OSD display parameters, and the fade out
-	_osdAlpha = SDL_ALPHA_TRANSPARENT + kOSDInitialAlpha * (SDL_ALPHA_OPAQUE - SDL_ALPHA_TRANSPARENT) / 100;
-	_osdFadeStartTime = SDL_GetTicks() + kOSDFadeOutDelay;
-	SDL_SetAlpha(_osdSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, _osdAlpha);
+	// Remove OSD message as well
+	removeOSDMessage();
 
 	// Ensure a full redraw takes place next time the screen is updated
 	_forceFull = true;
 }
 
+void SurfaceSdlGraphicsManager::removeOSDMessage() {
+	// Lock the OSD surface for drawing
+	if (SDL_LockSurface(_osdSurface))
+		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
+
+	//remove previous message
+	if (_osdMessageSurface) {
+		SDL_Rect osdRect;
+		osdRect.x = (_osdSurface->w - _osdMessageSurface->w) / 2;
+		osdRect.y = (_osdSurface->h - _osdMessageSurface->h) / 2;
+		osdRect.w = _osdMessageSurface->w;
+		osdRect.h = _osdMessageSurface->h;
+		SDL_FillRect(_osdSurface, &osdRect, kOSDColorKey);
+		SDL_FreeSurface(_osdMessageSurface);		
+	}
+
+	_osdMessageSurface = NULL;
+	_osdMessageAlpha = SDL_ALPHA_TRANSPARENT;
+
+	// Finished drawing, so unlock the OSD surface again
+	SDL_UnlockSurface(_osdSurface);
+}
+
+void SurfaceSdlGraphicsManager::blitOSDMessage(SDL_Rect dstRect) {
+	SDL_Surface *src = _osdMessageSurface;
+	SDL_Surface *dst = _osdSurface;
+	Graphics::PixelFormat srcFormat = getSurfaceFormat(src);
+	Graphics::PixelFormat dstFormat = _osdFormat;
+	for (int y = 0; y < dstRect.h; y++) {
+		const byte *srcRow = (const byte *)((const byte *)(src->pixels) + y * src->pitch); //src (x, y) == (0, 0)
+		byte *dstRow = (byte *)((const byte *)(dst->pixels) + (dstRect.y + y) * dst->pitch + dstRect.x * dstFormat.bytesPerPixel);
+
+		for (int x = 0; x < dstRect.w; x++) {
+			uint32 srcColor;
+			if (dst->format->BytesPerPixel == 2)
+				srcColor = READ_UINT16(srcRow);
+			else if (dst->format->BytesPerPixel == 3)
+				srcColor = READ_UINT24(srcRow);
+			else
+				srcColor = READ_UINT32(srcRow);
+
+			srcRow += srcFormat.bytesPerPixel;
+
+			// Convert that color to the new format
+			byte r, g, b, a;
+			srcFormat.colorToARGB(srcColor, a, r, g, b);
+			a = _osdMessageAlpha; //this is the important line, because apart from that this is plain surface copying
+			uint32 color = dstFormat.ARGBToColor(a, r, g, b);
+
+			if (dstFormat.bytesPerPixel == 2)
+				*((uint16 *)dstRow) = color;
+			else
+				*((uint32 *)dstRow) = color;
+
+			dstRow += dstFormat.bytesPerPixel;
+		}
+	}
+}
+
 Graphics::PixelFormat SurfaceSdlGraphicsManager::getOSDFormat() {
 	return _osdFormat;
 }
@@ -2589,4 +2635,22 @@ void SurfaceSdlGraphicsManager::SDL_UpdateRects(SDL_Surface *screen, int numrect
 }
 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
 
+Graphics::PixelFormat SurfaceSdlGraphicsManager::getSurfaceFormat(SDL_Surface *surface) {
+	Graphics::PixelFormat format;
+	if (surface) {
+		format.bytesPerPixel = surface->format->BytesPerPixel;
+
+		format.rLoss = surface->format->Rloss;
+		format.gLoss = surface->format->Gloss;
+		format.bLoss = surface->format->Bloss;
+		format.aLoss = surface->format->Aloss;
+
+		format.rShift = surface->format->Rshift;
+		format.gShift = surface->format->Gshift;
+		format.bShift = surface->format->Bshift;
+		format.aShift = surface->format->Ashift;
+	}
+	return format;
+}
+
 #endif
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index ff721ea..d8f826a 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -163,12 +163,14 @@ public:
 
 protected:
 #ifdef USE_OSD
-	/** Surface containing the OSD message */
+	/** Surface containing the OSD */
 	SDL_Surface *_osdSurface;
-	/** Transparency level of the OSD */
-	uint8 _osdAlpha;
+	/** Surface containing the OSD message */
+	SDL_Surface *_osdMessageSurface;
+	/** Transparency level of the OSD message */
+	uint8 _osdMessageAlpha;
 	/** When to start the fade out */
-	uint32 _osdFadeStartTime;
+	uint32 _osdMessageFadeStartTime;
 	/** Enum with OSD options */
 	enum {
 		kOSDFadeOutDelay = 2 * 1000,	/** < Delay before the OSD is faded out (in milliseconds) */
@@ -178,6 +180,9 @@ protected:
 	};
 	/** OSD pixel format */
 	Graphics::PixelFormat _osdFormat;
+
+	void removeOSDMessage();
+	void blitOSDMessage(SDL_Rect dstRect);
 #endif
 
 	/** Hardware screen */
@@ -363,6 +368,8 @@ protected:
 	Common::Rect _focusRect;
 #endif
 
+	static Graphics::PixelFormat getSurfaceFormat(SDL_Surface *surface);
+
 	virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false);
 
 	virtual void drawMouse();


Commit: 135f7d09a8ea790df37bff676682163732b1f6ad
    https://github.com/scummvm/scummvm/commit/135f7d09a8ea790df37bff676682163732b1f6ad
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make CloudIcon pulsate, fade in and fade out

That required ConnMan's timer stopping. Would be fixed in the next
commit.

Changed paths:
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h
    backends/networking/curl/connectionmanager.cpp



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index ec89c12..d79eef6 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -30,7 +30,11 @@
 
 namespace Networking {
 
-CloudIcon::CloudIcon(): _frame(0), _wasVisible(false), _iconsInited(false) {
+const float CloudIcon::ALPHA_STEP = 0.03;
+const float CloudIcon::ALPHA_MAX = 1;
+const float CloudIcon::ALPHA_MIN = 0.5;
+
+CloudIcon::CloudIcon(): _frame(0), _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
 	initIcons();
 }
 
@@ -51,17 +55,52 @@ void CloudIcon::initIcons() {
 		Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
 		if (s) {
 			Graphics::PixelFormat f = g_system->getOSDFormat();
-			//f.bytesPerPixel = 4;
-			debug("%d in osd vs %d in s", f.bytesPerPixel, s->format.bytesPerPixel);
-			Graphics::TransparentSurface *s2 = s->convertTo(f);
-			if (s2) _icon.copyFrom(*s2);
-			else warning("failed converting");
+			if (f != s->format) {
+				Graphics::TransparentSurface *s2 = s->convertTo(f);
+				if (s2) _icon.copyFrom(*s2);
+				else warning("failed converting");
+				delete s2;
+			} else {
+				_icon.copyFrom(*s);
+			}
+			delete s;
 		}
 		else warning("failed reading");
 	}
 	_iconsInited = true;
 }
 
+void CloudIcon::makeAlphaIcon(float alpha) {
+	_alphaIcon.copyFrom(_icon);
+
+	byte *pixels = (byte *)_alphaIcon.getPixels();
+	for (int y = 0; y < _alphaIcon.h; y++) {
+		byte *row = pixels + y * _alphaIcon.pitch;
+		for (int x = 0; x < _alphaIcon.w; x++) {
+			uint32 srcColor;
+			if (_alphaIcon.format.bytesPerPixel == 2)
+				srcColor = READ_UINT16(row);
+			else if (_alphaIcon.format.bytesPerPixel == 3)
+				srcColor = READ_UINT24(row);
+			else
+				srcColor = READ_UINT32(row);
+
+			// Update color's alpha
+			byte r, g, b, a;
+			_alphaIcon.format.colorToARGB(srcColor, a, r, g, b);
+			a = (byte)(a * alpha);
+			uint32 color = _alphaIcon.format.ARGBToColor(a, r, g, b);
+
+			if (_alphaIcon.format.bytesPerPixel == 2)
+				*((uint16 *)row) = color;
+			else
+				*((uint32 *)row) = color;
+
+			row += _alphaIcon.format.bytesPerPixel;
+		}
+	}
+}
+
 void CloudIcon::draw() {
 	initIcons();
 	_frame++;
@@ -73,17 +112,35 @@ void CloudIcon::draw() {
 				g_system->clearOSD();
 				_wasVisible = true;
 			}
+			if (_alphaRising) {
+				_currentAlpha += ALPHA_STEP;
+				if (_currentAlpha > ALPHA_MAX) {
+					_currentAlpha = ALPHA_MAX;
+					_alphaRising = false;
+				}
+			} else {
+				_currentAlpha -= ALPHA_STEP;
+				if (_currentAlpha < ALPHA_MIN) {
+					_currentAlpha = ALPHA_MIN;
+					_alphaRising = true;
+				}
+			}
 		} else {
 			_wasVisible = false;
 		}
 	} else {
 		_wasVisible = false;
+		_currentAlpha -= 3 * ALPHA_STEP;
+		if (_currentAlpha <= 0) _currentAlpha = 0;
 	}
 
 	if (g_system) {
-		if (_icon.getPixels()) {
-			int x = g_system->getOverlayWidth() - _icon.w - 10, y = 10;
-			g_system->copyRectToOSD(_icon.getPixels(), _icon.pitch, x, y, _icon.w, _icon.h);
+		Graphics::TransparentSurface *surface = &_icon;
+		makeAlphaIcon(_currentAlpha);
+		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
+		if (surface && surface->getPixels()) {
+			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
+			g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
 		}
 	}
 }
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index 7cecf3a..9419cf0 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -28,11 +28,16 @@
 namespace Networking {
 
 class CloudIcon {
+	static const float ALPHA_STEP, ALPHA_MAX, ALPHA_MIN;
+
 	int _frame;
 	bool _wasVisible, _iconsInited;
-	Graphics::TransparentSurface _icon;
+	Graphics::TransparentSurface _icon, _alphaIcon;
+	float _currentAlpha;
+	bool _alphaRising;
 
 	void initIcons();
+	void makeAlphaIcon(float alpha);
 
 public:
 	CloudIcon();
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 57ea2ce..af8432d 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -87,6 +87,7 @@ void ConnectionManager::startTimer(int interval) {
 }
 
 void ConnectionManager::stopTimer() {
+	return;
 	debug("timer stopped");
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(connectionsThread);


Commit: cec93e2c0319f92c31cbeea2a9e5c5763a71a7dc
    https://github.com/scummvm/scummvm/commit/cec93e2c0319f92c31cbeea2a9e5c5763a71a7dc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make CloudIcon switch ConnMan's timer off

CloudIcon is now a Request which is automatically added once first
Request is added to ConnMan. When icon decides it should disappear, it
gets FINISHED, so ConnMan would switch off the timer if it was the last
Request.

Changed paths:
  A cloudicon.png
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index d79eef6..3304c71 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -34,12 +34,65 @@ const float CloudIcon::ALPHA_STEP = 0.03;
 const float CloudIcon::ALPHA_MAX = 1;
 const float CloudIcon::ALPHA_MIN = 0.5;
 
-CloudIcon::CloudIcon(): _frame(0), _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
+CloudIcon::CloudIcon(): Request(nullptr, nullptr), _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
 	initIcons();
 }
 
 CloudIcon::~CloudIcon() {}
 
+void CloudIcon::draw() {
+	initIcons();
+
+	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
+	if (storage && storage->isWorking()) {
+		if (g_system) {
+			if (!_wasVisible) {
+				g_system->clearOSD();
+				_wasVisible = true;
+			}
+			if (_alphaRising) {
+				_currentAlpha += ALPHA_STEP;
+				if (_currentAlpha > ALPHA_MAX) {
+					_currentAlpha = ALPHA_MAX;
+					_alphaRising = false;
+				}
+			} else {
+				_currentAlpha -= ALPHA_STEP;
+				if (_currentAlpha < ALPHA_MIN) {
+					_currentAlpha = ALPHA_MIN;
+					_alphaRising = true;
+				}
+			}
+		} else {
+			_wasVisible = false;
+		}
+	} else {
+		_wasVisible = false;
+		_currentAlpha -= 3 * ALPHA_STEP;
+		if (_currentAlpha <= 0) {
+			_currentAlpha = 0;
+			finish();
+		}
+	}
+
+	if (g_system) {
+		Graphics::TransparentSurface *surface = &_icon;
+		makeAlphaIcon(_currentAlpha);
+		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
+		if (surface && surface->getPixels()) {
+			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
+			g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
+		}
+	}
+}
+
+void CloudIcon::handle() {}
+
+void CloudIcon::restart() {
+	_currentAlpha = 0;
+	_alphaRising = true;
+}
+
 void CloudIcon::initIcons() {
 	if (_iconsInited) return;
 
@@ -47,8 +100,8 @@ void CloudIcon::initIcons() {
 	Common::ArchiveMemberList members;
 	Common::File file;
 	if (!file.open("cloudicon.png")) warning("failed");
-	Common::SeekableReadStream *stream = &file;	
-	if (stream) {			
+	Common::SeekableReadStream *stream = &file;
+	if (stream) {
 		if (!decoder.loadStream(*stream))
 			error("Error decoding PNG");
 
@@ -64,8 +117,7 @@ void CloudIcon::initIcons() {
 				_icon.copyFrom(*s);
 			}
 			delete s;
-		}
-		else warning("failed reading");
+		} else warning("failed reading");
 	}
 	_iconsInited = true;
 }
@@ -101,48 +153,4 @@ void CloudIcon::makeAlphaIcon(float alpha) {
 	}
 }
 
-void CloudIcon::draw() {
-	initIcons();
-	_frame++;
-
-	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
-	if (storage && storage->isWorking()) {
-		if (g_system) {
-			if (!_wasVisible) {
-				g_system->clearOSD();
-				_wasVisible = true;
-			}
-			if (_alphaRising) {
-				_currentAlpha += ALPHA_STEP;
-				if (_currentAlpha > ALPHA_MAX) {
-					_currentAlpha = ALPHA_MAX;
-					_alphaRising = false;
-				}
-			} else {
-				_currentAlpha -= ALPHA_STEP;
-				if (_currentAlpha < ALPHA_MIN) {
-					_currentAlpha = ALPHA_MIN;
-					_alphaRising = true;
-				}
-			}
-		} else {
-			_wasVisible = false;
-		}
-	} else {
-		_wasVisible = false;
-		_currentAlpha -= 3 * ALPHA_STEP;
-		if (_currentAlpha <= 0) _currentAlpha = 0;
-	}
-
-	if (g_system) {
-		Graphics::TransparentSurface *surface = &_icon;
-		makeAlphaIcon(_currentAlpha);
-		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
-		if (surface && surface->getPixels()) {
-			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
-			g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
-		}
-	}
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index 9419cf0..0210899 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -23,14 +23,14 @@
 #ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
 #define BACKENDS_NETWORKING_CURL_CLOUDICON_H
 
+#include "backends/networking/curl/request.h"
 #include "graphics/transparent_surface.h"
 
 namespace Networking {
 
-class CloudIcon {
+class CloudIcon: public Request {
 	static const float ALPHA_STEP, ALPHA_MAX, ALPHA_MIN;
 
-	int _frame;
 	bool _wasVisible, _iconsInited;
 	Graphics::TransparentSurface _icon, _alphaIcon;
 	float _currentAlpha;
@@ -44,6 +44,8 @@ public:
 	~CloudIcon();
 
 	void draw();
+	virtual void handle();
+	virtual void restart();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index af8432d..95b38df 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -37,7 +37,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0), _icon(nullptr) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
@@ -84,14 +84,18 @@ void ConnectionManager::startTimer(int interval) {
 	} else {
 		warning("Failed to install Networking::ConnectionManager's timer");
 	}
+	if (_timerStarted && !_icon) {
+		_icon = new CloudIcon();
+		addRequest(_icon, new Common::Callback<ConnectionManager, Request *>(this, &ConnectionManager::cloudIconDeleted));
+	}
 }
 
 void ConnectionManager::stopTimer() {
-	return;
 	debug("timer stopped");
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(connectionsThread);
 	_timerStarted = false;
+	if (_icon) _icon->finish();
 }
 
 void ConnectionManager::handle() {
@@ -103,7 +107,7 @@ void ConnectionManager::handle() {
 	_handleMutex.unlock();
 
 	//icon redrawing is doesn't require any mutex, but must be done after requests are iterated
-	_icon.draw();
+	if (_icon) _icon->draw();
 }
 
 void ConnectionManager::interateRequests() {
@@ -154,4 +158,8 @@ void ConnectionManager::processTransfers() {
 	}
 }
 
+void ConnectionManager::cloudIconDeleted(Request *icon) {
+	if (_icon == icon) _icon = nullptr;
+}
+
 } // End of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 3a2e974..925312b 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -79,7 +79,7 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests;
 	Common::Mutex _handleMutex;
-	CloudIcon _icon;
+	CloudIcon *_icon;
 	uint32 _frame;
 	
 	void startTimer(int interval = TIMER_INTERVAL);
@@ -87,6 +87,7 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	void handle();
 	void interateRequests();
 	void processTransfers();
+	void cloudIconDeleted(Request *icon);
 
 public:
 	ConnectionManager();
diff --git a/cloudicon.png b/cloudicon.png
new file mode 100644
index 0000000..5f121a8
Binary files /dev/null and b/cloudicon.png differ


Commit: 45e83d06c2a42ceb557e1765b15b9200853fcfae
    https://github.com/scummvm/scummvm/commit/45e83d06c2a42ceb557e1765b15b9200853fcfae
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CloudIcon

It's not a Request again, but still it controls ConnMan's timer.

Changed paths:
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 3304c71..dfb870a 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -34,13 +34,13 @@ const float CloudIcon::ALPHA_STEP = 0.03;
 const float CloudIcon::ALPHA_MAX = 1;
 const float CloudIcon::ALPHA_MIN = 0.5;
 
-CloudIcon::CloudIcon(): Request(nullptr, nullptr), _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
+CloudIcon::CloudIcon(): _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
 	initIcons();
 }
 
 CloudIcon::~CloudIcon() {}
 
-void CloudIcon::draw() {
+bool CloudIcon::draw() {
 	initIcons();
 
 	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
@@ -71,7 +71,7 @@ void CloudIcon::draw() {
 		_currentAlpha -= 3 * ALPHA_STEP;
 		if (_currentAlpha <= 0) {
 			_currentAlpha = 0;
-			finish();
+			return true;
 		}
 	}
 
@@ -84,13 +84,8 @@ void CloudIcon::draw() {
 			g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
 		}
 	}
-}
-
-void CloudIcon::handle() {}
 
-void CloudIcon::restart() {
-	_currentAlpha = 0;
-	_alphaRising = true;
+	return false;
 }
 
 void CloudIcon::initIcons() {
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index 0210899..e981dda 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -23,12 +23,11 @@
 #ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
 #define BACKENDS_NETWORKING_CURL_CLOUDICON_H
 
-#include "backends/networking/curl/request.h"
 #include "graphics/transparent_surface.h"
 
 namespace Networking {
 
-class CloudIcon: public Request {
+class CloudIcon {
 	static const float ALPHA_STEP, ALPHA_MAX, ALPHA_MIN;
 
 	bool _wasVisible, _iconsInited;
@@ -43,9 +42,8 @@ public:
 	CloudIcon();
 	~CloudIcon();
 
-	void draw();
-	virtual void handle();
-	virtual void restart();
+	/** Returns true if ConnMan's timer could be stopped. */
+	bool draw();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 95b38df..f384760 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -37,7 +37,7 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 
 namespace Networking {
 
-ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0), _icon(nullptr) {
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
 }
@@ -84,10 +84,6 @@ void ConnectionManager::startTimer(int interval) {
 	} else {
 		warning("Failed to install Networking::ConnectionManager's timer");
 	}
-	if (_timerStarted && !_icon) {
-		_icon = new CloudIcon();
-		addRequest(_icon, new Common::Callback<ConnectionManager, Request *>(this, &ConnectionManager::cloudIconDeleted));
-	}
 }
 
 void ConnectionManager::stopTimer() {
@@ -95,7 +91,6 @@ void ConnectionManager::stopTimer() {
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(connectionsThread);
 	_timerStarted = false;
-	if (_icon) _icon->finish();
 }
 
 void ConnectionManager::handle() {
@@ -104,10 +99,10 @@ void ConnectionManager::handle() {
 	++_frame;
 	if (_frame % CLOUD_PERIOD == 0) interateRequests();
 	if (_frame % CURL_PERIOD == 0) processTransfers();
-	_handleMutex.unlock();
 
-	//icon redrawing is doesn't require any mutex, but must be done after requests are iterated
-	if (_icon) _icon->draw();
+	if (_icon.draw() && _requests.empty())
+		stopTimer();
+	_handleMutex.unlock();
 }
 
 void ConnectionManager::interateRequests() {
@@ -129,7 +124,6 @@ void ConnectionManager::interateRequests() {
 
 		++i;		
 	}
-	if (_requests.empty()) stopTimer();
 }
 
 void ConnectionManager::processTransfers() {
@@ -158,8 +152,4 @@ void ConnectionManager::processTransfers() {
 	}
 }
 
-void ConnectionManager::cloudIconDeleted(Request *icon) {
-	if (_icon == icon) _icon = nullptr;
-}
-
 } // End of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 925312b..3a2e974 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -79,7 +79,7 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests;
 	Common::Mutex _handleMutex;
-	CloudIcon *_icon;
+	CloudIcon _icon;
 	uint32 _frame;
 	
 	void startTimer(int interval = TIMER_INTERVAL);
@@ -87,7 +87,6 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	void handle();
 	void interateRequests();
 	void processTransfers();
-	void cloudIconDeleted(Request *icon);
 
 public:
 	ConnectionManager();


Commit: 602d17f5fd3f3d89716be9f2975f644512aeb12d
    https://github.com/scummvm/scummvm/commit/602d17f5fd3f3d89716be9f2975f644512aeb12d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Document CloudIcon::draw() more precisely

Changed paths:
    backends/networking/curl/cloudicon.h



diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index e981dda..9b30a86 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -42,7 +42,21 @@ public:
 	CloudIcon();
 	~CloudIcon();
 
-	/** Returns true if ConnMan's timer could be stopped. */
+	/**
+	 * This method is called from ConnectionManager every time
+	 * its own timer calls the handle() method. The primary
+	 * responsibility of this draw() method is to draw cloud icon
+	 * on ScummVM's OSD when current cloud Storage is working.
+	 *
+	 * As we don't want ConnectionManager to work when no
+	 * Requests are running, we'd like to stop the timer. But then
+	 * this icon wouldn't have time to disappear smoothly. So,
+	 * in order to do that, ConnectionManager stop its timer
+	 * only when this draw() method returns true, indicating that
+	 * the CloudIcon has disappeared and the timer could be stopped.
+	 *
+	 * @return true if ConnMan's timer could be stopped.
+	 */
 	bool draw();
 };
 


Commit: 1bcbab7ad23b7dd47392e0d7a1fd2e56b385b367
    https://github.com/scummvm/scummvm/commit/1bcbab7ad23b7dd47392e0d7a1fd2e56b385b367
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add new cloudicon.png

Changed paths:
    backends/networking/curl/cloudicon.cpp
    cloudicon.png



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index dfb870a..d6a6b51 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -30,9 +30,9 @@
 
 namespace Networking {
 
-const float CloudIcon::ALPHA_STEP = 0.03;
+const float CloudIcon::ALPHA_STEP = 0.025;
 const float CloudIcon::ALPHA_MAX = 1;
-const float CloudIcon::ALPHA_MIN = 0.5;
+const float CloudIcon::ALPHA_MIN = 0.6;
 
 CloudIcon::CloudIcon(): _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
 	initIcons();
@@ -41,6 +41,7 @@ CloudIcon::CloudIcon(): _wasVisible(false), _iconsInited(false), _currentAlpha(0
 CloudIcon::~CloudIcon() {}
 
 bool CloudIcon::draw() {
+	bool stop = false;
 	initIcons();
 
 	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
@@ -51,7 +52,10 @@ bool CloudIcon::draw() {
 				_wasVisible = true;
 			}
 			if (_alphaRising) {
-				_currentAlpha += ALPHA_STEP;
+				if (_currentAlpha < ALPHA_MIN)
+					_currentAlpha += 5 * ALPHA_STEP;
+				else
+					_currentAlpha += ALPHA_STEP;
 				if (_currentAlpha > ALPHA_MAX) {
 					_currentAlpha = ALPHA_MAX;
 					_alphaRising = false;
@@ -68,10 +72,10 @@ bool CloudIcon::draw() {
 		}
 	} else {
 		_wasVisible = false;
-		_currentAlpha -= 3 * ALPHA_STEP;
+		_currentAlpha -= 5 * ALPHA_STEP;
 		if (_currentAlpha <= 0) {
 			_currentAlpha = 0;
-			return true;
+			stop = true;
 		}
 	}
 
@@ -85,7 +89,7 @@ bool CloudIcon::draw() {
 		}
 	}
 
-	return false;
+	return stop;
 }
 
 void CloudIcon::initIcons() {
diff --git a/cloudicon.png b/cloudicon.png
index 5f121a8..3fa0d63 100644
Binary files a/cloudicon.png and b/cloudicon.png differ


Commit: e98b0b12adc9ac6440ec4940d595095115f00d23
    https://github.com/scummvm/scummvm/commit/e98b0b12adc9ac6440ec4940d595095115f00d23
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Extend Storage & SavesSyncRequest

Now one can learn whether SavesSyncRequest is running, its progress and
which files are being synced.

Changed paths:
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 86dc83a..c7572c7 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -53,6 +53,7 @@ void SavesSyncRequest::start() {
 	_filesToDownload.clear();
 	_filesToUpload.clear();
 	_localFilesTimestamps.clear();
+	_totalFilesToHandle = 0;
 	_ignoreCallback = false;
 
 	//load timestamps
@@ -120,6 +121,7 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 	for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
 		debug("%s", _filesToUpload[i].c_str());
 	}
+	_totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
 	///////
 
 	//start downloading files
@@ -209,7 +211,7 @@ void SavesSyncRequest::downloadNextFile() {
 	_filesToDownload.pop_back();
 
 	///////
-	debug("downloading %s", _currentDownloadingFile.name().c_str());
+	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	///////
 	_workingRequest = _storage->download(_currentDownloadingFile.path(), concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
@@ -253,7 +255,7 @@ void SavesSyncRequest::uploadNextFile() {
 	_filesToUpload.pop_back();
 	
 	///////
-	debug("uploading %s", _currentUploadingFile.c_str());
+	debug("uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));
 	///////
 	_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
@@ -285,6 +287,22 @@ void SavesSyncRequest::handle() {}
 
 void SavesSyncRequest::restart() { start(); }
 
+double SavesSyncRequest::getProgress() {
+	if (_totalFilesToHandle == 0) {
+		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
+		return 0; //directory not listed yet
+	}
+
+	return (double)(_totalFilesToHandle - _filesToDownload.size() - _filesToUpload.size()) / (double)(_totalFilesToHandle);
+}
+
+Common::Array<Common::String> SavesSyncRequest::getFilesToUpload() {
+	Common::Array<Common::String> result = _filesToUpload;
+	if (_currentUploadingFile != "")
+		result.push_back(_currentUploadingFile);
+	return result;
+}
+
 void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	debug("SavesSync::finishError");
 
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 0e20159..ad65610 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -43,6 +43,7 @@ class SavesSyncRequest: public Networking::Request {
 	Common::String _currentUploadingFile;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	uint32 _totalFilesToHandle;
 
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
@@ -65,7 +66,13 @@ public:
 	virtual ~SavesSyncRequest();
 
 	virtual void handle();
-	virtual void restart();	
+	virtual void restart();
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getProgress();
+
+	/** Returns an array of saves names which are not uploaded yet. */
+	Common::Array<Common::String> getFilesToUpload();
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index f035c93..95786c2 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -30,7 +30,7 @@
 
 namespace Cloud {
 
-Storage::Storage(): _runningRequestsCount(0) {}
+Storage::Storage(): _runningRequestsCount(0), _savesSyncRequest(nullptr) {}
 
 Storage::~Storage() {}
 
@@ -53,6 +53,8 @@ Networking::Request *Storage::addRequest(Networking::Request *request) {
 
 void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer) {
 	_runningRequestsMutex.lock();
+	if (invalidRequestPointer == _savesSyncRequest)
+		_savesSyncRequest = nullptr;
 	--_runningRequestsCount;
 	if (_runningRequestsCount == 0) debug("Storage is not working now");
 	_runningRequestsMutex.unlock();
@@ -96,8 +98,16 @@ Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::
 }
 
 Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest) {
+		warning("Storage::syncSaves: there is a sync in progress already");
+		_runningRequestsMutex.unlock();
+		return _savesSyncRequest;
+	}
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	return addRequest(new SavesSyncRequest(this, callback, errorCallback));
+	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
+	_runningRequestsMutex.unlock();
+	return addRequest(_savesSyncRequest);
 }
 
 bool Storage::isWorking() {
@@ -107,5 +117,30 @@ bool Storage::isWorking() {
 	return working;
 }
 
+bool Storage::isSyncing() {
+	_runningRequestsMutex.lock();
+	bool syncing = _savesSyncRequest != nullptr;
+	_runningRequestsMutex.unlock();
+	return syncing;
+}
+
+double Storage::getSyncProgress() {
+	double result = 1;
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest)
+		result = _savesSyncRequest->getProgress();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
+Common::Array<Common::String> Storage::getSyncingFiles() {
+	Common::Array<Common::String> result;
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest)
+		result = _savesSyncRequest->getFilesToUpload();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
 } // End of namespace Cloud
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 1276b81..0f518de 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -35,6 +35,8 @@
 
 namespace Cloud {
 
+class SavesSyncRequest;
+
 class Storage {
 public:
 	typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse;
@@ -53,6 +55,7 @@ protected:
 	/** Keeps track of running requests. */
 	uint32 _runningRequestsCount;
 	Common::Mutex _runningRequestsMutex;
+	SavesSyncRequest *_savesSyncRequest;
 
 	/** Returns default error callback (printErrorResponse). */
 	virtual Networking::ErrorCallback getErrorPrintingCallback();
@@ -134,6 +137,15 @@ public:
 
 	/** Returns whether there are any requests running. */
 	virtual bool isWorking();
+
+	/** Returns whether there is a SavesSyncRequest running. */
+	virtual bool isSyncing();
+
+	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
+	virtual double getSyncProgress();
+
+	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
+	virtual Common::Array<Common::String> getSyncingFiles();
 };
 
 } // End of namespace Cloud


Commit: 5a695040d8cbd4e840e7c21e415d2225f9318ea2
    https://github.com/scummvm/scummvm/commit/5a695040d8cbd4e840e7c21e415d2225f9318ea2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix SavesSyncRequest to return right files

Files we need are files to be downloaded, because that's what blocks us
from reading/writing in those.

Changed paths:
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index c7572c7..b952681 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -296,10 +296,12 @@ double SavesSyncRequest::getProgress() {
 	return (double)(_totalFilesToHandle - _filesToDownload.size() - _filesToUpload.size()) / (double)(_totalFilesToHandle);
 }
 
-Common::Array<Common::String> SavesSyncRequest::getFilesToUpload() {
-	Common::Array<Common::String> result = _filesToUpload;
-	if (_currentUploadingFile != "")
-		result.push_back(_currentUploadingFile);
+Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
+	Common::Array<Common::String> result;
+	for (uint32 i = 0; i < _filesToDownload.size(); ++i)
+		result.push_back(_filesToDownload[i].name());
+	if (_currentDownloadingFile.name() != "")
+		result.push_back(_currentDownloadingFile.name());
 	return result;
 }
 
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index ad65610..6e74688 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -71,8 +71,8 @@ public:
 	/** Returns a number in range [0, 1], where 1 is "complete". */
 	double getProgress();
 
-	/** Returns an array of saves names which are not uploaded yet. */
-	Common::Array<Common::String> getFilesToUpload();
+	/** Returns an array of saves names which are not downloaded yet. */
+	Common::Array<Common::String> getFilesToDownload();
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 95786c2..c1719d9 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -137,7 +137,7 @@ Common::Array<Common::String> Storage::getSyncingFiles() {
 	Common::Array<Common::String> result;
 	_runningRequestsMutex.lock();
 	if (_savesSyncRequest)
-		result = _savesSyncRequest->getFilesToUpload();
+		result = _savesSyncRequest->getFilesToDownload();
 	_runningRequestsMutex.unlock();
 	return result;
 }


Commit: 91f75efa99ffe6d1c4e8e9b9b1fec368af192b7d
    https://github.com/scummvm/scummvm/commit/91f75efa99ffe6d1c4e8e9b9b1fec368af192b7d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix copyRectOnOSD()

Now it doesn't require full redraw, but asks to redraw the area which is
copied to.

Changed paths:
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp



diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index b76b2c7..b4b0d33 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -2223,6 +2223,9 @@ void SurfaceSdlGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x,
 	if (SDL_LockSurface(_osdSurface))
 		error("displayMessageOnOSD: SDL_LockSurface failed: %s", SDL_GetError());
 
+	// Mark that area as "dirty"
+	addDirtyRect(x, y, w, h, true);
+
 #ifdef USE_RGB_COLOR
 	byte *dst = (byte *)_osdSurface->pixels + y * _osdSurface->pitch + x * _osdSurface->format->BytesPerPixel;
 	if (_videoMode.screenWidth == w && pitch == _osdSurface->pitch) {
@@ -2251,9 +2254,6 @@ void SurfaceSdlGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x,
 
 	// Finished drawing, so unlock the OSD surface again
 	SDL_UnlockSurface(_osdSurface);
-
-	// Ensure a full redraw takes place next time the screen is updated
-	_forceFull = true;
 }
 
 void SurfaceSdlGraphicsManager::clearOSD() {


Commit: 69aed03c4f6b23302836315502cd9abbb395415e
    https://github.com/scummvm/scummvm/commit/69aed03c4f6b23302836315502cd9abbb395415e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add new CloudManager shortcuts

CloudIcon can easily use CloudMan.isWorking()

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/networking/curl/cloudicon.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 92e45e8..20d7572 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -126,4 +126,28 @@ void CloudManager::testFeature() {
 	if (storage) storage->info(nullptr, nullptr);
 }
 
+bool CloudManager::isWorking() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->isWorking();
+	return false;
+}
+
+bool CloudManager::isSyncing() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->isSyncing();
+	return false;
+}
+
+double CloudManager::getSyncProgress() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getSyncProgress();
+	return 1;
+}
+
+Common::Array<Common::String> CloudManager::getSyncingFiles() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getSyncingFiles();
+	return Common::Array<Common::String>();
+}
+
 } // End of namespace Common
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index a13eeeb..fa3a87d 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -77,6 +77,18 @@ public:
 	 * Starts feature testing (the one I'm working on currently). (Temporary)
 	 */
 	void testFeature();
+
+	/** Returns whether there are any requests running. */
+	bool isWorking();
+
+	/** Returns whether there is a SavesSyncRequest running. */
+	bool isSyncing();
+
+	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
+	double getSyncProgress();
+
+	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
+	Common::Array<Common::String> getSyncingFiles();
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index d6a6b51..882b0ab 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -43,9 +43,8 @@ CloudIcon::~CloudIcon() {}
 bool CloudIcon::draw() {
 	bool stop = false;
 	initIcons();
-
-	Cloud::Storage *storage = CloudMan.getCurrentStorage();	
-	if (storage && storage->isWorking()) {
+	
+	if (CloudMan.isWorking()) {
 		if (g_system) {
 			if (!_wasVisible) {
 				g_system->clearOSD();


Commit: 8d09e1a6b3441315abf6e91b558b7ba820108d3e
    https://github.com/scummvm/scummvm/commit/8d09e1a6b3441315abf6e91b558b7ba820108d3e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update cloudicon.png

The cloud icon is now completely unique and free to use (because I drawn
it myself).

Changed paths:
    cloudicon.png



diff --git a/cloudicon.png b/cloudicon.png
index 3fa0d63..21373ae 100644
Binary files a/cloudicon.png and b/cloudicon.png differ


Commit: 8de2862eaa6513c43502bea86f53967015298884
    https://github.com/scummvm/scummvm/commit/8de2862eaa6513c43502bea86f53967015298884
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update syncSaves() to return SavesSyncRequest *

So other classes could use that information without casting.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 20d7572..b4598d7 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -116,9 +116,10 @@ void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
 
-void CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
+SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->syncSaves(callback, errorCallback);
+	if (storage) return storage->syncSaves(callback, errorCallback);
+	return nullptr;
 }
 
 void CloudManager::testFeature() {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index fa3a87d..a02dc90 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -71,7 +71,7 @@ public:
 	/**
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
-	void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
+	SavesSyncRequest *syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
 
 	/**
 	 * Starts feature testing (the one I'm working on currently). (Temporary)
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index c1719d9..19f0845 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -97,7 +97,7 @@ Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::
 	return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
-Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
+SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	_runningRequestsMutex.lock();
 	if (_savesSyncRequest) {
 		warning("Storage::syncSaves: there is a sync in progress already");
@@ -106,8 +106,8 @@ Networking::Request *Storage::syncSaves(BoolCallback callback, Networking::Error
 	}
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
-	_runningRequestsMutex.unlock();
-	return addRequest(_savesSyncRequest);
+	_runningRequestsMutex.unlock();	
+	return (SavesSyncRequest *)addRequest(_savesSyncRequest); //who knows what that ConnMan could return in the future
 }
 
 bool Storage::isWorking() {
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 0f518de..5941fe4 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -124,7 +124,7 @@ public:
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
+	virtual SavesSyncRequest *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;


Commit: e7763700e2e2016f4573e3feb77b5fab69268683
    https://github.com/scummvm/scummvm/commit/e7763700e2e2016f4573e3feb77b5fab69268683
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make Save/Load dialog start saves sync

It also shows a "sync disabled" icon in case it has a savepath override.

Changed paths:
  A cloudicon_disabled.png
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/saveload.cpp



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index b952681..a99c229 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -33,7 +33,7 @@ namespace Cloud {
 const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
 
 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
-	Request(nullptr, ecb), _storage(storage), _boolCallback(callback),
+	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 6e74688..397a8cb 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -27,10 +27,11 @@
 #include "backends/cloud/storage.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
+#include "gui/object.h"
 
 namespace Cloud {
 
-class SavesSyncRequest: public Networking::Request {
+class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
 	const uint32 INVALID_TIMESTAMP = UINT_MAX;
 	static const char *TIMESTAMPS_FILENAME;
 
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 882b0ab..8971d44 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -34,7 +34,9 @@ const float CloudIcon::ALPHA_STEP = 0.025;
 const float CloudIcon::ALPHA_MAX = 1;
 const float CloudIcon::ALPHA_MIN = 0.6;
 
-CloudIcon::CloudIcon(): _wasVisible(false), _iconsInited(false), _currentAlpha(0), _alphaRising(true) {
+CloudIcon::CloudIcon():
+	_wasVisible(false), _iconsInited(false), _showingDisabled(false),
+	_currentAlpha(0), _alphaRising(true), _disabledFrames(0) {
 	initIcons();
 }
 
@@ -44,12 +46,13 @@ bool CloudIcon::draw() {
 	bool stop = false;
 	initIcons();
 	
-	if (CloudMan.isWorking()) {
+	if (CloudMan.isWorking() || _disabledFrames > 0) {		
 		if (g_system) {
 			if (!_wasVisible) {
 				g_system->clearOSD();
 				_wasVisible = true;
 			}
+			--_disabledFrames;
 			if (_alphaRising) {
 				if (_currentAlpha < ALPHA_MIN)
 					_currentAlpha += 5 * ALPHA_STEP;
@@ -80,7 +83,7 @@ bool CloudIcon::draw() {
 
 	if (g_system) {
 		Graphics::TransparentSurface *surface = &_icon;
-		makeAlphaIcon(_currentAlpha);
+		makeAlphaIcon((_showingDisabled? _disabledIcon:_icon), _currentAlpha);
 		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
 		if (surface && surface->getPixels()) {
 			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
@@ -88,40 +91,50 @@ bool CloudIcon::draw() {
 		}
 	}
 
+	if (stop) _showingDisabled = false;
 	return stop;
 }
 
+void CloudIcon::showDisabled() {
+	_showingDisabled = true;
+	_disabledFrames = 20 * 3; //3 seconds 20 fps
+}
+
 void CloudIcon::initIcons() {
 	if (_iconsInited) return;
+	loadIcon(_icon, "cloudicon.png");
+	loadIcon(_disabledIcon, "cloudicon_disabled.png");
+	_iconsInited = true;
+}
 
+void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, const char *filename) {
 	Image::PNGDecoder decoder;
 	Common::ArchiveMemberList members;
 	Common::File file;
-	if (!file.open("cloudicon.png")) warning("failed");
+	if (!file.open(filename)) warning("CloudIcon::loadIcon: unable to open %s", filename);
 	Common::SeekableReadStream *stream = &file;
 	if (stream) {
 		if (!decoder.loadStream(*stream))
-			error("Error decoding PNG");
+			error("CloudIcon::loadIcon: error decoding PNG");
 
 		Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
 		if (s) {
 			Graphics::PixelFormat f = g_system->getOSDFormat();
 			if (f != s->format) {
 				Graphics::TransparentSurface *s2 = s->convertTo(f);
-				if (s2) _icon.copyFrom(*s2);
-				else warning("failed converting");
+				if (s2) icon.copyFrom(*s2);
+				else warning("CloudIcon::loadIcon: failed converting TransparentSurface");
 				delete s2;
 			} else {
-				_icon.copyFrom(*s);
+				icon.copyFrom(*s);
 			}
 			delete s;
-		} else warning("failed reading");
+		} else warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
 	}
-	_iconsInited = true;
 }
 
-void CloudIcon::makeAlphaIcon(float alpha) {
-	_alphaIcon.copyFrom(_icon);
+void CloudIcon::makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha) {
+	_alphaIcon.copyFrom(icon);
 
 	byte *pixels = (byte *)_alphaIcon.getPixels();
 	for (int y = 0; y < _alphaIcon.h; y++) {
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index 9b30a86..e007f95 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -30,13 +30,15 @@ namespace Networking {
 class CloudIcon {
 	static const float ALPHA_STEP, ALPHA_MAX, ALPHA_MIN;
 
-	bool _wasVisible, _iconsInited;
-	Graphics::TransparentSurface _icon, _alphaIcon;
+	bool _wasVisible, _iconsInited, _showingDisabled;
+	Graphics::TransparentSurface _icon, _disabledIcon, _alphaIcon;
 	float _currentAlpha;
 	bool _alphaRising;
+	int _disabledFrames;
 
 	void initIcons();
-	void makeAlphaIcon(float alpha);
+	void loadIcon(Graphics::TransparentSurface &icon, const char *filename);
+	void makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha);
 
 public:
 	CloudIcon();
@@ -58,6 +60,9 @@ public:
 	 * @return true if ConnMan's timer could be stopped.
 	 */
 	bool draw();
+
+	/** Draw a "cloud disabled" icon instead of "cloud syncing" one. */
+	void showDisabled();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index f384760..c675d0d 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -71,6 +71,11 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac
 	return request;
 }
 
+void ConnectionManager::showCloudDisabledIcon() {
+	_icon.showDisabled();
+	startTimer();
+}
+
 //private goes here:
 
 void connectionsThread(void *ignored) {
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 3a2e974..8b2153a 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -113,6 +113,9 @@ public:
 	 * @return the same Request pointer, just as a shortcut
 	 */
 	Request *addRequest(Request *request, RequestCallback callback = nullptr);
+
+	/** Shows a "cloud disabled" icon for a three seconds. */
+	void showCloudDisabledIcon();
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/cloudicon_disabled.png b/cloudicon_disabled.png
new file mode 100644
index 0000000..27c9600
Binary files /dev/null and b/cloudicon_disabled.png differ
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 3d4adff..d971350 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -21,6 +21,11 @@
  */
 
 #include "gui/saveload-dialog.h"
+
+#include "backends/cloud/cloudmanager.h"
+#include "backends/cloud/savessyncrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
+
 #include "common/translation.h"
 #include "common/config-manager.h"
 
@@ -135,6 +140,17 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	return Dialog::handleCommand(sender, cmd, data);
 }
 
+void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
+	if (!CloudMan.isSyncing()) {
+		if (hasSavepathOverride) {
+			ConnMan.showCloudDisabledIcon();
+		} else {
+			Cloud::SavesSyncRequest *request = CloudMan.syncSaves();
+			if (request) request->setTarget(this);
+		}
+	}
+}
+
 void SaveLoadChooserDialog::reflowLayout() {
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	addChooserButtons();
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 31f28f6..0e67ba8 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -60,6 +60,8 @@ public:
 
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 
+	virtual void runSaveSync(bool hasSavepathOverride);
+
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	virtual SaveLoadChooserType getType() const = 0;
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index b94d302..b3e6698 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -87,6 +87,8 @@ int SaveLoadChooser::runModalWithPluginAndTarget(const EnginePlugin *plugin, con
 	if (!_impl)
 		return -1;
 
+	_impl->runSaveSync(ConfMan.hasKey("savepath", target));
+
 	// Set up the game domain as newly active domain, so
 	// target specific savepath will be checked
 	String oldDomain = ConfMan.getActiveDomainName();


Commit: e9721976aa4fc604810cf1daf6d60b206197cd9a
    https://github.com/scummvm/scummvm/commit/e9721976aa4fc604810cf1daf6d60b206197cd9a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add SaveLoadCloudSyncProgressDialog

It's shown by SaveLoadChooserDialog when files are downloaded and some
save slots are locked. One can hide that dialog to interact with
non-locked slots or cancel saves sync completely. Dialog's label shows
current sync progress.

Dialog automatically hides itself when all files are downloaded.
WARNING: right now that results in a crash!

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index b4598d7..f5d60e0 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -151,4 +151,14 @@ Common::Array<Common::String> CloudManager::getSyncingFiles() {
 	return Common::Array<Common::String>();
 }
 
+void CloudManager::cancelSync() {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->cancelSync();
+}
+
+void CloudManager::setSyncTarget(GUI::CommandReceiver *target) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->setSyncTarget(target);
+}
+
 } // End of namespace Common
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index a02dc90..c7351da 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -27,6 +27,12 @@
 #include "common/array.h"
 #include "common/singleton.h"
 
+namespace GUI {
+
+class CommandReceiver;
+
+}
+
 namespace Cloud {
 
 class CloudManager : public Common::Singleton<CloudManager> {
@@ -89,6 +95,12 @@ public:
 
 	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
 	Common::Array<Common::String> getSyncingFiles();
+
+	/** Cancels running sync. */
+	void cancelSync();
+
+	/** Sets SavesSyncRequest's target to given CommandReceiver. */
+	void setSyncTarget(GUI::CommandReceiver *target);
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index a99c229..e066c53 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -24,14 +24,19 @@
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
+#include "common/json.h"
 #include "common/savefile.h"
 #include "common/system.h"
-#include <common/json.h>
 
 namespace Cloud {
 
 const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
 
+enum {
+	kSavesSyncProgressCmd = 'SSPR',
+	kSavesSyncEndedCmd = 'SSEN'
+};
+
 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
 	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -203,10 +208,13 @@ void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse e
 
 void SavesSyncRequest::downloadNextFile() {
 	if (_filesToDownload.empty()) {
+		sendCommand(kSavesSyncEndedCmd, 0);
 		uploadNextFile();
 		return;
 	}
 
+	sendCommand(kSavesSyncProgressCmd, (int)(getProgress() * 100));
+
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
 
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 19f0845..c98310d 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -142,5 +142,19 @@ Common::Array<Common::String> Storage::getSyncingFiles() {
 	return result;
 }
 
+void Storage::cancelSync() {
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest)
+		_savesSyncRequest->finish();
+	_runningRequestsMutex.unlock();
+}
+
+void Storage::setSyncTarget(GUI::CommandReceiver *target) {
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest)
+		_savesSyncRequest->setTarget(target);
+	_runningRequestsMutex.unlock();
+}
+
 } // End of namespace Cloud
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 5941fe4..40ea14a 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -33,6 +33,12 @@
 #include "common/stream.h"
 #include "common/str.h"
 
+namespace GUI {
+
+class CommandReceiver;
+
+}
+
 namespace Cloud {
 
 class SavesSyncRequest;
@@ -146,6 +152,12 @@ public:
 
 	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
 	virtual Common::Array<Common::String> getSyncingFiles();
+
+	/** Cancels running sync. */
+	virtual void cancelSync();
+
+	/** Sets SavesSyncRequest's target to given CommandReceiver. */
+	virtual void setSyncTarget(GUI::CommandReceiver *target);
 };
 
 } // End of namespace Cloud
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index d971350..e5e71a7 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -38,6 +38,53 @@
 
 namespace GUI {
 
+enum {
+	kSavesSyncProgressCmd = 'SSPR',
+	kSavesSyncEndedCmd = 'SSEN',
+
+	kCancelSyncCmd = 'PDCS',
+	kBackgroundSyncCmd = 'PDBS'
+};
+
+SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(10, 10, 320, 100) {
+	int x = 10;
+	int buttonHeight = 24;
+	int buttonWidth = 140;
+	int marginBottom = 8;
+	
+	uint32 progress = (uint32)(100 * CloudMan.getSyncProgress());
+	_label = new StaticTextWidget(this, 10, 10, 300, kLineHeight, Common::String::format("Downloading saves (%u%% complete)...", progress), Graphics::kTextAlignCenter);
+
+	//if (defaultButton)
+		new ButtonWidget(this, x, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog
+
+	//if (altButton)
+		new ButtonWidget(this, x + buttonWidth + 10, _h - buttonHeight - 8, buttonWidth, buttonHeight, "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
+}
+
+SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {}
+
+void SaveLoadCloudSyncProgressDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch(cmd) {
+	case kSavesSyncProgressCmd:				
+		_label->setLabel(Common::String::format("Downloading saves (%u%% complete)...", data));
+		break;
+
+	case kCancelSyncCmd:
+		setResult(kCancelSyncCmd);
+		close();
+		break;
+
+	case kSavesSyncEndedCmd:
+	case kBackgroundSyncCmd:
+		setResult(kBackgroundSyncCmd);
+		close();
+		break;
+	}
+
+	Dialog::handleCommand(sender, cmd, data);
+}
+
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) {
 	const Common::String &userConfig = ConfMan.get("gui_saveload_chooser", Common::ConfigManager::kApplicationDomain);
@@ -71,7 +118,8 @@ enum {
 
 SaveLoadChooserDialog::SaveLoadChooserDialog(const Common::String &dialogName, const bool saveMode)
 	: Dialog(dialogName), _metaEngine(0), _delSupport(false), _metaInfoSupport(false),
-	_thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode)
+	_thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode),
+	_dialogWasShown(false)
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	, _listButton(0), _gridButton(0)
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
@@ -83,7 +131,8 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(const Common::String &dialogName, c
 
 SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const bool saveMode)
 	: Dialog(x, y, w, h), _metaEngine(0), _delSupport(false), _metaInfoSupport(false),
-	_thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode)
+	_thumbnailSupport(false), _saveDateSupport(false), _playTimeSupport(false), _saveMode(saveMode),
+	_dialogWasShown(false)
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	, _listButton(0), _gridButton(0)
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
@@ -99,6 +148,8 @@ void SaveLoadChooserDialog::open() {
 	// So that quitting ScummVM will not cause the dialog result to say a
 	// saved game was selected.
 	setResult(-1);
+
+	_dialogWasShown = false;
 }
 
 int SaveLoadChooserDialog::run(const Common::String &target, const MetaEngine *metaEngine) {
@@ -137,6 +188,21 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	}
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
+	if (cmd == kSavesSyncProgressCmd || cmd == kSavesSyncEndedCmd) {
+		Cloud::SavesSyncRequest *request = (Cloud::SavesSyncRequest *)sender;
+
+		//this dialog only gets these commands if the progress dialog was shown and user clicked "run in background"
+		switch (cmd) {
+		case kSavesSyncProgressCmd:			
+			//TODO: unlock that save which was downloaded
+			break;
+
+		case kSavesSyncEndedCmd:
+			//TODO: ?
+			break;
+		}
+	}
+
 	return Dialog::handleCommand(sender, cmd, data);
 }
 
@@ -151,6 +217,23 @@ void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 	}
 }
 
+void SaveLoadChooserDialog::handleTickle() {
+	if (!_dialogWasShown && CloudMan.isSyncing()) {
+		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
+		if (!files.empty()) {
+			SaveLoadCloudSyncProgressDialog dialog;
+			CloudMan.setSyncTarget(&dialog);
+			int result = dialog.runModal();
+			if (result == kCancelSyncCmd) {
+				CloudMan.cancelSync();
+			}
+			CloudMan.setSyncTarget(this);
+			_dialogWasShown = true;
+		}
+	}
+	Dialog::handleTickle();
+}
+
 void SaveLoadChooserDialog::reflowLayout() {
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	addChooserButtons();
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 0e67ba8..ffb8f34 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -30,6 +30,15 @@
 
 namespace GUI {
 
+class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
+	StaticTextWidget *_label;
+public:
+	SaveLoadCloudSyncProgressDialog();
+	virtual ~SaveLoadCloudSyncProgressDialog();
+
+	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+};
+
 #define kSwitchSaveLoadDialog -2
 
 // TODO: We might want to disable the grid based save/load chooser for more
@@ -61,6 +70,8 @@ public:
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 
 	virtual void runSaveSync(bool hasSavepathOverride);
+	
+	virtual void handleTickle();
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	virtual SaveLoadChooserType getType() const = 0;
@@ -80,6 +91,7 @@ protected:
 	bool					_saveDateSupport;
 	bool					_playTimeSupport;
 	Common::String			_target;
+	bool _dialogWasShown;
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	ButtonWidget *_listButton;


Commit: 3db80154d60c98fe27018dc78c875df52c20cfe9
    https://github.com/scummvm/scummvm/commit/3db80154d60c98fe27018dc78c875df52c20cfe9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix SaveLoadCloudSyncProgressDialog crash

It's closing itself a bit later now.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index e5e71a7..bdae9ef 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -46,7 +46,7 @@ enum {
 	kBackgroundSyncCmd = 'PDBS'
 };
 
-SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(10, 10, 320, 100) {
+SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(10, 10, 320, 100), _close(false) {
 	int x = 10;
 	int buttonHeight = 24;
 	int buttonWidth = 140;
@@ -77,14 +77,22 @@ void SaveLoadCloudSyncProgressDialog::handleCommand(CommandSender *sender, uint3
 
 	case kSavesSyncEndedCmd:
 	case kBackgroundSyncCmd:
-		setResult(kBackgroundSyncCmd);
-		close();
+		_close = true;
 		break;
 	}
 
 	Dialog::handleCommand(sender, cmd, data);
 }
 
+void SaveLoadCloudSyncProgressDialog::handleTickle() {
+	if (_close) {
+		setResult(kBackgroundSyncCmd);
+		close();
+	}
+
+	Dialog::handleTickle();
+}
+
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) {
 	const Common::String &userConfig = ConfMan.get("gui_saveload_chooser", Common::ConfigManager::kApplicationDomain);
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index ffb8f34..e8f0c37 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -32,11 +32,13 @@ namespace GUI {
 
 class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
 	StaticTextWidget *_label;
+	bool _close;
 public:
 	SaveLoadCloudSyncProgressDialog();
 	virtual ~SaveLoadCloudSyncProgressDialog();
 
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void handleTickle();
 };
 
 #define kSwitchSaveLoadDialog -2


Commit: 0ce7be17d3fec380f726c1ff16c559344b3e24c1
    https://github.com/scummvm/scummvm/commit/0ce7be17d3fec380f726c1ff16c559344b3e24c1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make ProgressDialog display downloading progress

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    gui/saveload-dialog.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index f5d60e0..4230c14 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -139,6 +139,12 @@ bool CloudManager::isSyncing() {
 	return false;
 }
 
+double CloudManager::getSyncDownloadingProgress() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getSyncDownloadingProgress();
+	return 1;
+}
+
 double CloudManager::getSyncProgress() {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getSyncProgress();
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index c7351da..dbff018 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -90,6 +90,9 @@ public:
 	/** Returns whether there is a SavesSyncRequest running. */
 	bool isSyncing();
 
+	/** Returns a number in [0, 1] range which represents current sync downloading progress (1 = complete). */
+	double getSyncDownloadingProgress();
+
 	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
 	double getSyncProgress();
 
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index e066c53..0726857 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -208,12 +208,13 @@ void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse e
 
 void SavesSyncRequest::downloadNextFile() {
 	if (_filesToDownload.empty()) {
+		_currentDownloadingFile = StorageFile("", 0, 0, false); //so getFilesToDownload() would return an empty array
 		sendCommand(kSavesSyncEndedCmd, 0);
 		uploadNextFile();
 		return;
 	}
 
-	sendCommand(kSavesSyncProgressCmd, (int)(getProgress() * 100));
+	sendCommand(kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
 
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
@@ -295,6 +296,19 @@ void SavesSyncRequest::handle() {}
 
 void SavesSyncRequest::restart() { start(); }
 
+double SavesSyncRequest::getDownloadingProgress() {
+	if (_totalFilesToHandle == 0) {
+		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
+		return 0; //directory not listed yet
+	}
+
+	if (_totalFilesToHandle == _filesToUpload.size()) return 1; //nothing to download => download complete
+
+	uint32 totalFilesToDownload = _totalFilesToHandle - _filesToUpload.size();
+	uint32 filesLeftToDownload = _filesToDownload.size() + (_currentDownloadingFile.name() != "" ? 1 : 0);
+	return (double)(totalFilesToDownload - filesLeftToDownload) / (double)(totalFilesToDownload);
+}
+
 double SavesSyncRequest::getProgress() {
 	if (_totalFilesToHandle == 0) {
 		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 397a8cb..569feb4 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -70,6 +70,9 @@ public:
 	virtual void restart();
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getDownloadingProgress();
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
 	double getProgress();
 
 	/** Returns an array of saves names which are not downloaded yet. */
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index c98310d..5458276 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -124,6 +124,15 @@ bool Storage::isSyncing() {
 	return syncing;
 }
 
+double Storage::getSyncDownloadingProgress() {
+	double result = 1;
+	_runningRequestsMutex.lock();
+	if (_savesSyncRequest)
+		result = _savesSyncRequest->getDownloadingProgress();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
 double Storage::getSyncProgress() {
 	double result = 1;
 	_runningRequestsMutex.lock();
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 40ea14a..058e831 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -148,6 +148,9 @@ public:
 	virtual bool isSyncing();
 
 	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
+	virtual double getSyncDownloadingProgress();
+
+	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
 	virtual double getSyncProgress();
 
 	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index bdae9ef..6d343c8 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -52,7 +52,7 @@ SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(10, 1
 	int buttonWidth = 140;
 	int marginBottom = 8;
 	
-	uint32 progress = (uint32)(100 * CloudMan.getSyncProgress());
+	uint32 progress = (uint32)(100 * CloudMan.getSyncDownloadingProgress());
 	_label = new StaticTextWidget(this, 10, 10, 300, kLineHeight, Common::String::format("Downloading saves (%u%% complete)...", progress), Graphics::kTextAlignCenter);
 
 	//if (defaultButton)


Commit: 9eb4aad7fdea54fd99ad4a9aa4ab78bf64f81794
    https://github.com/scummvm/scummvm/commit/9eb4aad7fdea54fd99ad4a9aa4ab78bf64f81794
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make DefaultSaveFileManager ignore syncing files

MetaEngines don't get "locked" files in the list, so won't try to open
these.

Save/Load dialog updates save list every time SavesSyncRequest tells it
to.

Changed paths:
    backends/cloud/savessyncrequest.cpp
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h
    common/savefile.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 0726857..d983a0f 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -214,11 +214,11 @@ void SavesSyncRequest::downloadNextFile() {
 		return;
 	}
 
-	sendCommand(kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
-
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
 
+	sendCommand(kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
+
 	///////
 	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	///////
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 75ba50a..a11d687 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -59,19 +59,33 @@ void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) {
 	}
 }
 
+void DefaultSaveFileManager::updateSavefilesList(Common::StringArray &lockedFiles) {
+	//make it refresh the cache next time it lists the saves
+	_cachedDirectory = "";
+
+	//remember the locked files list because some of these files don't exist yet
+	_lockedFiles = lockedFiles;
+}
+
 Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &pattern) {
 	// Assure the savefile name cache is up-to-date.
 	assureCached(getSavePath());
 	if (getError().getCode() != Common::kNoError)
 		return Common::StringArray();
 
+	Common::HashMap<Common::String, bool> locked;
+	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
+		if (i->matchString(pattern, true)) {
+			locked[*i] = true;
+		}
+	}
+
 	Common::StringArray results;
-	for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) {
-		if (file->_key.matchString(pattern, true)) {
+	for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) {		
+		if (!locked.contains(file->_key) && file->_key.matchString(pattern, true)) {
 			results.push_back(file->_key);
 		}
 	}
-
 	return results;
 }
 
diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h
index 166e700..af30cf4 100644
--- a/backends/saves/default/default-saves.h
+++ b/backends/saves/default/default-saves.h
@@ -37,6 +37,7 @@ public:
 	DefaultSaveFileManager();
 	DefaultSaveFileManager(const Common::String &defaultSavepath);
 
+	virtual void updateSavefilesList(Common::StringArray &lockedFiles);
 	virtual Common::StringArray listSavefiles(const Common::String &pattern);
 	virtual Common::InSaveFile *openRawFile(const Common::String &filename);
 	virtual Common::InSaveFile *openForLoading(const Common::String &filename);
@@ -75,6 +76,12 @@ protected:
 	 */
 	SaveFileCache _saveFileCache;
 
+	/**
+	 * List of "locked" files. These cannot be used for saving/loading
+	 * because CloudManager is downloading those.
+	 */
+	Common::StringArray _lockedFiles;
+
 private:
 	/**
 	 * The currently cached directory.
diff --git a/common/savefile.h b/common/savefile.h
index d9c5512..38b21c9 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -183,6 +183,13 @@ public:
 	 * @see Common::matchString()
 	 */
 	virtual StringArray listSavefiles(const String &pattern) = 0;
+
+	/**
+	 * Refreshes the save files list (because some new files could've been added)
+	 * and remembers the "locked" files list. These files could not be used
+	 * for saving or loading because they are being synced by CloudManager.
+	 */
+	virtual void updateSavefilesList(StringArray &lockedFiles) = 0;
 };
 
 } // End of namespace Common
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 6d343c8..2f10182 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -35,6 +35,7 @@
 #include "gui/widgets/edittext.h"
 
 #include "graphics/scaler.h"
+#include <common/savefile.h>
 
 namespace GUI {
 
@@ -201,12 +202,9 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 
 		//this dialog only gets these commands if the progress dialog was shown and user clicked "run in background"
 		switch (cmd) {
-		case kSavesSyncProgressCmd:			
-			//TODO: unlock that save which was downloaded
-			break;
-
+		case kSavesSyncProgressCmd:
 		case kSavesSyncEndedCmd:
-			//TODO: ?
+			updateSaveList();
 			break;
 		}
 	}
@@ -237,6 +235,7 @@ void SaveLoadChooserDialog::handleTickle() {
 			}
 			CloudMan.setSyncTarget(this);
 			_dialogWasShown = true;
+			updateSaveList();
 		}
 	}
 	Dialog::handleTickle();
@@ -259,6 +258,11 @@ void SaveLoadChooserDialog::reflowLayout() {
 	Dialog::reflowLayout();
 }
 
+void SaveLoadChooserDialog::updateSaveList() {	
+	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
+	g_system->getSavefileManager()->updateSavefilesList(files);
+}
+
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 void SaveLoadChooserDialog::addChooserButtons() {
 	if (_listButton) {
@@ -570,6 +574,7 @@ void SaveLoadChooserSimple::close() {
 }
 
 void SaveLoadChooserSimple::updateSaveList() {
+	SaveLoadChooserDialog::updateSaveList();
 	_saveList = _metaEngine->listSaves(_target.c_str());
 
 	int curSlot = 0;
@@ -631,6 +636,7 @@ void SaveLoadChooserSimple::updateSaveList() {
 	}
 
 	_list->setList(saveNames, &colors);
+	draw();
 }
 
 // SaveLoadChooserGrid implementation
@@ -726,6 +732,13 @@ void SaveLoadChooserGrid::handleMouseWheel(int x, int y, int direction) {
 	}
 }
 
+void SaveLoadChooserGrid::updateSaveList() {
+	SaveLoadChooserDialog::updateSaveList();
+	_saveList = _metaEngine->listSaves(_target.c_str());
+	updateSaves();
+	draw();
+}
+
 void SaveLoadChooserGrid::open() {
 	SaveLoadChooserDialog::open();
 
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index e8f0c37..a767fb8 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -84,6 +84,7 @@ public:
 
 protected:
 	virtual int runIntern() = 0;
+	virtual void updateSaveList();
 
 	const bool				_saveMode;
 	const MetaEngine		*_metaEngine;
@@ -122,6 +123,8 @@ public:
 
 	virtual void open();
 	virtual void close();
+protected:
+	virtual void updateSaveList();
 private:
 	virtual int runIntern();
 
@@ -136,8 +139,7 @@ private:
 
 	SaveStateList			_saveList;
 	String					_resultString;
-
-	void updateSaveList();
+	
 	void updateSelection(bool redraw);
 };
 
@@ -180,6 +182,7 @@ public:
 protected:
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleMouseWheel(int x, int y, int direction);
+	virtual void updateSaveList();
 private:
 	virtual int runIntern();
 


Commit: 6c5a8f34eaf5fe6af0d885cb162b5ebf193030f8
    https://github.com/scummvm/scummvm/commit/6c5a8f34eaf5fe6af0d885cb162b5ebf193030f8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add SaveLoadCloudSyncProgress enum

It's common for Save/Load dialogs and SavesSyncRequest.

Changed paths:
    backends/cloud/savessyncrequest.cpp
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index d983a0f..f07d1d0 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -27,16 +27,12 @@
 #include "common/json.h"
 #include "common/savefile.h"
 #include "common/system.h"
+#include "gui/saveload-dialog.h"
 
 namespace Cloud {
 
 const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
 
-enum {
-	kSavesSyncProgressCmd = 'SSPR',
-	kSavesSyncEndedCmd = 'SSEN'
-};
-
 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
 	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -209,7 +205,7 @@ void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse e
 void SavesSyncRequest::downloadNextFile() {
 	if (_filesToDownload.empty()) {
 		_currentDownloadingFile = StorageFile("", 0, 0, false); //so getFilesToDownload() would return an empty array
-		sendCommand(kSavesSyncEndedCmd, 0);
+		sendCommand(GUI::kSavesSyncEndedCmd, 0);
 		uploadNextFile();
 		return;
 	}
@@ -217,7 +213,7 @@ void SavesSyncRequest::downloadNextFile() {
 	_currentDownloadingFile = _filesToDownload.back();
 	_filesToDownload.pop_back();
 
-	sendCommand(kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
+	sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
 
 	///////
 	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 2f10182..2646194 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -40,9 +40,6 @@
 namespace GUI {
 
 enum {
-	kSavesSyncProgressCmd = 'SSPR',
-	kSavesSyncEndedCmd = 'SSEN',
-
 	kCancelSyncCmd = 'PDCS',
 	kBackgroundSyncCmd = 'PDBS'
 };
@@ -198,15 +195,8 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
 	if (cmd == kSavesSyncProgressCmd || cmd == kSavesSyncEndedCmd) {
-		Cloud::SavesSyncRequest *request = (Cloud::SavesSyncRequest *)sender;
-
 		//this dialog only gets these commands if the progress dialog was shown and user clicked "run in background"
-		switch (cmd) {
-		case kSavesSyncProgressCmd:
-		case kSavesSyncEndedCmd:
-			updateSaveList();
-			break;
-		}
+		return updateSaveList();
 	}
 
 	return Dialog::handleCommand(sender, cmd, data);
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index a767fb8..435bfc7 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -30,6 +30,11 @@
 
 namespace GUI {
 
+enum SaveLoadCloudSyncProgress {
+	kSavesSyncProgressCmd = 'SSPR',
+	kSavesSyncEndedCmd = 'SSEN'
+};
+
 class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
 	StaticTextWidget *_label;
 	bool _close;


Commit: f1a56eaf3666a6535ff0d1654e4e014249933452
    https://github.com/scummvm/scummvm/commit/f1a56eaf3666a6535ff0d1654e4e014249933452
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Show "locked" saves during sync

Changed paths:
    backends/saves/default/default-saves.cpp
    engines/cge/detection.cpp
    engines/metaengine.h
    engines/savestate.cpp
    engines/savestate.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index a11d687..a669c2c 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -75,9 +75,7 @@ Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &
 
 	Common::HashMap<Common::String, bool> locked;
 	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
-		if (i->matchString(pattern, true)) {
-			locked[*i] = true;
-		}
+		locked[*i] = true;
 	}
 
 	Common::StringArray results;
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 82d27f8..0c79be5 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -131,6 +131,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual Common::String getSavefilesPattern(Common::String &target) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -239,6 +240,10 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+Common::String CGEMetaEngine::getSavefilesPattern(Common::String &target) const {
+	return target + ".###";
+}
+
 SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/metaengine.h b/engines/metaengine.h
index e7bfeba..913f61d 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -116,6 +116,16 @@ public:
 	}
 
 	/**
+	* Return a common pattern which all engine's save filenames should match.
+	*
+	* @param target	name of a config manager target
+	* @return			a pattern for filenames
+	*/
+	virtual Common::String getSavefilesPattern(Common::String &target) const {
+		return target + ".s##";
+	}
+
+	/**
 	 * Return a list of extra GUI options for the specified target.
 	 * If no target is specified, all of the available custom GUI options are
 	 * Returned for the plugin (used to set default values).
diff --git a/engines/savestate.cpp b/engines/savestate.cpp
index 186d7bc..7366aa6 100644
--- a/engines/savestate.cpp
+++ b/engines/savestate.cpp
@@ -27,12 +27,12 @@
 SaveStateDescriptor::SaveStateDescriptor()
 	// FIXME: default to 0 (first slot) or to -1 (invalid slot) ?
 	: _slot(-1), _description(), _isDeletable(true), _isWriteProtected(false),
-	  _saveDate(), _saveTime(), _playTime(), _thumbnail() {
+	  _isLocked(false), _saveDate(), _saveTime(), _playTime(), _thumbnail() {
 }
 
 SaveStateDescriptor::SaveStateDescriptor(int s, const Common::String &d)
 	: _slot(s), _description(d), _isDeletable(true), _isWriteProtected(false),
-	  _saveDate(), _saveTime(), _playTime(), _thumbnail() {
+	  _isLocked(false), _saveDate(), _saveTime(), _playTime(), _thumbnail() {
 }
 
 void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) {
diff --git a/engines/savestate.h b/engines/savestate.h
index 21ade60..3244d61 100644
--- a/engines/savestate.h
+++ b/engines/savestate.h
@@ -90,6 +90,24 @@ public:
 	bool getWriteProtectedFlag() const { return _isWriteProtected; }
 
 	/**
+	 * Defines whether the save state is "locked" because is being synced.
+	 */
+	void setLocked(bool state) {
+		_isLocked = state;
+
+		//just in case:
+		if (state) {
+			setDeletableFlag(false);
+			setWriteProtectedFlag(true);
+		}
+	}
+
+	/**
+	* Queries whether the save state is "locked" because is being synced.
+	*/
+	bool getLocked() const { return _isLocked; }
+
+	/**
 	 * Return a thumbnail graphics surface representing the savestate visually.
 	 * This is usually a scaled down version of the game graphics. The size
 	 * should be either 160x100 or 160x120 pixels, depending on the aspect
@@ -180,6 +198,11 @@ private:
 	bool _isWriteProtected;
 
 	/**
+	 * Whether the save state is "locked" because is being synced.
+	 */
+	bool _isLocked;
+
+	/**
 	 * Human readable description of the date the save state was created.
 	 */
 	Common::String _saveDate;
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 2646194..5360bfe 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -251,6 +251,32 @@ void SaveLoadChooserDialog::reflowLayout() {
 void SaveLoadChooserDialog::updateSaveList() {	
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
 	g_system->getSavefileManager()->updateSavefilesList(files);
+	listSaves();
+}
+
+void SaveLoadChooserDialog::listSaves() {
+	_saveList = _metaEngine->listSaves(_target.c_str());
+
+	Common::String pattern = _metaEngine->getSavefilesPattern(_target);
+	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
+	for (uint32 i = 0; i < files.size(); ++i) {		
+		if (!files[i].matchString(pattern, true)) continue;
+
+		//make up some slot number
+		int slotNum = 0;
+		for (int j = files[i].size() - 3; j < files[i].size(); ++j) { //3 last chars
+			if (j < 0) continue;
+			char c = files[i][j];
+			if (c < '0' || c > '9') continue;
+			slotNum = slotNum * 10 + (c - '0');
+		}
+
+		SaveStateDescriptor slot(slotNum, files[i]);
+		slot.setLocked(true);
+		_saveList.push_back(slot);
+	}
+
+	Common::sort(_saveList.begin(), _saveList.end(), SaveStateDescriptorSlotComparator());
 }
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
@@ -454,6 +480,7 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
 	bool isDeletable = _delSupport;
 	bool isWriteProtected = false;
 	bool startEditMode = _list->isEditable();
+	bool isLocked = false;
 
 	// We used to support letting the themes specify the fill color with our
 	// initial theme based GUI. But this support was dropped.
@@ -463,10 +490,11 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
 	_playtime->setLabel(_("No playtime saved"));
 
 	if (selItem >= 0 && _metaInfoSupport) {
-		SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), _saveList[selItem].getSaveSlot());
+		SaveStateDescriptor desc = (_saveList[selItem].getLocked() ? _saveList[selItem] : _metaEngine->querySaveMetaInfos(_target.c_str(), _saveList[selItem].getSaveSlot()));
 
 		isDeletable = desc.getDeletableFlag() && _delSupport;
 		isWriteProtected = desc.getWriteProtectedFlag();
+		isLocked = desc.getLocked();
 
 		// Don't allow the user to change the description of write protected games
 		if (isWriteProtected)
@@ -499,9 +527,9 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
 
 
 	if (_list->isEditable()) {
-		// Disable the save button if nothing is selected, or if the selected
-		// game is write protected
-		_chooseButton->setEnabled(selItem >= 0 && !isWriteProtected);
+		// Disable the save button if slot is locked, nothing is selected,
+		// or if the selected game is write protected
+		_chooseButton->setEnabled(!isLocked && selItem >= 0 && !isWriteProtected);
 
 		if (startEditMode) {
 			_list->startEditMode();
@@ -513,13 +541,13 @@ void SaveLoadChooserSimple::updateSelection(bool redraw) {
 			}
 		}
 	} else {
-		// Disable the load button if nothing is selected, or if an empty
-		// list item is selected.
-		_chooseButton->setEnabled(selItem >= 0 && !_list->getSelectedString().empty());
+		// Disable the load button if slot is locked, nothing is selected,
+		// or if an empty list item is selected.
+		_chooseButton->setEnabled(!isLocked && selItem >= 0 && !_list->getSelectedString().empty());
 	}
 
 	// Delete will always be disabled if the engine doesn't support it.
-	_deleteButton->setEnabled(isDeletable && (selItem >= 0) && (!_list->getSelectedString().empty()));
+	_deleteButton->setEnabled(isDeletable && !isLocked && (selItem >= 0) && (!_list->getSelectedString().empty()));
 
 	if (redraw) {
 		_gfxWidget->draw();
@@ -565,7 +593,6 @@ void SaveLoadChooserSimple::close() {
 
 void SaveLoadChooserSimple::updateSaveList() {
 	SaveLoadChooserDialog::updateSaveList();
-	_saveList = _metaEngine->listSaves(_target.c_str());
 
 	int curSlot = 0;
 	int saveSlot = 0;
@@ -598,7 +625,7 @@ void SaveLoadChooserSimple::updateSaveList() {
 			description = _("Untitled savestate");
 			colors.push_back(ThemeEngine::kFontColorAlternate);
 		} else {
-			colors.push_back(ThemeEngine::kFontColorNormal);
+			colors.push_back((x->getLocked() ? ThemeEngine::kFontColorAlternate : ThemeEngine::kFontColorNormal));
 		}
 
 		saveNames.push_back(description);
@@ -724,7 +751,6 @@ void SaveLoadChooserGrid::handleMouseWheel(int x, int y, int direction) {
 
 void SaveLoadChooserGrid::updateSaveList() {
 	SaveLoadChooserDialog::updateSaveList();
-	_saveList = _metaEngine->listSaves(_target.c_str());
 	updateSaves();
 	draw();
 }
@@ -732,7 +758,7 @@ void SaveLoadChooserGrid::updateSaveList() {
 void SaveLoadChooserGrid::open() {
 	SaveLoadChooserDialog::open();
 
-	_saveList = _metaEngine->listSaves(_target.c_str());
+	listSaves();
 	_resultString.clear();
 
 	// Load information to restore the last page the user had open.
@@ -973,7 +999,7 @@ void SaveLoadChooserGrid::updateSaves() {
 	for (uint i = _curPage * _entriesPerPage, curNum = 0; i < _saveList.size() && curNum < _entriesPerPage; ++i, ++curNum) {
 		const uint saveSlot = _saveList[i].getSaveSlot();
 
-		SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), saveSlot);
+		SaveStateDescriptor desc =  (_saveList[i].getLocked() ? _saveList[i] : _metaEngine->querySaveMetaInfos(_target.c_str(), saveSlot));
 		SlotButton &curButton = _buttons[curNum];
 		curButton.setVisible(true);
 		const Graphics::Surface *thumbnail = desc.getThumbnail();
@@ -984,6 +1010,10 @@ void SaveLoadChooserGrid::updateSaves() {
 		}
 		curButton.description->setLabel(Common::String::format("%d. %s", saveSlot, desc.getDescription().c_str()));
 
+		//that would make it look "disabled" if slot is locked
+		curButton.button->setEnabled(!desc.getLocked());
+		curButton.description->setEnabled(!desc.getLocked());
+
 		Common::String tooltip(_("Name: "));
 		tooltip += desc.getDescription();
 
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 435bfc7..a81e03c 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -89,8 +89,20 @@ public:
 
 protected:
 	virtual int runIntern() = 0;
+
+	/** Common function to refresh the list on the screen. */
 	virtual void updateSaveList();
 
+	/**
+	* Common function to get saves list from MetaEngine.
+	*
+	* It also checks whether there are some locked saves
+	* because of saves sync and adds such saves as locked
+	* slots. User sees these slots, but is unable to save
+	* or load from these.
+	*/
+	virtual void listSaves();
+
 	const bool				_saveMode;
 	const MetaEngine		*_metaEngine;
 	bool					_delSupport;
@@ -100,6 +112,7 @@ protected:
 	bool					_playTimeSupport;
 	Common::String			_target;
 	bool _dialogWasShown;
+	SaveStateList			_saveList;
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	ButtonWidget *_listButton;
@@ -128,8 +141,8 @@ public:
 
 	virtual void open();
 	virtual void close();
-protected:
-	virtual void updateSaveList();
+protected:	
+	virtual void updateSaveList();	
 private:
 	virtual int runIntern();
 
@@ -142,7 +155,6 @@ private:
 	StaticTextWidget	*_time;
 	StaticTextWidget	*_playtime;
 
-	SaveStateList			_saveList;
 	String					_resultString;
 	
 	void updateSelection(bool redraw);
@@ -194,7 +206,6 @@ private:
 	uint _columns, _lines;
 	uint _entriesPerPage;
 	uint _curPage;
-	SaveStateList _saveList;
 
 	ButtonWidget *_nextButton;
 	ButtonWidget *_prevButton;


Commit: 67789c3c1621e4b59167bd9f0c0d38eec122dd90
    https://github.com/scummvm/scummvm/commit/67789c3c1621e4b59167bd9f0c0d38eec122dd90
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Update SaveLoadCloudSyncProgressDialog

So now it's centered, includes a progress bar and two labels instead of
one. Works fine in 320x200.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 5360bfe..af4db17 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -44,20 +44,47 @@ enum {
 	kBackgroundSyncCmd = 'PDBS'
 };
 
-SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(10, 10, 320, 100), _close(false) {
-	int x = 10;
-	int buttonHeight = 24;
-	int buttonWidth = 140;
+SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(0,0,0,0), _close(false) {
+	const int screenW = g_system->getOverlayWidth();
+	const int screenH = g_system->getOverlayHeight();
+
+	int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0) * 1.4; // "Run in background" is too long
+	int buttonHeight = g_gui.xmlEval()->getVar("Globals.Button.Height", 0);
+	int progressBarHeight = buttonHeight;
+
+	int marginAround = 8;
 	int marginBottom = 8;
+	int marginBetween = 10;	
+
+	_w = screenW * 80 / 100;
+	_h = 0;
+	_h += buttonHeight + marginBottom; //buttons
+	_h += 2 * (kLineHeight + 2 * marginAround); //top label + bottom label
+	_h += progressBarHeight; //progress bar
+	if (_h > screenH) _h = screenH;
+
+	// Center the dialog
+	_x = (screenW - _w) / 2;
+	_y = (screenH - _h) / 2;	
 	
+	_label = new StaticTextWidget(this, marginAround, marginAround, _w - marginAround * 2, kLineHeight, "Downloading saves...", Graphics::kTextAlignCenter);
+
 	uint32 progress = (uint32)(100 * CloudMan.getSyncDownloadingProgress());
-	_label = new StaticTextWidget(this, 10, 10, 300, kLineHeight, Common::String::format("Downloading saves (%u%% complete)...", progress), Graphics::kTextAlignCenter);
+	_progressBar = new SliderWidget(this, marginAround, marginAround * 2 + kLineHeight, _w - marginAround * 2, progressBarHeight);
+	_progressBar->setMinValue(0);
+	_progressBar->setMaxValue(100);
+	_progressBar->setValue(progress);
+	
+	_percentLabel = new StaticTextWidget(this, marginAround, marginAround * 3 + kLineHeight + progressBarHeight, _w - marginAround * 2, kLineHeight, Common::String::format("%u %%", progress), Graphics::kTextAlignCenter);
+
+	int x1 = (_w - buttonWidth * 2 - marginBetween) / 2;
+	int x2 = x1 + buttonWidth + marginBetween;
 
 	//if (defaultButton)
-		new ButtonWidget(this, x, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog
+		new ButtonWidget(this, x1, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog
 
 	//if (altButton)
-		new ButtonWidget(this, x + buttonWidth + 10, _h - buttonHeight - 8, buttonWidth, buttonHeight, "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
+		new ButtonWidget(this, x2, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
 }
 
 SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {}
@@ -65,7 +92,9 @@ SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {}
 void SaveLoadCloudSyncProgressDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch(cmd) {
 	case kSavesSyncProgressCmd:				
-		_label->setLabel(Common::String::format("Downloading saves (%u%% complete)...", data));
+		_percentLabel->setLabel(Common::String::format("%u%%", data));
+		_progressBar->setValue(data);
+		_progressBar->draw();
 		break;
 
 	case kCancelSyncCmd:
@@ -264,8 +293,7 @@ void SaveLoadChooserDialog::listSaves() {
 
 		//make up some slot number
 		int slotNum = 0;
-		for (int j = files[i].size() - 3; j < files[i].size(); ++j) { //3 last chars
-			if (j < 0) continue;
+		for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars			
 			char c = files[i][j];
 			if (c < '0' || c > '9') continue;
 			slotNum = slotNum * 10 + (c - '0');
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index a81e03c..d308535 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -36,7 +36,8 @@ enum SaveLoadCloudSyncProgress {
 };
 
 class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
-	StaticTextWidget *_label;
+	StaticTextWidget *_label, *_percentLabel;
+	SliderWidget *_progressBar;
 	bool _close;
 public:
 	SaveLoadCloudSyncProgressDialog();


Commit: b614869de80759991c50827f0f1f35c13ef87f0d
    https://github.com/scummvm/scummvm/commit/b614869de80759991c50827f0f1f35c13ef87f0d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor fixes

Changed paths:
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/savessyncrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index bf8e43d..5765892 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -48,6 +48,11 @@ DropboxUploadRequest::~DropboxUploadRequest() {
 void DropboxUploadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
+	if (!_contentsStream) {
+		warning("DropboxUploadRequest: cannot start because stream is invalid");
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
+	}
 	if (!_contentsStream->seek(0)) {
 		warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index f07d1d0..3c41c00 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -342,7 +342,11 @@ void SavesSyncRequest::finishSuccess(bool success) {
 }
 
 void SavesSyncRequest::loadTimestamps() {
-	//start with listing all the files in saves/ directory and setting invalid timestamp to them
+	//refresh the files list
+	Common::Array<Common::String> files;
+	g_system->getSavefileManager()->updateSavefilesList(files);
+
+	//start with listing all the files in saves/ directory and setting invalid timestamp to them	
 	Common::StringArray localFiles = g_system->getSavefileManager()->listSavefiles("*");
 	for (uint32 i = 0; i < localFiles.size(); ++i)
 		_localFilesTimestamps[localFiles[i]] = INVALID_TIMESTAMP;


Commit: f5cb5be393d482130d86794a84358e9b37f37af7
    https://github.com/scummvm/scummvm/commit/f5cb5be393d482130d86794a84358e9b37f37af7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add SaveLoadCloudSyncProgress in ScummModern theme

ScummVM would probably crash when using a theme without
SaveLoadCloudSyncProgress dialog described.

Changed paths:
    gui/gui-manager.cpp
    gui/saveload-dialog.cpp
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp
index 9acd943..9d68d76 100644
--- a/gui/gui-manager.cpp
+++ b/gui/gui-manager.cpp
@@ -216,7 +216,7 @@ void GuiManager::redraw() {
 	// Tanoku: Do not apply shading more than once when opening many dialogs
 	// on top of each other. Screen ends up being too dark and it's a
 	// performance hog.
-	if (_redrawStatus == kRedrawOpenDialog && _dialogStack.size() > 2)
+	if (_redrawStatus == kRedrawOpenDialog && _dialogStack.size() > 3)
 		shading = ThemeEngine::kShadingNone;
 
 	switch (_redrawStatus) {
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index af4db17..e9248bd 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -44,47 +44,16 @@ enum {
 	kBackgroundSyncCmd = 'PDBS'
 };
 
-SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog(0,0,0,0), _close(false) {
-	const int screenW = g_system->getOverlayWidth();
-	const int screenH = g_system->getOverlayHeight();
-
-	int buttonWidth = g_gui.xmlEval()->getVar("Globals.Button.Width", 0) * 1.4; // "Run in background" is too long
-	int buttonHeight = g_gui.xmlEval()->getVar("Globals.Button.Height", 0);
-	int progressBarHeight = buttonHeight;
-
-	int marginAround = 8;
-	int marginBottom = 8;
-	int marginBetween = 10;	
-
-	_w = screenW * 80 / 100;
-	_h = 0;
-	_h += buttonHeight + marginBottom; //buttons
-	_h += 2 * (kLineHeight + 2 * marginAround); //top label + bottom label
-	_h += progressBarHeight; //progress bar
-	if (_h > screenH) _h = screenH;
-
-	// Center the dialog
-	_x = (screenW - _w) / 2;
-	_y = (screenH - _h) / 2;	
-	
-	_label = new StaticTextWidget(this, marginAround, marginAround, _w - marginAround * 2, kLineHeight, "Downloading saves...", Graphics::kTextAlignCenter);
-
+SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog("SaveLoadCloudSyncProgress"), _close(false) {
+	_label = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.TitleText", "Downloading saves...");
 	uint32 progress = (uint32)(100 * CloudMan.getSyncDownloadingProgress());
-	_progressBar = new SliderWidget(this, marginAround, marginAround * 2 + kLineHeight, _w - marginAround * 2, progressBarHeight);
+	_progressBar = new SliderWidget(this, "SaveLoadCloudSyncProgress.ProgressBar");
 	_progressBar->setMinValue(0);
 	_progressBar->setMaxValue(100);
 	_progressBar->setValue(progress);
-	
-	_percentLabel = new StaticTextWidget(this, marginAround, marginAround * 3 + kLineHeight + progressBarHeight, _w - marginAround * 2, kLineHeight, Common::String::format("%u %%", progress), Graphics::kTextAlignCenter);
-
-	int x1 = (_w - buttonWidth * 2 - marginBetween) / 2;
-	int x2 = x1 + buttonWidth + marginBetween;
-
-	//if (defaultButton)
-		new ButtonWidget(this, x1, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog
-
-	//if (altButton)
-		new ButtonWidget(this, x2, _h - buttonHeight - marginBottom, buttonWidth, buttonHeight, "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
+	_percentLabel = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.PercentText", Common::String::format("%u %%", progress));
+	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Cancel", "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog																																				
+	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Background", "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
 }
 
 SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {}
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 026fa7b..f14e447 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -1056,6 +1056,38 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SaveLoadCloudSyncProgress' overlays = 'screen_center' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'TitleText'
+					width = '496'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<widget name = 'ProgressBar'
+					width = '496'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					width = '496'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' center = 'true' spacing = '10'>
+				<widget name = 'Cancel'
+						width = '150'
+						height = 'Globals.Button.Height'
+				/>				
+				<widget name = 'Background'
+						width = '150'
+						height = 'Globals.Button.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name = 'SavenameDialog' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'DescriptionText'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 169e61a..7c2326d 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -1038,6 +1038,38 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SaveLoadCloudSyncProgress' overlays = 'screen_center' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'TitleText'
+					width = '240'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<widget name = 'ProgressBar'
+					width = '240'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					width = '240'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' center = 'true' spacing = '10'>
+				<widget name = 'Cancel'
+						width = '100'
+						height = 'Globals.Button.Height'
+				/>				
+				<widget name = 'Background'
+						width = '100'
+						height = 'Globals.Button.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name = 'SavenameDialog' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'DescriptionText'


Commit: f24a89e080df860f7a32a8b3c9f272c1623aedbb
    https://github.com/scummvm/scummvm/commit/f24a89e080df860f7a32a8b3c9f272c1623aedbb
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix SaveLoadCloudSyncProgressDialog

* disabled progress bar;
* removed syncTarget segfault;
* fixed grid slots disabling for "locked" slots.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index e9248bd..1ba1793 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -51,12 +51,15 @@ SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog("Save
 	_progressBar->setMinValue(0);
 	_progressBar->setMaxValue(100);
 	_progressBar->setValue(progress);
+	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.PercentText", Common::String::format("%u %%", progress));
 	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Cancel", "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog																																				
 	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Background", "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
 }
 
-SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {}
+SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {
+	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
+}
 
 void SaveLoadCloudSyncProgressDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch(cmd) {
@@ -146,6 +149,10 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const b
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 }
 
+SaveLoadChooserDialog::~SaveLoadChooserDialog() {
+	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
+}
+
 void SaveLoadChooserDialog::open() {
 	Dialog::open();
 
@@ -215,12 +222,15 @@ void SaveLoadChooserDialog::handleTickle() {
 	if (!_dialogWasShown && CloudMan.isSyncing()) {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
-			SaveLoadCloudSyncProgressDialog dialog;
-			CloudMan.setSyncTarget(&dialog);
-			int result = dialog.runModal();
-			if (result == kCancelSyncCmd) {
-				CloudMan.cancelSync();
+			{
+				SaveLoadCloudSyncProgressDialog dialog;
+				CloudMan.setSyncTarget(&dialog);
+				int result = dialog.runModal();
+				if (result == kCancelSyncCmd) {
+					CloudMan.cancelSync();
+				}
 			}
+			//dialog changes syncTarget to nullptr after that }
 			CloudMan.setSyncTarget(this);
 			_dialogWasShown = true;
 			updateSaveList();
@@ -253,6 +263,7 @@ void SaveLoadChooserDialog::updateSaveList() {
 }
 
 void SaveLoadChooserDialog::listSaves() {
+	if (!_metaEngine) return; //very strange
 	_saveList = _metaEngine->listSaves(_target.c_str());
 
 	Common::String pattern = _metaEngine->getSavefilesPattern(_target);
@@ -1007,10 +1018,6 @@ void SaveLoadChooserGrid::updateSaves() {
 		}
 		curButton.description->setLabel(Common::String::format("%d. %s", saveSlot, desc.getDescription().c_str()));
 
-		//that would make it look "disabled" if slot is locked
-		curButton.button->setEnabled(!desc.getLocked());
-		curButton.description->setEnabled(!desc.getLocked());
-
 		Common::String tooltip(_("Name: "));
 		tooltip += desc.getDescription();
 
@@ -1045,6 +1052,10 @@ void SaveLoadChooserGrid::updateSaves() {
 		} else {
 			curButton.button->setEnabled(true);
 		}
+
+		//that would make it look "disabled" if slot is locked
+		curButton.button->setEnabled(!desc.getLocked());
+		curButton.description->setEnabled(!desc.getLocked());
 	}
 
 	const uint numPages = (_entriesPerPage != 0 && !_saveList.empty()) ? ((_saveList.size() + _entriesPerPage - 1) / _entriesPerPage) : 1;
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index d308535..3f2c5df 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -70,6 +70,7 @@ class SaveLoadChooserDialog : protected Dialog {
 public:
 	SaveLoadChooserDialog(const Common::String &dialogName, const bool saveMode);
 	SaveLoadChooserDialog(int x, int y, int w, int h, const bool saveMode);
+	virtual ~SaveLoadChooserDialog();
 
 	virtual void open();
 


Commit: bb207ae513ef02bfaf8e76374af40419a20742fe
    https://github.com/scummvm/scummvm/commit/bb207ae513ef02bfaf8e76374af40419a20742fe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDriveResolveIdRequest

GoogleDriveResolveIdRequest gets a lowercase path and searches for the
specified file's id. To do that it lists path's subdirectories one by
one with GoogleDriveListDirectoryByIdRequest.

GoogleDriveListDirectoryByIdRequest gets a Google Drive id and lists
that directory (not recursively).

Changed paths:
  A backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
  A backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
  A backends/cloud/googledrive/googledriveresolveidrequest.cpp
  A backends/cloud/googledrive/googledriveresolveidrequest.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/storagefile.cpp
    backends/cloud/storagefile.h
    backends/module.mk



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 4230c14..03cc8f2 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -124,7 +124,14 @@ SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networ
 
 void CloudManager::testFeature() {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->info(nullptr, nullptr);
+	//if (storage) storage->info(nullptr, nullptr);
+	GoogleDrive::GoogleDriveStorage *gd = dynamic_cast<GoogleDrive::GoogleDriveStorage *>(storage);
+	if (gd) gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
+		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
+		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
+		//gd->createDirectoryWithParentId("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", "subfolder", nullptr, nullptr);
+		//gd->createDirectoryWithParentId("appDataFolder", "firstfolder", nullptr, nullptr);
+	else debug("FAILURE");
 }
 
 bool CloudManager::isWorking() {
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
new file mode 100644
index 0000000..0915b62
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -0,0 +1,144 @@
+/* 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 "backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+#include "googledrivetokenrefresher.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveListDirectoryByIdRequest::GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
+	 _workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveListDirectoryByIdRequest::~GoogleDriveListDirectoryByIdRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void GoogleDriveListDirectoryByIdRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_files.clear();
+	_ignoreCallback = false;
+
+	makeRequest("");
+}
+
+void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken) {
+	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=appDataFolder";
+	if (pageToken != "") url += "&pageToken=" + pageToken;
+	url += "&q=%27" + _requestedId + "%27+in+parents";
+
+	Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::JsonResponse>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryByIdRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
+	Common::JSONValue *json = response.value;
+	if (json) {
+		Common::JSONObject responseObject = json->asObject();
+
+		///debug("%s", json->stringify(true).c_str());
+		
+		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
+			warning("GoogleDrive returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
+			error.failed = true;
+			error.response = json->stringify();
+			finishError(error);
+			delete json;
+			return;
+		}
+
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+
+		if (responseObject.contains("files") && responseObject.getVal("files")->isArray()) {
+			Common::JSONArray items = responseObject.getVal("files")->asArray();
+			for (uint32 i = 0; i < items.size(); ++i) {
+				Common::JSONObject item = items[i]->asObject();
+				Common::String path = item.getVal("id")->asString();
+				Common::String name = item.getVal("name")->asString();
+				bool isDirectory = (item.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
+				uint32 size = 0, timestamp = 0;
+				if (!isDirectory) {
+					size = item.getVal("size")->asIntegerNumber();
+					timestamp = ISO8601::convertToTimestamp(item.getVal("modifiedTime")->asString());
+				}
+				_files.push_back(StorageFile(path, name, size, timestamp, isDirectory));
+			}
+		}
+
+		bool hasMore = (responseObject.contains("nextPageToken"));
+
+		if (hasMore) {
+			Common::String token = responseObject.getVal("nextPageToken")->asString();
+			makeRequest(token);
+		} else {			
+			finishSuccess(_files);
+		}		
+	} else {
+		warning("null, not json");
+		error.failed = true;
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void GoogleDriveListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveListDirectoryByIdRequest::handle() {}
+
+void GoogleDriveListDirectoryByIdRequest::restart() { start(); }
+
+void GoogleDriveListDirectoryByIdRequest::finishSuccess(Common::Array<StorageFile> &files) {	
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
new file mode 100644
index 0000000..5699845
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
@@ -0,0 +1,61 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYBYIDREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYBYIDREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
+	Common::String _requestedId;	
+	GoogleDriveStorage *_storage;
+
+	Storage::ListDirectoryCallback _listDirectoryCallback;
+	Common::Array<StorageFile> _files;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	
+	void start();
+	void makeRequest(Common::String pageToken);
+	void responseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishSuccess(Common::Array<StorageFile> &files);
+public:
+	GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb);
+	virtual ~GoogleDriveListDirectoryByIdRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
new file mode 100644
index 0000000..bde17b4
--- /dev/null
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -0,0 +1,128 @@
+/* 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 "backends/cloud/googledrive/googledriveresolveidrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "backends/cloud/googledrive/googledrivetokenrefresher.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveResolveIdRequest::GoogleDriveResolveIdRequest(GoogleDriveStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _storage(storage), _uploadCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveResolveIdRequest::~GoogleDriveResolveIdRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _uploadCallback;
+}
+
+void GoogleDriveResolveIdRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_currentDirectory = "";
+	_currentDirectoryId = "appDataFolder";
+	_ignoreCallback = false;
+	
+	listNextDirectory(StorageFile("", 0, 0, false));
+}
+
+void GoogleDriveResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
+	if (_currentDirectory == _requestedPath) {		
+		finishFile(fileToReturn);
+		return;
+	}
+
+	Storage::FileArrayCallback callback = new Common::Callback<GoogleDriveResolveIdRequest, Storage::FileArrayResponse>(this, &GoogleDriveResolveIdRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveResolveIdRequest, Networking::ErrorResponse>(this, &GoogleDriveResolveIdRequest::listedDirectoryErrorCallback);
+	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
+}
+
+void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	Common::String currentLevelName = _requestedPath;
+	///debug("'%s'", currentLevelName.c_str());
+	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
+	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
+	///debug("'%s'", currentLevelName.c_str());
+	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
+		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
+			currentLevelName.erase(i);
+			///debug("'%s'", currentLevelName.c_str());
+			break;
+		}
+	}
+
+	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
+
+	Common::Array<StorageFile> &files = response.value;
+	bool found = false;
+	for (uint32 i = 0; i < files.size(); ++i) {
+		if (files[i].isDirectory() && files[i].name() == currentLevelName) {
+			if (_currentDirectory != "") _currentDirectory += "/";
+			_currentDirectory += files[i].name();
+			_currentDirectoryId = files[i].path();
+			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
+			listNextDirectory(files[i]);
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		Common::String path = _currentDirectory;
+		if (path != "") path += "/";
+		path += currentLevelName;
+		if (path == _requestedPath) finishError(Networking::ErrorResponse(this, false, true, "no such file found in its parent directory", 404));
+		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
+	}
+}
+
+void GoogleDriveResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveResolveIdRequest::handle() {}
+
+void GoogleDriveResolveIdRequest::restart() { start(); }
+
+void GoogleDriveResolveIdRequest::finishFile(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.h b/backends/cloud/googledrive/googledriveresolveidrequest.h
new file mode 100644
index 0000000..cd6f244
--- /dev/null
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.h
@@ -0,0 +1,61 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVERESOLVEIDREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVERESOLVEIDREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveResolveIdRequest: public Networking::Request {
+	Common::String _requestedPath;	
+	GoogleDriveStorage *_storage;
+	Storage::UploadCallback _uploadCallback;
+	Common::String _currentDirectory;
+	Common::String _currentDirectoryId;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void listNextDirectory(StorageFile fileToReturn);
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishFile(StorageFile file);
+public:
+	GoogleDriveResolveIdRequest(GoogleDriveStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
+	virtual ~GoogleDriveResolveIdRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index eef7f1f..b0c0d47 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -31,6 +31,8 @@
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
+#include "googledrivelistdirectorybyidrequest.h"
+#include "googledriveresolveidrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -177,6 +179,25 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 	delete json;
 }
 
+void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	debug("%s", json->stringify(true).c_str());
+
+	if (outerCallback) {
+		Common::JSONObject info = json->asObject();
+		///(*outerCallback)(BoolResponse(nullptr, true);
+		delete outerCallback;
+	}
+
+	delete json;
+}
+
 void GoogleDriveStorage::printJson(Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
@@ -211,11 +232,23 @@ void GoogleDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback
 	delete response.value;
 }
 
+Networking::Request *GoogleDriveStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<GoogleDriveStorage, UploadResponse>(this, &GoogleDriveStorage::printFile);
+	return addRequest(new GoogleDriveResolveIdRequest(this, path, callback, errorCallback));
+}
+
 Networking::Request *GoogleDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	//return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
 	return nullptr; //TODO
 }
 
+Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
+	return addRequest(new GoogleDriveListDirectoryByIdRequest(this, id, callback, errorCallback));
+}
+
 Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	//return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));
 	return nullptr; //TODO
@@ -240,8 +273,11 @@ void GoogleDriveStorage::fileDownloaded(BoolResponse response) {
 void GoogleDriveStorage::printFiles(FileArrayResponse response) {
 	debug("files:");
 	Common::Array<StorageFile> &files = response.value;
-	for (uint32 i = 0; i < files.size(); ++i)
+	for (uint32 i = 0; i < files.size(); ++i) {
+		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
 		debug("\t%s", files[i].path().c_str());
+		debug("");
+	}
 }
 
 void GoogleDriveStorage::printBool(BoolResponse response) {
@@ -250,7 +286,8 @@ void GoogleDriveStorage::printBool(BoolResponse response) {
 
 void GoogleDriveStorage::printFile(UploadResponse response) {
 	debug("\nuploaded file info:");
-	debug("\tpath: %s", response.value.path().c_str());
+	debug("\tid: %s", response.value.path().c_str());
+	debug("\tname: %s", response.value.name().c_str());
 	debug("\tsize: %u", response.value.size());
 	debug("\ttimestamp: %u", response.value.timestamp());
 }
@@ -268,6 +305,33 @@ Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, Bo
 	return nullptr; //TODO
 }
 
+Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	//return addRequest(new GoogleDriveCreateDirectoryRequest(this, path, callback, errorCallback));
+	Common::String url = "https://www.googleapis.com/drive/v3/files";
+	//Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::JsonResponse>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
+	//Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryByIdRequest::errorCallback);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + accessToken());
+	request->addHeader("Content-Type: application/json");
+
+	Common::JSONArray parentsArray;
+	parentsArray.push_back(new Common::JSONValue(parentId));
+
+	Common::JSONObject jsonRequestParameters;
+	jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder"));
+	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+	jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
+	//jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
+
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	return addRequest(request);
+	return nullptr; //TODO
+}
+
 Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!callback) callback = new Common::Callback<GoogleDriveStorage, StorageInfoResponse>(this, &GoogleDriveStorage::printInfo);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::infoInnerCallback, callback);
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 8a82a54..274bc78 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -52,6 +52,9 @@ class GoogleDriveStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
+	/** Returns bool based on JSON response from cloud. */
+	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json);
+
 	void printJson(Networking::JsonResponse response);
 	void fileDownloaded(BoolResponse response);
 	void printFiles(FileArrayResponse response);
@@ -59,7 +62,7 @@ class GoogleDriveStorage: public Cloud::Storage {
 	void printFile(UploadResponse response);
 	void printInfo(StorageInfoResponse response);
 
-	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
+	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);	
 public:	
 	virtual ~GoogleDriveStorage();
 
@@ -78,9 +81,15 @@ public:
 
 	/** Public Cloud API comes down there. */
 
-	/** Returns ListDirectoryStatus struct with list of files. */
+	/** Returns StorageFile with the resolved file's id. */
+	virtual Networking::Request *resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns Array<StorageFile> - the list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
+	/** Returns Array<StorageFile> - the list of files. */
+	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
+
 	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
@@ -93,6 +102,9 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
+	/** Calls the callback when finished. */
+	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
+
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index b37b5e0..452cce3 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -53,4 +53,12 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 	_isDirectory = dir;
 }
 
+StorageFile::StorageFile(Common::String id, Common::String name, uint32 sz, uint32 ts, bool dir) {
+	_path = id;
+	_name = name;
+	_size = sz;
+	_timestamp = ts;
+	_isDirectory = dir;
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index 704a038..7503653 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -41,6 +41,9 @@ public:
 	StorageFile(); //invalid empty file
 	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir);
 
+	/** In this constructor <path> is used to storage <id> (in Google Drive, for example) */
+	StorageFile(Common::String id, Common::String name, uint32 sz, uint32 ts, bool dir);
+
 	Common::String path() const { return _path; }
 	Common::String name() const { return _name; }
 	uint32 size() const { return _size; }
diff --git a/backends/module.mk b/backends/module.mk
index f1ba8b8..14755b2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -32,6 +32,8 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
+	cloud/googledrive/googledrivelistdirectorybyidrequest.o \
+	cloud/googledrive/googledriveresolveidrequest.o \
 	cloud/googledrive/googledrivestorage.o \
 	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/onedrive/onedrivestorage.o \


Commit: b4b6ee0186750d6f2e8143313fc059c20512c306
    https://github.com/scummvm/scummvm/commit/b4b6ee0186750d6f2e8143313fc059c20512c306
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDriveCreateDirectory

Now we can create directories in Google Drive by path, not parent id +
directory name!

Changed paths:
  A backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
  A backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/googledrive/googledriveresolveidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/module.mk



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 03cc8f2..0be52cd 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -126,7 +126,38 @@ void CloudManager::testFeature() {
 	Storage *storage = getCurrentStorage();
 	//if (storage) storage->info(nullptr, nullptr);
 	GoogleDrive::GoogleDriveStorage *gd = dynamic_cast<GoogleDrive::GoogleDriveStorage *>(storage);
-	if (gd) gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
+	if (gd) {
+		//new folder in root: +
+		//gd->createDirectory("newfolder1", nullptr, nullptr);
+
+		//check it's there: +
+		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
+
+		//new folder in firstfolder: +
+		//gd->createDirectory("firstfolder/newfolder2", nullptr, nullptr);
+
+		//check it's there: +
+		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
+
+		//create existing folder in firstfolder: +
+		//gd->createDirectory("firstfolder/subfolder", nullptr, nullptr);
+
+		//check no new folder there: +
+		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
+
+		//create folder in subfolder: +
+		//gd->createDirectory("firstfolder/subfolder/newfolder3", nullptr, nullptr);
+
+		//check it's there: +
+		//gd->listDirectoryById("1OysvorQlmGl2ObMGb1c-JnjfC5yFL-Zj7AsQQhNNBnrk", nullptr, nullptr);
+
+		//one more time: +
+		//gd->createDirectory("firstfolder/subfolder/newfolder3/megafolder", nullptr, nullptr);
+
+		//check it's there: +
+		gd->listDirectoryById("1OXWPtfNgnmR_1K7SDm2v5J923bbAWrTdVDj-zRppLZDw", nullptr, nullptr);
+	}
+		//gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
 		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
 		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
 		//gd->createDirectoryWithParentId("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", "subfolder", nullptr, nullptr);
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
new file mode 100644
index 0000000..213f030
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
@@ -0,0 +1,114 @@
+/* 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 "backends/cloud/googledrive/googledrivecreatedirectoryrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "common/debug.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveCreateDirectoryRequest::GoogleDriveCreateDirectoryRequest(GoogleDriveStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb),
+	_requestedParentPath(parentPath), _requestedDirectoryName(directoryName), _storage(storage), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveCreateDirectoryRequest::~GoogleDriveCreateDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void GoogleDriveCreateDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_currentDirectory = "";
+	_currentDirectoryId = "appDataFolder";
+	_ignoreCallback = false;
+	
+	//find out the parent id
+	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::UploadResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolveFailedCallback);
+	Common::String path = _requestedParentPath;
+	path += "/";
+	path += _requestedDirectoryName;
+	_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//resolved => folder already exists
+	finishSuccess(false);
+}
+
+void GoogleDriveCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	//not resolved => folder not exists
+	if (error.response.contains("no such file found in its parent directory")) {		
+		//parent's id after the '\n'
+		Common::String parentId = error.response;
+		for (uint32 i = 0; i < parentId.size(); ++i)
+			if (parentId[i] == '\n') {
+				parentId.erase(0, i+1);
+				break;
+			}
+
+		Storage::BoolCallback callback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::BoolResponse>(this, &GoogleDriveCreateDirectoryRequest::createdDirectoryCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback);
+		_workingRequest = _storage->createDirectoryWithParentId(parentId, _requestedDirectoryName, callback, failureCallback);
+		return;
+	}
+
+	finishError(error);
+}
+
+void GoogleDriveCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishSuccess(response.value);
+}
+
+void GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveCreateDirectoryRequest::handle() {}
+
+void GoogleDriveCreateDirectoryRequest::restart() { start(); }
+
+void GoogleDriveCreateDirectoryRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
new file mode 100644
index 0000000..0ac178e
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
@@ -0,0 +1,62 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVECREATEDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVECREATEDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveCreateDirectoryRequest: public Networking::Request {
+	Common::String _requestedParentPath;
+	Common::String _requestedDirectoryName;
+	GoogleDriveStorage *_storage;
+	Storage::BoolCallback _boolCallback;
+	Common::String _currentDirectory;
+	Common::String _currentDirectoryId;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void createdDirectoryCallback(Storage::BoolResponse response);
+	void createdDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
+public:
+	GoogleDriveCreateDirectoryRequest(GoogleDriveStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~GoogleDriveCreateDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index bde17b4..5dcb5c6 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -104,7 +104,7 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 		Common::String path = _currentDirectory;
 		if (path != "") path += "/";
 		path += currentLevelName;
-		if (path == _requestedPath) finishError(Networking::ErrorResponse(this, false, true, "no such file found in its parent directory", 404));
+		if (path == _requestedPath) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
 		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
 	}
 }
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index b0c0d47..1750dc9 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -33,6 +33,7 @@
 #include <curl/curl.h>
 #include "googledrivelistdirectorybyidrequest.h"
 #include "googledriveresolveidrequest.h"
+#include "googledrivecreatedirectoryrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -187,11 +188,9 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
 		return;
 	}
 
-	debug("%s", json->stringify(true).c_str());
-
 	if (outerCallback) {
-		Common::JSONObject info = json->asObject();
-		///(*outerCallback)(BoolResponse(nullptr, true);
+		Common::JSONObject info = json->asObject();		
+		(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
 		delete outerCallback;
 	}
 
@@ -240,7 +239,7 @@ Networking::Request *GoogleDriveStorage::resolveFileId(Common::String path, Uplo
 
 Networking::Request *GoogleDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	//return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
-	return nullptr; //TODO
+	return nullptr;
 }
 
 Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
@@ -301,16 +300,30 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 
 Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	//return addRequest(new GoogleDriveCreateDirectoryRequest(this, path, callback, errorCallback));
-	return nullptr; //TODO
+	if (!callback) callback = new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::printBool);
+
+	//find out the parent path and directory name
+	Common::String parentPath = "", directoryName = path;
+	for (uint32 i = path.size(); i > 0; --i) {
+		if (path[i-1] == '/' || path[i-1] == '\\') {
+			parentPath = path;
+			parentPath.erase(i-1);
+			directoryName.erase(0, i);
+			break;
+		}
+	}
+
+	if (parentPath == "") {
+		return createDirectoryWithParentId("appDataFolder", directoryName, callback, errorCallback);
+	}
+
+	return addRequest(new GoogleDriveCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
 }
 
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	//return addRequest(new GoogleDriveCreateDirectoryRequest(this, path, callback, errorCallback));
-	Common::String url = "https://www.googleapis.com/drive/v3/files";
-	//Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::JsonResponse>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
-	//Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryByIdRequest::errorCallback);
+	
+	Common::String url = "https://www.googleapis.com/drive/v3/files";	
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
@@ -323,13 +336,11 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str
 	jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder"));
 	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
 	jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
-	//jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
 
 	Common::JSONValue value(jsonRequestParameters);
 	request->addPostField(Common::JSON::stringify(&value));
 
 	return addRequest(request);
-	return nullptr; //TODO
 }
 
 Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/module.mk b/backends/module.mk
index 14755b2..9387be5 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -32,6 +32,7 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
+	cloud/googledrive/googledrivecreatedirectoryrequest.o \
 	cloud/googledrive/googledrivelistdirectorybyidrequest.o \
 	cloud/googledrive/googledriveresolveidrequest.o \
 	cloud/googledrive/googledrivestorage.o \


Commit: d1d71afb0724c372143d2c303c70291ba43d2d68
    https://github.com/scummvm/scummvm/commit/d1d71afb0724c372143d2c303c70291ba43d2d68
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDriveListDirectoryRequest

When listing directories, you get a list of StorageFiles, which path()
is actually Google Drive id. Thus, if you list a directory recursively,
you won't be able to determine whether all files are within one
directory or have some hierarchy. I'd fix that as soon as it would be
needed.

Changed paths:
  A backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
  A backends/cloud/googledrive/googledrivelistdirectoryrequest.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/googledrive/googledriveresolveidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/module.mk



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 0be52cd..64fa19c 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -155,7 +155,11 @@ void CloudManager::testFeature() {
 		//gd->createDirectory("firstfolder/subfolder/newfolder3/megafolder", nullptr, nullptr);
 
 		//check it's there: +
-		gd->listDirectoryById("1OXWPtfNgnmR_1K7SDm2v5J923bbAWrTdVDj-zRppLZDw", nullptr, nullptr);
+		//gd->listDirectoryById("1OXWPtfNgnmR_1K7SDm2v5J923bbAWrTdVDj-zRppLZDw", nullptr, nullptr);
+
+		//gd->listDirectory("", nullptr, nullptr);
+		//gd->listDirectory("firstfolder", nullptr, nullptr);
+		gd->listDirectory("firstfolder/subfolder", nullptr, nullptr, true);
 	}
 		//gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
 		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
new file mode 100644
index 0000000..803682b
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
@@ -0,0 +1,117 @@
+/* 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 "backends/cloud/googledrive/googledrivelistdirectoryrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveListDirectoryRequest::GoogleDriveListDirectoryRequest(GoogleDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveListDirectoryRequest::~GoogleDriveListDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void GoogleDriveListDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_files.clear();
+	_directoriesQueue.clear();
+	_currentDirectory = "";
+	_ignoreCallback = false;
+
+	//find out that directory's id
+	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Storage::UploadResponse>(this, &GoogleDriveListDirectoryRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryRequest::idResolveErrorCallback);
+	_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	_directoriesQueue.push_back(response.value.path());
+	listNextDirectory();
+}
+
+void GoogleDriveListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveListDirectoryRequest::listNextDirectory() {
+	if (_directoriesQueue.empty()) {
+		finishSuccess(_files);
+		return;
+	}
+
+	_currentDirectory = _directoriesQueue.back();
+	_directoriesQueue.pop_back();
+
+	Storage::FileArrayCallback callback = new Common::Callback<GoogleDriveListDirectoryRequest, Storage::FileArrayResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback);	
+	_workingRequest = _storage->listDirectoryById(_currentDirectory, callback, failureCallback);
+}
+
+void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	for (uint32 i = 0; i < response.value.size(); ++i) {
+		StorageFile &file = response.value[i];
+		_files.push_back(file);
+		if (_requestedRecursive && file.isDirectory()) {
+			_directoriesQueue.push_back(file.path());
+		}
+	}
+
+	listNextDirectory();
+}
+
+void GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveListDirectoryRequest::handle() {}
+
+void GoogleDriveListDirectoryRequest::restart() { start(); }
+
+void GoogleDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
new file mode 100644
index 0000000..3eee83f
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
@@ -0,0 +1,65 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveListDirectoryRequest: public Networking::Request {
+	Common::String _requestedPath;
+	bool _requestedRecursive;
+	GoogleDriveStorage *_storage;
+	Storage::ListDirectoryCallback _listDirectoryCallback;
+	Common::Array<StorageFile> _files;
+	Common::Array<Common::String> _directoriesQueue;
+	Common::String _currentDirectory;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveErrorCallback(Networking::ErrorResponse error);
+	void listNextDirectory();
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(Common::Array<StorageFile> &files);
+public:
+	GoogleDriveListDirectoryRequest(GoogleDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
+	virtual ~GoogleDriveListDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index 5dcb5c6..0ef0cd9 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -53,7 +53,7 @@ void GoogleDriveResolveIdRequest::start() {
 	_currentDirectoryId = "appDataFolder";
 	_ignoreCallback = false;
 	
-	listNextDirectory(StorageFile("", 0, 0, false));
+	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
 }
 
 void GoogleDriveResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 1750dc9..3c85b1f 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -34,6 +34,7 @@
 #include "googledrivelistdirectorybyidrequest.h"
 #include "googledriveresolveidrequest.h"
 #include "googledrivecreatedirectoryrequest.h"
+#include "googledrivelistdirectoryrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -238,8 +239,9 @@ Networking::Request *GoogleDriveStorage::resolveFileId(Common::String path, Uplo
 }
 
 Networking::Request *GoogleDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	//return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
-	return nullptr;
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
+	return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));	
 }
 
 Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/module.mk b/backends/module.mk
index 9387be5..c4a8ae4 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -34,6 +34,7 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/googledrive/googledrivecreatedirectoryrequest.o \
 	cloud/googledrive/googledrivelistdirectorybyidrequest.o \
+	cloud/googledrive/googledrivelistdirectoryrequest.o \
 	cloud/googledrive/googledriveresolveidrequest.o \
 	cloud/googledrive/googledrivestorage.o \
 	cloud/googledrive/googledrivetokenrefresher.o \


Commit: 505d3764cb9c873b1dbefcb0d7b2cc7b587c6a42
    https://github.com/scummvm/scummvm/commit/505d3764cb9c873b1dbefcb0d7b2cc7b587c6a42
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix GoogleDriveStorage to work with root folder

Now it needs another scope and uses "root" instead of "appDataFolder".

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledriveresolveidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 64fa19c..7613b2c 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -127,39 +127,6 @@ void CloudManager::testFeature() {
 	//if (storage) storage->info(nullptr, nullptr);
 	GoogleDrive::GoogleDriveStorage *gd = dynamic_cast<GoogleDrive::GoogleDriveStorage *>(storage);
 	if (gd) {
-		//new folder in root: +
-		//gd->createDirectory("newfolder1", nullptr, nullptr);
-
-		//check it's there: +
-		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
-
-		//new folder in firstfolder: +
-		//gd->createDirectory("firstfolder/newfolder2", nullptr, nullptr);
-
-		//check it's there: +
-		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
-
-		//create existing folder in firstfolder: +
-		//gd->createDirectory("firstfolder/subfolder", nullptr, nullptr);
-
-		//check no new folder there: +
-		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
-
-		//create folder in subfolder: +
-		//gd->createDirectory("firstfolder/subfolder/newfolder3", nullptr, nullptr);
-
-		//check it's there: +
-		//gd->listDirectoryById("1OysvorQlmGl2ObMGb1c-JnjfC5yFL-Zj7AsQQhNNBnrk", nullptr, nullptr);
-
-		//one more time: +
-		//gd->createDirectory("firstfolder/subfolder/newfolder3/megafolder", nullptr, nullptr);
-
-		//check it's there: +
-		//gd->listDirectoryById("1OXWPtfNgnmR_1K7SDm2v5J923bbAWrTdVDj-zRppLZDw", nullptr, nullptr);
-
-		//gd->listDirectory("", nullptr, nullptr);
-		//gd->listDirectory("firstfolder", nullptr, nullptr);
-		gd->listDirectory("firstfolder/subfolder", nullptr, nullptr, true);
 	}
 		//gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
 		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
index 213f030..1554fef 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
@@ -45,8 +45,6 @@ void GoogleDriveCreateDirectoryRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
 	_workingRequest = nullptr;
-	_currentDirectory = "";
-	_currentDirectoryId = "appDataFolder";
 	_ignoreCallback = false;
 	
 	//find out the parent id
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
index 0ac178e..6c5ccdd 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
@@ -37,8 +37,6 @@ class GoogleDriveCreateDirectoryRequest: public Networking::Request {
 	Common::String _requestedDirectoryName;
 	GoogleDriveStorage *_storage;
 	Storage::BoolCallback _boolCallback;
-	Common::String _currentDirectory;
-	Common::String _currentDirectoryId;
 	Request *_workingRequest;
 	bool _ignoreCallback;
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 0915b62..f7aa7a1 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -55,7 +55,8 @@ void GoogleDriveListDirectoryByIdRequest::start() {
 }
 
 void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken) {
-	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=appDataFolder";
+	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken";
+	//files(id,mimeType,modifiedTime,name,size),nextPageToken
 	if (pageToken != "") url += "&pageToken=" + pageToken;
 	url += "&q=%27" + _requestedId + "%27+in+parents";
 
@@ -66,6 +67,17 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken)
 	_workingRequest = ConnMan.addRequest(request);
 }
 
+namespace {
+uint64 atoull(Common::String s) {
+	uint64 result = 0;
+	for (uint32 i = 0; i < s.size(); ++i) {
+		if (s[i] < '0' || s[i] > '9') break;
+		result = result * 10L + (s[i] - '0');
+	}
+	return result;
+}
+}
+
 void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
@@ -100,10 +112,10 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 				Common::String name = item.getVal("name")->asString();
 				bool isDirectory = (item.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
 				uint32 size = 0, timestamp = 0;
-				if (!isDirectory) {
-					size = item.getVal("size")->asIntegerNumber();
+				if (item.contains("size") && item.getVal("size")->isString())
+					size = atoull(item.getVal("size")->asString());
+				if (item.contains("modifiedTime") && item.getVal("modifiedTime")->isString())
 					timestamp = ISO8601::convertToTimestamp(item.getVal("modifiedTime")->asString());
-				}
 				_files.push_back(StorageFile(path, name, size, timestamp, isDirectory));
 			}
 		}
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index 0ef0cd9..a9adb82 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -50,7 +50,7 @@ void GoogleDriveResolveIdRequest::start() {
 	if (_workingRequest) _workingRequest->finish();
 	_workingRequest = nullptr;
 	_currentDirectory = "";
-	_currentDirectoryId = "appDataFolder";
+	_currentDirectoryId = "root";
 	_ignoreCallback = false;
 	
 	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 3c85b1f..dce35b5 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -316,7 +316,7 @@ Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, Bo
 	}
 
 	if (parentPath == "") {
-		return createDirectoryWithParentId("appDataFolder", directoryName, callback, errorCallback);
+		return createDirectoryWithParentId("root", directoryName, callback, errorCallback);
 	}
 
 	return addRequest(new GoogleDriveCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
@@ -379,7 +379,7 @@ Common::String GoogleDriveStorage::getAuthLink() {
 	url += "&redirect_uri=http://localhost"; //that's for copy-pasting
 	//url += "&redirect_uri=http%3A%2F%2Flocalhost"; //that's "http://localhost" for automatic opening
 	url += "&client_id="; url += KEY;	
-	url += "&scope=https://www.googleapis.com/auth/drive.appfolder"; //for copy-pasting
+	url += "&scope=https://www.googleapis.com/auth/drive"; //for copy-pasting
 	return url;
 }
 


Commit: 74a3eba8d67ec5909b950e6b2486ba5e102fb8d0
    https://github.com/scummvm/scummvm/commit/74a3eba8d67ec5909b950e6b2486ba5e102fb8d0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix ConnectionManager

It now keeps newly added Requests in separate array, so iterators don't
break when one adds a Request while ConnMan iterates its array.

The array is also shielded with mutex.

Changed paths:
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index c675d0d..edc7e81 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -66,8 +66,10 @@ void ConnectionManager::registerEasyHandle(CURL *easy) {
 }
 
 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
-	_requests.push_back(RequestWithCallback(request, callback));
-	if (!_timerStarted) startTimer();
+	_addedRequestsMutex.lock();	
+	_addedRequests.push_back(RequestWithCallback(request, callback));
+	if (!_timerStarted) startTimer();	
+	_addedRequestsMutex.unlock();
 	return request;
 }
 
@@ -98,21 +100,36 @@ void ConnectionManager::stopTimer() {
 	_timerStarted = false;
 }
 
+bool ConnectionManager::hasAddedRequests() {
+	_addedRequestsMutex.lock();
+	bool hasNewRequests = !_addedRequests.empty();
+	_addedRequestsMutex.unlock();
+	return hasNewRequests;
+}
+
 void ConnectionManager::handle() {
 	//lock mutex here (in case another handle() would be called before this one ends)
 	_handleMutex.lock();
 	++_frame;
 	if (_frame % CLOUD_PERIOD == 0) interateRequests();
 	if (_frame % CURL_PERIOD == 0) processTransfers();
-
-	if (_icon.draw() && _requests.empty())
+	
+	if (_icon.draw() && _requests.empty() && !hasAddedRequests())
 		stopTimer();
 	_handleMutex.unlock();
 }
 
 void ConnectionManager::interateRequests() {
+	//add new requests
+	_addedRequestsMutex.lock();
+	for (Common::Array<RequestWithCallback>::iterator i = _addedRequests.begin(); i != _addedRequests.end(); ++i) {
+		_requests.push_back(*i);
+	}
+	_addedRequests.clear();
+	_addedRequestsMutex.unlock();
+
 	//call handle() of all running requests (so they can do their work)
-	debug("handling %d request(s)", _requests.size());	
+	debug("handling %d request(s)", _requests.size());
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
 		Request *request = i->request;		
 		if (request) {
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 8b2153a..66994f0 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -77,8 +77,8 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 
 	CURLM *_multi;	
 	bool _timerStarted;
-	Common::Array<RequestWithCallback> _requests;
-	Common::Mutex _handleMutex;
+	Common::Array<RequestWithCallback> _requests, _addedRequests;
+	Common::Mutex _handleMutex, _addedRequestsMutex;
 	CloudIcon _icon;
 	uint32 _frame;
 	
@@ -87,6 +87,7 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	void handle();
 	void interateRequests();
 	void processTransfers();
+	bool hasAddedRequests();
 
 public:
 	ConnectionManager();


Commit: c968f0143c5e36cc9fc429832622eb180732caf8
    https://github.com/scummvm/scummvm/commit/c968f0143c5e36cc9fc429832622eb180732caf8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make GoogleDriveResolveIdRequest case-insensitive

Changed paths:
    backends/cloud/googledrive/googledriveresolveidrequest.cpp



diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index a9adb82..9cd13a7 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -57,7 +57,7 @@ void GoogleDriveResolveIdRequest::start() {
 }
 
 void GoogleDriveResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
-	if (_currentDirectory == _requestedPath) {		
+	if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
 		finishFile(fileToReturn);
 		return;
 	}
@@ -89,7 +89,7 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 	Common::Array<StorageFile> &files = response.value;
 	bool found = false;
 	for (uint32 i = 0; i < files.size(); ++i) {
-		if (files[i].isDirectory() && files[i].name() == currentLevelName) {
+		if (files[i].isDirectory() && files[i].name().equalsIgnoreCase(currentLevelName)) {
 			if (_currentDirectory != "") _currentDirectory += "/";
 			_currentDirectory += files[i].name();
 			_currentDirectoryId = files[i].path();
@@ -104,7 +104,7 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 		Common::String path = _currentDirectory;
 		if (path != "") path += "/";
 		path += currentLevelName;
-		if (path == _requestedPath) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
+		if (path.equalsIgnoreCase(_requestedPath)) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
 		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
 	}
 }


Commit: bf71ba9a1c98b39647edb248e913322ee38a0af5
    https://github.com/scummvm/scummvm/commit/bf71ba9a1c98b39647edb248e913322ee38a0af5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GoogleDriveCreateDirectoryRequest

Now it also creates the "base" ScummVM directory if there is no such
directory yet. This way SavesSyncRequest works fine when no "ScummVM" or
"ScummVM/Saves" folder exist in the Google Drive.

Changed paths:
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/savessyncrequest.cpp



diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
index 1554fef..54eff3d 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
@@ -46,12 +46,38 @@ void GoogleDriveCreateDirectoryRequest::start() {
 	if (_workingRequest) _workingRequest->finish();
 	_workingRequest = nullptr;
 	_ignoreCallback = false;
+
+	//the only exception when we create parent folder - is when it's ScummVM/ base folder
+	Common::String prefix = _requestedParentPath;
+	if (prefix.size() > 7) prefix.erase(7);
+	if (prefix.equalsIgnoreCase("ScummVM")) {
+		Storage::BoolCallback callback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::BoolResponse>(this, &GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::createdBaseDirectoryErrorCallback);
+		_workingRequest = _storage->createDirectory("ScummVM", callback, failureCallback);
+		return;
+	}
 	
-	//find out the parent id
+	resolveId();
+}
+
+void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	resolveId();
+}
+
+void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveCreateDirectoryRequest::resolveId() {
+	//check whether such folder already exists
 	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::UploadResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolvedCallback);
 	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolveFailedCallback);
 	Common::String path = _requestedParentPath;
-	path += "/";
+	if (_requestedParentPath != "") path += "/";
 	path += _requestedDirectoryName;
 	_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
 }
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
index 6c5ccdd..ede8427 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
@@ -41,6 +41,9 @@ class GoogleDriveCreateDirectoryRequest: public Networking::Request {
 	bool _ignoreCallback;
 
 	void start();
+	void createdBaseDirectoryCallback(Storage::BoolResponse response);
+	void createdBaseDirectoryErrorCallback(Networking::ErrorResponse error);
+	void resolveId();
 	void idResolvedCallback(Storage::UploadResponse response);
 	void idResolveFailedCallback(Networking::ErrorResponse error);
 	void createdDirectoryCallback(Storage::BoolResponse response);
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index dce35b5..d7dbd52 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -315,10 +315,6 @@ Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, Bo
 		}
 	}
 
-	if (parentPath == "") {
-		return createDirectoryWithParentId("root", directoryName, callback, errorCallback);
-	}
-
 	return addRequest(new GoogleDriveCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
 }
 
@@ -353,7 +349,7 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
 	return addRequest(request);	
 }
 
-Common::String GoogleDriveStorage::savesDirectoryPath() { return "saves/"; }
+Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; }
 
 GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 3c41c00..1d4ec4c 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -158,9 +158,18 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 						}
 					}
 				}
+
+				//TODO: Google Drive-related JSON error
 			}
 			delete value;
 		}
+
+		//Google Drive-related ScummVM-based error
+		if (error.response.contains("subdirectory not found")) {
+			irrecoverable = false; //base "/ScummVM/" folder not found
+		} else if (error.response.contains("no such file found in its parent directory")) {
+			irrecoverable = false; //"Saves" folder within "/ScummVM/" not found
+		}
 	}
 
 	if (irrecoverable) {


Commit: ab1d160ec8f99e472667b83aa4bdd7697b702f3a
    https://github.com/scummvm/scummvm/commit/ab1d160ec8f99e472667b83aa4bdd7697b702f3a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
ALL: Add MetaEngine::simpleSaveNames()

Engines with "simple" savenames would support "Run in background" in
save/load dialog and gradual save slots unlocking. Other engines
save/load feature would be locked until save sync is over.

Changed paths:
    engines/access/detection.cpp
    engines/adl/detection.cpp
    engines/agi/detection.cpp
    engines/agos/detection.cpp
    engines/avalanche/detection.cpp
    engines/bbvs/detection.cpp
    engines/cge/detection.cpp
    engines/cge2/detection.cpp
    engines/cine/detection.cpp
    engines/cruise/detection.cpp
    engines/draci/detection.cpp
    engines/drascula/detection.cpp
    engines/dreamweb/detection.cpp
    engines/fullpipe/detection.cpp
    engines/gnap/detection.cpp
    engines/groovie/detection.cpp
    engines/hopkins/detection.cpp
    engines/hugo/detection.cpp
    engines/kyra/detection.cpp
    engines/lab/detection.cpp
    engines/lure/detection.cpp
    engines/mads/detection.cpp
    engines/metaengine.h
    engines/mohawk/detection.cpp
    engines/mortevielle/detection.cpp
    engines/neverhood/detection.cpp
    engines/parallaction/detection.cpp
    engines/pegasus/detection.cpp
    engines/prince/detection.h
    engines/prince/saveload.cpp
    engines/queen/detection.cpp
    engines/saga/detection.cpp
    engines/sci/detection.cpp
    engines/scumm/detection.cpp
    engines/sherlock/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    engines/sword25/detection.cpp
    engines/teenagent/detection.cpp
    engines/tinsel/detection.cpp
    engines/toltecs/detection.cpp
    engines/tony/detection.cpp
    engines/toon/detection.cpp
    engines/touche/detection.cpp
    engines/tsage/detection.cpp
    engines/tucker/detection.cpp
    engines/voyeur/detection.cpp
    engines/wage/detection.cpp
    engines/wintermute/detection.cpp
    engines/zvision/detection.cpp
    gui/saveload-dialog.cpp



diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp
index 368753f..435f0d1 100644
--- a/engines/access/detection.cpp
+++ b/engines/access/detection.cpp
@@ -100,6 +100,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -171,6 +172,8 @@ SaveStateList AccessMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool AccessMetaEngine::simpleSaveNames() const { return true; }
+
 int AccessMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
index 4bdb722..7031a58 100644
--- a/engines/adl/detection.cpp
+++ b/engines/adl/detection.cpp
@@ -175,6 +175,7 @@ public:
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	int getMaximumSaveSlot() const { return 'O' - 'A'; }
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	void removeSaveState(const char *target, int slot) const;
 
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
@@ -289,6 +290,8 @@ SaveStateList AdlMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool AdlMetaEngine::simpleSaveNames() const { return true; }
+
 void AdlMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.s%02d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(fileName);
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 9f66d78..dc0dbbd 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -216,6 +216,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -323,6 +324,8 @@ SaveStateList AgiMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool AgiMetaEngine::simpleSaveNames() const { return true; }
+
 int AgiMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void AgiMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index 2c89522..dc96eb6 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -120,6 +120,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 };
 
@@ -207,6 +208,8 @@ SaveStateList AgosMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool AgosMetaEngine::simpleSaveNames() const { return true; }
+
 int AgosMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 #if PLUGIN_ENABLED_DYNAMIC(AGOS)
diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp
index e35c5d2..392d0a0 100644
--- a/engines/avalanche/detection.cpp
+++ b/engines/avalanche/detection.cpp
@@ -83,6 +83,7 @@ public:
 
 	int getMaximumSaveSlot() const { return 99; }
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 };
@@ -156,6 +157,8 @@ SaveStateList AvalancheMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool AvalancheMetaEngine::simpleSaveNames() const { return true; }
+
 void AvalancheMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(fileName);
diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp
index 7c0045e..9aca719 100644
--- a/engines/bbvs/detection.cpp
+++ b/engines/bbvs/detection.cpp
@@ -86,6 +86,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -135,6 +136,8 @@ SaveStateList BbvsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool BbvsMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor BbvsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String filename = Bbvs::BbvsEngine::getSavegameFilename(target, slot);
 	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 0c79be5..eb88b6c 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -131,7 +131,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual Common::String getSavefilesPattern(Common::String &target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -240,9 +240,7 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-Common::String CGEMetaEngine::getSavefilesPattern(Common::String &target) const {
-	return target + ".###";
-}
+bool CGEMetaEngine::simpleSaveNames() const { return true; }
 
 SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp
index 2b84d16..d980f82 100644
--- a/engines/cge2/detection.cpp
+++ b/engines/cge2/detection.cpp
@@ -127,6 +127,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -239,6 +240,8 @@ SaveStateList CGE2MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool CGE2MetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index ec01e87..2d10b81 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -104,6 +104,7 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -173,6 +174,8 @@ SaveStateList CineMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool CineMetaEngine::simpleSaveNames() const { return false; }
+
 int CineMetaEngine::getMaximumSaveSlot() const { return 9; }
 
 void CineMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index 6f5d236..9e3ebb5 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -211,6 +211,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -254,6 +255,8 @@ SaveStateList CruiseMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool CruiseMetaEngine::simpleSaveNames() const { return false; }
+
 void CruiseMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Cruise::CruiseEngine::getSavegameFile(slot));
 }
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index 65427bd..8a67981 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -98,6 +98,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -147,6 +148,8 @@ SaveStateList DraciMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool DraciMetaEngine::simpleSaveNames() const { return false; }
+
 void DraciMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Draci::DraciEngine::getSavegameFile(slot));
 }
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index ffec393..863ea98 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -326,6 +326,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -382,6 +383,8 @@ SaveStateList DrasculaMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool DrasculaMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 
diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp
index 8e24c44..abe1198 100644
--- a/engines/dreamweb/detection.cpp
+++ b/engines/dreamweb/detection.cpp
@@ -86,6 +86,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -151,6 +152,8 @@ SaveStateList DreamWebMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool DreamWebMetaEngine::simpleSaveNames() const { return false; }
+
 int DreamWebMetaEngine::getMaximumSaveSlot() const { return 99; }
 
 void DreamWebMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/fullpipe/detection.cpp b/engines/fullpipe/detection.cpp
index 6f92f19..a183be8 100644
--- a/engines/fullpipe/detection.cpp
+++ b/engines/fullpipe/detection.cpp
@@ -90,6 +90,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 8; }
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -134,6 +135,8 @@ SaveStateList FullpipeMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool FullpipeMetaEngine::simpleSaveNames() const { return false; }
+
 void FullpipeMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Fullpipe::getSavegameFile(slot));
 }
diff --git a/engines/gnap/detection.cpp b/engines/gnap/detection.cpp
index 7e4ab56..523a8b6 100644
--- a/engines/gnap/detection.cpp
+++ b/engines/gnap/detection.cpp
@@ -78,6 +78,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -141,6 +142,8 @@ SaveStateList GnapMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool GnapMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor GnapMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index b12e264..22ced10 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -352,6 +352,7 @@ public:
 
 	bool hasFeature(MetaEngineFeature f) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -376,6 +377,8 @@ SaveStateList GroovieMetaEngine::listSaves(const char *target) const {
 	return SaveLoad::listValidSaves(target);
 }
 
+bool GroovieMetaEngine::simpleSaveNames() const { return false; }
+
 int GroovieMetaEngine::getMaximumSaveSlot() const {
 	return SaveLoad::getMaximumSlot();
 }
diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp
index cfdbf80..84af4fc 100644
--- a/engines/hopkins/detection.cpp
+++ b/engines/hopkins/detection.cpp
@@ -117,6 +117,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -182,6 +183,8 @@ SaveStateList HopkinsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool HopkinsMetaEngine::simpleSaveNames() const { return true; }
+
 int HopkinsMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index 4e4746c..ed67eae 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -149,6 +149,7 @@ public:
 
 	int getMaximumSaveSlot() const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	void removeSaveState(const char *target, int slot) const;
 };
@@ -221,6 +222,8 @@ SaveStateList HugoMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool HugoMetaEngine::simpleSaveNames() const { return false; }
+
 SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 989a45b..1dcfd08 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -162,6 +162,7 @@ public:
 	bool hasFeature(MetaEngineFeature f) const;
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -273,6 +274,8 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool KyraMetaEngine::simpleSaveNames() const { return true; }
+
 int KyraMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/lab/detection.cpp b/engines/lab/detection.cpp
index 30890b5..2b2e57d 100644
--- a/engines/lab/detection.cpp
+++ b/engines/lab/detection.cpp
@@ -138,6 +138,7 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -191,6 +192,8 @@ SaveStateList LabMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool LabMetaEngine::simpleSaveNames() const { return true; }
+
 int LabMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp
index 690a358..808cea4 100644
--- a/engines/lure/detection.cpp
+++ b/engines/lure/detection.cpp
@@ -212,6 +212,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -266,6 +267,8 @@ SaveStateList LureMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool LureMetaEngine::simpleSaveNames() const { return false; }
+
 int LureMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void LureMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp
index 4736503..d5354c4 100644
--- a/engines/mads/detection.cpp
+++ b/engines/mads/detection.cpp
@@ -155,6 +155,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -217,6 +218,8 @@ SaveStateList MADSMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool MADSMetaEngine::simpleSaveNames() const { return true; }
+
 int MADSMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 913f61d..6afb122 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -116,14 +116,18 @@ public:
 	}
 
 	/**
-	* Return a common pattern which all engine's save filenames should match.
+	* Return whether engine's saves could be detected with
+	* "<target>.###" pattern and "###" corresponds to slot
+	* number.
 	*
-	* @param target	name of a config manager target
-	* @return			a pattern for filenames
+	* If that's not true or engine is using some unusual way
+	* of detecting saves and slot numbers, this should return
+	* false. In that case Save/Load dialog would be unavailable
+	* during cloud saves sync.
+	*
+	* @return	true, if "<target>.###" is OK for this engine
 	*/
-	virtual Common::String getSavefilesPattern(Common::String &target) const {
-		return target + ".s##";
-	}
+	virtual bool simpleSaveNames() const { return false; }
 
 	/**
 	 * Return a list of extra GUI options for the specified target.
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 246d3ec..d3c44a8 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -200,6 +200,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
 	SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 999; }
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -272,6 +273,8 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool MohawkMetaEngine::simpleSaveNames() const { return false; }
+
 void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
 
 	// Removing saved games is only supported in Myst/Riven currently.
diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp
index 6791707..2f5a790 100644
--- a/engines/mortevielle/detection.cpp
+++ b/engines/mortevielle/detection.cpp
@@ -72,6 +72,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 };
 
@@ -102,6 +103,8 @@ SaveStateList MortevielleMetaEngine::listSaves(const char *target) const {
 	return Mortevielle::SavegameManager::listSaves(target);
 }
 
+bool MortevielleMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor MortevielleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String filename = Mortevielle::MortevielleEngine::generateSaveFilename(target, slot);
 	return Mortevielle::SavegameManager::querySaveMetaInfos(filename);
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
index 0f409a6..903c437 100644
--- a/engines/neverhood/detection.cpp
+++ b/engines/neverhood/detection.cpp
@@ -214,6 +214,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -283,6 +284,8 @@ SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool NeverhoodMetaEngine::simpleSaveNames() const { return true; }
+
 int NeverhoodMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index 4c52990..989fc9d 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -234,6 +234,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -294,6 +295,8 @@ SaveStateList ParallactionMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ParallactionMetaEngine::simpleSaveNames() const { return false; }
+
 int ParallactionMetaEngine::getMaximumSaveSlot() const { return 99; }
 
 void ParallactionMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
index 161a133..54cb4ca 100644
--- a/engines/pegasus/detection.cpp
+++ b/engines/pegasus/detection.cpp
@@ -148,6 +148,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 999; }
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -178,6 +179,8 @@ SaveStateList PegasusMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool PegasusMetaEngine::simpleSaveNames() const { return false; }
+
 void PegasusMetaEngine::removeSaveState(const char *target, int slot) const {
 	// See listSaves() for info on the pattern
 	Common::StringArray fileNames = Pegasus::PegasusEngine::listSaveFiles();
diff --git a/engines/prince/detection.h b/engines/prince/detection.h
index 3076253..39cfdd9 100644
--- a/engines/prince/detection.h
+++ b/engines/prince/detection.h
@@ -121,6 +121,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp
index d3360ba..2855bdc 100644
--- a/engines/prince/saveload.cpp
+++ b/engines/prince/saveload.cpp
@@ -105,6 +105,8 @@ SaveStateList PrinceMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool PrinceMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp
index aed8b7d..3f8b97e 100644
--- a/engines/queen/detection.cpp
+++ b/engines/queen/detection.cpp
@@ -444,6 +444,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual void removeSaveState(const char *target, int slot) const;
 
@@ -529,6 +530,8 @@ SaveStateList QueenMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool QueenMetaEngine::simpleSaveNames() const { return false; }
+
 void QueenMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String filename = Common::String::format("queen.s%02d", slot);
 
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 0677e84..7f6e0a2 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -144,6 +144,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -207,6 +208,8 @@ SaveStateList SagaMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool SagaMetaEngine::simpleSaveNames() const { return true; }
+
 int SagaMetaEngine::getMaximumSaveSlot() const { return MAX_SAVES - 1; }
 
 void SagaMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index ad2b0f3..08794a0 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -518,6 +518,7 @@ public:
 	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -790,6 +791,8 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool SciMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int slotNr) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slotNr);
 	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 4c9d122..67344e8 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -960,6 +960,7 @@ public:
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -1299,6 +1300,8 @@ SaveStateList ScummMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ScummMetaEngine::simpleSaveNames() const { return true; }
+
 void ScummMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
 	g_system->getSavefileManager()->removeSavefile(filename);
diff --git a/engines/sherlock/detection.cpp b/engines/sherlock/detection.cpp
index 5a94b34..f54c6db 100644
--- a/engines/sherlock/detection.cpp
+++ b/engines/sherlock/detection.cpp
@@ -159,6 +159,8 @@ public:
 	 */
 	virtual SaveStateList listSaves(const char *target) const;
 
+	virtual bool simpleSaveNames() const;
+
 	/**
 	 * Returns the maximum number of allowed save slots
 	 */
@@ -217,6 +219,8 @@ SaveStateList SherlockMetaEngine::listSaves(const char *target) const {
 	return Sherlock::SaveManager::getSavegameList(target);
 }
 
+bool SherlockMetaEngine::simpleSaveNames() const { return true; }
+
 int SherlockMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVEGAME_SLOTS;
 }
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index 4b91f50..802687b 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -78,12 +78,13 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual GameList getSupportedGames() const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
-	virtual GameDescriptor findGame(const char *gameid) const;
+	virtual GameDescriptor findGame(const char *gameid) const;	
 	virtual GameList detectGames(const Common::FSList &fslist) const;
 
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -247,6 +248,8 @@ SaveStateList SkyMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool SkyMetaEngine::simpleSaveNames() const { return false; }
+
 int SkyMetaEngine::getMaximumSaveSlot() const { return MAX_SAVE_GAMES; }
 
 void SkyMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 0edf856..d9cc3cd 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -91,6 +91,7 @@ public:
 	virtual GameDescriptor findGame(const char *gameid) const;
 	virtual GameList detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -263,6 +264,8 @@ SaveStateList SwordMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool SwordMetaEngine::simpleSaveNames() const { return false; }
+
 int SwordMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void SwordMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 44371bf..3213a26 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -97,6 +97,7 @@ public:
 	virtual GameDescriptor findGame(const char *gameid) const;
 	virtual GameList detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 
@@ -256,6 +257,8 @@ SaveStateList Sword2MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool Sword2MetaEngine::simpleSaveNames() const { return true; }
+
 int Sword2MetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void Sword2MetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
index c5f55b5..1c4544c 100644
--- a/engines/sword25/detection.cpp
+++ b/engines/sword25/detection.cpp
@@ -69,6 +69,7 @@ public:
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); }
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 };
 
 bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
@@ -109,6 +110,8 @@ SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool Sword25MetaEngine::simpleSaveNames() const { return false; }
+
 #if PLUGIN_ENABLED_DYNAMIC(SWORD25)
 	REGISTER_PLUGIN_DYNAMIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
 #else
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index caa7bdb..a8d32e8 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -149,6 +149,8 @@ public:
 		return saveList;
 	}
 
+	virtual bool simpleSaveNames() const { return false; }
+
 	virtual int getMaximumSaveSlot() const {
 		return MAX_SAVES - 1;
 	}
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index c44f1f4..7d8b54e 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -101,6 +101,7 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -164,6 +165,8 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool TinselMetaEngine::simpleSaveNames() const { return true; }
+
 bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc;
 	if (gd) {
diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp
index 7c70789..8da9106 100644
--- a/engines/toltecs/detection.cpp
+++ b/engines/toltecs/detection.cpp
@@ -221,6 +221,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -288,6 +289,8 @@ SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ToltecsMetaEngine::simpleSaveNames() const { return true; }
+
 int ToltecsMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp
index ec0b3e1..5dda4c7 100644
--- a/engines/tony/detection.cpp
+++ b/engines/tony/detection.cpp
@@ -82,6 +82,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -141,6 +142,8 @@ SaveStateList TonyMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool TonyMetaEngine::simpleSaveNames() const { return false; }
+
 int TonyMetaEngine::getMaximumSaveSlot() const {
 	return 99;
 }
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index 5d2e0a9..eac91a5 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -148,6 +148,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -212,6 +213,8 @@ SaveStateList ToonMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ToonMetaEngine::simpleSaveNames() const { return true; }
+
 SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index dcb58ff..e2737d4 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -155,6 +155,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -210,6 +211,8 @@ SaveStateList ToucheMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ToucheMetaEngine::simpleSaveNames() const { return false; }
+
 int ToucheMetaEngine::getMaximumSaveSlot() const {
 	return Touche::kMaxSaveStates - 1;
 }
diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp
index 584ad87..716ac4a 100644
--- a/engines/tsage/detection.cpp
+++ b/engines/tsage/detection.cpp
@@ -145,6 +145,8 @@ public:
 		return saveList;
 	}
 
+	virtual bool simpleSaveNames() const { return true; }
+
 	virtual int getMaximumSaveSlot() const {
 		return MAX_SAVES - 1;
 	}
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index 2447e15..227924c 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -187,6 +187,8 @@ public:
 		return saveList;
 	}
 
+	virtual bool simpleSaveNames() const { return false; }
+
 	virtual int getMaximumSaveSlot() const {
 		return Tucker::kLastSaveSlot;
 	}
diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp
index 7b9fa67..76668eb 100644
--- a/engines/voyeur/detection.cpp
+++ b/engines/voyeur/detection.cpp
@@ -81,6 +81,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -143,6 +144,8 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool VoyeurMetaEngine::simpleSaveNames() const { return true; }
+
 int VoyeurMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp
index a27bfd7..1069d74 100644
--- a/engines/wage/detection.cpp
+++ b/engines/wage/detection.cpp
@@ -70,6 +70,7 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -136,6 +137,8 @@ SaveStateList WageMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool WageMetaEngine::simpleSaveNames() const { return true; }
+
 int WageMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void WageMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp
index 4e8eab5..f225cd3 100644
--- a/engines/wintermute/detection.cpp
+++ b/engines/wintermute/detection.cpp
@@ -164,6 +164,8 @@ public:
 		return saves;
 	}
 
+	virtual bool simpleSaveNames() const { return false; }
+
 	int getMaximumSaveSlot() const {
 		return 100;
 	}
diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp
index cc96707..05dfb8b 100644
--- a/engines/zvision/detection.cpp
+++ b/engines/zvision/detection.cpp
@@ -75,6 +75,7 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	SaveStateList listSaves(const char *target) const;
+	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -159,6 +160,8 @@ SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
+bool ZVisionMetaEngine::simpleSaveNames() const { return true; }
+
 int ZVisionMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 1ba1793..0dde1a4 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -266,25 +266,27 @@ void SaveLoadChooserDialog::listSaves() {
 	if (!_metaEngine) return; //very strange
 	_saveList = _metaEngine->listSaves(_target.c_str());
 
-	Common::String pattern = _metaEngine->getSavefilesPattern(_target);
-	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
-	for (uint32 i = 0; i < files.size(); ++i) {		
-		if (!files[i].matchString(pattern, true)) continue;
-
-		//make up some slot number
-		int slotNum = 0;
-		for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars			
-			char c = files[i][j];
-			if (c < '0' || c > '9') continue;
-			slotNum = slotNum * 10 + (c - '0');
+	if (_metaEngine->simpleSaveNames()) {
+		Common::String pattern = _target + ".###";
+		Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
+		for (uint32 i = 0; i < files.size(); ++i) {
+			if (!files[i].matchString(pattern, true)) continue;
+
+			//make up some slot number
+			int slotNum = 0;
+			for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars			
+				char c = files[i][j];
+				if (c < '0' || c > '9') continue;
+				slotNum = slotNum * 10 + (c - '0');
+			}
+
+			SaveStateDescriptor slot(slotNum, files[i]);
+			slot.setLocked(true);
+			_saveList.push_back(slot);
 		}
 
-		SaveStateDescriptor slot(slotNum, files[i]);
-		slot.setLocked(true);
-		_saveList.push_back(slot);
+		Common::sort(_saveList.begin(), _saveList.end(), SaveStateDescriptorSlotComparator());
 	}
-
-	Common::sort(_saveList.begin(), _saveList.end(), SaveStateDescriptorSlotComparator());
 }
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID


Commit: 62a640ff440919ae762072a6e6acea415f0957dd
    https://github.com/scummvm/scummvm/commit/62a640ff440919ae762072a6e6acea415f0957dd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Disable "Run in background" for "difficult" engines

During saves sync slots of MetaEngines with simpleSaveNames() == false
would not be available at all. User would have to wait for sync to
complete.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 0dde1a4..3ac6074 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -44,7 +44,7 @@ enum {
 	kBackgroundSyncCmd = 'PDBS'
 };
 
-SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog("SaveLoadCloudSyncProgress"), _close(false) {
+SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(bool canRunInBackground): Dialog("SaveLoadCloudSyncProgress"), _close(false) {
 	_label = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.TitleText", "Downloading saves...");
 	uint32 progress = (uint32)(100 * CloudMan.getSyncDownloadingProgress());
 	_progressBar = new SliderWidget(this, "SaveLoadCloudSyncProgress.ProgressBar");
@@ -54,7 +54,9 @@ SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(): Dialog("Save
 	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.PercentText", Common::String::format("%u %%", progress));
 	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Cancel", "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog																																				
-	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Background", "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
+	ButtonWidget *backgroundButton = new ButtonWidget(this, "SaveLoadCloudSyncProgress.Background", "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
+	backgroundButton->setEnabled(canRunInBackground);
+	draw();
 }
 
 SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {
@@ -223,7 +225,7 @@ void SaveLoadChooserDialog::handleTickle() {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
 			{
-				SaveLoadCloudSyncProgressDialog dialog;
+				SaveLoadCloudSyncProgressDialog dialog(_metaEngine ? _metaEngine->simpleSaveNames() : false);
 				CloudMan.setSyncTarget(&dialog);
 				int result = dialog.runModal();
 				if (result == kCancelSyncCmd) {
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 3f2c5df..890169c 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -40,7 +40,7 @@ class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
 	SliderWidget *_progressBar;
 	bool _close;
 public:
-	SaveLoadCloudSyncProgressDialog();
+	SaveLoadCloudSyncProgressDialog(bool canRunInBackground);
 	virtual ~SaveLoadCloudSyncProgressDialog();
 
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);


Commit: f6e69b62765482d290b7673e97fb7cf6a03fc943
    https://github.com/scummvm/scummvm/commit/f6e69b62765482d290b7673e97fb7cf6a03fc943
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix DefaultSaveFileManager again

Now openForLoading() and openForSaving() check whether file is locked,
so AGOS and SCUMM engines Ctrl+number and Alt+number hot keys shouldn't
be able to save/load in these slots during saves sync.

Changed paths:
    backends/saves/default/default-saves.cpp



diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index a669c2c..3fcb3cb 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -27,6 +27,10 @@
 
 #include "common/scummsys.h"
 
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#endif
+
 #if !defined(DISABLE_DEFAULT_SAVEFILEMANAGER)
 
 #include "backends/saves/default/default-saves.h"
@@ -109,6 +113,12 @@ Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String
 	if (getError().getCode() != Common::kNoError)
 		return nullptr;
 
+	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
+		if (filename == *i) {
+			return nullptr; //file is locked, no loading available
+		}
+	}
+
 	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
 	if (file == _saveFileCache.end()) {
 		return nullptr;
@@ -126,6 +136,12 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 	if (getError().getCode() != Common::kNoError)
 		return nullptr;
 
+	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
+		if (filename == *i) {			
+			return nullptr; //file is locked, no saving available
+		}
+	}
+
 	// Obtain node.
 	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
 	Common::FSNode fileNode;
@@ -209,6 +225,12 @@ void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
 	// Check that path exists and is usable.
 	checkPath(Common::FSNode(savePathName));
 
+#ifdef USE_CLOUD
+	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
+	if (!files.empty()) updateSavefilesList(files); //makes this cache invalid
+	else _lockedFiles = files;
+#endif
+
 	if (_cachedDirectory == savePathName) {
 		return;
 	}


Commit: 7e6a89c141d621b5caf8c66a58ecbcf60b2d52c1
    https://github.com/scummvm/scummvm/commit/7e6a89c141d621b5caf8c66a58ecbcf60b2d52c1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GoogleDriveStorage and StorageFile

Because of the Google Drive StorageFile now contains yet another field,
`id`. For other storages `id` == `path`, and thus all common Requests
(such as SavesSyncRequest, DownloadFolderRequest, etc) must be using
id() instead of path(). That way these Requests won't cause id resolving
which could be quite slow (when you call it for all files in the folder,
for example).

Changed paths:
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectoryrequest.h
    backends/cloud/googledrive/googledriveresolveidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/storagefile.cpp
    backends/cloud/storagefile.h



diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index f7aa7a1..f9af363 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -108,7 +108,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 			Common::JSONArray items = responseObject.getVal("files")->asArray();
 			for (uint32 i = 0; i < items.size(); ++i) {
 				Common::JSONObject item = items[i]->asObject();
-				Common::String path = item.getVal("id")->asString();
+				Common::String id = item.getVal("id")->asString();
 				Common::String name = item.getVal("name")->asString();
 				bool isDirectory = (item.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
 				uint32 size = 0, timestamp = 0;
@@ -116,7 +116,9 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 					size = atoull(item.getVal("size")->asString());
 				if (item.contains("modifiedTime") && item.getVal("modifiedTime")->isString())
 					timestamp = ISO8601::convertToTimestamp(item.getVal("modifiedTime")->asString());
-				_files.push_back(StorageFile(path, name, size, timestamp, isDirectory));
+
+				//as we list directory by id, we can't determine full path for the file, so we leave it empty
+				_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
 			}
 		}
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
index 803682b..8811ffc 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
@@ -46,7 +46,7 @@ void GoogleDriveListDirectoryRequest::start() {
 	_workingRequest = nullptr;
 	_files.clear();
 	_directoriesQueue.clear();
-	_currentDirectory = "";
+	_currentDirectory = StorageFile();
 	_ignoreCallback = false;
 
 	//find out that directory's id
@@ -59,7 +59,9 @@ void GoogleDriveListDirectoryRequest::idResolvedCallback(Storage::UploadResponse
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
-	_directoriesQueue.push_back(response.value.path());
+	StorageFile directory = response.value;
+	directory.setPath(_requestedPath);
+	_directoriesQueue.push_back(directory);
 	listNextDirectory();
 }
 
@@ -80,7 +82,7 @@ void GoogleDriveListDirectoryRequest::listNextDirectory() {
 
 	Storage::FileArrayCallback callback = new Common::Callback<GoogleDriveListDirectoryRequest, Storage::FileArrayResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback);	
-	_workingRequest = _storage->listDirectoryById(_currentDirectory, callback, failureCallback);
+	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
 }
 
 void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
@@ -89,9 +91,13 @@ void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArray
 
 	for (uint32 i = 0; i < response.value.size(); ++i) {
 		StorageFile &file = response.value[i];
+		Common::String path = _currentDirectory.path();
+		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+		path += file.name();
+		file.setPath(path);
 		_files.push_back(file);
 		if (_requestedRecursive && file.isDirectory()) {
-			_directoriesQueue.push_back(file.path());
+			_directoriesQueue.push_back(file);
 		}
 	}
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
index 3eee83f..bea195f 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
@@ -39,8 +39,8 @@ class GoogleDriveListDirectoryRequest: public Networking::Request {
 	GoogleDriveStorage *_storage;
 	Storage::ListDirectoryCallback _listDirectoryCallback;
 	Common::Array<StorageFile> _files;
-	Common::Array<Common::String> _directoriesQueue;
-	Common::String _currentDirectory;
+	Common::Array<StorageFile> _directoriesQueue;
+	StorageFile _currentDirectory;
 	Request *_workingRequest;
 	bool _ignoreCallback;
 
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index 9cd13a7..e3ab97e 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -92,7 +92,7 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 		if (files[i].isDirectory() && files[i].name().equalsIgnoreCase(currentLevelName)) {
 			if (_currentDirectory != "") _currentDirectory += "/";
 			_currentDirectory += files[i].name();
-			_currentDirectoryId = files[i].path();
+			_currentDirectoryId = files[i].id();
 			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
 			listNextDirectory(files[i]);
 			found = true;
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index d7dbd52..6b5c437 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -277,6 +277,7 @@ void GoogleDriveStorage::printFiles(FileArrayResponse response) {
 	for (uint32 i = 0; i < files.size(); ++i) {
 		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
 		debug("\t%s", files[i].path().c_str());
+		debug("\t%s", files[i].id().c_str());
 		debug("");
 	}
 }
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index 452cce3..4bee92e 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -25,6 +25,7 @@
 namespace Cloud {
 
 StorageFile::StorageFile() {
+	_id = "";
 	_path = "";
 	_name = "";
 	_size = 0;
@@ -33,6 +34,7 @@ StorageFile::StorageFile() {
 }
 
 StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
+	_id = pth;
 	_path = pth;
 
 	_name = pth;
@@ -53,8 +55,9 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 	_isDirectory = dir;
 }
 
-StorageFile::StorageFile(Common::String id, Common::String name, uint32 sz, uint32 ts, bool dir) {
-	_path = id;
+StorageFile::StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir) {
+	_id = id;
+	_path = path;
 	_name = name;
 	_size = sz;
 	_timestamp = ts;
diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index 7503653..1324caf 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -31,24 +31,33 @@ namespace Cloud {
  * StorageFile represents a file storaged on remote cloud storage.
  * It contains basic information about a file, and might be used
  * when listing directories or syncing files.
+ *
+ * Some storages (Google Drive, for example) don't have an actual
+ * path notation to address files. Instead, they are using ids.
+ * As resolving id by path is not a fast operation, it's required
+ * to use ids if they are known, but user-friendly paths are
+ * necessary too, because these are used by Requests.
+ *
+ * If storage supports path notation, id would actually contain path.
  */
 class StorageFile {
-	Common::String _path, _name;
+	Common::String _id, _path, _name;
 	uint32 _size, _timestamp;
 	bool _isDirectory;
 
 public:
 	StorageFile(); //invalid empty file
 	StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir);
+	StorageFile(Common::String id, Common::String path, Common::String name, uint32 sz, uint32 ts, bool dir);
 
-	/** In this constructor <path> is used to storage <id> (in Google Drive, for example) */
-	StorageFile(Common::String id, Common::String name, uint32 sz, uint32 ts, bool dir);
-
+	Common::String id() const { return _id; }
 	Common::String path() const { return _path; }
 	Common::String name() const { return _name; }
 	uint32 size() const { return _size; }
 	uint32 timestamp() const { return _timestamp; }
 	bool isDirectory() const { return _isDirectory; }
+
+	void setPath(Common::String path) { _path = path; }
 };
 
 } // End of namespace Cloud


Commit: 60add0df1b63dbd31b88edcabf0769eb675efab0
    https://github.com/scummvm/scummvm/commit/60add0df1b63dbd31b88edcabf0769eb675efab0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update Requests to use StorageFile::id()

Only two places to update, as others still require id resolving.

Changed paths:
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 19f6c6c..905b0c7 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -109,8 +109,8 @@ void FolderDownloadRequest::downloadNextFile() {
 			localPath = _localDirectoryPath + "/" + localPath;
 	}
 	debug("%s -> %s", remotePath.c_str(), localPath.c_str());
-	_workingRequest = _storage->download(
-		remotePath, localPath,
+	_workingRequest = _storage->downloadById(
+		_currentFile.id(), localPath,
 		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback),
 		new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::fileDownloadedErrorCallback)
 	);
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 1d4ec4c..cdf1dba 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -227,7 +227,7 @@ void SavesSyncRequest::downloadNextFile() {
 	///////
 	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	///////
-	_workingRequest = _storage->download(_currentDownloadingFile.path(), concatWithSavesPath(_currentDownloadingFile.name()),
+	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 5458276..f0c1319 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -92,6 +92,11 @@ Networking::Request *Storage::download(Common::String remotePath, Common::String
 	return addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
 }
 
+Networking::Request *Storage::downloadById(Common::String remoteId, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	//most Storages use paths instead of ids, so this should work
+	return download(remoteId, localPath, callback, errorCallback);
+}
+
 Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 058e831..3cf8fb5 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -122,6 +122,7 @@ public:
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *downloadById(Common::String remoteId, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
 	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);


Commit: f0d61084daf7292d157e451c7bfc5485757eac43
    https://github.com/scummvm/scummvm/commit/f0d61084daf7292d157e451c7bfc5485757eac43
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update downloading in Storages

Id should be used everywhere.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 7dde74f..307ea00 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -27,8 +27,8 @@
 
 namespace Cloud {
 
-DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile):
-	Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileName(remoteFile), _storage(storage),
+DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFileId, Common::DumpFile *dumpFile):
+	Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileId(remoteFileId), _storage(storage),
 	_remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
@@ -47,8 +47,8 @@ void DownloadRequest::start() {
 	//TODO: reopen DumpFile
 	_ignoreCallback = false;
 
-	_workingRequest = _storage->streamFile(
-		_remoteFileName,
+	_workingRequest = _storage->streamFileById(
+		_remoteFileId,
 		new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback),
 		new Common::Callback<DownloadRequest, Networking::ErrorResponse>(this, &DownloadRequest::streamErrorCallback)
 	);
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 9e3421d..def69d4 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -33,7 +33,7 @@ namespace Cloud {
 class DownloadRequest: public Networking::Request {	
 	Storage::BoolCallback _boolCallback;	
 	Common::DumpFile *_localFile;
-	Common::String _remoteFileName;
+	Common::String _remoteFileId;
 	Storage *_storage;
 	Networking::NetworkReadStream *_remoteFileStream;
 	Request *_workingRequest;
@@ -44,7 +44,7 @@ class DownloadRequest: public Networking::Request {
 	void streamErrorCallback(Networking::ErrorResponse error);
 	void finishSuccess(bool success);
 public:
-	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile);
+	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFileId, Common::DumpFile *dumpFile);
 	virtual ~DownloadRequest();
 
 	virtual void handle();
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 861a58d..038a168 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -117,7 +117,7 @@ Networking::Request *DropboxStorage::upload(Common::String path, Common::Seekabl
 	return addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback));
 }
 
-Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
+Networking::Request *DropboxStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index c186d1e..9ac0ffb 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -74,7 +74,7 @@ public:
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);	
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 6b5c437..c6e423e 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -263,6 +263,18 @@ Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Network
 	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
 	*/
+	//TODO: resolve id
+	//TODO: then call streamFileById()
+	return nullptr; //TODO
+}
+
+Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
+	return nullptr; //TODO
+}
+
+Networking::Request *GoogleDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	//TODO: resolve id
+	//TODO: then call downloadById()
 	return nullptr; //TODO
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 274bc78..a456030 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -95,6 +95,10 @@ public:
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index ca8a234..c494f38 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -204,7 +204,7 @@ Networking::Request *OneDriveStorage::upload(Common::String path, Common::Seekab
 	return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
 }
 
-Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
+Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index a09d68b..8afdac8 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -84,7 +84,7 @@ public:
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index f0c1319..08ab9e9 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -76,7 +76,17 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 	return upload(remotePath, f, callback, errorCallback);
 }
 
+Networking::Request *Storage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
+	//most Storages use paths instead of ids, so this should work
+	return streamFile(path, callback, errorCallback);
+}
+
 Networking::Request *Storage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	//most Storages use paths instead of ids, so this should work
+	return downloadById(remotePath, localPath, callback, errorCallback);
+}
+
+Networking::Request *Storage::downloadById(Common::String remoteId, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 
 	Common::DumpFile *f = new Common::DumpFile();
@@ -89,12 +99,7 @@ Networking::Request *Storage::download(Common::String remotePath, Common::String
 		return nullptr;
 	}
 
-	return addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f));
-}
-
-Networking::Request *Storage::downloadById(Common::String remoteId, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	//most Storages use paths instead of ids, so this should work
-	return download(remoteId, localPath, callback, errorCallback);
+	return addRequest(new DownloadRequest(this, callback, errorCallback, remoteId, f));
 }
 
 Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 3cf8fb5..4a5e123 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -118,7 +118,8 @@ public:
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Calls the callback when finished. */
 	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);


Commit: e273e3d6e8dcca6f7e70d3e7c4e6dfc836832378
    https://github.com/scummvm/scummvm/commit/e273e3d6e8dcca6f7e70d3e7c4e6dfc836832378
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDrive download-related requests

GoogleDriveDownloadRequest, which resolves file id and then downloads it
with GoogleDriveStorage::downloadById().

GoogleDriveStreamFileRequest, which resolves file id and then returns
file stream with GoogleDriveStorage::streamFileById().

This commit also adds GoogleDriveStorage::streamFileById() itself.

A minor GoogleDriveResolveIdRequest fix added.

With these one can download files from Google Drive.

Changed paths:
  A backends/cloud/googledrive/googledrivedownloadrequest.cpp
  A backends/cloud/googledrive/googledrivedownloadrequest.h
  A backends/cloud/googledrive/googledrivestreamfilerequest.cpp
  A backends/cloud/googledrive/googledrivestreamfilerequest.h
    backends/cloud/googledrive/googledriveresolveidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/module.mk



diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.cpp b/backends/cloud/googledrive/googledrivedownloadrequest.cpp
new file mode 100644
index 0000000..c885352
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivedownloadrequest.cpp
@@ -0,0 +1,91 @@
+/* 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 "backends/cloud/googledrive/googledrivedownloadrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveDownloadRequest::GoogleDriveDownloadRequest(GoogleDriveStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedFile(remotePath), _requestedLocalFile(localPath), _storage(storage), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveDownloadRequest::~GoogleDriveDownloadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void GoogleDriveDownloadRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_ignoreCallback = false;
+
+	//find file's id
+	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveDownloadRequest, Storage::UploadResponse>(this, &GoogleDriveDownloadRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveDownloadRequest, Networking::ErrorResponse>(this, &GoogleDriveDownloadRequest::idResolveFailedCallback);	
+	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveDownloadRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Storage::BoolCallback innerCallback = new Common::Callback<GoogleDriveDownloadRequest, Storage::BoolResponse>(this, &GoogleDriveDownloadRequest::downloadCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveDownloadRequest, Networking::ErrorResponse>(this, &GoogleDriveDownloadRequest::downloadErrorCallback);
+	_workingRequest = _storage->downloadById(response.value.id(), _requestedLocalFile, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveDownloadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveDownloadRequest::downloadCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishSuccess(response.value);
+}
+
+void GoogleDriveDownloadRequest::downloadErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveDownloadRequest::handle() {}
+
+void GoogleDriveDownloadRequest::restart() { start(); }
+
+void GoogleDriveDownloadRequest::finishSuccess(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.h b/backends/cloud/googledrive/googledrivedownloadrequest.h
new file mode 100644
index 0000000..89bd313
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivedownloadrequest.h
@@ -0,0 +1,59 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEDOWNLOADREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEDOWNLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveDownloadRequest: public Networking::Request {
+	Common::String _requestedFile, _requestedLocalFile;
+	GoogleDriveStorage *_storage;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void downloadCallback(Storage::BoolResponse response);
+	void downloadErrorCallback(Networking::ErrorResponse error);
+	void finishSuccess(bool success);
+public:
+	GoogleDriveDownloadRequest(GoogleDriveStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~GoogleDriveDownloadRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
index e3ab97e..6d8da83 100644
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
@@ -84,12 +84,17 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 		}
 	}
 
+	Common::String path = _currentDirectory;
+	if (path != "") path += "/";
+	path += currentLevelName;
+	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
+
 	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
 
 	Common::Array<StorageFile> &files = response.value;
 	bool found = false;
 	for (uint32 i = 0; i < files.size(); ++i) {
-		if (files[i].isDirectory() && files[i].name().equalsIgnoreCase(currentLevelName)) {
+		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
 			if (_currentDirectory != "") _currentDirectory += "/";
 			_currentDirectory += files[i].name();
 			_currentDirectoryId = files[i].id();
@@ -101,10 +106,7 @@ void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResp
 	}
 
 	if (!found) {
-		Common::String path = _currentDirectory;
-		if (path != "") path += "/";
-		path += currentLevelName;
-		if (path.equalsIgnoreCase(_requestedPath)) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
+		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
 		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
 	}
 }
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index c6e423e..d9db4f7 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -35,6 +35,8 @@
 #include "googledriveresolveidrequest.h"
 #include "googledrivecreatedirectoryrequest.h"
 #include "googledrivelistdirectoryrequest.h"
+#include "googledrivestreamfilerequest.h"
+#include "googledrivedownloadrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -209,29 +211,6 @@ void GoogleDriveStorage::printJson(Networking::JsonResponse response) {
 	delete json;
 }
 
-void GoogleDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
-	if (!response.value) {
-		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
-		return;
-	}
-
-	Common::JSONObject result = response.value->asObject();
-	if (result.contains("@content.downloadUrl")) {
-		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
-		if (outerCallback)
-			(*outerCallback)(Networking::NetworkReadStreamResponse(
-				response.request,
-				new Networking::NetworkReadStream(url, 0, "")
-			));
-	} else {
-		warning("downloadUrl not found in passed JSON");
-		debug("%s", response.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
-	}
-	delete response.value;
-}
-
 Networking::Request *GoogleDriveStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	if (!callback) callback = new Common::Callback<GoogleDriveStorage, UploadResponse>(this, &GoogleDriveStorage::printFile);
@@ -255,27 +234,25 @@ Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::See
 	return nullptr; //TODO
 }
 
-Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
-	/*
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::fileInfoCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
-	request->addHeader("Authorization: Bearer " + _token);
-	return addRequest(request);
-	*/
-	//TODO: resolve id
-	//TODO: then call streamFileById()
-	return nullptr; //TODO
+Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {	
+	return addRequest(new GoogleDriveStreamFileRequest(this, path, outerCallback, errorCallback));
 }
 
 Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
-	return nullptr; //TODO
+	if (callback) {
+		Common::String url = "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media";
+		Common::String header = "Authorization: Bearer " + _token;
+		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
+		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
+		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));		
+	}
+	delete callback;
+	delete errorCallback;
+	return nullptr;
 }
 
 Networking::Request *GoogleDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	//TODO: resolve id
-	//TODO: then call downloadById()
-	return nullptr; //TODO
+	return addRequest(new GoogleDriveDownloadRequest(this, remotePath, localPath, callback, errorCallback));
 }
 
 void GoogleDriveStorage::fileDownloaded(BoolResponse response) {
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index a456030..2af1ede 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -61,8 +61,6 @@ class GoogleDriveStorage: public Cloud::Storage {
 	void printBool(BoolResponse response);
 	void printFile(UploadResponse response);
 	void printInfo(StorageInfoResponse response);
-
-	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);	
 public:	
 	virtual ~GoogleDriveStorage();
 
diff --git a/backends/cloud/googledrive/googledrivestreamfilerequest.cpp b/backends/cloud/googledrive/googledrivestreamfilerequest.cpp
new file mode 100644
index 0000000..424e52c
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivestreamfilerequest.cpp
@@ -0,0 +1,91 @@
+/* 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 "backends/cloud/googledrive/googledrivestreamfilerequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveStreamFileRequest::GoogleDriveStreamFileRequest(GoogleDriveStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedFile(path), _storage(storage), _streamCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveStreamFileRequest::~GoogleDriveStreamFileRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _streamCallback;
+}
+
+void GoogleDriveStreamFileRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_ignoreCallback = false;
+
+	//find file's id
+	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveStreamFileRequest, Storage::UploadResponse>(this, &GoogleDriveStreamFileRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::ErrorResponse>(this, &GoogleDriveStreamFileRequest::idResolveFailedCallback);	
+	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveStreamFileRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Networking::NetworkReadStreamCallback innerCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::NetworkReadStreamResponse>(this, &GoogleDriveStreamFileRequest::streamFileCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::ErrorResponse>(this, &GoogleDriveStreamFileRequest::streamFileErrorCallback);
+	_workingRequest = _storage->streamFileById(response.value.id(), innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveStreamFileRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveStreamFileRequest::streamFileCallback(Networking::NetworkReadStreamResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishStream(response.value);
+}
+
+void GoogleDriveStreamFileRequest::streamFileErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveStreamFileRequest::handle() {}
+
+void GoogleDriveStreamFileRequest::restart() { start(); }
+
+void GoogleDriveStreamFileRequest::finishStream(Networking::NetworkReadStream *stream) {
+	Request::finishSuccess();
+	if (_streamCallback) (*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivestreamfilerequest.h b/backends/cloud/googledrive/googledrivestreamfilerequest.h
new file mode 100644
index 0000000..aa55961
--- /dev/null
+++ b/backends/cloud/googledrive/googledrivestreamfilerequest.h
@@ -0,0 +1,59 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTREAMFILEREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTREAMFILEREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+class GoogleDriveStorage;
+
+class GoogleDriveStreamFileRequest: public Networking::Request {
+	Common::String _requestedFile;
+	GoogleDriveStorage *_storage;
+	Networking::NetworkReadStreamCallback _streamCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void streamFileCallback(Networking::NetworkReadStreamResponse response);
+	void streamFileErrorCallback(Networking::ErrorResponse error);
+	void finishStream(Networking::NetworkReadStream *stream);
+public:
+	GoogleDriveStreamFileRequest(GoogleDriveStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb);
+	virtual ~GoogleDriveStreamFileRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index c4a8ae4..817c829 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -33,10 +33,12 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/googledrive/googledrivecreatedirectoryrequest.o \
+	cloud/googledrive/googledrivedownloadrequest.o \
 	cloud/googledrive/googledrivelistdirectorybyidrequest.o \
 	cloud/googledrive/googledrivelistdirectoryrequest.o \
 	cloud/googledrive/googledriveresolveidrequest.o \
 	cloud/googledrive/googledrivestorage.o \
+	cloud/googledrive/googledrivestreamfilerequest.o \
 	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \


Commit: b29497effe60eaec891ca1b5ce78f8dae69fd599
    https://github.com/scummvm/scummvm/commit/b29497effe60eaec891ca1b5ce78f8dae69fd599
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GoogleDriveUploadRequest

Includes NetworkReadStream PATCH method and Headers remembering feature.

Changed paths:
  A backends/cloud/googledrive/googledriveuploadrequest.cpp
  A backends/cloud/googledrive/googledriveuploadrequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/module.mk
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index d9db4f7..bb762a4 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -37,6 +37,7 @@
 #include "googledrivelistdirectoryrequest.h"
 #include "googledrivestreamfilerequest.h"
 #include "googledrivedownloadrequest.h"
+#include "googledriveuploadrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -230,8 +231,7 @@ Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, Li
 }
 
 Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	//return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));
-	return nullptr; //TODO
+	return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));	
 }
 
 Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {	
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
new file mode 100644
index 0000000..7ab9f2e
--- /dev/null
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -0,0 +1,341 @@
+/* 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 "backends/cloud/googledrive/googledriveuploadrequest.h"
+#include "backends/cloud/googledrive/googledrivestorage.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+#include "common/debug.h"
+#include "googledrivetokenrefresher.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+
+GoogleDriveUploadRequest::GoogleDriveUploadRequest(GoogleDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+GoogleDriveUploadRequest::~GoogleDriveUploadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _contentsStream;
+	delete _uploadCallback;
+}
+
+void GoogleDriveUploadRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	if (!_contentsStream->seek(0)) {
+		warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+	}
+	_resolvedId = ""; //used to update file contents
+	_parentId = ""; //used to create file within parent directory
+	_serverReceivedBytes = 0;
+	_ignoreCallback = false;
+
+	resolveId();
+}
+
+void GoogleDriveUploadRequest::resolveId() {
+	//check whether such file already exists
+	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveUploadRequest, Storage::UploadResponse>(this, &GoogleDriveUploadRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::idResolveFailedCallback);
+	_workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback);
+}
+
+void GoogleDriveUploadRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	_resolvedId = response.value.id();
+	startUpload();
+}
+
+void GoogleDriveUploadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//not resolved => error or no such file
+	if (error.response.contains("no such file found in its parent directory")) {
+		//parent's id after the '\n'
+		Common::String parentId = error.response;
+		for (uint32 i = 0; i < parentId.size(); ++i)
+			if (parentId[i] == '\n') {
+				parentId.erase(0, i + 1);
+				break;
+			}
+
+		_parentId = parentId;
+		startUpload();
+		return;
+	}
+
+	finishError(error);
+}
+
+void GoogleDriveUploadRequest::startUpload() {
+	Common::String name = _savePath;
+	for (uint32 i = name.size(); i > 0; --i) {
+		if (name[i - 1] == '/' || name[i - 1] == '\\') {
+			name.erase(0, i);
+			break;
+		}
+	}
+
+	Common::String url = "https://www.googleapis.com/upload/drive/v3/files";
+	if (_resolvedId != "") url += "/" + _resolvedId;
+	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
+	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::startUploadCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+	request->addHeader("Content-Type: application/json");
+	if (_resolvedId != "") request->usePatch();
+
+	Common::JSONObject jsonRequestParameters;
+	if (_resolvedId != "") jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
+	else {
+		Common::JSONArray parentsArray;
+		parentsArray.push_back(new Common::JSONValue(_parentId));
+		jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
+	}
+	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Networking::ErrorResponse error(this, false, true, "", -1);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq) {
+		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
+		if (stream) {
+			long code = stream->httpResponseCode();
+			Common::String headers = stream->responseHeaders();
+			if (code == 200) {
+				const char *cstr = headers.c_str();
+				const char *position = strstr(cstr, "Location: ");
+
+				if (position) {
+					Common::String result = "";
+					char c;
+					for (const char *i = position + 10; c = *i, c != 0; ++i) {
+						if (c == '\n' || c == '\r') break;
+						result += c;
+					}
+					_uploadUrl = result;
+					uploadNextPart();
+					return;
+				}
+			}
+
+			error.httpResponseCode = code;
+		}
+	}
+
+	Common::JSONValue *json = response.value;	
+	delete json;
+
+	finishError(error);
+}
+
+void GoogleDriveUploadRequest::startUploadErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void GoogleDriveUploadRequest::uploadNextPart() {
+	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
+	Common::String url = _uploadUrl;
+	
+	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::partUploadedCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::partUploadedErrorCallback);
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());	
+	request->usePut();
+
+	uint32 oldPos = _contentsStream->pos();
+	if (oldPos != _serverReceivedBytes) {
+		if (!_contentsStream->seek(_serverReceivedBytes)) {
+			warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%u)", _serverReceivedBytes);
+			finishError(Networking::ErrorResponse(this, false, true, "", -1));
+			return;
+		}
+		oldPos = _serverReceivedBytes;
+	}
+
+	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
+	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
+	request->setBuffer(buffer, size);
+
+	//request->addHeader(Common::String::format("Content-Length: %u", size));
+	if (_uploadUrl != "")
+		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));	;
+	
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+namespace {
+uint64 atoull(Common::String s) {
+	uint64 result = 0;
+	for (uint32 i = 0; i < s.size(); ++i) {
+		if (s[i] < '0' || s[i] > '9') break;
+		result = result * 10L + (s[i] - '0');
+	}
+	return result;
+}
+}
+
+bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
+	//308 Resume Incomplete, with Range: X-Y header
+	if (!stream) return false;
+	if (stream->httpResponseCode() != 308) return false; //seriously	
+	
+	Common::String headers = stream->responseHeaders();
+	const char *cstr = headers.c_str();	
+	for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
+		const char *needle = (rangeTry==0 ? "Range: 0-" : "Range: bytes=0-");
+		uint32 needleLength = (rangeTry == 0 ? 9 : 15);
+
+		const char *position = strstr(cstr, needle); //if it lost the first part, I refuse to talk with it
+
+		if (position) {
+			Common::String result = "";
+			char c;
+			for (const char *i = position + needleLength; c = *i, c != 0; ++i) {
+				if (c == '\n' || c == '\r') break;
+				result += c;
+			}
+			_serverReceivedBytes = atoull(result) + 1;			
+			uploadNextPart();
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+		
+	Networking::ErrorResponse error(this, false, true, "", -1);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;	
+	if (rq) {
+		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
+		if (stream) {
+			long code = stream->httpResponseCode();
+			error.httpResponseCode = code;
+			if (code == 308 && handleHttp308(stream)) {
+				delete (Common::JSONValue *)response.value;
+				return;
+			}
+		}
+	}
+
+	Common::JSONValue *json = response.value;
+	if (json) {
+		debug("%s", json->stringify(true).c_str());		
+		if (json->isObject()) {
+			Common::JSONObject object = json->asObject();
+
+			if (object.contains("error")) {
+				warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
+				delete json;
+				error.response = json->stringify(true);
+				finishError(error);
+				return;
+			}
+
+			if (object.contains("id") && object.contains("name")) {
+				//finished
+				Common::String id = object.getVal("id")->asString();
+				Common::String name = object.getVal("name")->asString();
+				bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
+				uint32 size = 0, timestamp = 0;
+				if (object.contains("size") && object.getVal("size")->isString())
+					size = atoull(object.getVal("size")->asString());
+				if (object.contains("modifiedTime") && object.getVal("modifiedTime")->isString())
+					timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
+
+				//as we list directory by id, we can't determine full path for the file, so we leave it empty
+				finishSuccess(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
+				return;
+			}
+		}
+
+		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
+			warning("no file info to return");
+			finishSuccess(StorageFile(_savePath, 0, 0, false));
+		} else {
+			uploadNextPart();
+		}
+	} else {
+		warning("null, not json");
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void GoogleDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)error.request;
+	if (rq) {
+		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
+		if (stream) {
+			long code = stream->httpResponseCode();			
+			if (code == 308 && handleHttp308(stream)) {				
+				return;
+			}
+		}
+	}
+
+	finishError(error);
+}
+
+void GoogleDriveUploadRequest::handle() {}
+
+void GoogleDriveUploadRequest::restart() { start(); }
+
+void GoogleDriveUploadRequest::finishSuccess(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.h b/backends/cloud/googledrive/googledriveuploadrequest.h
new file mode 100644
index 0000000..e417403
--- /dev/null
+++ b/backends/cloud/googledrive/googledriveuploadrequest.h
@@ -0,0 +1,70 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEUPLOADREQUEST_H
+#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEUPLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace GoogleDrive {
+class GoogleDriveStorage;
+
+class GoogleDriveUploadRequest: public Networking::Request {
+	GoogleDriveStorage *_storage;
+	Common::String _savePath;
+	Common::SeekableReadStream *_contentsStream;
+	Storage::UploadCallback _uploadCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _resolvedId, _parentId;
+	Common::String _uploadUrl;
+	uint64 _serverReceivedBytes;
+	
+	void start();
+	void resolveId();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void startUpload();
+	void startUploadCallback(Networking::JsonResponse response);
+	void startUploadErrorCallback(Networking::ErrorResponse error);
+	void uploadNextPart();
+	void partUploadedCallback(Networking::JsonResponse response);
+	void partUploadedErrorCallback(Networking::ErrorResponse error);
+	bool handleHttp308(const Networking::NetworkReadStream *stream);
+	void finishSuccess(StorageFile status);
+
+public:
+	GoogleDriveUploadRequest(GoogleDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
+	virtual ~GoogleDriveUploadRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace GoogleDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 817c829..95334fe 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -40,6 +40,7 @@ MODULE_OBJS += \
 	cloud/googledrive/googledrivestorage.o \
 	cloud/googledrive/googledrivestreamfilerequest.o \
 	cloud/googledrive/googledrivetokenrefresher.o \
+	cloud/googledrive/googledriveuploadrequest.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index a3f997a..6ef0e34 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -31,7 +31,8 @@
 namespace Networking {
 
 CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url):
-	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0), _uploading(false) {}
+	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr),
+	_bytesBufferSize(0), _uploading(false), _usingPatch(false) {}
 
 CurlRequest::~CurlRequest() {
 	delete _stream;
@@ -40,11 +41,10 @@ CurlRequest::~CurlRequest() {
 
 NetworkReadStream *CurlRequest::makeStream() {
 	if (_bytesBuffer)
-		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, true);
-	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading);
+		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true);
+	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch);
 }
 
-
 void CurlRequest::handle() {
 	if (!_stream) _stream = makeStream();	
 
@@ -99,6 +99,8 @@ void CurlRequest::setBuffer(byte *buffer, uint32 size) {
 
 void CurlRequest::usePut() { _uploading = true; }
 
+void CurlRequest::usePatch() { _usingPatch = true; }
+
 NetworkReadStreamResponse CurlRequest::execute() {
 	if (!_stream) {
 		_stream = makeStream();
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 5737078..5c06b58 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -45,6 +45,7 @@ protected:
 	byte *_bytesBuffer;
 	uint32 _bytesBufferSize;
 	bool _uploading; //using PUT method
+	bool _usingPatch; //using PATCH method
 
 	virtual NetworkReadStream *makeStream();
 
@@ -70,6 +71,9 @@ public:
 	/** Remembers to use PUT method when it would create NetworkReadStream. */
 	virtual void usePut();
 
+	/** Remembers to use PATCH method when it would create NetworkReadStream. */
+	virtual void usePatch();
+
 	/**
 	 * Starts this Request with ConnMan.
 	 * @return its NetworkReadStream in NetworkReadStreamResponse.
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 283e5e6..ccfb3d5 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -41,16 +41,24 @@ static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading):
-	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, false) {}
+static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
+	NetworkReadStream *stream = (NetworkReadStream *)p;
+	if (stream) return stream->addResponseHeaders(d, n*l);
+	return 0;
+}
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool post):
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch):
+	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false) {}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post):
 	_easy(0), _eos(false), _requestComplete(false), _sendingContentsBuffer(nullptr), _sendingContentsSize(0), _sendingContentsPos(0) {
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
 	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
 	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+	curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
+	curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
@@ -61,6 +69,8 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, b
 		curl_easy_setopt(_easy, CURLOPT_READFUNCTION, curlReadDataCallback);
 		_sendingContentsBuffer = buffer;
 		_sendingContentsSize = bufferSize;
+	} else if (usingPatch) {		
+		curl_easy_setopt(_easy, CURLOPT_CUSTOMREQUEST, "PATCH");
 	} else {
 		if (post || bufferSize != 0) {
 			curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
@@ -101,6 +111,20 @@ long NetworkReadStream::httpResponseCode() const {
 	return responseCode;
 }
 
+Common::String NetworkReadStream::currentLocation() const {
+	Common::String result = "";
+	if (_easy) {
+		char *pointer;
+		curl_easy_getinfo(_easy, CURLINFO_EFFECTIVE_URL, &pointer);
+		result = Common::String(pointer);
+	}
+	return result;
+}
+
+Common::String NetworkReadStream::responseHeaders() const {
+	return _responseHeaders;
+}
+
 uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
 	uint32 size = _sendingContentsSize - _sendingContentsPos;
 	if (size > maxSize) size = maxSize;
@@ -111,4 +135,9 @@ uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 max
 	return size;
 }
 
+uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) {
+	_responseHeaders += Common::String(buffer, size);
+	return size;
+}
+
 } // End of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index d48d01b..991fdb3 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -38,10 +38,11 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 	byte *_sendingContentsBuffer;
 	uint32 _sendingContentsSize;
 	uint32 _sendingContentsPos;
+	Common::String _responseHeaders;
 
 public:	
-	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false);
-	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading = false, bool post = true);
+	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);
+	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
 	virtual ~NetworkReadStream();
 
 	/**
@@ -87,6 +88,21 @@ public:
 	long httpResponseCode() const;
 
 	/**
+	* Return current location URL from inner CURL handle.
+	* "" is returned to indicate there is no inner handle.
+	*
+	* @note This method should be called when eos() == true.
+	*/
+	Common::String currentLocation() const;
+
+	/**
+	* Return response headers.
+	*
+	* @note This method should be called when eos() == true.
+	*/
+	Common::String responseHeaders() const;
+
+	/**
 	 * Fills the passed buffer with _sendingContentsBuffer contents.
 	 * It works similarly to read(), expect it's not for reading
 	 * Stream's contents, but for sending our own data to the server.
@@ -94,6 +110,13 @@ public:
 	 * @returns how many bytes were actually read (filled in)
 	 */
 	uint32 fillWithSendingContents(char *bufferToFill, uint32 maxSize);
+
+	/**
+	* Remembers headers returned to CURL in server's response.
+	*
+	* @returns how many bytes were actually read
+	*/
+	uint32 addResponseHeaders(char *buffer, uint32 size);
 };
 
 } // End of namespace Networking


Commit: 1479d126520a9f3472797c1bb98b534f0b2a6b97
    https://github.com/scummvm/scummvm/commit/1479d126520a9f3472797c1bb98b534f0b2a6b97
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor GoogleDriveUploadRequest fix

Just checked that out on cloud sync: Google Drive is officially
supported!

Changed paths:
    backends/cloud/googledrive/googledriveuploadrequest.cpp



diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 7ab9f2e..f5636ef 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -267,7 +267,6 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 
 	Common::JSONValue *json = response.value;
 	if (json) {
-		debug("%s", json->stringify(true).c_str());		
 		if (json->isObject()) {
 			Common::JSONObject object = json->asObject();
 


Commit: 870e96eb9ca6e69bea5f47a215d171fd58ab1265
    https://github.com/scummvm/scummvm/commit/870e96eb9ca6e69bea5f47a215d171fd58ab1265
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update CloudManager and Storage

* Storage::name();
* CloudManager::getStorageName();
* CloudManager::getStorageIndex();
* CloudManager::listStorages();
* CloudManager::switchStorage().

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 7613b2c..a9baac5 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -26,6 +26,7 @@
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
+#include "common/translation.h"
 
 namespace Common {
 
@@ -106,12 +107,47 @@ void CloudManager::addStorage(Storage *storage, bool makeCurrent, bool saveConfi
 	if (saveConfig) save();
 }
 
-Storage *CloudManager::getCurrentStorage() {
+Storage *CloudManager::getCurrentStorage() const {
 	if (_currentStorageIndex < _storages.size())
 		return _storages[_currentStorageIndex];
 	return nullptr;
 }
 
+Common::String CloudManager::getStorageName() const {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->name();
+	return _("No active storage");
+}
+
+uint32 CloudManager::getStorageIndex() const {
+	return _currentStorageIndex;
+}
+
+Common::StringArray CloudManager::listStorages() const {
+	Common::StringArray result;
+	for (uint32 i = 0; i < _storages.size(); ++i) {
+		result.push_back(_storages[i]->name());
+	}
+	return result;
+}
+
+bool CloudManager::switchStorage(uint32 index) {
+	if (index < 0 || index > _storages.size()) {
+		warning("CloudManager::switchStorage: invalid index passed");
+		return false;
+	}
+
+	Storage *storage = getCurrentStorage();
+	if (storage && storage->isWorking()) {
+		warning("CloudManager::switchStorage: another storage is working now");
+		return false;
+	}
+
+	_currentStorageIndex = index;
+	save();
+	return true;
+}
+
 void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index dbff018..7e8cfd6 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -26,6 +26,7 @@
 #include "backends/cloud/storage.h"
 #include "common/array.h"
 #include "common/singleton.h"
+#include <cge/console.h>
 
 namespace GUI {
 
@@ -72,7 +73,35 @@ public:
 	 *
 	 * @return	active Cloud::Storage or null, if there is no active Storage.
 	 */
-	Cloud::Storage *getCurrentStorage();
+	Cloud::Storage *getCurrentStorage() const;
+
+	/**
+	* Return active Storage's name.
+	*
+	* @return	active Storage's or _("No active storage"), if there is no active Storage.
+	*/
+	Common::String getStorageName() const;
+
+	/**
+	* Return active Storage's index.
+	*
+	* @return	active Storage's index.
+	*/
+	uint32 getStorageIndex() const;
+
+	/**
+	* Return Storages names as list.
+	*
+	* @return	a list of Storages names.
+	*/
+	Common::StringArray listStorages() const;
+
+	/**
+	* Changes the storage to the one with given index.
+	*
+	* @param	new Storage's index.
+	*/
+	bool switchStorage(uint32 index);
 
 	/**
 	 * Starts saves syncing process in currently active storage if there is any.
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 038a168..d11d97d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -91,6 +91,10 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
 
+Common::String DropboxStorage::name() const {
+	return "Dropbox";
+}
+
 void DropboxStorage::printFiles(FileArrayResponse response) {
 	debug("files:");
 	Common::Array<StorageFile> &files = response.value;
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 9ac0ffb..60a8075 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -65,6 +65,12 @@ public:
 	 */
 	virtual void saveConfig(Common::String keyPrefix);
 
+	/**
+	* Return unique storage name.
+	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	*/
+	virtual Common::String name() const;
+
 	/** Public Cloud API comes down there. */
 
 	/** Returns ListDirectoryStatus struct with list of files. */
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index bb762a4..30ca1be 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -134,6 +134,10 @@ void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
+Common::String GoogleDriveStorage::name() const {
+	return "Google Drive";
+}
+
 namespace {
 uint64 atoull(Common::String s) {
 	uint64 result = 0;
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 2af1ede..489260d 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -77,6 +77,12 @@ public:
 	 */
 	virtual void saveConfig(Common::String keyPrefix);
 
+	/**
+	* Return unique storage name.
+	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	*/
+	virtual Common::String name() const;
+
 	/** Public Cloud API comes down there. */
 
 	/** Returns StorageFile with the resolved file's id. */
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index c494f38..c391065 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -128,6 +128,10 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
 
+Common::String OneDriveStorage::name() const {
+	return "OneDrive";
+}
+
 void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 8afdac8..3932d44 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -75,6 +75,12 @@ public:
 	 */
 	virtual void saveConfig(Common::String keyPrefix);
 
+	/**
+	* Return unique storage name.
+	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	*/
+	virtual Common::String name() const;
+
 	/** Public Cloud API comes down there. */
 
 	/** Returns ListDirectoryStatus struct with list of files. */
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 4a5e123..11d8f6b 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -103,6 +103,12 @@ public:
 	virtual void saveConfig(Common::String keyPrefix) = 0;
 
 	/**
+	* Return unique storage name.		
+	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	*/
+	virtual Common::String name() const = 0;
+
+	/**
 	 * Public Cloud API comes down there.
 	 *
 	 * All Cloud API methods return Networking::Request *, which


Commit: 90ae7b7337ece337cfd6ed2eabcef4f42a3abe7a
    https://github.com/scummvm/scummvm/commit/90ae7b7337ece337cfd6ed2eabcef4f42a3abe7a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Options dialog Cloud tab

With StorageBrowser to select a Storage. It actually uses CloudMan to
switch active Storage.

Changed paths:
  A gui/storagebrowser.cpp
  A gui/storagebrowser.h
    gui/module.mk
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/module.mk b/gui/module.mk
index 2ffea5a..ece360b 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -18,6 +18,7 @@ MODULE_OBJS := \
 	predictivedialog.o \
 	saveload.o \
 	saveload-dialog.o \
+	storagebrowser.o \
 	themebrowser.o \
 	ThemeEngine.o \
 	ThemeEval.o \
diff --git a/gui/options.cpp b/gui/options.cpp
index e410971..df457a8 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -43,6 +43,11 @@
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
 
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#include "gui/storagebrowser.h"
+#endif
+
 namespace GUI {
 
 enum {
@@ -84,6 +89,12 @@ enum {
 };
 #endif
 
+#ifdef USE_CLOUD
+enum {
+	kChooseStorageCmd = 'chst'
+};
+#endif
+
 static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("every 10 mins"), _s("every 15 mins"), _s("every 30 mins"), 0 };
 static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 };
 static const char *outputRateLabels[] = { _s("<default>"), _s("8 kHz"), _s("11 kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 };
@@ -1251,6 +1262,19 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	new ButtonWidget(tab, "GlobalOptions_Misc.UpdatesCheckManuallyButton", _("Check now"), 0, kUpdatesCheckCmd);
 #endif
 
+#ifdef USE_CLOUD
+	//
+	// 7) The cloud tab
+	//
+	if (g_system->getOverlayWidth() > 320)
+		tab->addTab(_("Cloud"));
+	else
+		tab->addTab(_c("Cloud", "lowres"));
+
+	new ButtonWidget(tab, "GlobalOptions_Cloud.StorageButton", _("Storage:"), 0, kChooseStorageCmd);
+	_curStorage = new StaticTextWidget(tab, "GlobalOptions_Cloud.CurStorage", CloudMan.getStorageName());
+#endif
+
 	// Activate the first tab
 	tab->setActiveTab(0);
 	_tabWidget = tab;
@@ -1481,7 +1505,8 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		}
 		break;
 	}
-	case kChooseThemeCmd: {
+	case kChooseThemeCmd:
+	{
 		ThemeBrowser browser;
 		if (browser.runModal() > 0) {
 			// User made his choice...
@@ -1513,6 +1538,33 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		}
 		break;
 	}
+#ifdef USE_CLOUD
+	case kChooseStorageCmd:
+	{
+		StorageBrowser storageBrowser;
+		if (storageBrowser.runModal() > 0) {
+			// User made his choice...
+			uint32 storageIndex = storageBrowser.getSelected();
+			// FIXME: Actually, any changes (including the storage change?) should
+			// only become active *after* the options dialog has closed.			
+			if (CloudMan.switchStorage(storageIndex)) {
+				_curStorage->setLabel(CloudMan.getStorageName());
+				//automatically saves in the config if switched successfully
+			} else {
+				bool anotherStorageIsWorking = CloudMan.isWorking();
+				Common::String message = _("Failed to change cloud storage!");
+				if (anotherStorageIsWorking) {
+					message += "\n";
+					message += _("Current cloud storage is working at the moment.");
+				}
+				MessageDialog dialog(message);
+				dialog.runModal();
+			}
+			draw();
+		}
+		break;
+	}
+#endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
 		_keysDialog->runModal();
diff --git a/gui/options.h b/gui/options.h
index 294b417..feb7785 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -241,6 +241,13 @@ protected:
 	StaticTextWidget *_updatesPopUpDesc;
 	PopUpWidget *_updatesPopUp;
 #endif
+
+#ifdef USE_CLOUD
+	//
+	// Misc controls
+	//
+	StaticTextWidget *_curStorage;
+#endif
 };
 
 } // End of namespace GUI
diff --git a/gui/storagebrowser.cpp b/gui/storagebrowser.cpp
new file mode 100644
index 0000000..30a0e9e
--- /dev/null
+++ b/gui/storagebrowser.cpp
@@ -0,0 +1,96 @@
+/* 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/storagebrowser.h"
+#include "gui/widgets/list.h"
+#include "gui/widget.h"
+#include "gui/gui-manager.h"
+
+#include "common/translation.h"
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#endif
+
+namespace GUI {
+
+enum {
+	kChooseCmd = 'Chos'
+};
+
+StorageBrowser::StorageBrowser() : Dialog("Browser") {
+	new StaticTextWidget(this, "Browser.Headline", _("Select a Storage"));
+
+	// Add storages list
+	_storagesList = new ListWidget(this, "Browser.List");
+	_storagesList->setNumberingMode(kListNumberingOff);
+	_storagesList->setEditable(false);
+
+	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
+
+	// Buttons
+	new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd);
+	new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd);
+}
+
+void StorageBrowser::open() {
+	// Always refresh storages list
+	updateListing();
+
+	// Call super implementation
+	Dialog::open();
+}
+
+void StorageBrowser::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kChooseCmd:
+	case kListItemActivatedCmd:
+	case kListItemDoubleClickedCmd: {		
+		int selection = _storagesList->getSelected();
+		if (selection < 0)
+			break;
+		_selectionIndex = selection;
+		setResult(1);
+		close();
+		break;
+	}
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+void StorageBrowser::updateListing() {
+	Common::StringArray list; 
+	uint32 currentStorageIndex = 0;
+#ifdef USE_CLOUD
+	list = CloudMan.listStorages();
+	currentStorageIndex = CloudMan.getStorageIndex();
+#endif
+
+	_storagesList->setList(list);
+	_storagesList->scrollTo(0);
+	_storagesList->setSelected(currentStorageIndex);
+
+	// Finally, redraw
+	draw();
+}
+
+} // End of namespace GUI
diff --git a/gui/storagebrowser.h b/gui/storagebrowser.h
new file mode 100644
index 0000000..b18c30f
--- /dev/null
+++ b/gui/storagebrowser.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_STORAGEBROWSER_H
+#define GUI_STORAGEBROWSER_H
+
+#include "gui/dialog.h"
+#include "common/str.h"
+
+namespace GUI {
+
+class CommandSender;
+class ListWidget;
+
+class StorageBrowser : public Dialog {
+public:
+	StorageBrowser();
+
+	void open();
+	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+
+	uint32 getSelected() const { return _selectionIndex; }
+private:
+	ListWidget *_storagesList;	
+	uint32 _selectionIndex;
+
+	void updateListing();
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index f14e447..693be09 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -538,6 +538,19 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageButton'
+						type = 'Button'
+				/>
+				<widget name = 'CurStorage'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>
 		<layout type='vertical' padding='8,8,8,8' center='true'>
 			<widget name='Action'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 7c2326d..105c121 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -527,6 +527,19 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageButton'
+						type = 'Button'
+				/>
+				<widget name = 'CurStorage'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>
 		<layout type='vertical' padding='8,8,8,8' center='true'>
 			<widget name='Action'


Commit: 4ff1ed5fe9ba86ccf5d7ad72dab8286c70ab7af3
    https://github.com/scummvm/scummvm/commit/4ff1ed5fe9ba86ccf5d7ad72dab8286c70ab7af3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Cloud tab information labels

And corresponding stub implementations in CloudManager.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a9baac5..cb7e6f7 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -132,7 +132,7 @@ Common::StringArray CloudManager::listStorages() const {
 }
 
 bool CloudManager::switchStorage(uint32 index) {
-	if (index < 0 || index > _storages.size()) {
+	if (index >= _storages.size()) {
 		warning("CloudManager::switchStorage: invalid index passed");
 		return false;
 	}
@@ -148,6 +148,22 @@ bool CloudManager::switchStorage(uint32 index) {
 	return true;
 }
 
+Common::String CloudManager::getStorageUsername(uint32 index) {
+	if (index >= _storages.size()) return "";
+	return _storages[index]->name(); //TODO
+}
+
+uint64 CloudManager::getStorageUsedSpace(uint32 index) {
+	if (index >= _storages.size()) return 0;
+	return 0; //return _storages[index]->usedSpace(); //TODO
+}
+
+Common::String CloudManager::getStorageLastSync(uint32 index) {
+	if (index >= _storages.size()) return "";
+	if (_storages[index]->isSyncing()) return "";
+	return _storages[index]->name(); //->lastSyncDate(); //TODO
+}
+
 void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 7e8cfd6..9956a92 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -104,6 +104,32 @@ public:
 	bool switchStorage(uint32 index);
 
 	/**
+	* Return username used by Storage.
+	*
+	* @param	Storage's index.
+	* @returns	username or "" if index is invalid (no such Storage).
+	*/
+	Common::String getStorageUsername(uint32 index);
+
+	/**
+	* Return space used by Storage.
+	*
+	* @param	Storage's index.
+	* @returns	used space in bytes or 0 if index is invalid (no such Storage).
+	*/
+	uint64 getStorageUsedSpace(uint32 index);
+
+	/**
+	* Return Storage's last sync date.
+	*
+	* @param	Storage's index.
+	* @returns	last sync date or "" if index is invalid (no such Storage).
+				It also returns "" if there never was any sync
+				or if storage is syncing right now.
+	*/
+	Common::String getStorageLastSync(uint32 index);
+
+	/**
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
 	SavesSyncRequest *syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
diff --git a/gui/options.cpp b/gui/options.cpp
index df457a8..b0a7968 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1273,6 +1273,17 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 
 	new ButtonWidget(tab, "GlobalOptions_Cloud.StorageButton", _("Storage:"), 0, kChooseStorageCmd);
 	_curStorage = new StaticTextWidget(tab, "GlobalOptions_Cloud.CurStorage", CloudMan.getStorageName());
+
+	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
+	_storageUsername = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameLabel", "<none>");
+
+	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM on this storage"));
+	_storageUsedSpace = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceLabel", "0 bytes");
+
+	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did last saves sync"));
+	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
+
+	setupCloudTab();
 #endif
 
 	// Activate the first tab
@@ -1550,6 +1561,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 			if (CloudMan.switchStorage(storageIndex)) {
 				_curStorage->setLabel(CloudMan.getStorageName());
 				//automatically saves in the config if switched successfully
+				setupCloudTab();
 			} else {
 				bool anotherStorageIsWorking = CloudMan.isWorking();
 				Common::String message = _("Failed to change cloud storage!");
@@ -1621,4 +1633,27 @@ void GlobalOptionsDialog::reflowLayout() {
 	OptionsDialog::reflowLayout();
 }
 
+#ifdef USE_CLOUD
+void GlobalOptionsDialog::setupCloudTab() {
+	uint32 index = CloudMan.getStorageIndex();
+	if (_storageUsername) {
+		Common::String username = CloudMan.getStorageUsername(index);
+		if (username == "") username = _("<none>");
+		_storageUsername->setLabel(username);
+	}
+	if (_storageUsedSpace) {
+		uint64 usedSpace = CloudMan.getStorageUsedSpace(index);
+		_storageUsedSpace->setLabel(Common::String::format(_("%llu bytes"), usedSpace));
+	}
+	if (_storageLastSync) {
+		Common::String sync = CloudMan.getStorageLastSync(index);
+		if (sync == "") {
+			if (CloudMan.isSyncing()) sync = _("<right now>");
+			else sync = _("<never>");
+		}
+		_storageLastSync->setLabel(sync);
+	}
+}
+#endif
+
 } // End of namespace GUI
diff --git a/gui/options.h b/gui/options.h
index feb7785..228a415 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -244,9 +244,14 @@ protected:
 
 #ifdef USE_CLOUD
 	//
-	// Misc controls
+	// Cloud controls
 	//
 	StaticTextWidget *_curStorage;
+	StaticTextWidget *_storageUsername;
+	StaticTextWidget *_storageUsedSpace;
+	StaticTextWidget *_storageLastSync;
+
+	void setupCloudTab();
 #endif
 };
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 693be09..093eeba 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -548,6 +548,30 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageUsernameDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageUsernameLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageUsedSpaceDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageUsedSpaceLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageLastSyncDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageLastSyncLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 


Commit: af9930482e17f4b55e46707fc017090e4c24a38e
    https://github.com/scummvm/scummvm/commit/af9930482e17f4b55e46707fc017090e4c24a38e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update CloudManager

It now supports only one storage of each type. Only one Storage could be
loaded to the memory as well.

Options' Cloud tab now changes the Storage only when user pressed OK
button, giving the ability to look through the Storages without actually
changing them.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    gui/options.cpp
    gui/options.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index cb7e6f7..5c69e30 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -36,87 +36,101 @@ DECLARE_SINGLETON(Cloud::CloudManager);
 
 namespace Cloud {
 
-CloudManager::CloudManager() : _currentStorageIndex(0) {}
+CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr) {}
 
 CloudManager::~CloudManager() {
 	//TODO: do we have to save storages on manager destruction?	
-	for (uint32 i = 0; i < _storages.size(); ++i)
-		delete _storages[i];
-	_storages.clear();
+	delete _activeStorage;
 }
 
-void CloudManager::init() {
-	bool offerDropbox = false;
-	bool offerOneDrive = false;
-	bool offerGoogleDrive = true;
-	
-	if (ConfMan.hasKey("storages_number", "cloud")) {
-		int storages = ConfMan.getInt("storages_number", "cloud");
-		for (int i = 1; i <= storages; ++i) {
-			Storage *loaded = 0;
-			Common::String keyPrefix = Common::String::format("storage%d_", i);
-			if (ConfMan.hasKey(keyPrefix + "type", "cloud")) {
-				Common::String storageType = ConfMan.get(keyPrefix + "type", "cloud");
-				if (storageType == "Dropbox") loaded = Dropbox::DropboxStorage::loadFromConfig(keyPrefix);
-				else if (storageType == "OneDrive") loaded = OneDrive::OneDriveStorage::loadFromConfig(keyPrefix);
-				else if (storageType == "Google Drive") {
-					loaded = GoogleDrive::GoogleDriveStorage::loadFromConfig(keyPrefix);
-					offerGoogleDrive = false;
-				} else warning("Unknown cloud storage type '%s' passed", storageType.c_str());
-			} else {
-				warning("Cloud storage #%d (out of %d) is missing.", i, storages);
-			}
-			if (loaded) _storages.push_back(loaded);
-		}
-
-		uint32 index = 0;
-		if (ConfMan.hasKey("current_storage", "cloud")) {
-			index = ConfMan.getInt("current_storage", "cloud") - 1; //count from 1, all for UX
-		}
-		if (index >= _storages.size()) index = 0;
-		_currentStorageIndex = index;
-
-		if (_storages.size() == 0) offerDropbox = true;
-	} else {
-		offerDropbox = true;
+namespace {
+uint64 atoull(Common::String s) {
+	uint64 result = 0;
+	for (uint32 i = 0; i < s.size(); ++i) {
+		if (s[i] < '0' || s[i] > '9') break;
+		result = result * 10L + (s[i] - '0');
 	}
-	if (offerDropbox) {
-		//this is temporary console offer to auth with Dropbox
-		Dropbox::DropboxStorage::authThroughConsole();
-	} else if (offerOneDrive) {
-		//OneDrive time
-		OneDrive::OneDriveStorage::authThroughConsole();
-	} else if (offerGoogleDrive) {		
-		GoogleDrive::GoogleDriveStorage::authThroughConsole();
-		_currentStorageIndex = 100;
+	return result;
+}
+}
+
+Common::String CloudManager::getStorageConfigName(uint32 index) const {
+	switch (index) {
+	case kStorageNoneId: return "<none>";
+	case kStorageDropboxId: return "Dropbox";
+	case kStorageOneDriveId: return "OneDrive";
+	case kStorageGoogleDriveId: return "GoogleDrive";
+	}
+	return "Unknown";
+}
+
+void CloudManager::loadStorage() {
+	switch (_currentStorageIndex) {
+	case kStorageDropboxId:
+		_activeStorage = Dropbox::DropboxStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		break;
+
+	case kStorageOneDriveId:
+		_activeStorage = OneDrive::OneDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		break;
+
+	case kStorageGoogleDriveId:
+		_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		break;
+
+	default:
+		_activeStorage = nullptr;
+	}
+
+	if (!_activeStorage) {
+		_currentStorageIndex = kStorageNoneId;
 	}
 }
 
+void CloudManager::init() {
+	//init configs structs
+	for (uint32 i = 0; i < kStorageTotal; ++i) {
+		Common::String name = getStorageConfigName(i);
+		StorageConfig config;
+		config.name = _(name);
+		config.username = "";
+		config.lastSyncDate = "";
+		config.usedBytes = 0;
+		if (ConfMan.hasKey("storage_" + name + "_username", "cloud"))
+			config.username = ConfMan.get("storage_" + name + "_username", "cloud");
+		if (ConfMan.hasKey("storage_" + name + "_lastSync", "cloud"))
+			config.lastSyncDate = ConfMan.get("storage_" + name + "_lastSync", "cloud");
+		if (ConfMan.hasKey("storage_" + name + "_usedBytes", "cloud"))
+			config.usedBytes = atoull(ConfMan.get("storage_" + name + "_usedBytes", "cloud"));
+		_storages.push_back(config);
+	}
+
+	//load an active storage if there is any
+	_currentStorageIndex = kStorageNoneId;
+	if (ConfMan.hasKey("current_storage", "cloud"))
+		_currentStorageIndex = ConfMan.getInt("current_storage", "cloud");
+
+	loadStorage();
+}
+
 void CloudManager::save() {
-	ConfMan.set("storages_number", Common::String::format("%d", _storages.size()), "cloud");
-	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex + 1), "cloud");
-	for (uint32 i = 0; i < _storages.size(); ++i)
-		_storages[i]->saveConfig(Common::String::format("storage%d_", i + 1));
+	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex), "cloud");
+	if (_activeStorage)
+		_activeStorage->saveConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
 	ConfMan.flushToDisk();
 }
 
-void CloudManager::addStorage(Storage *storage, bool makeCurrent, bool saveConfig) {
-	if (!storage) error("Cloud::CloudManager: NULL storage passed");
-	_storages.push_back(storage);
-	if (makeCurrent) _currentStorageIndex = _storages.size() - 1;
-	if (saveConfig) save();
+void CloudManager::replaceStorage(Storage *storage, uint32 index) {
+	if (!storage) error("CloudManager::replaceStorage: NULL storage passed");
+	if (index >= kStorageTotal) error("CloudManager::replaceStorage: invalid index passed");
+	delete _activeStorage;
+	_activeStorage = storage;
+	_currentStorageIndex = index;
+	save();
 }
 
 Storage *CloudManager::getCurrentStorage() const {
-	if (_currentStorageIndex < _storages.size())
-		return _storages[_currentStorageIndex];
-	return nullptr;
-}
-
-Common::String CloudManager::getStorageName() const {
-	Storage *storage = getCurrentStorage();
-	if (storage) return storage->name();
-	return _("No active storage");
+	return _activeStorage;
 }
 
 uint32 CloudManager::getStorageIndex() const {
@@ -126,7 +140,7 @@ uint32 CloudManager::getStorageIndex() const {
 Common::StringArray CloudManager::listStorages() const {
 	Common::StringArray result;
 	for (uint32 i = 0; i < _storages.size(); ++i) {
-		result.push_back(_storages[i]->name());
+		result.push_back(_storages[i].name);
 	}
 	return result;
 }
@@ -144,24 +158,25 @@ bool CloudManager::switchStorage(uint32 index) {
 	}
 
 	_currentStorageIndex = index;
+	loadStorage();
 	save();
 	return true;
 }
 
 Common::String CloudManager::getStorageUsername(uint32 index) {
 	if (index >= _storages.size()) return "";
-	return _storages[index]->name(); //TODO
+	return _storages[index].username;
 }
 
 uint64 CloudManager::getStorageUsedSpace(uint32 index) {
 	if (index >= _storages.size()) return 0;
-	return 0; //return _storages[index]->usedSpace(); //TODO
+	return _storages[index].usedBytes;
 }
 
 Common::String CloudManager::getStorageLastSync(uint32 index) {
 	if (index >= _storages.size()) return "";
-	if (_storages[index]->isSyncing()) return "";
-	return _storages[index]->name(); //->lastSyncDate(); //TODO
+	if (index == _currentStorageIndex && isSyncing()) return "";
+	return _storages[index].lastSyncDate;
 }
 
 void CloudManager::printBool(Storage::BoolResponse response) const {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 9956a92..5e26ece 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -36,12 +36,33 @@ class CommandReceiver;
 
 namespace Cloud {
 
+//that's actual indexes in CloudManager's array
+enum StorageIDs {
+	kStorageNoneId = 0,
+	kStorageDropboxId = 1,
+	kStorageOneDriveId = 2,
+	kStorageGoogleDriveId = 3,
+
+	kStorageTotal
+};
+
 class CloudManager : public Common::Singleton<CloudManager> {
-	Common::Array<Cloud::Storage *> _storages;
-	uint _currentStorageIndex;	
+	struct StorageConfig {
+		Common::String name, username;
+		uint64 usedBytes;
+		Common::String lastSyncDate;
+	};
+
+	Common::Array<StorageConfig> _storages;
+	uint _currentStorageIndex;
+	Storage *_activeStorage;
 
 	void printBool(Cloud::Storage::BoolResponse response) const;
 
+	void loadStorage();
+
+	Common::String getStorageConfigName(uint32 index) const;
+
 public:
 	CloudManager();
 	virtual ~CloudManager();
@@ -59,13 +80,13 @@ public:
 	void save();
 
 	/**
-	 * Adds new Storage into list.	
+	 * Replace active Storage.
+	 * @note this method automatically saves the changes with ConfMan.
 	 *
-	 * @param	storage Cloud::Storage to add.
-	 * @param	makeCurrent whether added storage should be the new current storage.
-	 * @param	saveConfig whether save() should be called to update configuration file.
+	 * @param	storage Cloud::Storage to replace active storage with.
+	 * @param	index   one of Cloud::StorageIDs enum values to indicate what storage type is replaced.	 
 	 */
-	void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true);
+	void replaceStorage(Storage *storage, uint32 index);
 
 	/**
 	 * Returns active Storage, which could be used to interact
@@ -76,13 +97,6 @@ public:
 	Cloud::Storage *getCurrentStorage() const;
 
 	/**
-	* Return active Storage's name.
-	*
-	* @return	active Storage's or _("No active storage"), if there is no active Storage.
-	*/
-	Common::String getStorageName() const;
-
-	/**
 	* Return active Storage's index.
 	*
 	* @return	active Storage's index.
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d11d97d..af73138 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -61,11 +61,11 @@ static void saveAccessTokenCallback(Networking::JsonResponse pair) {
 			warning("Bad response, no token/uid passed");
 		} else {
 			//we suppose that's the first storage
-			ConfMan.set("storages_number", "1", "cloud");
+			//TODO: update it to use CloudMan.replaceStorage()			
 			ConfMan.set("current_storage", "1", "cloud");
-			ConfMan.set("storage1_type", "Dropbox", "cloud");
-			ConfMan.set("storage1_access_token", result.getVal("access_token")->asString(), "cloud");
-			ConfMan.set("storage1_user_id", result.getVal("uid")->asString(), "cloud");
+			ConfMan.set("storage_Dropbox_type", "Dropbox", "cloud");
+			ConfMan.set("storage_Dropbox_access_token", result.getVal("access_token")->asString(), "cloud");
+			ConfMan.set("storage_Dropbox_user_id", result.getVal("uid")->asString(), "cloud");
 			ConfMan.removeKey("dropbox_code", "cloud");
 			ConfMan.flushToDisk();
 			debug("Now please restart ScummVM to apply the changes.");
@@ -85,8 +85,7 @@ DropboxStorage::~DropboxStorage() {
 	curl_global_cleanup();
 }
 
-void DropboxStorage::saveConfig(Common::String keyPrefix) {
-	ConfMan.set(keyPrefix + "type", "Dropbox", "cloud");
+void DropboxStorage::saveConfig(Common::String keyPrefix) {	
 	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 }
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 30ca1be..18ddca5 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -121,15 +121,14 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	ConfMan.removeKey("googledrive_code", "cloud");
-	CloudMan.addStorage(this);
+	ConfMan.removeKey("googledrive_code", "cloud");	
+	CloudMan.replaceStorage(this, kStorageGoogleDriveId);
 	ConfMan.flushToDisk();
 	debug("Done! You can use Google Drive now! Look:");
 	CloudMan.testFeature();
 }
 
-void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
-	ConfMan.set(keyPrefix + "type", "Google Drive", "cloud");
+void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
 	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
 }
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index c391065..d73bcdb 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -115,14 +115,14 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	CloudMan.addStorage(this);
 	ConfMan.removeKey("onedrive_code", "cloud");
+	CloudMan.replaceStorage(this, kStorageOneDriveId);
+	ConfMan.flushToDisk();
 	debug("Done! You can use OneDrive now! Look:");
 	CloudMan.syncSaves();
 }
 
-void OneDriveStorage::saveConfig(Common::String keyPrefix) {
-	ConfMan.set(keyPrefix + "type", "OneDrive", "cloud");
+void OneDriveStorage::saveConfig(Common::String keyPrefix) {	
 	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
diff --git a/gui/options.cpp b/gui/options.cpp
index b0a7968..2febf84 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1271,16 +1271,18 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	else
 		tab->addTab(_c("Cloud", "lowres"));
 
+	_selectedStorageIndex = CloudMan.getStorageIndex();
+
 	new ButtonWidget(tab, "GlobalOptions_Cloud.StorageButton", _("Storage:"), 0, kChooseStorageCmd);
-	_curStorage = new StaticTextWidget(tab, "GlobalOptions_Cloud.CurStorage", CloudMan.getStorageName());
+	_curStorage = new StaticTextWidget(tab, "GlobalOptions_Cloud.CurStorage", CloudMan.listStorages()[_selectedStorageIndex]);
 
-	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
+	_storageUsernameDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
 	_storageUsername = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameLabel", "<none>");
 
-	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM on this storage"));
+	_storageUsedSpaceDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM on this storage"));
 	_storageUsedSpace = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceLabel", "0 bytes");
 
-	new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did last saves sync"));
+	_storageLastSyncDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did last saves sync"));
 	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
 
 	setupCloudTab();
@@ -1437,6 +1439,21 @@ void GlobalOptionsDialog::close() {
 		}
 #endif
 
+#ifdef USE_CLOUD
+		if (CloudMan.getStorageIndex() != _selectedStorageIndex) {
+			if (!CloudMan.switchStorage(_selectedStorageIndex)) {
+				bool anotherStorageIsWorking = CloudMan.isWorking();
+				Common::String message = _("Failed to change cloud storage!");
+				if (anotherStorageIsWorking) {
+					message += "\n";
+					message += _("Current cloud storage is working at the moment.");
+				}
+				MessageDialog dialog(message);
+				dialog.runModal();
+			}
+		}
+#endif
+
 	}
 	OptionsDialog::close();
 }
@@ -1555,23 +1572,8 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		StorageBrowser storageBrowser;
 		if (storageBrowser.runModal() > 0) {
 			// User made his choice...
-			uint32 storageIndex = storageBrowser.getSelected();
-			// FIXME: Actually, any changes (including the storage change?) should
-			// only become active *after* the options dialog has closed.			
-			if (CloudMan.switchStorage(storageIndex)) {
-				_curStorage->setLabel(CloudMan.getStorageName());
-				//automatically saves in the config if switched successfully
-				setupCloudTab();
-			} else {
-				bool anotherStorageIsWorking = CloudMan.isWorking();
-				Common::String message = _("Failed to change cloud storage!");
-				if (anotherStorageIsWorking) {
-					message += "\n";
-					message += _("Current cloud storage is working at the moment.");
-				}
-				MessageDialog dialog(message);
-				dialog.runModal();
-			}
+			_selectedStorageIndex = storageBrowser.getSelected();
+			setupCloudTab();
 			draw();
 		}
 		break;
@@ -1635,23 +1637,32 @@ void GlobalOptionsDialog::reflowLayout() {
 
 #ifdef USE_CLOUD
 void GlobalOptionsDialog::setupCloudTab() {
-	uint32 index = CloudMan.getStorageIndex();
+	if (_curStorage)
+		_curStorage->setLabel(CloudMan.listStorages()[_selectedStorageIndex]);
+
+	bool shown = (_selectedStorageIndex != Cloud::kStorageNoneId);
+	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(shown);
 	if (_storageUsername) {
-		Common::String username = CloudMan.getStorageUsername(index);
+		Common::String username = CloudMan.getStorageUsername(_selectedStorageIndex);
 		if (username == "") username = _("<none>");
 		_storageUsername->setLabel(username);
+		_storageUsername->setVisible(shown);
 	}
+	if (_storageUsedSpaceDesc) _storageUsedSpaceDesc->setVisible(shown);
 	if (_storageUsedSpace) {
-		uint64 usedSpace = CloudMan.getStorageUsedSpace(index);
+		uint64 usedSpace = CloudMan.getStorageUsedSpace(_selectedStorageIndex);
 		_storageUsedSpace->setLabel(Common::String::format(_("%llu bytes"), usedSpace));
+		_storageUsedSpace->setVisible(shown);
 	}
+	if (_storageLastSyncDesc) _storageLastSyncDesc->setVisible(shown);
 	if (_storageLastSync) {
-		Common::String sync = CloudMan.getStorageLastSync(index);
+		Common::String sync = CloudMan.getStorageLastSync(_selectedStorageIndex);
 		if (sync == "") {
-			if (CloudMan.isSyncing()) sync = _("<right now>");
+			if (_selectedStorageIndex == CloudMan.getStorageIndex() && CloudMan.isSyncing()) sync = _("<right now>");
 			else sync = _("<never>");
 		}
 		_storageLastSync->setLabel(sync);
+		_storageLastSync->setVisible(shown);
 	}
 }
 #endif
diff --git a/gui/options.h b/gui/options.h
index 228a415..16ea424 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -246,9 +246,13 @@ protected:
 	//
 	// Cloud controls
 	//
+	uint32 _selectedStorageIndex;
 	StaticTextWidget *_curStorage;
+	StaticTextWidget *_storageUsernameDesc;
 	StaticTextWidget *_storageUsername;
+	StaticTextWidget *_storageUsedSpaceDesc;
 	StaticTextWidget *_storageUsedSpace;
+	StaticTextWidget *_storageLastSyncDesc;
 	StaticTextWidget *_storageLastSync;
 
 	void setupCloudTab();


Commit: 9b15ec9989fc67a0537b1e70732d3dba48797165
    https://github.com/scummvm/scummvm/commit/9b15ec9989fc67a0537b1e70732d3dba48797165
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update CloudManager

It now has methods to update Storage's information.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 5c69e30..a1b1ed1 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -114,6 +114,14 @@ void CloudManager::init() {
 }
 
 void CloudManager::save() {
+	for (uint32 i = 0; i < _storages.size(); ++i) {
+		if (i == kStorageNoneId) continue;
+		Common::String name = getStorageConfigName(i);
+		ConfMan.set("storage_" + name + "_username", _storages[i].username, "cloud");		
+		ConfMan.set("storage_" + name + "_lastSync", _storages[i].lastSyncDate, "cloud");
+		ConfMan.set("storage_" + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), "cloud");
+	}
+
 	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex), "cloud");
 	if (_activeStorage)
 		_activeStorage->saveConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
@@ -179,13 +187,34 @@ Common::String CloudManager::getStorageLastSync(uint32 index) {
 	return _storages[index].lastSyncDate;
 }
 
+void CloudManager::setStorageUsername(uint32 index, Common::String name) {
+	if (index >= _storages.size()) return;
+	_storages[index].username = name;
+	save();
+}
+
+void CloudManager::setStorageUsedSpace(uint32 index, uint64 used) {
+	if (index >= _storages.size()) return;
+	_storages[index].usedBytes = used;
+	save();
+}
+
+void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
+	if (index >= _storages.size()) return;
+	_storages[index].lastSyncDate = date;
+	save();
+}
+
 void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
 
 SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->syncSaves(callback, errorCallback);
+	if (storage) {
+		setStorageLastSync(_currentStorageIndex, "???"); //TODO get the date
+		return storage->syncSaves(callback, errorCallback);
+	}
 	return nullptr;
 }
 
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 5e26ece..7ce7e92 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -144,6 +144,33 @@ public:
 	Common::String getStorageLastSync(uint32 index);
 
 	/**
+	* Set Storage's username.
+	* Automatically saves changes to the config.
+	*
+	* @param	index	Storage's index.
+	* @param	name	username to set
+	*/
+	void setStorageUsername(uint32 index, Common::String name);
+
+	/**	
+	* Set Storage's used space field.
+	* Automatically saves changes to the config.
+	*
+	* @param	index	Storage's index.
+	* @param	used	value to set
+	*/
+	void setStorageUsedSpace(uint32 index, uint64 used);
+
+	/**
+	* Set Storage's last sync date.
+	* Automatically saves changes to the config.
+	*
+	* @param	index	Storage's index.
+	* @param	date	date to set
+	*/
+	void setStorageLastSync(uint32 index, Common::String date);
+
+	/**
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
 	SavesSyncRequest *syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index af73138..e59e19e 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -25,6 +25,7 @@
 #include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxuploadrequest.h"
+#include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "common/config-manager.h"
@@ -171,6 +172,10 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 		uint64 quotaNormal = quota.getVal("normal")->asIntegerNumber();
 		uint64 quotaShared = quota.getVal("shared")->asIntegerNumber();
 		uint64 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
+
+		CloudMan.setStorageUsedSpace(kStorageDropboxId, quotaNormal + quotaShared); //TODO that's not ScummVM's actually
+		CloudMan.setStorageUsername(kStorageDropboxId, email);
+
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 18ddca5..143b7ac 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -180,6 +180,9 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 			quotaAllocated = atoull(limit);
 		}
 
+		CloudMan.setStorageUsedSpace(kStorageGoogleDriveId, quotaUsed); //TODO that's not ScummVM's actually
+		CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
+
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index d73bcdb..8268175 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -159,6 +159,8 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 			quotaUsed = info.getVal("size")->asIntegerNumber();
 		}
 
+		CloudMan.setStorageUsedSpace(kStorageOneDriveId, quotaUsed); //TODO that's not ScummVM's actually
+		CloudMan.setStorageUsername(kStorageOneDriveId, email);
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
 		delete outerCallback;
 	}


Commit: e1e48968b4e5b7d55594adf038657bf6a8d7bc43
    https://github.com/scummvm/scummvm/commit/e1e48968b4e5b7d55594adf038657bf6a8d7bc43
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Replace Cloud tab's StorageBrowser with PopUp

Changed paths:
  R gui/storagebrowser.cpp
  R gui/storagebrowser.h
    gui/module.mk
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/module.mk b/gui/module.mk
index ece360b..2ffea5a 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -18,7 +18,6 @@ MODULE_OBJS := \
 	predictivedialog.o \
 	saveload.o \
 	saveload-dialog.o \
-	storagebrowser.o \
 	themebrowser.o \
 	ThemeEngine.o \
 	ThemeEval.o \
diff --git a/gui/options.cpp b/gui/options.cpp
index 2febf84..ec17a7a 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -45,7 +45,6 @@
 
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
-#include "gui/storagebrowser.h"
 #endif
 
 namespace GUI {
@@ -1273,16 +1272,20 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 
 	_selectedStorageIndex = CloudMan.getStorageIndex();
 
-	new ButtonWidget(tab, "GlobalOptions_Cloud.StorageButton", _("Storage:"), 0, kChooseStorageCmd);
-	_curStorage = new StaticTextWidget(tab, "GlobalOptions_Cloud.CurStorage", CloudMan.listStorages()[_selectedStorageIndex]);
+	_storagePopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StoragePopupDesc", _("Storage:"), _("Active cloud storage"));
+	_storagePopUp = new PopUpWidget(tab, "GlobalOptions_Cloud.StoragePopup");
+	Common::StringArray list = CloudMan.listStorages();
+	for (uint32 i = 0; i < list.size(); ++i)
+		_storagePopUp->appendEntry(list[i], i);
+	_storagePopUp->setSelected(_selectedStorageIndex);
 
 	_storageUsernameDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
 	_storageUsername = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameLabel", "<none>");
 
-	_storageUsedSpaceDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM on this storage"));
+	_storageUsedSpaceDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM's saves on this storage"));
 	_storageUsedSpace = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceLabel", "0 bytes");
 
-	_storageLastSyncDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did last saves sync"));
+	_storageLastSyncDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did saves sync last time"));
 	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
 
 	setupCloudTab();
@@ -1567,15 +1570,10 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 #ifdef USE_CLOUD
-	case kChooseStorageCmd:
+	case kPopUpItemSelectedCmd:
 	{
-		StorageBrowser storageBrowser;
-		if (storageBrowser.runModal() > 0) {
-			// User made his choice...
-			_selectedStorageIndex = storageBrowser.getSelected();
-			setupCloudTab();
-			draw();
-		}
+		setupCloudTab();
+		draw();
 		break;
 	}
 #endif
@@ -1637,8 +1635,7 @@ void GlobalOptionsDialog::reflowLayout() {
 
 #ifdef USE_CLOUD
 void GlobalOptionsDialog::setupCloudTab() {
-	if (_curStorage)
-		_curStorage->setLabel(CloudMan.listStorages()[_selectedStorageIndex]);
+	_selectedStorageIndex = _storagePopUp->getSelectedTag();
 
 	bool shown = (_selectedStorageIndex != Cloud::kStorageNoneId);
 	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(shown);
diff --git a/gui/options.h b/gui/options.h
index 16ea424..89670fd 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -247,7 +247,8 @@ protected:
 	// Cloud controls
 	//
 	uint32 _selectedStorageIndex;
-	StaticTextWidget *_curStorage;
+	StaticTextWidget *_storagePopUpDesc;
+	PopUpWidget *_storagePopUp;
 	StaticTextWidget *_storageUsernameDesc;
 	StaticTextWidget *_storageUsername;
 	StaticTextWidget *_storageUsedSpaceDesc;
diff --git a/gui/storagebrowser.cpp b/gui/storagebrowser.cpp
deleted file mode 100644
index 30a0e9e..0000000
--- a/gui/storagebrowser.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/* 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/storagebrowser.h"
-#include "gui/widgets/list.h"
-#include "gui/widget.h"
-#include "gui/gui-manager.h"
-
-#include "common/translation.h"
-#ifdef USE_CLOUD
-#include "backends/cloud/cloudmanager.h"
-#endif
-
-namespace GUI {
-
-enum {
-	kChooseCmd = 'Chos'
-};
-
-StorageBrowser::StorageBrowser() : Dialog("Browser") {
-	new StaticTextWidget(this, "Browser.Headline", _("Select a Storage"));
-
-	// Add storages list
-	_storagesList = new ListWidget(this, "Browser.List");
-	_storagesList->setNumberingMode(kListNumberingOff);
-	_storagesList->setEditable(false);
-
-	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
-
-	// Buttons
-	new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd);
-	new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd);
-}
-
-void StorageBrowser::open() {
-	// Always refresh storages list
-	updateListing();
-
-	// Call super implementation
-	Dialog::open();
-}
-
-void StorageBrowser::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
-	switch (cmd) {
-	case kChooseCmd:
-	case kListItemActivatedCmd:
-	case kListItemDoubleClickedCmd: {		
-		int selection = _storagesList->getSelected();
-		if (selection < 0)
-			break;
-		_selectionIndex = selection;
-		setResult(1);
-		close();
-		break;
-	}
-	default:
-		Dialog::handleCommand(sender, cmd, data);
-	}
-}
-
-void StorageBrowser::updateListing() {
-	Common::StringArray list; 
-	uint32 currentStorageIndex = 0;
-#ifdef USE_CLOUD
-	list = CloudMan.listStorages();
-	currentStorageIndex = CloudMan.getStorageIndex();
-#endif
-
-	_storagesList->setList(list);
-	_storagesList->scrollTo(0);
-	_storagesList->setSelected(currentStorageIndex);
-
-	// Finally, redraw
-	draw();
-}
-
-} // End of namespace GUI
diff --git a/gui/storagebrowser.h b/gui/storagebrowser.h
deleted file mode 100644
index b18c30f..0000000
--- a/gui/storagebrowser.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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.
- *
- */
-
-#ifndef GUI_STORAGEBROWSER_H
-#define GUI_STORAGEBROWSER_H
-
-#include "gui/dialog.h"
-#include "common/str.h"
-
-namespace GUI {
-
-class CommandSender;
-class ListWidget;
-
-class StorageBrowser : public Dialog {
-public:
-	StorageBrowser();
-
-	void open();
-	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
-
-	uint32 getSelected() const { return _selectionIndex; }
-private:
-	ListWidget *_storagesList;	
-	uint32 _selectionIndex;
-
-	void updateListing();
-};
-
-} // End of namespace GUI
-
-#endif
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 093eeba..a4b4ccd 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -541,11 +541,11 @@
 	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
-				<widget name = 'StorageButton'
-						type = 'Button'
+				<widget name = 'StoragePopupDesc'
+						type = 'OptionsLabel'
 				/>
-				<widget name = 'CurStorage'
-						height = 'Globals.Line.Height'
+				<widget name = 'StoragePopup'
+						type = 'PopUp'
 				/>
 			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 105c121..4a4479c 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -529,11 +529,43 @@
 
 	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
-			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
-				<widget name = 'StorageButton'
-						type = 'Button'
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StoragePopupDesc'						
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StoragePopup'
+						type = 'PopUp'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageUsernameDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StorageUsernameLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageUsedSpaceDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StorageUsedSpaceLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageLastSyncDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
 				/>
-				<widget name = 'CurStorage'
+				<widget name = 'StorageLastSyncLabel'
 						height = 'Globals.Line.Height'
 				/>
 			</layout>


Commit: beb168a3a5bac602a9bf1455e7fe93dda0b13a1c
    https://github.com/scummvm/scummvm/commit/beb168a3a5bac602a9bf1455e7fe93dda0b13a1c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Cloud tab StorageWizardDialog

This is a dialog which guides user through Storage connection procedure.

Changed paths:
  A gui/storagewizarddialog.cpp
  A gui/storagewizarddialog.h
    gui/module.mk
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/module.mk b/gui/module.mk
index 2ffea5a..ef00531 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -18,6 +18,7 @@ MODULE_OBJS := \
 	predictivedialog.o \
 	saveload.o \
 	saveload-dialog.o \
+	storagewizarddialog.o \
 	themebrowser.o \
 	ThemeEngine.o \
 	ThemeEval.o \
diff --git a/gui/options.cpp b/gui/options.cpp
index ec17a7a..ac3cc0c 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -45,6 +45,7 @@
 
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
+#include "gui/storagewizarddialog.h"
 #endif
 
 namespace GUI {
@@ -90,7 +91,7 @@ enum {
 
 #ifdef USE_CLOUD
 enum {
-	kChooseStorageCmd = 'chst'
+	kConfigureStorageCmd = 'cfst'
 };
 #endif
 
@@ -1288,6 +1289,8 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	_storageLastSyncDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did saves sync last time"));
 	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
 
+	_storageConnectButton = new ButtonWidget(tab, "GlobalOptions_Cloud.ConnectButton", _("Connect"), _("Open wizard dialog to connect your cloud storage account"), kConfigureStorageCmd);
+
 	setupCloudTab();
 #endif
 
@@ -1576,6 +1579,14 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		draw();
 		break;
 	}
+	case kConfigureStorageCmd:
+	{
+		StorageWizardDialog dialog(_selectedStorageIndex);
+		dialog.runModal();
+		setupCloudTab();
+		draw();
+		break;
+	}
 #endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
@@ -1661,6 +1672,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_storageLastSync->setLabel(sync);
 		_storageLastSync->setVisible(shown);
 	}
+	if (_storageConnectButton) _storageConnectButton->setVisible(shown);
 }
 #endif
 
diff --git a/gui/options.h b/gui/options.h
index 89670fd..4addf71 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -255,6 +255,7 @@ protected:
 	StaticTextWidget *_storageUsedSpace;
 	StaticTextWidget *_storageLastSyncDesc;
 	StaticTextWidget *_storageLastSync;
+	ButtonWidget	 *_storageConnectButton;
 
 	void setupCloudTab();
 #endif
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
new file mode 100644
index 0000000..996365d
--- /dev/null
+++ b/gui/storagewizarddialog.cpp
@@ -0,0 +1,73 @@
+/* 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/widgets/list.h"
+#include "gui/widget.h"
+#include "gui/gui-manager.h"
+
+#include "common/translation.h"
+#include "backends/cloud/cloudmanager.h"
+
+namespace GUI {
+
+enum {
+	kConnectCmd = 'Cnnt'
+};
+
+StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId) {
+	_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:"));
+
+	Common::String url = "https://www.scummvm.org/cloud-";
+	switch (storageId) {
+	case Cloud::kStorageDropboxId: url += "dropbox"; break;
+	case Cloud::kStorageOneDriveId: url += "onedrive"; break;
+	case Cloud::kStorageGoogleDriveId: url += "googledrive"; break;
+	}
+
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", url);
+
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine1", _s("Press 'Continue' when you obtain"));
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine2", _s("the code from the storage."));
+
+	// Buttons
+	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
+	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
+}
+
+void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kConnectCmd:
+		setResult(1);
+		close();
+		break;
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+} // End of namespace GUI
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
new file mode 100644
index 0000000..ec5329b
--- /dev/null
+++ b/gui/storagewizarddialog.h
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_STORAGEWIZARDDIALOG_H
+#define GUI_STORAGEWIZARDDIALOG_H
+
+#include "gui/dialog.h"
+#include "common/str.h"
+
+namespace GUI {
+
+class CommandSender;
+
+class StorageWizardDialog : public Dialog {
+	uint32 _storageId;
+public:
+	StorageWizardDialog(uint32 storageId);
+
+	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index a4b4ccd..8406fc9 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -572,6 +572,49 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'Picture'
+						type = 'OptionsLabel'
+				/>
+				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
+					<widget name = 'Headline'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '4' />
+					<widget name = 'NavigateLine'
+							height = 'Globals.Line.Height'
+					/>
+					<widget name = 'URLLine'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '4' />
+					<widget name = 'ReturnLine1'
+							height = 'Globals.Line.Height'
+					/>
+					<widget name = 'ReturnLine2'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '6' />
+				</layout>
+			</layout>			
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'CancelButton'
+						type = 'Button'
+				/>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 4a4479c..13d854c 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -569,6 +569,44 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '4'>
+				<widget name = 'Headline'
+						height = 'Globals.Line.Height'
+				/>
+				<space size = '2' />
+				<widget name = 'NavigateLine'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'URLLine'
+						height = 'Globals.Line.Height'
+				/>
+				<space size = '2' />
+				<widget name = 'ReturnLine1'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'ReturnLine2'
+						height = 'Globals.Line.Height'
+				/>
+				<space size = '4' />
+			</layout>		
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'CancelButton'
+						type = 'Button'
+				/>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 


Commit: e6242b0be8fc9f9abc4daf87f80675cca46df4d9
    https://github.com/scummvm/scummvm/commit/e6242b0be8fc9f9abc4daf87f80675cca46df4d9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Refresh button in Options Cloud tab

Commit changes CloudManager and Storages so they would automatically
refresh the fields when the could.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.h
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a1b1ed1..9456dd8 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -135,6 +135,7 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
 	_activeStorage = storage;
 	_currentStorageIndex = index;
 	save();
+	if (_activeStorage) _activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
 }
 
 Storage *CloudManager::getCurrentStorage() const {
@@ -209,6 +210,34 @@ void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
 
+Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->listDirectory(path, callback, errorCallback, recursive);
+	else {
+		delete callback;
+		delete errorCallback;
+		//TODO: should we call errorCallback?
+	}
+	return nullptr;
+}
+
+Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->info(callback, errorCallback);
+	else {
+		delete callback;
+		delete errorCallback;
+		//TODO: should we call errorCallback?
+	}
+	return nullptr;
+}
+
+Common::String CloudManager::savesDirectoryPath() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->savesDirectoryPath();
+	return "";
+}
+
 SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
 	if (storage) {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 7ce7e92..fd130c5 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -170,6 +170,15 @@ public:
 	*/
 	void setStorageLastSync(uint32 index, Common::String date);
 
+	/** Returns ListDirectoryResponse with list of files. */
+	Networking::Request *listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+
+	/** Return the StorageInfo struct. */
+	Networking::Request *info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns storage's saves directory path with the trailing slash. */
+	Common::String savesDirectoryPath();
+
 	/**
 	 * Starts saves syncing process in currently active storage if there is any.
 	 */
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index e59e19e..faff10f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -162,20 +162,19 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ
 		return;
 	}
 
-	if (outerCallback) {		
-		//Dropbox documentation states there is no errors for this API method
-		Common::JSONObject info = json->asObject();
-		Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asIntegerNumber());
-		Common::String name = info.getVal("display_name")->asString();
-		Common::String email = info.getVal("email")->asString();
-		Common::JSONObject quota = info.getVal("quota_info")->asObject();
-		uint64 quotaNormal = quota.getVal("normal")->asIntegerNumber();
-		uint64 quotaShared = quota.getVal("shared")->asIntegerNumber();
-		uint64 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
-
-		CloudMan.setStorageUsedSpace(kStorageDropboxId, quotaNormal + quotaShared); //TODO that's not ScummVM's actually
-		CloudMan.setStorageUsername(kStorageDropboxId, email);
-
+	//Dropbox documentation states there is no errors for this API method
+	Common::JSONObject info = json->asObject();
+	Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asIntegerNumber());
+	Common::String name = info.getVal("display_name")->asString();
+	Common::String email = info.getVal("email")->asString();
+	Common::JSONObject quota = info.getVal("quota_info")->asObject();
+	uint64 quotaNormal = quota.getVal("normal")->asIntegerNumber();
+	uint64 quotaShared = quota.getVal("shared")->asIntegerNumber();
+	uint64 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
+		
+	CloudMan.setStorageUsername(kStorageDropboxId, email);
+
+	if (outerCallback) {
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 143b7ac..3196cbe 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -156,33 +156,32 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 		return;
 	}
 
-	if (outerCallback) {
-		Common::JSONObject info = json->asObject();
-
-		Common::String uid, name, email;
-		uint64 quotaUsed = 0, quotaAllocated = 0;
-
-		if (info.contains("user") && info.getVal("user")->isObject()) {
-			//"me":true, "kind":"drive#user","photoLink": "",
-			//"displayName":"Alexander Tkachev","emailAddress":"alexander at tkachov.ru","permissionId":""
-			Common::JSONObject user = info.getVal("user")->asObject();
-			uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
-			name = user.getVal("displayName")->asString();
-			email = user.getVal("emailAddress")->asString();
-		}
-
-		if (info.contains("storageQuota") && info.getVal("storageQuota")->isObject()) {
-			//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
-			Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
-			Common::String usage = storageQuota.getVal("usage")->asString();
-			Common::String limit = storageQuota.getVal("limit")->asString();			
-			quotaUsed = atoull(usage);
-			quotaAllocated = atoull(limit);
-		}
+	Common::JSONObject info = json->asObject();
+
+	Common::String uid, name, email;
+	uint64 quotaUsed = 0, quotaAllocated = 0;
+
+	if (info.contains("user") && info.getVal("user")->isObject()) {
+		//"me":true, "kind":"drive#user","photoLink": "",
+		//"displayName":"Alexander Tkachev","emailAddress":"alexander at tkachov.ru","permissionId":""
+		Common::JSONObject user = info.getVal("user")->asObject();
+		uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
+		name = user.getVal("displayName")->asString();
+		email = user.getVal("emailAddress")->asString();
+	}
 
-		CloudMan.setStorageUsedSpace(kStorageGoogleDriveId, quotaUsed); //TODO that's not ScummVM's actually
-		CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
+	if (info.contains("storageQuota") && info.getVal("storageQuota")->isObject()) {
+		//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
+		Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
+		Common::String usage = storageQuota.getVal("usage")->asString();
+		Common::String limit = storageQuota.getVal("limit")->asString();			
+		quotaUsed = atoull(usage);
+		quotaAllocated = atoull(limit);
+	}
+		
+	CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
 
+	if (outerCallback) {
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 8268175..178d43c 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -139,28 +139,31 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 		delete outerCallback;
 		return;
 	}
-
-	if (outerCallback) {
-		Common::JSONObject info = json->asObject();
-
-		Common::String uid, name, email;
-		uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
-
-		if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
-			Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
-			if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
-				Common::JSONObject user = createdBy.getVal("user")->asObject();
-				uid = user.getVal("id")->asString();
-				name = user.getVal("displayName")->asString();
-			}
+	
+	Common::JSONObject info = json->asObject();
+
+	Common::String uid, name, email;
+	uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
+
+	if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
+		Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
+		if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
+			Common::JSONObject user = createdBy.getVal("user")->asObject();
+			uid = user.getVal("id")->asString();
+			name = user.getVal("displayName")->asString();
 		}
+	}
 
-		if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
-			quotaUsed = info.getVal("size")->asIntegerNumber();
-		}
+	if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
+		quotaUsed = info.getVal("size")->asIntegerNumber();
+	}
+
+	Common::String username = email;
+	if (username == "") username = name;
+	if (username == "") username = uid;
+	CloudMan.setStorageUsername(kStorageOneDriveId, username);
 
-		CloudMan.setStorageUsedSpace(kStorageOneDriveId, quotaUsed); //TODO that's not ScummVM's actually
-		CloudMan.setStorageUsername(kStorageOneDriveId, email);
+	if (outerCallback) {
 		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
 		delete outerCallback;
 	}
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index cdf1dba..e1739f9 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -21,6 +21,7 @@
 */
 
 #include "backends/cloud/savessyncrequest.h"
+#include "backends/cloud/cloudmanager.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
@@ -82,9 +83,11 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 
 	//determine which files to download and which files to upload
 	Common::Array<StorageFile> &remoteFiles = response.value;
+	uint64 totalSize = 0;
 	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
 		StorageFile &file = remoteFiles[i];
 		if (file.isDirectory()) continue;
+		totalSize += file.size();
 		if (file.name() == TIMESTAMPS_FILENAME) continue;
 
 		Common::String name = file.name();
@@ -107,6 +110,8 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 		}
 	}
 
+	CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
+
 	//upload files which are unavailable in cloud
 	for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
 		if (i->_key == TIMESTAMPS_FILENAME) continue;
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 11d8f6b..a76f216 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -116,7 +116,7 @@ public:
 	 * a callback, which is called, when request is complete.
 	 */
 
-	/** Returns ListDirectoryStatus struct with list of files. */
+	/** Returns ListDirectoryResponse with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
 	
 	/** Returns UploadStatus struct with info about uploaded file. */
@@ -143,7 +143,13 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
-	/** Returns the StorageInfo struct. */
+	/**
+	 * Return the StorageInfo struct via <callback>.
+	 * Call the <errorCallback> if failed to get information.
+	 *
+	 * @note on success Storage should also call
+	 *	     CloudMan.setStorageUsername().
+	 */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/** Returns storage's saves directory path with the trailing slash. */
diff --git a/gui/options.cpp b/gui/options.cpp
index ac3cc0c..2ab6b1e 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -91,7 +91,8 @@ enum {
 
 #ifdef USE_CLOUD
 enum {
-	kConfigureStorageCmd = 'cfst'
+	kConfigureStorageCmd = 'cfst',
+	kRefreshStorageCmd = 'rfst'
 };
 #endif
 
@@ -1290,8 +1291,10 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
 
 	_storageConnectButton = new ButtonWidget(tab, "GlobalOptions_Cloud.ConnectButton", _("Connect"), _("Open wizard dialog to connect your cloud storage account"), kConfigureStorageCmd);
+	_storageRefreshButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RefreshButton", _("Refresh"), _("Refresh current cloud storage information (username and usage)"), kRefreshStorageCmd);
 
 	setupCloudTab();
+	_redrawCloudTab = false;
 #endif
 
 	// Activate the first tab
@@ -1587,6 +1590,14 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		draw();
 		break;
 	}
+	case kRefreshStorageCmd:
+	{
+		CloudMan.info(new Common::Callback<GlobalOptionsDialog, Cloud::Storage::StorageInfoResponse>(this, &GlobalOptionsDialog::storageInfoCallback), nullptr);
+		Common::String dir = CloudMan.savesDirectoryPath();
+		if (dir.lastChar() == '/') dir.deleteLastChar();
+		CloudMan.listDirectory(dir, new Common::Callback<GlobalOptionsDialog, Cloud::Storage::ListDirectoryResponse>(this, &GlobalOptionsDialog::storageListDirectoryCallback), nullptr);
+		break;
+	}
 #endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
@@ -1609,6 +1620,17 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 }
 
+void GlobalOptionsDialog::handleTickle() {
+	OptionsDialog::handleTickle();
+#ifdef USE_CLOUD
+	if (_redrawCloudTab) {
+		setupCloudTab();
+		draw();
+		_redrawCloudTab = false;
+	}
+#endif
+}
+
 void GlobalOptionsDialog::reflowLayout() {
 	int activeTab = _tabWidget->getActiveTab();
 
@@ -1673,6 +1695,24 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_storageLastSync->setVisible(shown);
 	}
 	if (_storageConnectButton) _storageConnectButton->setVisible(shown);
+	if (_storageRefreshButton) _storageRefreshButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
+}
+
+void GlobalOptionsDialog::storageInfoCallback(Cloud::Storage::StorageInfoResponse response) {
+	//we could've used response.value.email()
+	//but Storage already notified CloudMan
+	//so we just set the flag to redraw our cloud tab
+	_redrawCloudTab = true;
+}
+
+void GlobalOptionsDialog::storageListDirectoryCallback(Cloud::Storage::ListDirectoryResponse response) {
+	Common::Array<Cloud::StorageFile> &files = response.value;
+	uint64 totalSize = 0;
+	for (uint32 i = 0; i < files.size(); ++i)
+		if (!files[i].isDirectory())
+			totalSize += files[i].size();	
+	CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
+	_redrawCloudTab = true;
 }
 #endif
 
diff --git a/gui/options.h b/gui/options.h
index 4addf71..1454ddb 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -37,6 +37,10 @@
 #include "gui/fluidsynth-dialog.h"
 #endif
 
+#ifdef USE_CLOUD
+#include "backends/cloud/storage.h"
+#endif
+
 namespace GUI {
 
 class CheckboxWidget;
@@ -206,6 +210,7 @@ public:
 	void open();
 	void close();
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	void handleTickle();
 
 	virtual void reflowLayout();
 
@@ -256,8 +261,12 @@ protected:
 	StaticTextWidget *_storageLastSyncDesc;
 	StaticTextWidget *_storageLastSync;
 	ButtonWidget	 *_storageConnectButton;
+	ButtonWidget	 *_storageRefreshButton;
+	bool _redrawCloudTab;
 
 	void setupCloudTab();
+	void storageInfoCallback(Cloud::Storage::StorageInfoResponse response);
+	void storageListDirectoryCallback(Cloud::Storage::ListDirectoryResponse response);
 #endif
 };
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 8406fc9..824c6fa 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -576,6 +576,9 @@
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
+				<widget name = 'RefreshButton'
+						type = 'Button'
+				/>
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 13d854c..3d2c3b4 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -573,6 +573,9 @@
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
+				<widget name = 'RefreshButton'
+						type = 'Button'
+				/>
 			</layout>
 		</layout>
 	</dialog>


Commit: c99b24c16d1111a701d915832f24ac457aef697d
    https://github.com/scummvm/scummvm/commit/c99b24c16d1111a701d915832f24ac457aef697d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
COMMON: Add String::asUint64()

Instead of all these atoull() I've added everywhere.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    common/str.cpp
    common/str.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 9456dd8..fed35a9 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -43,17 +43,6 @@ CloudManager::~CloudManager() {
 	delete _activeStorage;
 }
 
-namespace {
-uint64 atoull(Common::String s) {
-	uint64 result = 0;
-	for (uint32 i = 0; i < s.size(); ++i) {
-		if (s[i] < '0' || s[i] > '9') break;
-		result = result * 10L + (s[i] - '0');
-	}
-	return result;
-}
-}
-
 Common::String CloudManager::getStorageConfigName(uint32 index) const {
 	switch (index) {
 	case kStorageNoneId: return "<none>";
@@ -101,7 +90,7 @@ void CloudManager::init() {
 		if (ConfMan.hasKey("storage_" + name + "_lastSync", "cloud"))
 			config.lastSyncDate = ConfMan.get("storage_" + name + "_lastSync", "cloud");
 		if (ConfMan.hasKey("storage_" + name + "_usedBytes", "cloud"))
-			config.usedBytes = atoull(ConfMan.get("storage_" + name + "_usedBytes", "cloud"));
+			config.usedBytes = ConfMan.get("storage_" + name + "_usedBytes", "cloud").asUint64();
 		_storages.push_back(config);
 	}
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index f9af363..86494e8c 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -67,17 +67,6 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken)
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-namespace {
-uint64 atoull(Common::String s) {
-	uint64 result = 0;
-	for (uint32 i = 0; i < s.size(); ++i) {
-		if (s[i] < '0' || s[i] > '9') break;
-		result = result * 10L + (s[i] - '0');
-	}
-	return result;
-}
-}
-
 void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
@@ -113,7 +102,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 				bool isDirectory = (item.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
 				uint32 size = 0, timestamp = 0;
 				if (item.contains("size") && item.getVal("size")->isString())
-					size = atoull(item.getVal("size")->asString());
+					size = item.getVal("size")->asString().asUint64();
 				if (item.contains("modifiedTime") && item.getVal("modifiedTime")->isString())
 					timestamp = ISO8601::convertToTimestamp(item.getVal("modifiedTime")->asString());
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 3196cbe..91d8c81 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -137,17 +137,6 @@ Common::String GoogleDriveStorage::name() const {
 	return "Google Drive";
 }
 
-namespace {
-uint64 atoull(Common::String s) {
-	uint64 result = 0;
-	for (uint32 i = 0; i < s.size(); ++i) {
-		if (s[i] < '0' || s[i] > '9') break;
-		result = result * 10L + (s[i] - '0');
-	}
-	return result;
-}
-}
-
 void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
@@ -175,8 +164,8 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 		Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
 		Common::String usage = storageQuota.getVal("usage")->asString();
 		Common::String limit = storageQuota.getVal("limit")->asString();			
-		quotaUsed = atoull(usage);
-		quotaAllocated = atoull(limit);
+		quotaUsed = usage.asUint64();
+		quotaAllocated = limit.asUint64();
 	}
 		
 	CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index f5636ef..8dd8ffc 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -207,17 +207,6 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-namespace {
-uint64 atoull(Common::String s) {
-	uint64 result = 0;
-	for (uint32 i = 0; i < s.size(); ++i) {
-		if (s[i] < '0' || s[i] > '9') break;
-		result = result * 10L + (s[i] - '0');
-	}
-	return result;
-}
-}
-
 bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
 	//308 Resume Incomplete, with Range: X-Y header
 	if (!stream) return false;
@@ -238,7 +227,7 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
 				if (c == '\n' || c == '\r') break;
 				result += c;
 			}
-			_serverReceivedBytes = atoull(result) + 1;			
+			_serverReceivedBytes = result.asUint64() + 1;
 			uploadNextPart();
 			return true;
 		}
@@ -285,7 +274,7 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 				bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
 				uint32 size = 0, timestamp = 0;
 				if (object.contains("size") && object.getVal("size")->isString())
-					size = atoull(object.getVal("size")->asString());
+					size = object.getVal("size")->asString().asUint64();
 				if (object.contains("modifiedTime") && object.getVal("modifiedTime")->isString())
 					timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
 
diff --git a/common/str.cpp b/common/str.cpp
index 3ff49a9..326b4a8 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -335,6 +335,15 @@ bool String::contains(char x) const {
 	return strchr(c_str(), x) != NULL;
 }
 
+uint64 String::asUint64() const {
+	uint64 result = 0;
+	for (uint32 i = 0; i < _size; ++i) {
+		if (_str[i] < '0' || _str[i] > '9') break;
+		result = result * 10L + (_str[i] - '0');
+	}
+	return result;
+}
+
 bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const {
 	return Common::matchString(c_str(), pat, ignoreCase, pathMode);
 }
diff --git a/common/str.h b/common/str.h
index 9ada8aa..89b832d 100644
--- a/common/str.h
+++ b/common/str.h
@@ -162,6 +162,9 @@ public:
 	bool contains(const char *x) const;
 	bool contains(char x) const;
 
+	/** Return uint64 corrensponding to String's contents. */
+	uint64 asUint64() const;
+
 	/**
 	 * Simple DOS-style pattern matching function (understands * and ? like used in DOS).
 	 * Taken from exult/files/listfiles.cc


Commit: 6a93e8dd09ae2eeab616d14189a58633fd928c07
    https://github.com/scummvm/scummvm/commit/6a93e8dd09ae2eeab616d14189a58633fd928c07
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ConnMan::urlEncode()

Tried to use it everywhere I should've use it.

Changed paths:
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h



diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 91d8c81..5eab071 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -234,7 +234,7 @@ Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Network
 
 Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
-		Common::String url = "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media";
+		Common::String url = "https://www.googleapis.com/drive/v3/files/" + ConnMan.urlEncode(id) + "?alt=media";
 		Common::String header = "Authorization: Bearer " + _token;
 		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 8dd8ffc..ce1fae2 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -108,7 +108,7 @@ void GoogleDriveUploadRequest::startUpload() {
 	}
 
 	Common::String url = "https://www.googleapis.com/upload/drive/v3/files";
-	if (_resolvedId != "") url += "/" + _resolvedId;
+	if (_resolvedId != "") url += "/" + ConnMan.urlEncode(_resolvedId);
 	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::startUploadCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index c48ae1d..fe1128b 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -63,7 +63,7 @@ void OneDriveCreateDirectoryRequest::start() {
 	}
 
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";	
-	if (parent != "") url += ":/" + parent + ":";
+	if (parent != "") url += ":/" + ConnMan.urlEncode(parent) + ":";
 	url += "/children";	
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index e362600..040ef15 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -70,8 +70,9 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
 	if (_currentDirectory != "" && _currentDirectory.lastChar() != '/' && _currentDirectory.lastChar() != '\\')
 		_currentDirectory += '/';
 
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + _currentDirectory;
-	url.deleteLastChar();
+	Common::String dir = _currentDirectory;
+	dir.deleteLastChar();
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(dir);
 	url += ":/children";
 	makeRequest(url);
 }
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 178d43c..d1971f9 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -214,7 +214,7 @@ Networking::Request *OneDriveStorage::upload(Common::String path, Common::Seekab
 }
 
 Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path;
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(path);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 752907f..bc54a81 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -63,7 +63,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
 	if (_uploadUrl == "" && _contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
-		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+_savePath+":/upload.createSession"; //folder must exist
+		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/upload.createSession"; //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
 		Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
@@ -75,7 +75,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 	Common::String url;
 	if (_uploadUrl == "") {		
-		url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+_savePath+":/content";
+		url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/content";
 	} else {		
 		url = _uploadUrl;
 	}
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index edc7e81..5a1e3e9 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -78,6 +78,17 @@ void ConnectionManager::showCloudDisabledIcon() {
 	startTimer();
 }
 
+Common::String ConnectionManager::urlEncode(Common::String s) {
+	if (!_multi) return "";
+	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
+	if (output) {
+		Common::String result = output;
+		curl_free(output);
+		return result;
+	}
+	return "";
+}
+
 //private goes here:
 
 void connectionsThread(void *ignored) {
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 66994f0..602f893 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -117,6 +117,9 @@ public:
 
 	/** Shows a "cloud disabled" icon for a three seconds. */
 	void showCloudDisabledIcon();
+
+	/** Return URL-encoded version of given string. */
+	Common::String urlEncode(Common::String s);
 };
 
 /** Shortcut for accessing the connection manager. */


Commit: 3e6503743c2f5d90c64bf37e943338c33fc58d2b
    https://github.com/scummvm/scummvm/commit/3e6503743c2f5d90c64bf37e943338c33fc58d2b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Request::date()

Used in SavesSyncRequest to update Storage's last sync date.

Changed paths:
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
    backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/request.cpp
    backends/networking/curl/request.h



diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index c077d8d..61cc9dd 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -68,6 +68,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 		delete json;
 		return;
 	}
+	if (response.request) _date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -93,6 +94,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 void DropboxCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -100,6 +102,8 @@ void DropboxCreateDirectoryRequest::handle() {}
 
 void DropboxCreateDirectoryRequest::restart() { start(); }
 
+Common::String DropboxCreateDirectoryRequest::date() const { return _date; }
+
 void DropboxCreateDirectoryRequest::finishSuccess(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
index ea3175b..e8599c7 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
@@ -36,6 +36,7 @@ class DropboxCreateDirectoryRequest: public Networking::Request {
 	Storage::BoolCallback _boolCallback;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 	
 	void start();
 	void responseCallback(Networking::JsonResponse response);
@@ -47,6 +48,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index d782f81..933ea2b 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -71,6 +71,8 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
+	if (response.request) _date = response.request->date();
+
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
@@ -137,6 +139,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 void DropboxListDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -144,6 +147,8 @@ void DropboxListDirectoryRequest::handle() {}
 
 void DropboxListDirectoryRequest::restart() { start(); }
 
+Common::String DropboxListDirectoryRequest::date() const { return _date; }
+
 void DropboxListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {	
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 3a83af0..0d96edd 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -40,6 +40,7 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Common::Array<StorageFile> _files;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 	
 	void start();
 	void responseCallback(Networking::JsonResponse response);
@@ -51,6 +52,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
index 54eff3d..2b7a805 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
@@ -63,12 +63,14 @@ void GoogleDriveCreateDirectoryRequest::start() {
 void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 	resolveId();
 }
 
 void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -85,6 +87,7 @@ void GoogleDriveCreateDirectoryRequest::resolveId() {
 void GoogleDriveCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 
 	//resolved => folder already exists
 	finishSuccess(false);
@@ -93,6 +96,7 @@ void GoogleDriveCreateDirectoryRequest::idResolvedCallback(Storage::UploadRespon
 void GoogleDriveCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	
 	//not resolved => folder not exists
 	if (error.response.contains("no such file found in its parent directory")) {		
@@ -116,12 +120,14 @@ void GoogleDriveCreateDirectoryRequest::idResolveFailedCallback(Networking::Erro
 void GoogleDriveCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 	finishSuccess(response.value);
 }
 
 void GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -129,6 +135,8 @@ void GoogleDriveCreateDirectoryRequest::handle() {}
 
 void GoogleDriveCreateDirectoryRequest::restart() { start(); }
 
+Common::String GoogleDriveCreateDirectoryRequest::date() const { return _date; }
+
 void GoogleDriveCreateDirectoryRequest::finishSuccess(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
index ede8427..f71afeb 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
@@ -39,6 +39,7 @@ class GoogleDriveCreateDirectoryRequest: public Networking::Request {
 	Storage::BoolCallback _boolCallback;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 
 	void start();
 	void createdBaseDirectoryCallback(Storage::BoolResponse response);
@@ -55,6 +56,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 86494e8c..2530bab 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -70,6 +70,7 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken)
 void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -131,6 +132,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 void GoogleDriveListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -138,6 +140,8 @@ void GoogleDriveListDirectoryByIdRequest::handle() {}
 
 void GoogleDriveListDirectoryByIdRequest::restart() { start(); }
 
+Common::String GoogleDriveListDirectoryByIdRequest::date() const { return _date; }
+
 void GoogleDriveListDirectoryByIdRequest::finishSuccess(Common::Array<StorageFile> &files) {	
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
index 5699845..ceb533e 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
@@ -41,6 +41,7 @@ class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
 	Common::Array<StorageFile> _files;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 	
 	void start();
 	void makeRequest(Common::String pageToken);
@@ -53,6 +54,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
index 8811ffc..3387b43 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
@@ -58,6 +58,7 @@ void GoogleDriveListDirectoryRequest::start() {
 void GoogleDriveListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 
 	StorageFile directory = response.value;
 	directory.setPath(_requestedPath);
@@ -68,6 +69,7 @@ void GoogleDriveListDirectoryRequest::idResolvedCallback(Storage::UploadResponse
 void GoogleDriveListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -88,6 +90,7 @@ void GoogleDriveListDirectoryRequest::listNextDirectory() {
 void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
 
 	for (uint32 i = 0; i < response.value.size(); ++i) {
 		StorageFile &file = response.value[i];
@@ -107,6 +110,7 @@ void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArray
 void GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -114,6 +118,8 @@ void GoogleDriveListDirectoryRequest::handle() {}
 
 void GoogleDriveListDirectoryRequest::restart() { start(); }
 
+Common::String GoogleDriveListDirectoryRequest::date() const { return _date; }
+
 void GoogleDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
index bea195f..b3d8ff6 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
@@ -43,6 +43,7 @@ class GoogleDriveListDirectoryRequest: public Networking::Request {
 	StorageFile _currentDirectory;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 
 	void start();
 	void idResolvedCallback(Storage::UploadResponse response);
@@ -57,6 +58,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index fe1128b..2c644c4 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -87,6 +87,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 		delete json;
 		return;
 	}
+	if (response.request) _date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -112,6 +113,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 void OneDriveCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -119,6 +121,8 @@ void OneDriveCreateDirectoryRequest::handle() {}
 
 void OneDriveCreateDirectoryRequest::restart() { start(); }
 
+Common::String OneDriveCreateDirectoryRequest::date() const { return _date; }
+
 void OneDriveCreateDirectoryRequest::finishSuccess(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
index 4bf0d9e..880e94e 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -38,6 +38,7 @@ class OneDriveCreateDirectoryRequest: public Networking::Request {
 	Storage::BoolCallback _boolCallback;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 	
 	void start();
 	void responseCallback(Networking::JsonResponse response);
@@ -49,6 +50,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 040ef15..be6fcb7 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -94,6 +94,8 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 		return;
 	}
 
+	if (response.request) _date = response.request->date();
+
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
@@ -138,6 +140,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 void OneDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -145,6 +148,8 @@ void OneDriveListDirectoryRequest::handle() {}
 
 void OneDriveListDirectoryRequest::restart() { start(); }
 
+Common::String OneDriveListDirectoryRequest::date() const { return _date; }
+
 void OneDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index b8adfe7..5e80f4f 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -43,6 +43,7 @@ class OneDriveListDirectoryRequest: public Networking::Request {
 	Common::String _currentDirectory;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	Common::String _date;
 
 	void start();
 	void listNextDirectory();
@@ -56,6 +57,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 };
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index e1739f9..f059e29 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -76,6 +76,8 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
+	if (response.request) _date = response.request->date();
+
 	Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
 	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {		
 		localFileNotAvailableInCloud[i->_key] = true;
@@ -352,6 +354,9 @@ void SavesSyncRequest::finishSuccess(bool success) {
 	//save updated timestamps (even if Request failed, there would be only valid timestamps)
 	saveTimestamps();
 
+	//update last successful sync date
+	CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
+
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 569feb4..1a615e8 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -45,6 +45,7 @@ class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	uint32 _totalFilesToHandle;
+	Common::String _date;
 
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 6ef0e34..3a143e5 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -66,6 +66,25 @@ void CurlRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
+Common::String CurlRequest::date() const {	
+	if (_stream) {
+		Common::String headers = _stream->responseHeaders();
+		const char *cstr = headers.c_str();
+		const char *position = strstr(cstr, "Date: ");
+
+		if (position) {
+			Common::String result = "";
+			char c;
+			for (const char *i = position + 6; c = *i, c != 0; ++i) {
+				if (c == '\n' || c == '\r') break;
+				result += c;
+			}
+			return result;
+		}
+	}
+	return "";
+}
+
 void CurlRequest::setHeaders(Common::Array<Common::String> &headers) {
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 5c06b58..68ea3a5 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -55,6 +55,7 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+	virtual Common::String date() const;
 
 	/** Replaces all headers with the passed array of headers. */
 	virtual void setHeaders(Common::Array<Common::String> &headers);
diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp
index d2f9158..4a2704f 100644
--- a/backends/networking/curl/request.cpp
+++ b/backends/networking/curl/request.cpp
@@ -60,6 +60,8 @@ void Request::retry(uint32 seconds) {
 
 RequestState Request::state() const { return _state; }
 
+Common::String Request::date() const { return ""; }
+
 void Request::finishError(ErrorResponse error) {
 	_state = FINISHED;
 	if (_errorCallback) (*_errorCallback)(error);
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index de5308f..6a1bc12 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -182,6 +182,20 @@ public:
 
 	/** Returns Request's current state. */
 	RequestState state() const;
+
+	/**
+	 * Return date this Request received from server.
+	 * It could be extracted from "Date" header,
+	 * which is kept in NetworkReadStream.
+	 *
+	 * @note not all Requests do that, so "" is returned
+	 * to indicate the date is unknown. That's also true
+	 * if no server response available or no "Date" header
+	 * was passed.
+	 *
+	 * @returns date from "Date" response header.
+	 */
+	virtual Common::String date() const;
 };
 
 } // End of namespace Networking


Commit: 9ee2eb4e60a34948797620a0f80ae0a80037efc0
    https://github.com/scummvm/scummvm/commit/9ee2eb4e60a34948797620a0f80ae0a80037efc0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add EditText in StorageWizardDialog

One can enter the code, press 'Connect' button and get a working
Storage!

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/onedrive/onedrivestorage.h
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index fed35a9..adfebdc 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -195,6 +195,16 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
 	save();
 }
 
+void CloudManager::connectStorage(uint32 index, Common::String code) {
+	Storage *storage = nullptr;
+	switch (index) {
+	case kStorageDropboxId: storage = new Dropbox::DropboxStorage(code); break;
+	case kStorageOneDriveId: storage = new OneDrive::OneDriveStorage(code); break;
+	case kStorageGoogleDriveId: storage = new GoogleDrive::GoogleDriveStorage(code); break;
+	}
+	//these would automatically request replaceStorage() when they receive the token
+}
+
 void CloudManager::printBool(Storage::BoolResponse response) const {
 	debug("bool = %s", (response.value ? "true" : "false"));
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index fd130c5..9f4882d 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -97,79 +97,88 @@ public:
 	Cloud::Storage *getCurrentStorage() const;
 
 	/**
-	* Return active Storage's index.
-	*
-	* @return	active Storage's index.
-	*/
+	 * Return active Storage's index.
+	 *
+	 * @return	active Storage's index.
+	 */
 	uint32 getStorageIndex() const;
 
 	/**
-	* Return Storages names as list.
-	*
-	* @return	a list of Storages names.
-	*/
+	 * Return Storages names as list.
+	 *
+	 * @return	a list of Storages names.
+	 */
 	Common::StringArray listStorages() const;
 
 	/**
-	* Changes the storage to the one with given index.
-	*
-	* @param	new Storage's index.
-	*/
+	 * Changes the storage to the one with given index.
+	 *
+	 * @param	new Storage's index.
+	 */
 	bool switchStorage(uint32 index);
 
 	/**
-	* Return username used by Storage.
-	*
-	* @param	Storage's index.
-	* @returns	username or "" if index is invalid (no such Storage).
-	*/
+	 * Return username used by Storage.
+	 *
+	 * @param	Storage's index.
+	 * @returns	username or "" if index is invalid (no such Storage).
+	 */
 	Common::String getStorageUsername(uint32 index);
 
 	/**
-	* Return space used by Storage.
-	*
-	* @param	Storage's index.
-	* @returns	used space in bytes or 0 if index is invalid (no such Storage).
-	*/
+	 * Return space used by Storage.
+	 *
+	 * @param	Storage's index.
+	 * @returns	used space in bytes or 0 if index is invalid (no such Storage).
+	 */
 	uint64 getStorageUsedSpace(uint32 index);
 
 	/**
-	* Return Storage's last sync date.
-	*
-	* @param	Storage's index.
-	* @returns	last sync date or "" if index is invalid (no such Storage).
+	 * Return Storage's last sync date.
+	 *
+	 * @param	Storage's index.
+	 * @returns	last sync date or "" if index is invalid (no such Storage).
 				It also returns "" if there never was any sync
 				or if storage is syncing right now.
-	*/
+	 */
 	Common::String getStorageLastSync(uint32 index);
 
 	/**
-	* Set Storage's username.
-	* Automatically saves changes to the config.
-	*
-	* @param	index	Storage's index.
-	* @param	name	username to set
-	*/
+	 * Set Storage's username.
+	 * Automatically saves changes to the config.
+	 *
+	 * @param	index	Storage's index.
+	 * @param	name	username to set
+	 */
 	void setStorageUsername(uint32 index, Common::String name);
 
 	/**	
-	* Set Storage's used space field.
-	* Automatically saves changes to the config.
-	*
-	* @param	index	Storage's index.
-	* @param	used	value to set
-	*/
+	 * Set Storage's used space field.
+	 * Automatically saves changes to the config.
+	 *
+	 * @param	index	Storage's index.
+	 * @param	used	value to set
+	 */
 	void setStorageUsedSpace(uint32 index, uint64 used);
 
 	/**
-	* Set Storage's last sync date.
-	* Automatically saves changes to the config.
-	*
-	* @param	index	Storage's index.
-	* @param	date	date to set
-	*/
+	 * Set Storage's last sync date.
+	 * Automatically saves changes to the config.
+	 *
+	 * @param	index	Storage's index.
+	 * @param	date	date to set
+	 */
 	void setStorageLastSync(uint32 index, Common::String date);
 
+	/**
+	 * Replace Storage which has given index with a
+	 * storage created with given code.
+	 *
+	 * @param	index	Storage's index
+	 * @param	code	OAuth2 code received from user
+	 */
+	void connectStorage(uint32 index, Common::String code);
+
 	/** Returns ListDirectoryResponse with list of files. */
 	Networking::Request *listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index faff10f..180b40c 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -51,41 +51,47 @@ void DropboxStorage::loadKeyAndSecret() {
 	SECRET[k.size()] = 0;
 }
 
-static void saveAccessTokenCallback(Networking::JsonResponse pair) {
-	Common::JSONValue *json = (Common::JSONValue *)pair.value;
-	if (json) {
-		debug("saveAccessTokenCallback:");
-		debug("%s", json->stringify(true).c_str());
+DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {}
+
+DropboxStorage::DropboxStorage(Common::String code) {
+	getAccessToken(code);
+}
+
+DropboxStorage::~DropboxStorage() {}
+
+void DropboxStorage::getAccessToken(Common::String code) {	
+	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);		
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token");
+	request->addPostField("code=" + code);
+	request->addPostField("grant_type=authorization_code");
+	request->addPostField("client_id=" + Common::String(KEY));
+	request->addPostField("client_secret=" + Common::String(SECRET));
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+	addRequest(request);
+}
 
+void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
+	Common::JSONValue *json = (Common::JSONValue *)response.value;
+	if (json) {
 		Common::JSONObject result = json->asObject();
 		if (!result.contains("access_token") || !result.contains("uid")) {
 			warning("Bad response, no token/uid passed");
 		} else {
-			//we suppose that's the first storage
-			//TODO: update it to use CloudMan.replaceStorage()			
-			ConfMan.set("current_storage", "1", "cloud");
-			ConfMan.set("storage_Dropbox_type", "Dropbox", "cloud");
-			ConfMan.set("storage_Dropbox_access_token", result.getVal("access_token")->asString(), "cloud");
-			ConfMan.set("storage_Dropbox_user_id", result.getVal("uid")->asString(), "cloud");
+			_token = result.getVal("access_token")->asString();
+			_uid = result.getVal("user_id")->asString();			
 			ConfMan.removeKey("dropbox_code", "cloud");
+			CloudMan.replaceStorage(this, kStorageDropboxId);
 			ConfMan.flushToDisk();
-			debug("Now please restart ScummVM to apply the changes.");
+			debug("Done! You can use Dropbox now! Look:");
+			CloudMan.testFeature();
 		}
 
 		delete json;
 	} else {
-		debug("saveAccessTokenCallback: got NULL instead of JSON!");
+		debug("DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
 	}
 }
 
-DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {
-	curl_global_init(CURL_GLOBAL_ALL);
-}
-
-DropboxStorage::~DropboxStorage() {
-	curl_global_cleanup();
-}
-
 void DropboxStorage::saveConfig(Common::String keyPrefix) {	
 	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
 	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
@@ -216,37 +222,5 @@ Common::String DropboxStorage::getAuthLink() {
 	return url;
 }
 
-void DropboxStorage::authThroughConsole() {
-	if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) {
-		warning("No Dropbox keys available, cannot do auth");
-		return;
-	}
-
-	loadKeyAndSecret();
-
-	if (ConfMan.hasKey("dropbox_code", "cloud")) {
-		//phase 2: get access_token using specified code
-		getAccessToken(ConfMan.get("dropbox_code", "cloud"));
-		return;
-	}
-
-	debug("Navigate to this URL and press \"Allow\":");
-	debug("%s\n", getAuthLink().c_str());
-	debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
-	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
-	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
-}
-
-void DropboxStorage::getAccessToken(Common::String code) {
-	Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::JsonResponse>(saveAccessTokenCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token"); //TODO
-	request->addPostField("code=" + code);
-	request->addPostField("grant_type=authorization_code");
-	request->addPostField("client_id=" + Common::String(KEY));
-	request->addPostField("client_secret=" + Common::String(SECRET));
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");	
-	ConnMan.addRequest(request);	
-}
-
 } // End of namespace Dropbox
 } // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 60a8075..d256e05 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -40,7 +40,8 @@ class DropboxStorage: public Cloud::Storage {
 	/** This private constructor is called from loadFromConfig(). */
 	DropboxStorage(Common::String token, Common::String uid);
 
-	static void getAccessToken(Common::String code);
+	void getAccessToken(Common::String code);
+	void codeFlowComplete(Networking::JsonResponse response);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
@@ -49,7 +50,9 @@ class DropboxStorage: public Cloud::Storage {
 	void printBool(BoolResponse response);
 	void printStorageFile(UploadResponse response);
 
-public:	
+public:
+	/** This constructor uses OAuth code flow to get tokens. */
+	DropboxStorage(Common::String code);
 	virtual ~DropboxStorage();
 
 	/**
@@ -107,11 +110,6 @@ public:
 	 * Returns Dropbox auth link.
 	 */
 	static Common::String getAuthLink();
-
-	/**
-	 * Show message with Dropbox auth instructions. (Temporary)
-	 */
-	static void authThroughConsole();
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 489260d..8093ef1 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -40,12 +40,6 @@ class GoogleDriveStorage: public Cloud::Storage {
 	/** This private constructor is called from loadFromConfig(). */
 	GoogleDriveStorage(Common::String token, Common::String refreshToken);
 
-	/**
-	 * This private constructor is called from authThroughConsole() (phase 2).
-	 * It uses OAuth code flow to get tokens.
-	 */
-	GoogleDriveStorage(Common::String code);
-
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
 
@@ -61,7 +55,9 @@ class GoogleDriveStorage: public Cloud::Storage {
 	void printBool(BoolResponse response);
 	void printFile(UploadResponse response);
 	void printInfo(StorageInfoResponse response);
-public:	
+public:
+	/** This constructor uses OAuth code flow to get tokens. */
+	GoogleDriveStorage(Common::String code);
 	virtual ~GoogleDriveStorage();
 
 	/**
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 3932d44..061d0fa 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -40,12 +40,6 @@ class OneDriveStorage: public Cloud::Storage {
 	/** This private constructor is called from loadFromConfig(). */
 	OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
 
-	/**
-	 * This private constructor is called from authThroughConsole() (phase 2).
-	 * It uses OAuth code flow to get tokens.
-	 */
-	OneDriveStorage(Common::String code);
-
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
 
@@ -60,6 +54,8 @@ class OneDriveStorage: public Cloud::Storage {
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 public:	
+	/** This constructor uses OAuth code flow to get tokens. */
+	OneDriveStorage(Common::String code);
 	virtual ~OneDriveStorage();
 
 	/**
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 996365d..d637440 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -27,11 +27,13 @@
 
 #include "common/translation.h"
 #include "backends/cloud/cloudmanager.h"
+#include "widgets/edittext.h"
 
 namespace GUI {
 
 enum {
-	kConnectCmd = 'Cnnt'
+	kConnectCmd = 'Cnnt',
+	kCodeBoxCmd = 'CdBx'
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId) {
@@ -42,17 +44,18 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOption
 	
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.NavigateLine", _s("Navigate to the following URL:"));
 
-	Common::String url = "https://www.scummvm.org/cloud-";
+	Common::String url = "https://www.scummvm.org/c/";
 	switch (storageId) {
-	case Cloud::kStorageDropboxId: url += "dropbox"; break;
-	case Cloud::kStorageOneDriveId: url += "onedrive"; break;
-	case Cloud::kStorageGoogleDriveId: url += "googledrive"; break;
+	case Cloud::kStorageDropboxId: url += "db"; break;
+	case Cloud::kStorageOneDriveId: url += "od"; break;
+	case Cloud::kStorageGoogleDriveId: url += "gd"; break;
 	}
 
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", url);
 
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine1", _s("Press 'Continue' when you obtain"));
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine2", _s("the code from the storage."));
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine1", _s("Obtain the code from the storage, enter it"));
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine2", _s("in the following field and press 'Connect':"));
+	_codeWidget = new EditTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CodeBox", _s("Code"), 0, kCodeBoxCmd);
 
 	// Buttons
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
@@ -61,7 +64,10 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOption
 
 void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
+	case kCodeBoxCmd:
+		break;
 	case kConnectCmd:
+		CloudMan.connectStorage(_storageId, _codeWidget->getEditString());
 		setResult(1);
 		close();
 		break;
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index ec5329b..00a3617 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -29,9 +29,11 @@
 namespace GUI {
 
 class CommandSender;
+class EditTextWidget;
 
 class StorageWizardDialog : public Dialog {
 	uint32 _storageId;
+	EditTextWidget *_codeWidget;
 public:
 	StorageWizardDialog(uint32 storageId);
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 824c6fa..cd76856 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -607,6 +607,10 @@
 					<widget name = 'ReturnLine2'
 							height = 'Globals.Line.Height'
 					/>
+					<widget name = 'CodeBox'
+						width = '150'
+						height = 'Globals.Line.Height'
+					/>
 					<space size = '6' />
 				</layout>
 			</layout>			
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 3d2c3b4..dd6700e 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -600,6 +600,10 @@
 				<widget name = 'ReturnLine2'
 						height = 'Globals.Line.Height'
 				/>
+				<widget name = 'CodeBox'
+						width = '150'
+						height = '16'
+				/>
 				<space size = '4' />
 			</layout>		
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>


Commit: a651a983dd75c1b775c1a633b22831812f780b65
    https://github.com/scummvm/scummvm/commit/a651a983dd75c1b775c1a633b22831812f780b65
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add warning message for game's savepath

Changed paths:
    gui/launcher.cpp



diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 9a3300b..dee7f17 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -563,6 +563,10 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 			// User made his choice...
 			Common::FSNode dir(browser.getResult());
 			_savePathWidget->setLabel(dir.getPath());
+#ifdef USE_CLOUD
+			MessageDialog warningMessage(_("Saves sync feature doesn't work with non-default directories. If you want your saves to sync, use default directory."));
+			warningMessage.runModal();
+#endif
 			draw();
 		}
 		draw();


Commit: a966de42a97dff932b6292f5021b7eb278310d1d
    https://github.com/scummvm/scummvm/commit/a966de42a97dff932b6292f5021b7eb278310d1d
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix compilation error in savesyncrequest.h

"savessyncrequest.h:35:35: error: use of undeclared identifier 'UINT_MAX'"

Changed paths:
    backends/cloud/savessyncrequest.h



diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 1a615e8..105d7f7 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -28,6 +28,7 @@
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 #include "gui/object.h"
+#include <limits.h>
 
 namespace Cloud {
 


Commit: bad2ec3ef4326593f3eac90af8b847062e2554cd
    https://github.com/scummvm/scummvm/commit/bad2ec3ef4326593f3eac90af8b847062e2554cd
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix initialization of NetworkReadStream

"networkreadstream.cpp:51:2: error: delegating constructors are permitted only in C++11"

Changed paths:
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index ccfb3d5..d761cbb 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -47,11 +47,11 @@ static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch):
-	NetworkReadStream(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false) {}
+void NetworkReadStream::init(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+	_eos = _requestComplete = false;
+	_sendingContentsBuffer = nullptr;
+	_sendingContentsSize = _sendingContentsPos = 0;
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post):
-	_easy(0), _eos(false), _requestComplete(false), _sendingContentsBuffer(nullptr), _sendingContentsSize(0), _sendingContentsPos(0) {
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
@@ -80,6 +80,14 @@ NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, b
 	ConnMan.registerEasyHandle(_easy);
 }
 
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch) {
+	init(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
+}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+	init(url, headersList, buffer, bufferSize, uploading, usingPatch, post);
+}
+
 NetworkReadStream::~NetworkReadStream() {
 	if (_easy)
 		curl_easy_cleanup(_easy);
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 991fdb3..7a9d9ff 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -39,6 +39,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 	uint32 _sendingContentsSize;
 	uint32 _sendingContentsPos;
 	Common::String _responseHeaders;
+	void init(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
 
 public:	
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);


Commit: 2a2beaebc53ff6ef580a12b986d17147df3f15bc
    https://github.com/scummvm/scummvm/commit/2a2beaebc53ff6ef580a12b986d17147df3f15bc
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
Fix DropboxStorage::codeFlowComplete()

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 180b40c..b8851b0 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -78,7 +78,7 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 			warning("Bad response, no token/uid passed");
 		} else {
 			_token = result.getVal("access_token")->asString();
-			_uid = result.getVal("user_id")->asString();			
+			_uid = result.getVal("uid")->asString();			
 			ConfMan.removeKey("dropbox_code", "cloud");
 			CloudMan.replaceStorage(this, kStorageDropboxId);
 			ConfMan.flushToDisk();


Commit: dbafbf25693f13a9a3105379dd2b964095712fce
    https://github.com/scummvm/scummvm/commit/dbafbf25693f13a9a3105379dd2b964095712fce
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix getAccessToken()

KEY and SECRET should now load before getAccessToken() uses them, so it
should work now.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b8851b0..bcafcf5 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -36,8 +36,8 @@
 namespace Cloud {
 namespace Dropbox {
 
-char *DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
-char *DropboxStorage::SECRET; //TODO: hide these secrets somehow
+char *DropboxStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void DropboxStorage::loadKeyAndSecret() {
 	Common::String k = ConfMan.get("DROPBOX_KEY", "cloud");
@@ -59,7 +59,8 @@ DropboxStorage::DropboxStorage(Common::String code) {
 
 DropboxStorage::~DropboxStorage() {}
 
-void DropboxStorage::getAccessToken(Common::String code) {	
+void DropboxStorage::getAccessToken(Common::String code) {
+	if (!KEY || !SECRET) loadKeyAndSecret();
 	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);		
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token");
 	request->addPostField("code=" + code);
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 5eab071..76ff1dd 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -42,8 +42,8 @@
 namespace Cloud {
 namespace GoogleDrive {
 
-char *GoogleDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
-char *GoogleDriveStorage::SECRET; //TODO: hide these secrets somehow
+char *GoogleDriveStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void GoogleDriveStorage::loadKeyAndSecret() {
 	Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", "cloud");
@@ -67,6 +67,7 @@ GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
 GoogleDriveStorage::~GoogleDriveStorage() {}
 
 void GoogleDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+	if (!KEY || !SECRET) loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
 	if (!codeFlow && _refreshToken == "") {
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index d1971f9..6ae5cb0 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -38,8 +38,8 @@
 namespace Cloud {
 namespace OneDrive {
 
-char *OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth
-char *OneDriveStorage::SECRET; //TODO: hide these secrets somehow
+char *OneDriveStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void OneDriveStorage::loadKeyAndSecret() {
 	Common::String k = ConfMan.get("ONEDRIVE_KEY", "cloud");
@@ -63,6 +63,7 @@ OneDriveStorage::OneDriveStorage(Common::String code) {
 OneDriveStorage::~OneDriveStorage() {}
 
 void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+	if (!KEY || !SECRET) loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
 	if (!codeFlow && _refreshToken == "") {


Commit: c068b74f303484819a8432379890b637d7bba029
    https://github.com/scummvm/scummvm/commit/c068b74f303484819a8432379890b637d7bba029
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Force handling of all StorageIDs values in CloudManager::getStorageConfigName()

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index adfebdc..0c087be 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -50,7 +50,8 @@ Common::String CloudManager::getStorageConfigName(uint32 index) const {
 	case kStorageOneDriveId: return "OneDrive";
 	case kStorageGoogleDriveId: return "GoogleDrive";
 	}
-	return "Unknown";
+	assert(false); // Unhandled StorageIDs value
+	return "";
 }
 
 void CloudManager::loadStorage() {


Commit: 1403cf006c5ebfd4a6101b89f4ff1d8760bb0ea4
    https://github.com/scummvm/scummvm/commit/1403cf006c5ebfd4a6101b89f4ff1d8760bb0ea4
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make enum StorageIDs' name singular

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 0c087be..4a87098 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -50,7 +50,7 @@ Common::String CloudManager::getStorageConfigName(uint32 index) const {
 	case kStorageOneDriveId: return "OneDrive";
 	case kStorageGoogleDriveId: return "GoogleDrive";
 	}
-	assert(false); // Unhandled StorageIDs value
+	assert(false); // Unhandled StorageID value
 	return "";
 }
 
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 9f4882d..48182dc 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -36,8 +36,8 @@ class CommandReceiver;
 
 namespace Cloud {
 
-//that's actual indexes in CloudManager's array
-enum StorageIDs {
+// The actual indexes in CloudManager's array
+enum StorageID {
 	kStorageNoneId = 0,
 	kStorageDropboxId = 1,
 	kStorageOneDriveId = 2,
@@ -84,7 +84,7 @@ public:
 	 * @note this method automatically saves the changes with ConfMan.
 	 *
 	 * @param	storage Cloud::Storage to replace active storage with.
-	 * @param	index   one of Cloud::StorageIDs enum values to indicate what storage type is replaced.	 
+	 * @param	index   one of Cloud::StorageID enum values to indicate what storage type is replaced.	 
 	 */
 	void replaceStorage(Storage *storage, uint32 index);
 


Commit: 98788a5e7d0879d9f64e11e3d2308af2b03bc1de
    https://github.com/scummvm/scummvm/commit/98788a5e7d0879d9f64e11e3d2308af2b03bc1de
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove unnecessary blank lines in switch statement

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 4a87098..8d12c0f 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -59,15 +59,12 @@ void CloudManager::loadStorage() {
 	case kStorageDropboxId:
 		_activeStorage = Dropbox::DropboxStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
-
 	case kStorageOneDriveId:
 		_activeStorage = OneDrive::OneDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
-
 	case kStorageGoogleDriveId:
 		_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
-
 	default:
 		_activeStorage = nullptr;
 	}


Commit: fc3e7dec1a786c5990e37bd963838c235fba098b
    https://github.com/scummvm/scummvm/commit/fc3e7dec1a786c5990e37bd963838c235fba098b
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Introduce kStoragePrefix in CloudManager

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 8d12c0f..83b06d9 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -36,6 +36,8 @@ DECLARE_SINGLETON(Cloud::CloudManager);
 
 namespace Cloud {
 
+const char *const CloudManager::kStoragePrefix = "storage_";
+
 CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr) {}
 
 CloudManager::~CloudManager() {
@@ -57,13 +59,13 @@ Common::String CloudManager::getStorageConfigName(uint32 index) const {
 void CloudManager::loadStorage() {
 	switch (_currentStorageIndex) {
 	case kStorageDropboxId:
-		_activeStorage = Dropbox::DropboxStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		_activeStorage = Dropbox::DropboxStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
 	case kStorageOneDriveId:
-		_activeStorage = OneDrive::OneDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		_activeStorage = OneDrive::OneDriveStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
 	case kStorageGoogleDriveId:
-		_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
 	default:
 		_activeStorage = nullptr;
@@ -83,12 +85,12 @@ void CloudManager::init() {
 		config.username = "";
 		config.lastSyncDate = "";
 		config.usedBytes = 0;
-		if (ConfMan.hasKey("storage_" + name + "_username", "cloud"))
-			config.username = ConfMan.get("storage_" + name + "_username", "cloud");
-		if (ConfMan.hasKey("storage_" + name + "_lastSync", "cloud"))
-			config.lastSyncDate = ConfMan.get("storage_" + name + "_lastSync", "cloud");
-		if (ConfMan.hasKey("storage_" + name + "_usedBytes", "cloud"))
-			config.usedBytes = ConfMan.get("storage_" + name + "_usedBytes", "cloud").asUint64();
+		if (ConfMan.hasKey(kStoragePrefix + name + "_username", "cloud"))
+			config.username = ConfMan.get(kStoragePrefix + name + "_username", "cloud");
+		if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync", "cloud"))
+			config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync", "cloud");
+		if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes", "cloud"))
+			config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes", "cloud").asUint64();
 		_storages.push_back(config);
 	}
 
@@ -104,14 +106,14 @@ void CloudManager::save() {
 	for (uint32 i = 0; i < _storages.size(); ++i) {
 		if (i == kStorageNoneId) continue;
 		Common::String name = getStorageConfigName(i);
-		ConfMan.set("storage_" + name + "_username", _storages[i].username, "cloud");		
-		ConfMan.set("storage_" + name + "_lastSync", _storages[i].lastSyncDate, "cloud");
-		ConfMan.set("storage_" + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), "cloud");
+		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, "cloud");		
+		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, "cloud");
+		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), "cloud");
 	}
 
 	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex), "cloud");
 	if (_activeStorage)
-		_activeStorage->saveConfig("storage_" + getStorageConfigName(_currentStorageIndex) + "_");
+		_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 	ConfMan.flushToDisk();
 }
 
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 48182dc..8e4c60e 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -47,6 +47,8 @@ enum StorageID {
 };
 
 class CloudManager : public Common::Singleton<CloudManager> {
+	static const char *const kStoragePrefix;
+
 	struct StorageConfig {
 		Common::String name, username;
 		uint64 usedBytes;


Commit: fd6be018a34f315969371d9183311a56e3a9eaa2
    https://github.com/scummvm/scummvm/commit/fd6be018a34f315969371d9183311a56e3a9eaa2
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix include in CloudManager

Changed paths:
    backends/cloud/cloudmanager.h



diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 8e4c60e..70b32f0 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -26,7 +26,7 @@
 #include "backends/cloud/storage.h"
 #include "common/array.h"
 #include "common/singleton.h"
-#include <cge/console.h>
+#include "common/str-array.h"
 
 namespace GUI {
 


Commit: bfc5cab9e88a2f70ac4a60c441c91a8b141ce113
    https://github.com/scummvm/scummvm/commit/bfc5cab9e88a2f70ac4a60c441c91a8b141ce113
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix end of namespace comment in CloudManager

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 83b06d9..77152d2 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -300,4 +300,4 @@ void CloudManager::setSyncTarget(GUI::CommandReceiver *target) {
 	if (storage) storage->setSyncTarget(target);
 }
 
-} // End of namespace Common
+} // End of namespace Cloud


Commit: 219e565c32d81e375677b62f6e82e48fae75f26f
    https://github.com/scummvm/scummvm/commit/219e565c32d81e375677b62f6e82e48fae75f26f
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Introduce CloudConfigHelper

Changed paths:
  A backends/cloud/cloudconfighelper.cpp
  A backends/cloud/cloudconfighelper.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/cloudconfighelper.cpp b/backends/cloud/cloudconfighelper.cpp
new file mode 100644
index 0000000..8c31b1c
--- /dev/null
+++ b/backends/cloud/cloudconfighelper.cpp
@@ -0,0 +1,58 @@
+/* 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 "backends/cloud/cloudconfighelper.h"
+#include "common/config-manager.h"
+
+namespace Common {
+
+DECLARE_SINGLETON(Cloud::CloudConfigHelper);
+
+}
+
+namespace Cloud {
+
+bool CloudConfigHelper::hasKey(const Common::String &key) const {
+	return ConfMan.hasKey(key, ConfMan.kCloudDomain);
+}
+
+void CloudConfigHelper::removeKey(const Common::String &key) {
+	ConfMan.removeKey(key, ConfMan.kCloudDomain);
+}
+
+const Common::String &CloudConfigHelper::get(const Common::String &key) const {
+	return ConfMan.get(key, ConfMan.kCloudDomain);
+}
+
+int CloudConfigHelper::getInt(const Common::String &key) const {
+	return ConfMan.getInt(key, ConfMan.kCloudDomain);
+}
+
+void CloudConfigHelper::set(const Common::String &key, const Common::String &value) {
+	ConfMan.set(key, value, ConfMan.kCloudDomain);
+}
+
+void CloudConfigHelper::flushToDisk() {
+	ConfMan.flushToDisk();
+}
+
+} // End of namespace Cloud
diff --git a/backends/cloud/cloudconfighelper.h b/backends/cloud/cloudconfighelper.h
new file mode 100644
index 0000000..efa8792
--- /dev/null
+++ b/backends/cloud/cloudconfighelper.h
@@ -0,0 +1,49 @@
+/* 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.
+*
+*/
+
+#ifndef CLOUD_CLOUDCONFIGHELPER_H
+#define CLOUD_CLOUDCONFIGHELPER_H
+
+#include "common/singleton.h"
+#include "common/str.h"
+
+namespace Cloud {
+
+/**
+ * Convenience wrapper around ConfMan for the cloud backend.
+ */
+class CloudConfigHelper : public Common::Singleton<CloudConfigHelper> {
+public:
+	bool hasKey(const Common::String &key) const;
+	void removeKey(const Common::String &key);
+	const Common::String &get(const Common::String &key) const;
+	int getInt(const Common::String &key) const;
+	void set(const Common::String &key, const Common::String &value);
+	void flushToDisk();
+};
+
+/** Shortcut for accessing the cloud configuration helper. */
+#define CloudConfig		Cloud::CloudConfigHelper::instance()
+
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 77152d2..38b7402 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/translation.h"
+#include "backends/cloud/cloudconfighelper.h"
 
 namespace Common {
 
@@ -85,19 +85,19 @@ void CloudManager::init() {
 		config.username = "";
 		config.lastSyncDate = "";
 		config.usedBytes = 0;
-		if (ConfMan.hasKey(kStoragePrefix + name + "_username", "cloud"))
-			config.username = ConfMan.get(kStoragePrefix + name + "_username", "cloud");
-		if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync", "cloud"))
-			config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync", "cloud");
-		if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes", "cloud"))
-			config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes", "cloud").asUint64();
+		if (CloudConfig.hasKey(kStoragePrefix + name + "_username"))
+			config.username = CloudConfig.get(kStoragePrefix + name + "_username");
+		if (CloudConfig.hasKey(kStoragePrefix + name + "_lastSync"))
+			config.lastSyncDate = CloudConfig.get(kStoragePrefix + name + "_lastSync");
+		if (CloudConfig.hasKey(kStoragePrefix + name + "_usedBytes"))
+			config.usedBytes = CloudConfig.get(kStoragePrefix + name + "_usedBytes").asUint64();
 		_storages.push_back(config);
 	}
 
 	//load an active storage if there is any
 	_currentStorageIndex = kStorageNoneId;
-	if (ConfMan.hasKey("current_storage", "cloud"))
-		_currentStorageIndex = ConfMan.getInt("current_storage", "cloud");
+	if (CloudConfig.hasKey("current_storage"))
+		_currentStorageIndex = CloudConfig.getInt("current_storage");
 
 	loadStorage();
 }
@@ -106,15 +106,15 @@ void CloudManager::save() {
 	for (uint32 i = 0; i < _storages.size(); ++i) {
 		if (i == kStorageNoneId) continue;
 		Common::String name = getStorageConfigName(i);
-		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, "cloud");		
-		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, "cloud");
-		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), "cloud");
+		CloudConfig.set(kStoragePrefix + name + "_username", _storages[i].username);		
+		CloudConfig.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate);
+		CloudConfig.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes));
 	}
 
-	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex), "cloud");
+	CloudConfig.set("current_storage", Common::String::format("%d", _currentStorageIndex));
 	if (_activeStorage)
 		_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
-	ConfMan.flushToDisk();
+	CloudConfig.flushToDisk();
 }
 
 void CloudManager::replaceStorage(Storage *storage, uint32 index) {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index bcafcf5..b677b56 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -28,24 +28,24 @@
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
+#include "backends/cloud/cloudconfighelper.h"
 
 namespace Cloud {
 namespace Dropbox {
 
-char *DropboxStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void DropboxStorage::loadKeyAndSecret() {
-	Common::String k = ConfMan.get("DROPBOX_KEY", "cloud");
+	Common::String k = CloudConfig.get("DROPBOX_KEY");
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = ConfMan.get("DROPBOX_SECRET", "cloud");
+	k = CloudConfig.get("DROPBOX_SECRET");
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -80,9 +80,9 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 		} else {
 			_token = result.getVal("access_token")->asString();
 			_uid = result.getVal("uid")->asString();			
-			ConfMan.removeKey("dropbox_code", "cloud");
+			CloudConfig.removeKey("dropbox_code");
 			CloudMan.replaceStorage(this, kStorageDropboxId);
-			ConfMan.flushToDisk();
+			CloudConfig.flushToDisk();
 			debug("Done! You can use Dropbox now! Look:");
 			CloudMan.testFeature();
 		}
@@ -94,8 +94,8 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 }
 
 void DropboxStorage::saveConfig(Common::String keyPrefix) {	
-	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
-	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
+	CloudConfig.set(keyPrefix + "access_token", _token);
+	CloudConfig.set(keyPrefix + "user_id", _uid);
 }
 
 Common::String DropboxStorage::name() const {
@@ -199,18 +199,18 @@ void DropboxStorage::infoMethodCallback(StorageInfoResponse response) {
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!ConfMan.hasKey(keyPrefix + "user_id", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "user_id")) {
 		warning("No user_id found");
 		return 0;
 	}
 
-	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
-	Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
+	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");
+	Common::String userId = CloudConfig.get(keyPrefix + "user_id");
 	return new DropboxStorage(accessToken, userId);
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 76ff1dd..2b044ce 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -27,7 +27,6 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
@@ -38,20 +37,21 @@
 #include "googledrivestreamfilerequest.h"
 #include "googledrivedownloadrequest.h"
 #include "googledriveuploadrequest.h"
+#include "backends/cloud/cloudconfighelper.h"
 
 namespace Cloud {
 namespace GoogleDrive {
 
-char *GoogleDriveStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void GoogleDriveStorage::loadKeyAndSecret() {
-	Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", "cloud");
+	Common::String k = CloudConfig.get("GOOGLE_DRIVE_KEY");
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = ConfMan.get("GOOGLE_DRIVE_SECRET", "cloud");
+	k = CloudConfig.get("GOOGLE_DRIVE_SECRET");
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -122,16 +122,16 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	ConfMan.removeKey("googledrive_code", "cloud");	
+	CloudConfig.removeKey("googledrive_code");	
 	CloudMan.replaceStorage(this, kStorageGoogleDriveId);
-	ConfMan.flushToDisk();
+	CloudConfig.flushToDisk();
 	debug("Done! You can use Google Drive now! Look:");
 	CloudMan.testFeature();
 }
 
 void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
-	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
-	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
+	CloudConfig.set(keyPrefix + "access_token", _token);
+	CloudConfig.set(keyPrefix + "refresh_token", _refreshToken);
 }
 
 Common::String GoogleDriveStorage::name() const {
@@ -339,18 +339,18 @@ Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/
 GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "refresh_token")) {
 		warning("No refresh_token found");
 		return 0;
 	}
 
-	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");	
-	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud");
+	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");	
+	Common::String refreshToken = CloudConfig.get(keyPrefix + "refresh_token");
 	return new GoogleDriveStorage(accessToken, refreshToken);
 }
 
@@ -365,16 +365,16 @@ Common::String GoogleDriveStorage::getAuthLink() {
 }
 
 void GoogleDriveStorage::authThroughConsole() {
-	if (!ConfMan.hasKey("GOOGLE_DRIVE_KEY", "cloud") || !ConfMan.hasKey("GOOGLE_DRIVE_SECRET", "cloud")) {
+	if (!CloudConfig.hasKey("GOOGLE_DRIVE_KEY") || !CloudConfig.hasKey("GOOGLE_DRIVE_SECRET")) {
 		warning("No Google Drive keys available, cannot do auth");
 		return;
 	}
 
 	loadKeyAndSecret();
 
-	if (ConfMan.hasKey("googledrive_code", "cloud")) {
+	if (CloudConfig.hasKey("googledrive_code")) {
 		//phase 2: get access_token using specified code
-		new GoogleDriveStorage(ConfMan.get("googledrive_code", "cloud"));
+		new GoogleDriveStorage(CloudConfig.get("googledrive_code"));
 		return;
 	}
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 6ae5cb0..bc1a4ff 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -30,24 +30,24 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
+#include "backends/cloud/cloudconfighelper.h"
 
 namespace Cloud {
 namespace OneDrive {
 
-char *OneDriveStorage::KEY = nullptr; //can't use ConfMan there yet, loading it on instance creation/auth
+char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void OneDriveStorage::loadKeyAndSecret() {
-	Common::String k = ConfMan.get("ONEDRIVE_KEY", "cloud");
+	Common::String k = CloudConfig.get("ONEDRIVE_KEY");
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = ConfMan.get("ONEDRIVE_SECRET", "cloud");
+	k = CloudConfig.get("ONEDRIVE_SECRET");
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -116,17 +116,17 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	ConfMan.removeKey("onedrive_code", "cloud");
+	CloudConfig.removeKey("onedrive_code");
 	CloudMan.replaceStorage(this, kStorageOneDriveId);
-	ConfMan.flushToDisk();
+	CloudConfig.flushToDisk();
 	debug("Done! You can use OneDrive now! Look:");
 	CloudMan.syncSaves();
 }
 
 void OneDriveStorage::saveConfig(Common::String keyPrefix) {	
-	ConfMan.set(keyPrefix + "access_token", _token, "cloud");
-	ConfMan.set(keyPrefix + "user_id", _uid, "cloud");
-	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud");
+	CloudConfig.set(keyPrefix + "access_token", _token);
+	CloudConfig.set(keyPrefix + "user_id", _uid);
+	CloudConfig.set(keyPrefix + "refresh_token", _refreshToken);
 }
 
 Common::String OneDriveStorage::name() const {
@@ -262,24 +262,24 @@ Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!ConfMan.hasKey(keyPrefix + "access_token", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!ConfMan.hasKey(keyPrefix + "user_id", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "user_id")) {
 		warning("No user_id found");
 		return 0;
 	}
 
-	if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) {
+	if (!CloudConfig.hasKey(keyPrefix + "refresh_token")) {
 		warning("No refresh_token found");
 		return 0;
 	}
 
-	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud");
-	Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud");
-	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud");
+	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");
+	Common::String userId = CloudConfig.get(keyPrefix + "user_id");
+	Common::String refreshToken = CloudConfig.get(keyPrefix + "refresh_token");
 	return new OneDriveStorage(accessToken, userId, refreshToken);
 }
 
@@ -294,16 +294,16 @@ Common::String OneDriveStorage::getAuthLink() {
 }
 
 void OneDriveStorage::authThroughConsole() {
-	if (!ConfMan.hasKey("ONEDRIVE_KEY", "cloud") || !ConfMan.hasKey("ONEDRIVE_SECRET", "cloud")) {
+	if (!CloudConfig.hasKey("ONEDRIVE_KEY") || !CloudConfig.hasKey("ONEDRIVE_SECRET")) {
 		warning("No OneDrive keys available, cannot do auth");
 		return;
 	}
 
 	loadKeyAndSecret();
 
-	if (ConfMan.hasKey("onedrive_code", "cloud")) {
+	if (CloudConfig.hasKey("onedrive_code")) {
 		//phase 2: get access_token using specified code
-		new OneDriveStorage(ConfMan.get("onedrive_code", "cloud"));
+		new OneDriveStorage(CloudConfig.get("onedrive_code"));
 		return;
 	}
 


Commit: ca0b58513d572419c52ba28ca09f4a086c543893
    https://github.com/scummvm/scummvm/commit/ca0b58513d572419c52ba28ca09f4a086c543893
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CloudConfigHelper to module.mk

Changed paths:
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index 95334fe..8e9b14f 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
+	cloud/cloudconfighelper.o \
 	cloud/cloudmanager.o \
 	cloud/iso8601.o \
 	cloud/storage.o \


Commit: 0aea8db7e1a59e8cf88436f78019685c9aff6332
    https://github.com/scummvm/scummvm/commit/0aea8db7e1a59e8cf88436f78019685c9aff6332
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make Storage::savesSync() restart

If Storage::syncSaves() is called when sync is running, another sync
would be automatically scheduled in the end of the current one.

That could be helpful when we want to specify that we changed something
during sync (created new save slot, for example).

Changed paths:
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 08ab9e9..b98f213 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -52,12 +52,18 @@ Networking::Request *Storage::addRequest(Networking::Request *request) {
 }
 
 void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer) {
+	bool restartSync = false;
+
 	_runningRequestsMutex.lock();
 	if (invalidRequestPointer == _savesSyncRequest)
 		_savesSyncRequest = nullptr;
 	--_runningRequestsCount;
-	if (_runningRequestsCount == 0) debug("Storage is not working now");
+	if (_syncRestartRequestsed) restartSync = true;
+	if (_runningRequestsCount == 0 && !restartSync) debug("Storage is not working now");
 	_runningRequestsMutex.unlock();
+
+	if (restartSync)
+		syncSaves(nullptr, nullptr);
 }
 
 Networking::Request *Storage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
@@ -111,11 +117,13 @@ SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCal
 	_runningRequestsMutex.lock();
 	if (_savesSyncRequest) {
 		warning("Storage::syncSaves: there is a sync in progress already");
+		_syncRestartRequestsed = true;
 		_runningRequestsMutex.unlock();
 		return _savesSyncRequest;
 	}
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
+	_syncRestartRequestsed = false;
 	_runningRequestsMutex.unlock();	
 	return (SavesSyncRequest *)addRequest(_savesSyncRequest); //who knows what that ConnMan could return in the future
 }
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index a76f216..ace2f30 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -62,6 +62,7 @@ protected:
 	uint32 _runningRequestsCount;
 	Common::Mutex _runningRequestsMutex;
 	SavesSyncRequest *_savesSyncRequest;
+	bool _syncRestartRequestsed;
 
 	/** Returns default error callback (printErrorResponse). */
 	virtual Networking::ErrorCallback getErrorPrintingCallback();


Commit: 8a84263d2b7f30bdb87a0b49c1d84454ece38b21
    https://github.com/scummvm/scummvm/commit/8a84263d2b7f30bdb87a0b49c1d84454ece38b21
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Do saves sync on Storage connect

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 38b7402..a6f5575 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -124,7 +124,12 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
 	_activeStorage = storage;
 	_currentStorageIndex = index;
 	save();
-	if (_activeStorage) _activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
+
+	//do what should be done on first Storage connect
+	if (_activeStorage) {
+		_activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
+		_activeStorage->syncSaves(nullptr, nullptr);
+	}
 }
 
 Storage *CloudManager::getCurrentStorage() const {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index b677b56..d77e958 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -76,6 +76,7 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 	if (json) {
 		Common::JSONObject result = json->asObject();
 		if (!result.contains("access_token") || !result.contains("uid")) {
+			warning(json->stringify(true).c_str());
 			warning("Bad response, no token/uid passed");
 		} else {
 			_token = result.getVal("access_token")->asString();
@@ -83,8 +84,6 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 			CloudConfig.removeKey("dropbox_code");
 			CloudMan.replaceStorage(this, kStorageDropboxId);
 			CloudConfig.flushToDisk();
-			debug("Done! You can use Dropbox now! Look:");
-			CloudMan.testFeature();
 		}
 
 		delete json;
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 2b044ce..1e31121 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -125,8 +125,6 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 	CloudConfig.removeKey("googledrive_code");	
 	CloudMan.replaceStorage(this, kStorageGoogleDriveId);
 	CloudConfig.flushToDisk();
-	debug("Done! You can use Google Drive now! Look:");
-	CloudMan.testFeature();
 }
 
 void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index bc1a4ff..b91d1cd 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -119,8 +119,6 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 	CloudConfig.removeKey("onedrive_code");
 	CloudMan.replaceStorage(this, kStorageOneDriveId);
 	CloudConfig.flushToDisk();
-	debug("Done! You can use OneDrive now! Look:");
-	CloudMan.syncSaves();
 }
 
 void OneDriveStorage::saveConfig(Common::String keyPrefix) {	


Commit: a8eebbe8517bf75131af4d7846592ebcc7e79d25
    https://github.com/scummvm/scummvm/commit/a8eebbe8517bf75131af4d7846592ebcc7e79d25
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Get rid of CloudConfigHelper, use kCloudDomain where approriate

Changed paths:
  R backends/cloud/cloudconfighelper.cpp
  R backends/cloud/cloudconfighelper.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/module.mk



diff --git a/backends/cloud/cloudconfighelper.cpp b/backends/cloud/cloudconfighelper.cpp
deleted file mode 100644
index 8c31b1c..0000000
--- a/backends/cloud/cloudconfighelper.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* 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 "backends/cloud/cloudconfighelper.h"
-#include "common/config-manager.h"
-
-namespace Common {
-
-DECLARE_SINGLETON(Cloud::CloudConfigHelper);
-
-}
-
-namespace Cloud {
-
-bool CloudConfigHelper::hasKey(const Common::String &key) const {
-	return ConfMan.hasKey(key, ConfMan.kCloudDomain);
-}
-
-void CloudConfigHelper::removeKey(const Common::String &key) {
-	ConfMan.removeKey(key, ConfMan.kCloudDomain);
-}
-
-const Common::String &CloudConfigHelper::get(const Common::String &key) const {
-	return ConfMan.get(key, ConfMan.kCloudDomain);
-}
-
-int CloudConfigHelper::getInt(const Common::String &key) const {
-	return ConfMan.getInt(key, ConfMan.kCloudDomain);
-}
-
-void CloudConfigHelper::set(const Common::String &key, const Common::String &value) {
-	ConfMan.set(key, value, ConfMan.kCloudDomain);
-}
-
-void CloudConfigHelper::flushToDisk() {
-	ConfMan.flushToDisk();
-}
-
-} // End of namespace Cloud
diff --git a/backends/cloud/cloudconfighelper.h b/backends/cloud/cloudconfighelper.h
deleted file mode 100644
index efa8792..0000000
--- a/backends/cloud/cloudconfighelper.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef CLOUD_CLOUDCONFIGHELPER_H
-#define CLOUD_CLOUDCONFIGHELPER_H
-
-#include "common/singleton.h"
-#include "common/str.h"
-
-namespace Cloud {
-
-/**
- * Convenience wrapper around ConfMan for the cloud backend.
- */
-class CloudConfigHelper : public Common::Singleton<CloudConfigHelper> {
-public:
-	bool hasKey(const Common::String &key) const;
-	void removeKey(const Common::String &key);
-	const Common::String &get(const Common::String &key) const;
-	int getInt(const Common::String &key) const;
-	void set(const Common::String &key, const Common::String &value);
-	void flushToDisk();
-};
-
-/** Shortcut for accessing the cloud configuration helper. */
-#define CloudConfig		Cloud::CloudConfigHelper::instance()
-
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a6f5575..1955121 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -26,7 +26,8 @@
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "common/debug.h"
 #include "common/translation.h"
-#include "backends/cloud/cloudconfighelper.h"
+#include "common/config-manager.h"
+#include "common/str.h"
 
 namespace Common {
 
@@ -77,6 +78,9 @@ void CloudManager::loadStorage() {
 }
 
 void CloudManager::init() {
+	Common::String oldDomain = ConfMan.getActiveDomainName();
+	ConfMan.setActiveDomain(ConfMan.kCloudDomain);
+
 	//init configs structs
 	for (uint32 i = 0; i < kStorageTotal; ++i) {
 		Common::String name = getStorageConfigName(i);
@@ -85,36 +89,43 @@ void CloudManager::init() {
 		config.username = "";
 		config.lastSyncDate = "";
 		config.usedBytes = 0;
-		if (CloudConfig.hasKey(kStoragePrefix + name + "_username"))
-			config.username = CloudConfig.get(kStoragePrefix + name + "_username");
-		if (CloudConfig.hasKey(kStoragePrefix + name + "_lastSync"))
-			config.lastSyncDate = CloudConfig.get(kStoragePrefix + name + "_lastSync");
-		if (CloudConfig.hasKey(kStoragePrefix + name + "_usedBytes"))
-			config.usedBytes = CloudConfig.get(kStoragePrefix + name + "_usedBytes").asUint64();
+		if (ConfMan.hasKey(kStoragePrefix + name + "_username"))
+			config.username = ConfMan.get(kStoragePrefix + name + "_username");
+		if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync"))
+			config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync");
+		if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes"))
+			config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes").asUint64();
 		_storages.push_back(config);
 	}
 
 	//load an active storage if there is any
 	_currentStorageIndex = kStorageNoneId;
-	if (CloudConfig.hasKey("current_storage"))
-		_currentStorageIndex = CloudConfig.getInt("current_storage");
+	if (ConfMan.hasKey("current_storage"))
+		_currentStorageIndex = ConfMan.getInt("current_storage");
 
 	loadStorage();
+
+	ConfMan.setActiveDomain(oldDomain);
 }
 
 void CloudManager::save() {
+	Common::String oldDomain = ConfMan.getActiveDomainName();
+	ConfMan.setActiveDomain(ConfMan.kCloudDomain);
+
 	for (uint32 i = 0; i < _storages.size(); ++i) {
 		if (i == kStorageNoneId) continue;
 		Common::String name = getStorageConfigName(i);
-		CloudConfig.set(kStoragePrefix + name + "_username", _storages[i].username);		
-		CloudConfig.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate);
-		CloudConfig.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes));
+		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username);
+		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate);
+		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes));
 	}
 
-	CloudConfig.set("current_storage", Common::String::format("%d", _currentStorageIndex));
+	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex));
 	if (_activeStorage)
 		_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
-	CloudConfig.flushToDisk();
+	ConfMan.flushToDisk();
+
+	ConfMan.setActiveDomain(oldDomain);
 }
 
 void CloudManager::replaceStorage(Storage *storage, uint32 index) {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d77e958..6442581 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -31,7 +31,7 @@
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "backends/cloud/cloudconfighelper.h"
+#include "common/config-manager.h"
 
 namespace Cloud {
 namespace Dropbox {
@@ -40,12 +40,12 @@ char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading
 char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void DropboxStorage::loadKeyAndSecret() {
-	Common::String k = CloudConfig.get("DROPBOX_KEY");
+	Common::String k = ConfMan.get("DROPBOX_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = CloudConfig.get("DROPBOX_SECRET");
+	k = ConfMan.get("DROPBOX_SECRET", ConfMan.kCloudDomain);
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -81,9 +81,9 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 		} else {
 			_token = result.getVal("access_token")->asString();
 			_uid = result.getVal("uid")->asString();			
-			CloudConfig.removeKey("dropbox_code");
+			ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
 			CloudMan.replaceStorage(this, kStorageDropboxId);
-			CloudConfig.flushToDisk();
+			ConfMan.flushToDisk();
 		}
 
 		delete json;
@@ -92,9 +92,9 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 	}
 }
 
-void DropboxStorage::saveConfig(Common::String keyPrefix) {	
-	CloudConfig.set(keyPrefix + "access_token", _token);
-	CloudConfig.set(keyPrefix + "user_id", _uid);
+void DropboxStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
+	ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
 }
 
 Common::String DropboxStorage::name() const {
@@ -198,18 +198,19 @@ void DropboxStorage::infoMethodCallback(StorageInfoResponse response) {
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
+	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!CloudConfig.hasKey(keyPrefix + "user_id")) {
+	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
 		warning("No user_id found");
 		return 0;
 	}
 
-	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");
-	Common::String userId = CloudConfig.get(keyPrefix + "user_id");
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
+	Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
+
 	return new DropboxStorage(accessToken, userId);
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 1e31121..6d0a152 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -37,7 +37,7 @@
 #include "googledrivestreamfilerequest.h"
 #include "googledrivedownloadrequest.h"
 #include "googledriveuploadrequest.h"
-#include "backends/cloud/cloudconfighelper.h"
+#include "common/config-manager.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -46,12 +46,12 @@ char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, load
 char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void GoogleDriveStorage::loadKeyAndSecret() {
-	Common::String k = CloudConfig.get("GOOGLE_DRIVE_KEY");
+	Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = CloudConfig.get("GOOGLE_DRIVE_SECRET");
+	k = ConfMan.get("GOOGLE_DRIVE_SECRET", ConfMan.kCloudDomain);
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -122,14 +122,14 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	CloudConfig.removeKey("googledrive_code");	
+	ConfMan.removeKey("googledrive_code", ConfMan.kCloudDomain);
 	CloudMan.replaceStorage(this, kStorageGoogleDriveId);
-	CloudConfig.flushToDisk();
+	ConfMan.flushToDisk();
 }
 
 void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
-	CloudConfig.set(keyPrefix + "access_token", _token);
-	CloudConfig.set(keyPrefix + "refresh_token", _refreshToken);
+	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
+	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
 }
 
 Common::String GoogleDriveStorage::name() const {
@@ -337,18 +337,18 @@ Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/
 GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
+	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!CloudConfig.hasKey(keyPrefix + "refresh_token")) {
+	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
 		warning("No refresh_token found");
 		return 0;
 	}
 
-	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");	
-	Common::String refreshToken = CloudConfig.get(keyPrefix + "refresh_token");
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
+	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
 	return new GoogleDriveStorage(accessToken, refreshToken);
 }
 
@@ -363,16 +363,16 @@ Common::String GoogleDriveStorage::getAuthLink() {
 }
 
 void GoogleDriveStorage::authThroughConsole() {
-	if (!CloudConfig.hasKey("GOOGLE_DRIVE_KEY") || !CloudConfig.hasKey("GOOGLE_DRIVE_SECRET")) {
+	if (!ConfMan.hasKey("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain) || !ConfMan.hasKey("GOOGLE_DRIVE_SECRET", ConfMan.kCloudDomain)) {
 		warning("No Google Drive keys available, cannot do auth");
 		return;
 	}
 
 	loadKeyAndSecret();
 
-	if (CloudConfig.hasKey("googledrive_code")) {
+	if (ConfMan.hasKey("googledrive_code", ConfMan.kCloudDomain)) {
 		//phase 2: get access_token using specified code
-		new GoogleDriveStorage(CloudConfig.get("googledrive_code"));
+		new GoogleDriveStorage(ConfMan.get("googledrive_code", ConfMan.kCloudDomain));
 		return;
 	}
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index b91d1cd..5c23036 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -33,7 +33,7 @@
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "backends/cloud/cloudconfighelper.h"
+#include "common/config-manager.h"
 
 namespace Cloud {
 namespace OneDrive {
@@ -42,12 +42,12 @@ char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading
 char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void OneDriveStorage::loadKeyAndSecret() {
-	Common::String k = CloudConfig.get("ONEDRIVE_KEY");
+	Common::String k = ConfMan.get("ONEDRIVE_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
 	KEY[k.size()] = 0;
 
-	k = CloudConfig.get("ONEDRIVE_SECRET");
+	k = ConfMan.get("ONEDRIVE_SECRET", ConfMan.kCloudDomain);
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
@@ -116,15 +116,15 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 		return;
 	}
 
-	CloudConfig.removeKey("onedrive_code");
+	ConfMan.removeKey("onedrive_code", ConfMan.kCloudDomain);
 	CloudMan.replaceStorage(this, kStorageOneDriveId);
-	CloudConfig.flushToDisk();
+	ConfMan.flushToDisk();
 }
 
-void OneDriveStorage::saveConfig(Common::String keyPrefix) {	
-	CloudConfig.set(keyPrefix + "access_token", _token);
-	CloudConfig.set(keyPrefix + "user_id", _uid);
-	CloudConfig.set(keyPrefix + "refresh_token", _refreshToken);
+void OneDriveStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
+	ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
+	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
 }
 
 Common::String OneDriveStorage::name() const {
@@ -260,24 +260,24 @@ Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
-	if (!CloudConfig.hasKey(keyPrefix + "access_token")) {
+	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
 		return 0;
 	}
 
-	if (!CloudConfig.hasKey(keyPrefix + "user_id")) {
+	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
 		warning("No user_id found");
 		return 0;
 	}
 
-	if (!CloudConfig.hasKey(keyPrefix + "refresh_token")) {
+	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
 		warning("No refresh_token found");
 		return 0;
 	}
 
-	Common::String accessToken = CloudConfig.get(keyPrefix + "access_token");
-	Common::String userId = CloudConfig.get(keyPrefix + "user_id");
-	Common::String refreshToken = CloudConfig.get(keyPrefix + "refresh_token");
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
+	Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
+	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
 	return new OneDriveStorage(accessToken, userId, refreshToken);
 }
 
@@ -292,16 +292,16 @@ Common::String OneDriveStorage::getAuthLink() {
 }
 
 void OneDriveStorage::authThroughConsole() {
-	if (!CloudConfig.hasKey("ONEDRIVE_KEY") || !CloudConfig.hasKey("ONEDRIVE_SECRET")) {
+	if (!ConfMan.hasKey("ONEDRIVE_KEY", ConfMan.kCloudDomain) || !ConfMan.hasKey("ONEDRIVE_SECRET", ConfMan.kCloudDomain)) {
 		warning("No OneDrive keys available, cannot do auth");
 		return;
 	}
 
 	loadKeyAndSecret();
 
-	if (CloudConfig.hasKey("onedrive_code")) {
+	if (ConfMan.hasKey("onedrive_code", ConfMan.kCloudDomain)) {
 		//phase 2: get access_token using specified code
-		new OneDriveStorage(CloudConfig.get("onedrive_code"));
+		new OneDriveStorage(ConfMan.get("onedrive_code", ConfMan.kCloudDomain));
 		return;
 	}
 
diff --git a/backends/module.mk b/backends/module.mk
index 8e9b14f..95334fe 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -21,7 +21,6 @@ MODULE_OBJS := \
 
 ifdef USE_CLOUD
 MODULE_OBJS += \
-	cloud/cloudconfighelper.o \
 	cloud/cloudmanager.o \
 	cloud/iso8601.o \
 	cloud/storage.o \


Commit: 3db4915b663d989c01a0e8bf7f8d10a6be754432
    https://github.com/scummvm/scummvm/commit/3db4915b663d989c01a0e8bf7f8d10a6be754432
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add checks in StorageWizardDialog

It now calculates the checksums for code pieces to determine whether
it's correct and CRC-32 for user to compare with one shown on site.

Changed paths:
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index d637440..a409170 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -55,25 +55,167 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOption
 
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine1", _s("Obtain the code from the storage, enter it"));
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine2", _s("in the following field and press 'Connect':"));
-	_codeWidget = new EditTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CodeBox", _s("Code"), 0, kCodeBoxCmd);
+	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", "");
 
 	// Buttons
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
-	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
+	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 }
 
 void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
-	case kCodeBoxCmd:
+	case kCodeBoxCmd: {		
+		Common::String code, message;
+		int correctFields = 0;
+		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
+			Common::String subcode = _codeWidget[i]->getEditString();
+			if (subcode.size() == 0) {
+				++correctFields;
+				continue;
+			}
+			bool correct = correctChecksum(subcode);			
+			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);
+				}
+			}
+		}
+		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());
+		}
+		if (correctFields == CODE_FIELDS && code.size() > 0) {
+			message = Common::String::format(_("CRC-32 for this code is: %x"), crc32(code));
+			_connectWidget->setEnabled(true);
+		} else {
+			_connectWidget->setEnabled(false);
+		}
+		_messageWidget->setLabel(message);
 		break;
-	case kConnectCmd:
-		CloudMan.connectStorage(_storageId, _codeWidget->getEditString());
+	}
+	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();
+		}
+		CloudMan.connectStorage(_storageId, code);
 		setResult(1);
 		close();
 		break;
+	}
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
 }
 
+int StorageWizardDialog::calculate(char b) {
+	int r = 0;
+	for (; b; b = b >> 1)
+		r += b & 1;
+	return r;
+}
+
+bool StorageWizardDialog::correctChecksum(Common::String s) {
+	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
+	char c = s.lastChar();
+	int providedChecksum = -1;
+	for (uint32 i = 0; i < 64; ++i)
+		if (c == HASHCHARS[i]) {
+			providedChecksum = i;
+			break;
+		}
+	int calculatedChecksum = 0;
+	for (uint32 i = 0; i < s.size()-1; ++i) {
+		calculatedChecksum = (calculatedChecksum << 1) | (calculate(s[i]) & 1);
+	}	
+	return providedChecksum == calculatedChecksum; //we can use compare bits to determine which characters are wrong
+}
+
+const uint32 Crc32Table[256] = {
+	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32 StorageWizardDialog::crc32(Common::String s) {
+	uint32 crc = 0xFFFFFFFF;
+	for (uint32 i = 0; i < s.size(); ++i)
+		crc = (crc >> 8) ^ Crc32Table[(crc ^ s[i]) & 0xFF];
+	return crc ^ 0xFFFFFFFF;
+}
+
 } // End of namespace GUI
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 00a3617..e2bab7e 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -30,10 +30,19 @@ namespace GUI {
 
 class CommandSender;
 class EditTextWidget;
+class StaticTextWidget;
+class ButtonWidget;
 
 class StorageWizardDialog : public Dialog {
+	static const uint32 CODE_FIELDS = 8;
 	uint32 _storageId;
-	EditTextWidget *_codeWidget;
+	EditTextWidget *_codeWidget[CODE_FIELDS];
+	StaticTextWidget *_messageWidget;
+	ButtonWidget *_connectWidget;
+
+	int calculate(char b);
+	bool correctChecksum(Common::String s);
+	uint32 crc32(Common::String s);
 public:
 	StorageWizardDialog(uint32 storageId);
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index cd76856..5954196 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -606,10 +606,45 @@
 					/>
 					<widget name = 'ReturnLine2'
 							height = 'Globals.Line.Height'
-					/>
-					<widget name = 'CodeBox'
-						width = '150'
-						height = 'Globals.Line.Height'
+					/>					
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox1'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox2'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox3'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox4'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+					</layout>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox5'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox6'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox7'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox8'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+					</layout>
+					<widget name = 'MessageLine'
+							height = 'Globals.Line.Height'
 					/>
 					<space size = '6' />
 				</layout>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index dd6700e..ad9a3b6 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -600,10 +600,45 @@
 				<widget name = 'ReturnLine2'
 						height = 'Globals.Line.Height'
 				/>
-				<widget name = 'CodeBox'
-						width = '150'
-						height = '16'
-				/>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox1'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox2'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox3'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox4'
+							width = '60'
+							height = '16'
+						/>
+					</layout>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox5'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox6'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox7'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox8'
+							width = '60'
+							height = '16'
+						/>
+					</layout>
+					<widget name = 'MessageLine'
+							height = 'Globals.Line.Height'
+					/>
 				<space size = '4' />
 			</layout>		
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>


Commit: c1ffb09fb0e75a96e48299c1df0742f357e5d03d
    https://github.com/scummvm/scummvm/commit/c1ffb09fb0e75a96e48299c1df0742f357e5d03d
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix configuration handling in CloudManager

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 1955121..8b104df 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -78,9 +78,6 @@ void CloudManager::loadStorage() {
 }
 
 void CloudManager::init() {
-	Common::String oldDomain = ConfMan.getActiveDomainName();
-	ConfMan.setActiveDomain(ConfMan.kCloudDomain);
-
 	//init configs structs
 	for (uint32 i = 0; i < kStorageTotal; ++i) {
 		Common::String name = getStorageConfigName(i);
@@ -89,43 +86,36 @@ void CloudManager::init() {
 		config.username = "";
 		config.lastSyncDate = "";
 		config.usedBytes = 0;
-		if (ConfMan.hasKey(kStoragePrefix + name + "_username"))
-			config.username = ConfMan.get(kStoragePrefix + name + "_username");
-		if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync"))
-			config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync");
-		if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes"))
-			config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes").asUint64();
+		if (ConfMan.hasKey(kStoragePrefix + name + "_username", ConfMan.kCloudDomain))
+			config.username = ConfMan.get(kStoragePrefix + name + "_username", ConfMan.kCloudDomain);
+		if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain))
+			config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain);
+		if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain))
+			config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain).asUint64();
 		_storages.push_back(config);
 	}
 
 	//load an active storage if there is any
 	_currentStorageIndex = kStorageNoneId;
-	if (ConfMan.hasKey("current_storage"))
-		_currentStorageIndex = ConfMan.getInt("current_storage");
+	if (ConfMan.hasKey("current_storage", ConfMan.kCloudDomain))
+		_currentStorageIndex = ConfMan.getInt("current_storage", ConfMan.kCloudDomain);
 
 	loadStorage();
-
-	ConfMan.setActiveDomain(oldDomain);
 }
 
 void CloudManager::save() {
-	Common::String oldDomain = ConfMan.getActiveDomainName();
-	ConfMan.setActiveDomain(ConfMan.kCloudDomain);
-
 	for (uint32 i = 0; i < _storages.size(); ++i) {
 		if (i == kStorageNoneId) continue;
 		Common::String name = getStorageConfigName(i);
-		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username);
-		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate);
-		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes));
+		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, ConfMan.kCloudDomain);
+		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, ConfMan.kCloudDomain);
+		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes, ConfMan.kCloudDomain));
 	}
 
-	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex));
+	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex, ConfMan.kCloudDomain));
 	if (_activeStorage)
 		_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 	ConfMan.flushToDisk();
-
-	ConfMan.setActiveDomain(oldDomain);
 }
 
 void CloudManager::replaceStorage(Storage *storage, uint32 index) {


Commit: a83e91e1ca6666be620d1cd1b0a8e4f267d7eaf2
    https://github.com/scummvm/scummvm/commit/a83e91e1ca6666be620d1cd1b0a8e4f267d7eaf2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update StorageWizardDialog's code check

Now the code contains its own crc16 in it, plus the way checksum is
calculated has changed.

Some online tool calls this exact way of calculating crc16
"CRC16_CCITT_FALSE".

Changed paths:
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index a409170..b419d11 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -88,18 +88,28 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 				}
 			}
 		}
+
 		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());
 		}
+
+		bool ok = false;
 		if (correctFields == CODE_FIELDS && code.size() > 0) {
-			message = Common::String::format(_("CRC-32 for this code is: %x"), crc32(code));
-			_connectWidget->setEnabled(true);
-		} else {
-			_connectWidget->setEnabled(false);
+			//the last 3 chars must be an encoded crc16			
+			if (code.size() > 3) {
+				uint32 size = code.size();
+				uint32 gotcrc = decodeHashchar(code[size-3]) | (decodeHashchar(code[size-2]) << 6) | (decodeHashchar(code[size-1]) << 12);				
+				code.erase(size - 3);
+				uint32 crc = crc16(code);
+				ok = (crc == gotcrc);
+			}			
+			if (ok) message = _("All OK!");
+			else message = _("Invalid code");
 		}
+		_connectWidget->setEnabled(ok);		
 		_messageWidget->setLabel(message);
 		break;
 	}
@@ -111,6 +121,7 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 			code += subcode;
 			code.deleteLastChar();
 		}
+		code.erase(code.size() - 3);
 		CloudMan.connectStorage(_storageId, code);
 		setResult(1);
 		close();
@@ -121,101 +132,31 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 }
 
-int StorageWizardDialog::calculate(char b) {
-	int r = 0;
-	for (; b; b = b >> 1)
-		r += b & 1;
-	return r;
+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;
 }
 
 bool StorageWizardDialog::correctChecksum(Common::String s) {
-	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
-	char c = s.lastChar();
-	int providedChecksum = -1;
-	for (uint32 i = 0; i < 64; ++i)
-		if (c == HASHCHARS[i]) {
-			providedChecksum = i;
-			break;
-		}
-	int calculatedChecksum = 0;
+	int providedChecksum = decodeHashchar(s.lastChar());
+	int calculatedChecksum = 0x2A;
 	for (uint32 i = 0; i < s.size()-1; ++i) {
-		calculatedChecksum = (calculatedChecksum << 1) | (calculate(s[i]) & 1);
+		calculatedChecksum = calculatedChecksum ^ s[i];
 	}	
-	return providedChecksum == calculatedChecksum; //we can use compare bits to determine which characters are wrong
+	return providedChecksum == (calculatedChecksum % 64);
 }
 
-const uint32 Crc32Table[256] = {
-	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
-	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
-	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
-	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
-	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
-	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
-	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
-	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
-	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
-	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
-	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
-	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
-	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
-	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
-	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
-	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
-	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
-	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
-	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
-	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
-	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
-	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
-	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
-	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
-	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
-	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
-	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
-	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
-	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
-	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
-	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
-	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
-	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
-	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
-	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
-	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
-	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
-	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
-	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
-	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
-	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
-	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
-	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
-	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
-	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
-	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
-	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
-	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
-	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
-	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
-	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
-	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
-	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
-	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
-	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
-	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
-	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
-	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
-	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
-	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
-	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
-	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
-	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
-	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
-};
-
-uint32 StorageWizardDialog::crc32(Common::String s) {
-	uint32 crc = 0xFFFFFFFF;
-	for (uint32 i = 0; i < s.size(); ++i)
-		crc = (crc >> 8) ^ Crc32Table[(crc ^ s[i]) & 0xFF];
-	return crc ^ 0xFFFFFFFF;
+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;
+		crc = ((crc << 8) ^ (x << 12) ^ (x << 5) ^ x) & 0xFFFF;		
+	}
+	return crc;
 }
 
 } // End of namespace GUI
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index e2bab7e..b75b952 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -40,9 +40,9 @@ class StorageWizardDialog : public Dialog {
 	StaticTextWidget *_messageWidget;
 	ButtonWidget *_connectWidget;
 
-	int calculate(char b);
+	int decodeHashchar(char c);
 	bool correctChecksum(Common::String s);
-	uint32 crc32(Common::String s);
+	uint32 crc16(Common::String s);
 public:
 	StorageWizardDialog(uint32 storageId);
 


Commit: f571f3dd28a4b82973d26ff724edec60ad6ea845
    https://github.com/scummvm/scummvm/commit/f571f3dd28a4b82973d26ff724edec60ad6ea845
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add comments for StorageWizardDialog methods

Changed paths:
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index b419d11..46be812 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -141,6 +141,7 @@ int StorageWizardDialog::decodeHashchar(char c) {
 }
 
 bool StorageWizardDialog::correctChecksum(Common::String s) {
+	if (s.size() == 0) return false; //no last char
 	int providedChecksum = decodeHashchar(s.lastChar());
 	int calculatedChecksum = 0x2A;
 	for (uint32 i = 0; i < s.size()-1; ++i) {
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index b75b952..93e3684 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -40,8 +40,28 @@ class StorageWizardDialog : public Dialog {
 	StaticTextWidget *_messageWidget;
 	ButtonWidget *_connectWidget;
 
+	/**
+	 * Return the value corresponding to the given character.
+	 *
+	 * There is a value corresponding to each of 64 selected
+	 * printable characters (0-9, A-Z, a-z, ? and !).
+	 *
+	 * When given another character, -1 is returned.
+	 */
 	int decodeHashchar(char c);
+
+	/**
+	 * Return whether checksum is correct.
+	 *
+	 * The last character of the string is treated as
+	 * the checksum of all the others (decoded with
+	 * decodeHashchar()).
+	 *
+	 * Checksum = (c[0] ^ c[1] ^ ...) % 64
+	 */
 	bool correctChecksum(Common::String s);
+
+	/** The "CRC16_CCITT_FALSE" CRC-16 algorithm. */
 	uint32 crc16(Common::String s);
 public:
 	StorageWizardDialog(uint32 storageId);


Commit: e2b3a9366eaad23549ad8be9316872e48a9ac11b
    https://github.com/scummvm/scummvm/commit/e2b3a9366eaad23549ad8be9316872e48a9ac11b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CONFIGURE: Add --with-sdlnet-prefix option

Changed paths:
    configure



diff --git a/configure b/configure
index 448da8a..fb24e1e 100755
--- a/configure
+++ b/configure
@@ -1046,6 +1046,8 @@ Optional Libraries:
   --with-sndio-prefix=DIR  Prefix where sndio is installed (optional)
   --disable-sndio          disable sndio MIDI driver [autodetect]
 
+  --with-sdlnet-prefix=DIR  Prefix where SDL_Net is
+                           installed (optional)
   --disable-sdlnet         disable SDL_Net networking library [autodetect]
   --disable-libcurl        disable libcurl networking library [autodetect]
 
@@ -1241,6 +1243,11 @@ for ac_option in $@; do
 		LIBUNITY_CFLAGS="-I$arg/include"
 		LIBUNITY_LIBS="-L$arg/lib"
 		;;
+	--with-sdlnet-prefix=*)
+		arg=`echo $ac_option | cut -d '=' -f 2`
+		SDL_NET_CFLAGS="-I$arg/include"
+		SDL_NET_LIBS="-L$arg/lib"
+		;;
 	--backend=*)
 		_backend=`echo $ac_option | cut -d '=' -f 2`
 		;;
@@ -4132,10 +4139,11 @@ if test "$_sdlnet" = auto ; then
 #include "SDL/SDL_net.h"
 int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
 EOF
-	cc_check $LIBS $INCLUDES -lSDL_net && _sdlnet=yes
+	cc_check $LIBS $INCLUDES $SDL_NET_CFLAGS $SDL_NET_LIBS -lSDL_net && _sdlnet=yes
 fi
-if test "$_sdlnet" = yes ; then
-	LIBS="$LIBS -lSDL_net"
+if test "$_sdlnet" = yes ; then	
+	append_var LIBS "$SDL_NET_LIBS -lSDL_net"
+	append_var INCLUDES "$SDL_NET_CFLAGS"
 fi
 define_in_config_if_yes "$_sdlnet" 'USE_SDL_NET'
 echo "$_sdlnet"


Commit: 9f7bea156aa4c763fd637a9a12ff89e124b51a6c
    https://github.com/scummvm/scummvm/commit/9f7bea156aa4c763fd637a9a12ff89e124b51a6c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Init SDL_Net

Changed paths:
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h



diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index dca6891..9f7b292 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -60,6 +60,10 @@
 #endif // !WIN32
 #endif
 
+#ifdef USE_SDL_NET
+#include <SDL/SDL_net.h>
+#endif
+
 OSystem_SDL::OSystem_SDL()
 	:
 #ifdef USE_OPENGL
@@ -73,6 +77,9 @@ OSystem_SDL::OSystem_SDL()
 #endif
 	_inited(false),
 	_initedSDL(false),
+#ifdef USE_SDL_NET
+	_initedSDLnet(false),
+#endif
 	_logger(0),
 	_mixerManager(0),
 	_eventSource(0),
@@ -120,6 +127,10 @@ OSystem_SDL::~OSystem_SDL() {
 	delete _logger;
 	_logger = 0;
 
+#ifdef USE_SDL_NET
+	if (_initedSDLnet) SDLNet_Quit();
+#endif
+
 	SDL_Quit();
 }
 
@@ -294,6 +305,17 @@ void OSystem_SDL::initSDL() {
 
 		_initedSDL = true;
 	}
+
+#ifdef USE_SDL_NET
+	// Check if SDL_net has not been initialized
+	if (!_initedSDLnet) {
+		// Initialize SDL_net
+		if (SDLNet_Init() == -1)
+			error("Could not initialize SDL_net: %s", SDLNet_GetError());
+
+		_initedSDLnet = true;
+	}
+#endif
 }
 
 void OSystem_SDL::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index 1fe670c..f440cd7 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -81,6 +81,9 @@ public:
 protected:
 	bool _inited;
 	bool _initedSDL;
+#ifdef USE_SDL_NET
+	bool _initedSDLnet;
+#endif
 
 	/**
 	 * Mixer manager that configures and setups SDL for


Commit: 0af97e59bc63538d18d2241285b68ed287ccd87c
    https://github.com/scummvm/scummvm/commit/0af97e59bc63538d18d2241285b68ed287ccd87c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add LocalWebserver

Available as LocalServer singleton. It's being started and stopped by
StorageWizardDialog. It doesn't handle clients yet, though.

Changed paths:
  A backends/networking/sdl_net/localwebserver.cpp
  A backends/networking/sdl_net/localwebserver.h
    backends/cloud/cloudmanager.cpp
    backends/module.mk
    base/main.cpp
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 8b104df..9652117 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -253,17 +253,8 @@ SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networ
 }
 
 void CloudManager::testFeature() {
-	Storage *storage = getCurrentStorage();
+	//Storage *storage = getCurrentStorage();
 	//if (storage) storage->info(nullptr, nullptr);
-	GoogleDrive::GoogleDriveStorage *gd = dynamic_cast<GoogleDrive::GoogleDriveStorage *>(storage);
-	if (gd) {
-	}
-		//gd->resolveFileId("firstfolder/subfolder", nullptr, nullptr);
-		//gd->listDirectoryById("appDataFolder", nullptr, nullptr);
-		//gd->listDirectoryById("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", nullptr, nullptr);
-		//gd->createDirectoryWithParentId("1LWq-r1IwegkJJ0eZpswGlyjj8nu6XyUmosvxD7L0F9X3", "subfolder", nullptr, nullptr);
-		//gd->createDirectoryWithParentId("appDataFolder", "firstfolder", nullptr, nullptr);
-	else debug("FAILURE");
 }
 
 bool CloudManager::isWorking() {
diff --git a/backends/module.mk b/backends/module.mk
index 95334fe..6fb8758 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -58,6 +58,11 @@ MODULE_OBJS += \
 	networking/curl/request.o
 endif
 
+ifdef USE_SDL_NET
+MODULE_OBJS += \
+	networking/sdl_net/localwebserver.o
+endif
+
 ifdef USE_ELF_LOADER
 MODULE_OBJS += \
 	plugins/elf/arm-loader.o \
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
new file mode 100644
index 0000000..6814857
--- /dev/null
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -0,0 +1,144 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "common/timer.h"
+#include "common/textconsole.h"
+#include <SDL/SDL_net.h>
+
+namespace Common {
+
+DECLARE_SINGLETON(Networking::LocalWebserver);
+
+}
+
+namespace Networking {
+
+LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _clients(0) {
+	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+		_clientSocket[i] = nullptr;
+}
+
+LocalWebserver::~LocalWebserver() {
+	stop();
+}
+
+void localWebserverTimer(void *ignored) {
+	LocalServer.handle();
+}
+
+void LocalWebserver::startTimer(int interval) {
+	Common::TimerManager *manager = g_system->getTimerManager();
+	if (manager->installTimerProc(localWebserverTimer, interval, 0, "Networking::LocalWebserver's Timer")) {
+		_timerStarted = true;
+	} else {
+		warning("Failed to install Networking::LocalWebserver's timer");
+	}
+}
+
+void LocalWebserver::stopTimer() {	
+	Common::TimerManager *manager = g_system->getTimerManager();
+	manager->removeTimerProc(localWebserverTimer);
+	_timerStarted = false;
+}
+
+void LocalWebserver::start() {
+	if (_timerStarted) return;
+	startTimer();
+
+	// Create a listening TCP socket
+	IPaddress ip;	
+	if (SDLNet_ResolveHost(&ip, NULL, SERVER_PORT) == -1) {
+		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+	}
+	_serverSocket = SDLNet_TCP_Open(&ip);
+	if (!_serverSocket) {
+		error("SDLNet_TCP_Open: %s\n", SDLNet_GetError());		
+	}
+
+	// Create a socket set
+	_set = SDLNet_AllocSocketSet(MAX_CONNECTIONS + 1); //one more for our server socket
+	if (!_set) {
+		error("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());		
+	}
+	
+	int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
+	if (numused == -1) {
+		error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
+	}
+}
+
+void LocalWebserver::stop() {
+	if (_timerStarted) stopTimer();
+
+	if (_set) {
+		SDLNet_FreeSocketSet(_set);
+		_set = nullptr;
+	}
+
+	if (_serverSocket) {
+		SDLNet_TCP_Close(_serverSocket);
+		_serverSocket = nullptr;
+	}
+
+	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+		if (_clientSocket[i]) {			
+			SDLNet_TCP_Close(_clientSocket[i]);
+			_clientSocket[i] = nullptr;
+		}
+
+	_clients = 0;
+}
+
+void LocalWebserver::handle() {
+	int numready = SDLNet_CheckSockets(_set, 0);
+	if (numready == -1) {
+		error("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
+	} else if (numready) {
+		if (SDLNet_SocketReady(_serverSocket)) {
+			TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
+			if (client) {
+				if (_clients == MAX_CONNECTIONS) { //drop the connection
+					SDLNet_TCP_Close(client);
+				} else {
+					int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
+					if (numused == -1) {
+						error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
+					}					
+					_clientSocket[_clients++] = client;
+				}
+			}
+		}
+
+		for (uint32 i = 0; i < MAX_CONNECTIONS; ++i) {
+			if (!_clientSocket[i]) continue;
+			if (!SDLNet_SocketReady(_clientSocket[i])) continue;
+			//TODO: handle client
+		}
+	}
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
new file mode 100644
index 0000000..a11be0a
--- /dev/null
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -0,0 +1,65 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
+#define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
+
+#include "common/singleton.h"
+#include "common/scummsys.h"
+
+typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
+typedef struct _TCPsocket *TCPsocket;
+
+namespace Networking {
+
+class LocalWebserver : public Common::Singleton<LocalWebserver> {
+	static const uint32 FRAMES_PER_SECOND = 20;
+	static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
+	static const uint32 SERVER_PORT = 12345;
+	static const uint32 MAX_CONNECTIONS = 10;
+
+	friend void localWebserverTimer(void *); //calls handle()
+
+	SDLNet_SocketSet _set;
+	TCPsocket _serverSocket;
+	TCPsocket _clientSocket[MAX_CONNECTIONS];
+	int _clients;
+	bool _timerStarted;
+
+	void startTimer(int interval = TIMER_INTERVAL);
+	void stopTimer();
+	void handle();
+
+public:
+	LocalWebserver();
+	virtual ~LocalWebserver();
+
+	void start();
+	void stop();
+};
+
+/** Shortcut for accessing the local webserver. */
+#define LocalServer		Networking::LocalWebserver::instance()
+
+} // End of namespace Networking
+
+#endif
diff --git a/base/main.cpp b/base/main.cpp
index 4edc7a9..6c91305 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -72,6 +72,9 @@
 #ifdef USE_LIBCURL
 #include "backends/networking/curl/connectionmanager.h"
 #endif
+#ifdef USE_SDL_NET
+#include "backends/networking/sdl_net/localwebserver.h"
+#endif
 
 #if defined(_WIN32_WCE)
 #include "backends/platform/wince/CELauncherDialog.h"
@@ -596,6 +599,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			launcherDialog();
 		}
 	}
+#ifdef USE_SDL_NET
+	Networking::LocalWebserver::destroy();
+#endif
 #ifdef USE_LIBCURL
 	Networking::ConnectionManager::destroy();
 #endif
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 46be812..579591b 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -24,9 +24,11 @@
 #include "gui/widgets/list.h"
 #include "gui/widget.h"
 #include "gui/gui-manager.h"
-
-#include "common/translation.h"
 #include "backends/cloud/cloudmanager.h"
+#ifdef USE_SDL_NET
+#include "backends/networking/sdl_net/localwebserver.h"
+#endif
+#include "common/translation.h"
 #include "widgets/edittext.h"
 
 namespace GUI {
@@ -64,6 +66,20 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOption
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 }
 
+void StorageWizardDialog::open() {
+	Dialog::open();
+#ifdef USE_SDL_NET
+	LocalServer.start();
+#endif
+}
+
+void StorageWizardDialog::close() {
+#ifdef USE_SDL_NET
+	LocalServer.stop();
+#endif
+	Dialog::close();
+}
+
 void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kCodeBoxCmd: {		
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 93e3684..fa45f92 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -66,7 +66,9 @@ class StorageWizardDialog : public Dialog {
 public:
 	StorageWizardDialog(uint32 storageId);
 
-	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void open();
+	virtual void close();
+	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 };
 
 } // End of namespace GUI


Commit: 6126435b64442ae838953e095191230737391dca
    https://github.com/scummvm/scummvm/commit/6126435b64442ae838953e095191230737391dca
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Networking::Client

Keeps current client's state

Changed paths:
  A backends/networking/sdl_net/client.cpp
  A backends/networking/sdl_net/client.h
    backends/module.mk
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/module.mk b/backends/module.mk
index 6fb8758..74412a7 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -60,6 +60,7 @@ endif
 
 ifdef USE_SDL_NET
 MODULE_OBJS += \
+	networking/sdl_net/client.o \
 	networking/sdl_net/localwebserver.o
 endif
 
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
new file mode 100644
index 0000000..2af940c
--- /dev/null
+++ b/backends/networking/sdl_net/client.cpp
@@ -0,0 +1,99 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/sdl_net/client.h"
+#include "common/textconsole.h"
+#include <SDL/SDL_net.h>
+
+namespace Networking {
+
+Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr) {}
+
+Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr) {
+	open(set, socket);
+}
+
+Client::~Client() {
+	close();
+}
+
+void Client::open(SDLNet_SocketSet set, TCPsocket socket) {	
+	if (_state != INVALID) close();
+	_state = READING_HEADERS;
+	_socket = socket;
+	_set = set;
+	if (set) {
+		int numused = SDLNet_TCP_AddSocket(set, socket);
+		if (numused == -1) {
+			error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
+		}
+	}
+}
+
+void Client::readHeaders() {
+	if (!_socket) return;
+	if (!SDLNet_SocketReady(_socket)) return;
+
+	const uint32 BUFFER_SIZE = 16 * 1024;
+	char buffer[BUFFER_SIZE];	
+	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);	
+	if (bytes <= 0) {
+		warning("Client::readHeaders recv fail");		
+		_state = INVALID;
+		return;
+	}	
+	_headers += Common::String(buffer, bytes);	
+	checkIfHeadersEnded();
+}
+
+void Client::checkIfHeadersEnded() {
+	const char *cstr = _headers.c_str();
+	const char *position = strstr(cstr, "\r\n\r\n");
+	if (position) _state = READ_HEADERS;
+}
+
+void Client::close() {
+	if (_set) {
+		if (_socket) {
+			int numused = SDLNet_TCP_DelSocket(_set, _socket);
+			if (numused == -1)
+				error("SDLNet_DelSocket: %s\n", SDLNet_GetError());
+		}
+		_set = nullptr;
+	}
+
+	if (_socket) {
+		SDLNet_TCP_Close(_socket);
+		_socket = nullptr;
+	}
+
+	_state = INVALID;
+}
+
+
+ClientState Client::state() { return _state; }
+
+Common::String Client::headers() { return _headers; }
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
new file mode 100644
index 0000000..74c700e
--- /dev/null
+++ b/backends/networking/sdl_net/client.h
@@ -0,0 +1,63 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_CLIENT_H
+#define BACKENDS_NETWORKING_SDL_NET_CLIENT_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
+typedef struct _TCPsocket *TCPsocket;
+
+namespace Networking {
+
+enum ClientState {
+	INVALID,
+	READING_HEADERS,
+	READ_HEADERS
+};
+
+class Client {
+	ClientState _state;
+	SDLNet_SocketSet _set;
+	TCPsocket _socket;
+	Common::String _headers;
+
+	void checkIfHeadersEnded();
+
+public:
+	Client();
+	Client(SDLNet_SocketSet set, TCPsocket socket);
+	virtual ~Client();
+
+	void open(SDLNet_SocketSet set, TCPsocket socket);
+	void readHeaders();
+	void close();
+
+	ClientState state();
+	Common::String headers();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 6814857..783814d 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -37,10 +37,7 @@ DECLARE_SINGLETON(Networking::LocalWebserver);
 
 namespace Networking {
 
-LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _clients(0) {
-	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
-		_clientSocket[i] = nullptr;
-}
+LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _clients(0) {}
 
 LocalWebserver::~LocalWebserver() {
 	stop();
@@ -94,51 +91,59 @@ void LocalWebserver::start() {
 void LocalWebserver::stop() {
 	if (_timerStarted) stopTimer();
 
-	if (_set) {
-		SDLNet_FreeSocketSet(_set);
-		_set = nullptr;
-	}
-
 	if (_serverSocket) {
 		SDLNet_TCP_Close(_serverSocket);
 		_serverSocket = nullptr;
 	}
 
 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
-		if (_clientSocket[i]) {			
-			SDLNet_TCP_Close(_clientSocket[i]);
-			_clientSocket[i] = nullptr;
-		}
+		_client[i].close();
 
 	_clients = 0;
+
+	if (_set) {
+		SDLNet_FreeSocketSet(_set);
+		_set = nullptr;
+	}
 }
 
 void LocalWebserver::handle() {
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
 		error("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
-	} else if (numready) {
-		if (SDLNet_SocketReady(_serverSocket)) {
-			TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
-			if (client) {
-				if (_clients == MAX_CONNECTIONS) { //drop the connection
-					SDLNet_TCP_Close(client);
-				} else {
-					int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
-					if (numused == -1) {
-						error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
-					}					
-					_clientSocket[_clients++] = client;
-				}
-			}
-		}
-
-		for (uint32 i = 0; i < MAX_CONNECTIONS; ++i) {
-			if (!_clientSocket[i]) continue;
-			if (!SDLNet_SocketReady(_clientSocket[i])) continue;
-			//TODO: handle client
-		}
+	} else if (numready) acceptClient();
+
+	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+		handleClient(i);
+}
+
+void LocalWebserver::handleClient(uint32 i) {	
+	switch (_client[i].state()) {
+	case INVALID: return;
+	case READING_HEADERS: _client[i].readHeaders(); break;
+	case READ_HEADERS: //decide what to do next with that client
+		//if GET, check whether we know a handler for such URL
+		//if PUT, check whether we know a handler for that URL
+		//if no handler, answer with default BAD REQUEST
+		warning("headers %s", _client[i].headers().c_str());
+		_client[i].close();
+		break;
 	}
 }
 
+
+void LocalWebserver::acceptClient() {
+	if (!SDLNet_SocketReady(_serverSocket)) return;
+
+	TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
+	if (!client) return;
+
+	if (_clients == MAX_CONNECTIONS) { //drop the connection
+		SDLNet_TCP_Close(client);
+		return;
+	}
+	
+	_client[_clients++].open(_set, client);
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index a11be0a..8db05f7 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -23,6 +23,7 @@
 #ifndef BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
 #define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
 
+#include "backends/networking/sdl_net/client.h"
 #include "common/singleton.h"
 #include "common/scummsys.h"
 
@@ -41,13 +42,15 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 
 	SDLNet_SocketSet _set;
 	TCPsocket _serverSocket;
-	TCPsocket _clientSocket[MAX_CONNECTIONS];
+	Client _client[MAX_CONNECTIONS];
 	int _clients;
 	bool _timerStarted;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
 	void handle();
+	void handleClient(uint32 i);
+	void acceptClient();
 
 public:
 	LocalWebserver();


Commit: 99c51380fdc866ce393c52eb41803a9ec119a9ad
    https://github.com/scummvm/scummvm/commit/99c51380fdc866ce393c52eb41803a9ec119a9ad
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ClientState::BAD_REQUEST

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/localwebserver.cpp



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 2af940c..8ab9ed3 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -60,11 +60,12 @@ void Client::readHeaders() {
 	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);	
 	if (bytes <= 0) {
 		warning("Client::readHeaders recv fail");		
-		_state = INVALID;
+		close();
 		return;
-	}	
+	}
 	_headers += Common::String(buffer, bytes);	
 	checkIfHeadersEnded();
+	checkIfBadRequest();
 }
 
 void Client::checkIfHeadersEnded() {
@@ -73,6 +74,46 @@ void Client::checkIfHeadersEnded() {
 	if (position) _state = READ_HEADERS;
 }
 
+void Client::checkIfBadRequest() {
+	if (_state != READING_HEADERS) return;
+	uint32 headersSize = _headers.size();
+	bool bad = false;
+
+	const uint32 SUSPICIOUS_HEADERS_SIZE = 128 * 1024;
+	if (headersSize > SUSPICIOUS_HEADERS_SIZE) bad = true;
+
+	if (!bad) {
+		if (headersSize > 0) {
+			const char *cstr = _headers.c_str();
+			const char *position = strstr(cstr, "\r\n");
+			if (position) { //we have at least one line - and we want the first one
+				//"<METHOD> <path> HTTP/<VERSION>\r\n"
+				Common::String method, path, http, buf;
+				for (uint32 i = 0; i < headersSize; ++i) {
+					if (_headers[i] == ' ') {
+						if (method == "") method = buf;
+						else if (path == "") path = buf;
+						else if (http == "") http = buf;
+						else {
+							bad = true;
+							break;
+						}
+						buf = "";
+					} else buf += _headers[i];
+				}
+
+				//check that method is supported
+				if (method != "GET" && method != "PUT" && method != "POST") bad = true;
+
+				//check that HTTP/<VERSION> is OK
+				if (!http.hasPrefix("HTTP/")) bad = true;
+			}
+		}
+	}
+
+	if (bad) _state = BAD_REQUEST;	
+}
+
 void Client::close() {
 	if (_set) {
 		if (_socket) {
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 74c700e..7f78947 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -34,7 +34,8 @@ namespace Networking {
 enum ClientState {
 	INVALID,
 	READING_HEADERS,
-	READ_HEADERS
+	READ_HEADERS,
+	BAD_REQUEST
 };
 
 class Client {
@@ -44,6 +45,7 @@ class Client {
 	Common::String _headers;
 
 	void checkIfHeadersEnded();
+	void checkIfBadRequest();
 
 public:
 	Client();
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 783814d..ee67f78 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -128,6 +128,10 @@ void LocalWebserver::handleClient(uint32 i) {
 		warning("headers %s", _client[i].headers().c_str());
 		_client[i].close();
 		break;
+	case BAD_REQUEST:
+		//TODO: answer with BAD REQUEST
+		_client[i].close();
+		break;
 	}
 }
 


Commit: 13c54f66850226a516da0450b633ebafbc26584e
    https://github.com/scummvm/scummvm/commit/13c54f66850226a516da0450b633ebafbc26584e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GetClientHandler

That ClientHandler is made for responding GET requests. It calculates
stream's length, it allows to specify response code and headers, it can
be used to transfer any ReadStream.

Changed paths:
  A backends/networking/sdl_net/getclienthandler.cpp
  A backends/networking/sdl_net/getclienthandler.h
    backends/module.mk
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/module.mk b/backends/module.mk
index 74412a7..0f1eea0 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -61,6 +61,7 @@ endif
 ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/client.o \
+	networking/sdl_net/getclienthandler.o \
 	networking/sdl_net/localwebserver.o
 endif
 
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 8ab9ed3..22bfb60 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -28,9 +28,9 @@
 
 namespace Networking {
 
-Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr) {}
+Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {}
 
-Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr) {
+Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {
 	open(set, socket);
 }
 
@@ -74,8 +74,7 @@ void Client::checkIfHeadersEnded() {
 	if (position) _state = READ_HEADERS;
 }
 
-void Client::checkIfBadRequest() {
-	if (_state != READING_HEADERS) return;
+void Client::checkIfBadRequest() {	
 	uint32 headersSize = _headers.size();
 	bool bad = false;
 
@@ -114,6 +113,18 @@ void Client::checkIfBadRequest() {
 	if (bad) _state = BAD_REQUEST;	
 }
 
+void Client::setHandler(ClientHandler *handler) {	
+	if (_handler) delete _handler;
+	_state = BEING_HANDLED;
+	_handler = handler;
+}
+
+void Client::handle() {
+	if (_state != BEING_HANDLED) warning("handle() called in a wrong Client's state");
+	if (!_handler) warning("Client doesn't have handler to be handled by");
+	if (_handler) _handler->handle(this);
+}
+
 void Client::close() {
 	if (_set) {
 		if (_socket) {
@@ -137,4 +148,10 @@ ClientState Client::state() { return _state; }
 
 Common::String Client::headers() { return _headers; }
 
+bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
+
+int Client::recv(void *data, int maxlen) { return SDLNet_TCP_Recv(_socket, data, maxlen); }
+
+int Client::send(void *data, int len) { return SDLNet_TCP_Send(_socket, data, len); }
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 7f78947..8b7a7d5 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -35,7 +35,16 @@ enum ClientState {
 	INVALID,
 	READING_HEADERS,
 	READ_HEADERS,
-	BAD_REQUEST
+	BAD_REQUEST,
+	BEING_HANDLED
+};
+
+class Client;
+
+class ClientHandler {
+public:
+	virtual ~ClientHandler() {};
+	virtual void handle(Client *client) = 0;
 };
 
 class Client {
@@ -43,6 +52,7 @@ class Client {
 	SDLNet_SocketSet _set;
 	TCPsocket _socket;
 	Common::String _headers;
+	ClientHandler *_handler;
 
 	void checkIfHeadersEnded();
 	void checkIfBadRequest();
@@ -54,10 +64,23 @@ public:
 
 	void open(SDLNet_SocketSet set, TCPsocket socket);
 	void readHeaders();
+	void setHandler(ClientHandler *handler);
+	void handle();
 	void close();
 
 	ClientState state();
 	Common::String headers();
+
+	/**
+	 * Return SDLNet_SocketReady(_socket).
+	 *
+	 * It's "ready" when it has something
+	 * to read (recv()). You can send()
+	 * when this is false.
+	 */
+	bool socketIsReady();
+	int recv(void *data, int maxlen);
+	int send(void *data, int len);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
new file mode 100644
index 0000000..48a03e0
--- /dev/null
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -0,0 +1,94 @@
+/* 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 "backends/networking/sdl_net/getclienthandler.h"
+#include "common/textconsole.h"
+
+namespace Networking {
+
+GetClientHandler::GetClientHandler(Common::SeekableReadStream *stream): _responseCode(200), _headersPrepared(false), _stream(stream) {}
+
+GetClientHandler::~GetClientHandler() { delete _stream; }
+
+const char *GetClientHandler::responseMessage(long responseCode) {
+	switch (responseCode) {
+	case 200: return "OK";
+	case 400: return "Bad Request";
+	case 404: return "Not Found";
+	case 500: return "Internal Server Error";
+	}
+	return "Unknown";
+}
+
+void GetClientHandler::prepareHeaders() {
+	if (!_specialHeaders.contains("Content-Type"))
+		setHeader("Content-Type", "text/html");
+
+	if (!_specialHeaders.contains("Content-Length") && _stream)
+		setHeader("Content-Length", Common::String::format("%u", _stream->size()));
+
+	_headers = Common::String::format("HTTP/1.1 %d %s\r\n", _responseCode, responseMessage(_responseCode));
+	for (Common::HashMap<Common::String, Common::String>::iterator i = _specialHeaders.begin(); i != _specialHeaders.end(); ++i)
+		_headers += i->_key + ": " + i->_value + "\r\n";
+	_headers += "\r\n";
+
+	_headersPrepared = true;
+}
+
+void GetClientHandler::handle(Client *client) {
+	if (!client) return;
+	if (!_headersPrepared) prepareHeaders();
+	
+	const int kBufSize = 16 * 1024;
+	char buf[kBufSize];
+	uint32 readBytes;
+
+	// send headers first
+	if (_headers.size() > 0) {
+		readBytes = _headers.size();
+		if (readBytes > kBufSize) readBytes = kBufSize;
+		memcpy(buf, _headers.c_str(), readBytes);
+		_headers.erase(0, readBytes);
+	} else {
+		if (!_stream) {
+			client->close();
+			return;
+		}
+
+		readBytes = _stream->read(buf, kBufSize);
+	}
+
+	if (readBytes != 0)
+		if (client->send(buf, readBytes) != readBytes) {
+			warning("GetClientHandler: unable to send all bytes to the client");
+			client->close();
+			return;
+		}
+
+	// we're done here!
+	if (_stream->eos()) client->close();
+}
+
+void GetClientHandler::setHeader(Common::String name, Common::String value) { _specialHeaders[name] = value; }
+void GetClientHandler::setResponseCode(long code) { _responseCode = code; }
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/getclienthandler.h b/backends/networking/sdl_net/getclienthandler.h
new file mode 100644
index 0000000..f434df1
--- /dev/null
+++ b/backends/networking/sdl_net/getclienthandler.h
@@ -0,0 +1,54 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_GETCLIENTHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_GETCLIENTHANDLER_H
+
+#include "backends/networking/sdl_net/client.h"
+#include "common/hashmap.h"
+#include "common/stream.h"
+#include "common/hash-str.h"
+
+namespace Networking {
+
+class GetClientHandler: public ClientHandler {
+	Common::HashMap<Common::String, Common::String> _specialHeaders;
+	long _responseCode;
+	bool _headersPrepared;
+	Common::String _headers;
+	Common::SeekableReadStream *_stream;
+
+	static const char *responseMessage(long responseCode);
+	void prepareHeaders();
+
+public:
+	GetClientHandler(Common::SeekableReadStream *stream);
+	virtual ~GetClientHandler();
+
+	virtual void handle(Client *client);
+	void setHeader(Common::String name, Common::String value);
+	void setResponseCode(long code);
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index ee67f78..a25dd74 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -23,6 +23,8 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/networking/sdl_net/getclienthandler.h"
+#include "common/memstream.h"
 #include "common/str.h"
 #include "common/system.h"
 #include "common/timer.h"
@@ -30,6 +32,7 @@
 #include <SDL/SDL_net.h>
 
 namespace Common {
+class MemoryReadWriteStream;
 
 DECLARE_SINGLETON(Networking::LocalWebserver);
 
@@ -126,16 +129,17 @@ void LocalWebserver::handleClient(uint32 i) {
 		//if PUT, check whether we know a handler for that URL
 		//if no handler, answer with default BAD REQUEST
 		warning("headers %s", _client[i].headers().c_str());
-		_client[i].close();
+		setClientGetHandler(_client[i], "<html><head><title>ScummVM</title></head><body>Hello, World!</body></html>");
 		break;
 	case BAD_REQUEST:
-		//TODO: answer with BAD REQUEST
-		_client[i].close();
+		setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
+		break;
+	case BEING_HANDLED:
+		_client[i].handle();
 		break;
 	}
 }
 
-
 void LocalWebserver::acceptClient() {
 	if (!SDLNet_SocketReady(_serverSocket)) return;
 
@@ -150,4 +154,13 @@ void LocalWebserver::acceptClient() {
 	_client[_clients++].open(_set, client);
 }
 
+void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code) {
+	byte *data = new byte[response.size()];
+	memcpy(data, response.c_str(), response.size());
+	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
+	GetClientHandler *handler = new GetClientHandler(stream);
+	handler->setResponseCode(code);
+	client.setHandler(handler);
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 8db05f7..ab45398 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -51,6 +51,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handle();
 	void handleClient(uint32 i);
 	void acceptClient();
+	void setClientGetHandler(Client &client, Common::String response, long code = 200);
 
 public:
 	LocalWebserver();


Commit: 6e1b667dd67b3a99f2a8a2b27cf3dfe032981cbd
    https://github.com/scummvm/scummvm/commit/6e1b667dd67b3a99f2a8a2b27cf3dfe032981cbd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor Client fix

Changed paths:
    backends/networking/sdl_net/client.cpp



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 22bfb60..0e86610 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -88,8 +88,11 @@ void Client::checkIfBadRequest() {
 			if (position) { //we have at least one line - and we want the first one
 				//"<METHOD> <path> HTTP/<VERSION>\r\n"
 				Common::String method, path, http, buf;
+				uint32 length = position - cstr;
+				if (headersSize > length) headersSize = length;
 				for (uint32 i = 0; i < headersSize; ++i) {
-					if (_headers[i] == ' ') {
+					if (_headers[i] != ' ') buf += _headers[i];
+					if (_headers[i] == ' ' || i == headersSize-1) {
 						if (method == "") method = buf;
 						else if (path == "") path = buf;
 						else if (http == "") http = buf;
@@ -98,7 +101,7 @@ void Client::checkIfBadRequest() {
 							break;
 						}
 						buf = "";
-					} else buf += _headers[i];
+					}
 				}
 
 				//check that method is supported


Commit: 892c1bf84c4b095d373dfcfb2d84663f55776b46
    https://github.com/scummvm/scummvm/commit/892c1bf84c4b095d373dfcfb2d84663f55776b46
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add HTTP response codes in GetClientHandler

Changed paths:
    backends/networking/sdl_net/getclienthandler.cpp



diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index 48a03e0..f3d596e 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -31,10 +31,71 @@ GetClientHandler::~GetClientHandler() { delete _stream; }
 
 const char *GetClientHandler::responseMessage(long responseCode) {
 	switch (responseCode) {
+	case 100: return "Continue";
+	case 101: return "Switching Protocols";
+	case 102: return "Processing";
+
 	case 200: return "OK";
+	case 201: return "Created";
+	case 202: return "Accepted";
+	case 203: return "Non-Authoritative Information";
+	case 204: return "No Content";
+	case 205: return "Reset Content";
+	case 206: return "Partial Content";
+	case 207: return "Multi-Status";
+	case 226: return "IM Used";
+
+	case 300: return "Multiple Choices";
+	case 301: return "Moved Permanently";
+	case 302: return "Moved Temporarily"; //case 302: return "Found";
+	case 303: return "See Other";
+	case 304: return "Not Modified";
+	case 305: return "Use Proxy";
+	case 306: return "RESERVED";
+	case 307: return "Temporary Redirect";
+
 	case 400: return "Bad Request";
+	case 401: return "Unauthorized";
+	case 402: return "Payment Required";
+	case 403: return "Forbidden";
 	case 404: return "Not Found";
+	case 405: return "Method Not Allowed";
+	case 406: return "Not Acceptable";
+	case 407: return "Proxy Authentication Required";
+	case 408: return "Request Timeout";
+	case 409: return "Conflict";
+	case 410: return "Gone";
+	case 411: return "Length Required";
+	case 412: return "Precondition Failed";
+	case 413: return "Request Entity Too Large";
+	case 414: return "Request-URI Too Large";
+	case 415: return "Unsupported Media Type";
+	case 416: return "Requested Range Not Satisfiable";
+	case 417: return "Expectation Failed";
+	case 422: return "Unprocessable Entity";
+	case 423: return "Locked";
+	case 424: return "Failed Dependency";
+	case 425: return "Unordered Collection";
+	case 426: return "Upgrade Required";
+	case 428: return "Precondition Required";
+	case 429: return "Too Many Requests";
+	case 431: return "Request Header Fields Too Large";
+	case 434: return "Requested Host Unavailable";
+	case 449: return "Retry With";
+	case 451: return "Unavailable For Legal Reasons";
+
 	case 500: return "Internal Server Error";
+	case 501: return "Not Implemented";
+	case 502: return "Bad Gateway";
+	case 503: return "Service Unavailable";
+	case 504: return "Gateway Timeout";
+	case 505: return "HTTP Version Not Supported";
+	case 506: return "Variant Also Negotiates";
+	case 507: return "Insufficient Storage";
+	case 508: return "Loop Detected";
+	case 509: return "Bandwidth Limit Exceeded";
+	case 510: return "Not Extended";
+	case 511: return "Network Authentication Required";
 	}
 	return "Unknown";
 }


Commit: 434b740f4da7f77df902c0be6476cd360688c7ac
    https://github.com/scummvm/scummvm/commit/434b740f4da7f77df902c0be6476cd360688c7ac
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove a couple of unnecessary whitespaces

Changed paths:
    gui/storagewizarddialog.cpp



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 579591b..f93721f 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -91,7 +91,7 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 				++correctFields;
 				continue;
 			}
-			bool correct = correctChecksum(subcode);			
+			bool correct = correctChecksum(subcode);
 			if (correct) {
 				code += subcode;
 				code.deleteLastChar();
@@ -114,18 +114,18 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 
 		bool ok = false;
 		if (correctFields == CODE_FIELDS && code.size() > 0) {
-			//the last 3 chars must be an encoded crc16			
+			//the last 3 chars must be an encoded crc16
 			if (code.size() > 3) {
 				uint32 size = code.size();
-				uint32 gotcrc = decodeHashchar(code[size-3]) | (decodeHashchar(code[size-2]) << 6) | (decodeHashchar(code[size-1]) << 12);				
+				uint32 gotcrc = decodeHashchar(code[size-3]) | (decodeHashchar(code[size-2]) << 6) | (decodeHashchar(code[size-1]) << 12);
 				code.erase(size - 3);
 				uint32 crc = crc16(code);
 				ok = (crc == gotcrc);
-			}			
+			}
 			if (ok) message = _("All OK!");
 			else message = _("Invalid code");
 		}
-		_connectWidget->setEnabled(ok);		
+		_connectWidget->setEnabled(ok);
 		_messageWidget->setLabel(message);
 		break;
 	}
@@ -171,7 +171,7 @@ uint32 StorageWizardDialog::crc16(Common::String s) { //"CRC16_CCITT_FALSE"
 	for (uint32 i = 0; i < s.size(); ++i) {
 		x = ((crc >> 8) ^ s[i]) & 0xFF;
 		x ^= x >> 4;
-		crc = ((crc << 8) ^ (x << 12) ^ (x << 5) ^ x) & 0xFFFF;		
+		crc = ((crc << 8) ^ (x << 12) ^ (x << 5) ^ x) & 0xFFFF;
 	}
 	return crc;
 }


Commit: ceb86a0dd8421047fc3d067da2a4c7faccc2f782
    https://github.com/scummvm/scummvm/commit/ceb86a0dd8421047fc3d067da2a4c7faccc2f782
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Clarify calculatedChecksum's initial value

Changed paths:
    gui/storagewizarddialog.cpp



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index f93721f..58ca095 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -159,7 +159,7 @@ int StorageWizardDialog::decodeHashchar(char c) {
 bool StorageWizardDialog::correctChecksum(Common::String s) {
 	if (s.size() == 0) return false; //no last char
 	int providedChecksum = decodeHashchar(s.lastChar());
-	int calculatedChecksum = 0x2A;
+	int calculatedChecksum = 0x2A; //any initial value would do, but it must equal to the one used on the page where these checksums were generated 
 	for (uint32 i = 0; i < s.size()-1; ++i) {
 		calculatedChecksum = calculatedChecksum ^ s[i];
 	}	


Commit: 3946f23d172f55411748171af9822d2be3863701
    https://github.com/scummvm/scummvm/commit/3946f23d172f55411748171af9822d2be3863701
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Prepare code for path handlers

LocalWebserver would storage the handlers.

Client now has methods like path() or query() to access different parts
of the request.

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 0e86610..60f6be2 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -28,9 +28,9 @@
 
 namespace Networking {
 
-Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {}
+Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {}
 
-Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {
+Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {
 	open(set, socket);
 }
 
@@ -38,7 +38,7 @@ Client::~Client() {
 	close();
 }
 
-void Client::open(SDLNet_SocketSet set, TCPsocket socket) {	
+void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	if (_state != INVALID) close();
 	_state = READING_HEADERS;
 	_socket = socket;
@@ -56,14 +56,14 @@ void Client::readHeaders() {
 	if (!SDLNet_SocketReady(_socket)) return;
 
 	const uint32 BUFFER_SIZE = 16 * 1024;
-	char buffer[BUFFER_SIZE];	
-	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);	
+	char buffer[BUFFER_SIZE];
+	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);
 	if (bytes <= 0) {
-		warning("Client::readHeaders recv fail");		
+		warning("Client::readHeaders recv fail");
 		close();
 		return;
 	}
-	_headers += Common::String(buffer, bytes);	
+	_headers += Common::String(buffer, bytes);
 	checkIfHeadersEnded();
 	checkIfBadRequest();
 }
@@ -74,7 +74,7 @@ void Client::checkIfHeadersEnded() {
 	if (position) _state = READ_HEADERS;
 }
 
-void Client::checkIfBadRequest() {	
+void Client::checkIfBadRequest() {
 	uint32 headersSize = _headers.size();
 	bool bad = false;
 
@@ -92,7 +92,7 @@ void Client::checkIfBadRequest() {
 				if (headersSize > length) headersSize = length;
 				for (uint32 i = 0; i < headersSize; ++i) {
 					if (_headers[i] != ' ') buf += _headers[i];
-					if (_headers[i] == ' ' || i == headersSize-1) {
+					if (_headers[i] == ' ' || i == headersSize - 1) {
 						if (method == "") method = buf;
 						else if (path == "") path = buf;
 						else if (http == "") http = buf;
@@ -109,11 +109,35 @@ void Client::checkIfBadRequest() {
 
 				//check that HTTP/<VERSION> is OK
 				if (!http.hasPrefix("HTTP/")) bad = true;
+
+				_method = method;
+				parsePathQueryAndAnchor(path);
 			}
 		}
 	}
 
-	if (bad) _state = BAD_REQUEST;	
+	if (bad) _state = BAD_REQUEST;
+}
+
+void Client::parsePathQueryAndAnchor(Common::String path) {
+	//<path>[?query][#anchor]
+	bool readingPath = true;
+	bool readingQuery = false;
+	_path = "";
+	_query = "";
+	_anchor = "";
+	for (uint32 i = 0; i < path.size(); ++i) {
+		if (readingPath) {
+			if (path[i] == '?') {
+				readingPath = false;
+				readingQuery = true;
+			} else _path += path[i];
+		} else if(readingQuery) {
+			if (path[i] == '#') {
+				readingQuery = false;
+			} else _query += path[i];
+		} else _anchor += path[i];
+	}
 }
 
 void Client::setHandler(ClientHandler *handler) {	
@@ -147,9 +171,40 @@ void Client::close() {
 }
 
 
-ClientState Client::state() { return _state; }
+ClientState Client::state() const { return _state; }
+
+Common::String Client::headers() const { return _headers; }
+
+Common::String Client::method() const { return _method; }
+
+Common::String Client::path() const { return _path; }
+
+Common::String Client::query() const { return _query; }
+
+Common::String Client::queryParameter(Common::String name) const {
+	// this approach is a bit slower than searching for the <name>
+	// yet I believe it to be the right one, because we probably can have "<name>=" in the value of other key
+	Common::String key = "";
+	Common::String value = "";
+	bool readingKey = true;
+	for (uint32 i = 0; i < _query.size(); ++i) {
+		if (readingKey) {
+			if (_query[i] == '=') {
+				readingKey = false;
+				value = "";
+			} else key += _query[i];
+		} else {
+			if (_query[i] == '&') {
+				if (key == name) return value;
+				readingKey = true;
+				key = "";
+			} else value += _query[i];
+		}
+	}
+	return "";
+}
 
-Common::String Client::headers() { return _headers; }
+Common::String Client::anchor() const { return _anchor; }
 
 bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
 
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 8b7a7d5..eba5dab 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -52,10 +52,12 @@ class Client {
 	SDLNet_SocketSet _set;
 	TCPsocket _socket;
 	Common::String _headers;
+	Common::String _method, _path, _query, _anchor;
 	ClientHandler *_handler;
 
 	void checkIfHeadersEnded();
 	void checkIfBadRequest();
+	void parsePathQueryAndAnchor(Common::String path);
 
 public:
 	Client();
@@ -68,8 +70,13 @@ public:
 	void handle();
 	void close();
 
-	ClientState state();
-	Common::String headers();
+	ClientState state() const;
+	Common::String headers() const;
+	Common::String method() const;
+	Common::String path() const;
+	Common::String query() const;
+	Common::String queryParameter(Common::String name) const;
+	Common::String anchor() const;
 
 	/**
 	 * Return SDLNet_SocketReady(_socket).
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index a25dd74..689bab4 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -110,6 +110,11 @@ void LocalWebserver::stop() {
 	}
 }
 
+void LocalWebserver::addPathHandler(Common::String path, ClientHandler handler) {
+	if (_pathHandlers.contains(path)) warning("LocalWebserver::addPathHandler: path already had a handler");
+	_pathHandlers[path] = handler;
+}
+
 void LocalWebserver::handle() {
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
@@ -127,10 +132,13 @@ void LocalWebserver::handleClient(uint32 i) {
 	case READ_HEADERS: //decide what to do next with that client
 		//if GET, check whether we know a handler for such URL
 		//if PUT, check whether we know a handler for that URL
+		if (_pathHandlers.contains(_client[i].path()))
+			(*_pathHandlers[_client[i].path()])(_client[i]);		
+		
+		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break;
+
 		//if no handler, answer with default BAD REQUEST
-		warning("headers %s", _client[i].headers().c_str());
-		setClientGetHandler(_client[i], "<html><head><title>ScummVM</title></head><body>Hello, World!</body></html>");
-		break;
+		//fallthrough
 	case BAD_REQUEST:
 		setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
 		break;
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index ab45398..bda0bfd 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -24,6 +24,8 @@
 #define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
 
 #include "backends/networking/sdl_net/client.h"
+#include "common/callback.h"
+#include "common/hash-str.h"
 #include "common/singleton.h"
 #include "common/scummsys.h"
 
@@ -38,6 +40,8 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	static const uint32 SERVER_PORT = 12345;
 	static const uint32 MAX_CONNECTIONS = 10;
 
+	typedef Common::BaseCallback<Client &> *ClientHandler;
+
 	friend void localWebserverTimer(void *); //calls handle()
 
 	SDLNet_SocketSet _set;
@@ -45,6 +49,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	Client _client[MAX_CONNECTIONS];
 	int _clients;
 	bool _timerStarted;
+	Common::HashMap<Common::String, ClientHandler> _pathHandlers;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
@@ -59,6 +64,7 @@ public:
 
 	void start();
 	void stop();
+	void addPathHandler(Common::String path, ClientHandler handler);
 };
 
 /** Shortcut for accessing the local webserver. */


Commit: 733d998e6a005af78b308817ea7326d1f5192069
    https://github.com/scummvm/scummvm/commit/733d998e6a005af78b308817ea7326d1f5192069
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor Client fix

Changed paths:
    backends/networking/sdl_net/client.cpp



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 60f6be2..24b6865 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -201,6 +201,7 @@ Common::String Client::queryParameter(Common::String name) const {
 			} else value += _query[i];
 		}
 	}
+	if (key == name) return value; //the last key doesn't have an '&' in the end of the query
 	return "";
 }
 


Commit: 5ac3adbd4fbf40c22fccab6ce8bf8dcab8f98eff
    https://github.com/scummvm/scummvm/commit/5ac3adbd4fbf40c22fccab6ce8bf8dcab8f98eff
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add IndexPageHandler

This commit also adds LocalWebserver's stopOnIdle().
That means server is not stopped immediately, but only when all clients
are served.

Changed paths:
  A backends/networking/sdl_net/indexpagehandler.cpp
  A backends/networking/sdl_net/indexpagehandler.h
    backends/module.mk
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/backends/module.mk b/backends/module.mk
index 0f1eea0..ecdca2e 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -62,6 +62,7 @@ ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/client.o \
 	networking/sdl_net/getclienthandler.o \
+	networking/sdl_net/indexpagehandler.o \
 	networking/sdl_net/localwebserver.o
 endif
 
diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
new file mode 100644
index 0000000..58c4761
--- /dev/null
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -0,0 +1,56 @@
+/* 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 "backends/networking/sdl_net/indexpagehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "gui/storagewizarddialog.h"
+
+namespace Networking {
+
+IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
+
+IndexPageHandler::~IndexPageHandler() {
+	LocalServer.removePathHandler("/");
+}
+
+void IndexPageHandler::handle(Client &client) {	
+	Common::String code = client.queryParameter("code");
+
+	if (code == "") {
+		LocalWebserver::setClientGetHandler(client, "<html><head><title>ScummVM</title></head><body>This is a local webserver index page.</body></html>");
+		return;
+	}
+
+	_code = code;
+	sendCommand(GUI::kStorageCodePassedCmd, 0);
+	LocalWebserver::setClientGetHandler(client, "<html><head><title>ScummVM</title></head><body>ScummVM got the code and already connects to your cloud storage!</body></html>");
+}
+
+void IndexPageHandler::addPathHandler(LocalWebserver &server) {
+	// we can't use LocalServer yet, because IndexPageHandler is created while LocalWebserver is created
+	// (thus no _instance is available and it causes stack overflow)
+	server.addPathHandler("/", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle));
+}
+
+Common::String IndexPageHandler::code() { return _code; }
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
new file mode 100644
index 0000000..5a1d2d7
--- /dev/null
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -0,0 +1,47 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
+
+#include "backends/networking/sdl_net/client.h"
+#include "gui/object.h"
+
+namespace Networking {
+class LocalWebserver;
+
+class IndexPageHandler: public GUI::CommandSender {
+	Common::String _code;
+
+	void handle(Client &client);
+
+public:
+	IndexPageHandler();
+	virtual ~IndexPageHandler();
+
+	void addPathHandler(LocalWebserver &server);
+	Common::String code();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 689bab4..971f00a 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -40,7 +40,9 @@ DECLARE_SINGLETON(Networking::LocalWebserver);
 
 namespace Networking {
 
-LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _clients(0) {}
+LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _stopOnIdle(false), _clients(0) {
+	_indexPageHandler.addPathHandler(*this);
+}
 
 LocalWebserver::~LocalWebserver() {
 	stop();
@@ -110,11 +112,20 @@ void LocalWebserver::stop() {
 	}
 }
 
+void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
+
 void LocalWebserver::addPathHandler(Common::String path, ClientHandler handler) {
 	if (_pathHandlers.contains(path)) warning("LocalWebserver::addPathHandler: path already had a handler");
 	_pathHandlers[path] = handler;
 }
 
+void LocalWebserver::removePathHandler(Common::String path) {
+	if (!_pathHandlers.contains(path)) warning("LocalWebserver::removePathHandler: no handler known for this path");
+	_pathHandlers.erase(path);
+}
+
+IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
+
 void LocalWebserver::handle() {
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
@@ -123,6 +134,16 @@ void LocalWebserver::handle() {
 
 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
 		handleClient(i);
+
+	if (_stopOnIdle) {
+		bool idle = true;
+		for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+			if (_client[i].state() != INVALID) {
+				idle = false;
+				break;
+			}
+		if (idle) stop();
+	}
 }
 
 void LocalWebserver::handleClient(uint32 i) {	
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index bda0bfd..2ad83f7 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -24,6 +24,7 @@
 #define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
 
 #include "backends/networking/sdl_net/client.h"
+#include "backends/networking/sdl_net/indexpagehandler.h"
 #include "common/callback.h"
 #include "common/hash-str.h"
 #include "common/singleton.h"
@@ -48,23 +49,29 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	TCPsocket _serverSocket;
 	Client _client[MAX_CONNECTIONS];
 	int _clients;
-	bool _timerStarted;
+	bool _timerStarted, _stopOnIdle;
 	Common::HashMap<Common::String, ClientHandler> _pathHandlers;
+	IndexPageHandler _indexPageHandler;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
 	void handle();
 	void handleClient(uint32 i);
 	void acceptClient();
-	void setClientGetHandler(Client &client, Common::String response, long code = 200);
-
+	
 public:
 	LocalWebserver();
 	virtual ~LocalWebserver();
 
 	void start();
 	void stop();
+	void stopOnIdle();
 	void addPathHandler(Common::String path, ClientHandler handler);
+	void removePathHandler(Common::String path);
+
+	IndexPageHandler &indexPageHandler();
+
+	static void setClientGetHandler(Client &client, Common::String response, long code = 200);
 };
 
 /** Shortcut for accessing the local webserver. */
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 58ca095..c2759f2 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -38,7 +38,8 @@ enum {
 	kCodeBoxCmd = 'CdBx'
 };
 
-StorageWizardDialog::StorageWizardDialog(uint32 storageId): Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId) {
+StorageWizardDialog::StorageWizardDialog(uint32 storageId):
+	Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	Common::String headline = Common::String::format(_("%s Storage Connection Wizard"), CloudMan.listStorages()[_storageId].c_str());
@@ -70,12 +71,14 @@ void StorageWizardDialog::open() {
 	Dialog::open();
 #ifdef USE_SDL_NET
 	LocalServer.start();
+	LocalServer.indexPageHandler().setTarget(this);
 #endif
 }
 
 void StorageWizardDialog::close() {
 #ifdef USE_SDL_NET
-	LocalServer.stop();
+	LocalServer.stopOnIdle();
+	LocalServer.indexPageHandler().setTarget(nullptr);
 #endif
 	Dialog::close();
 }
@@ -143,11 +146,26 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		close();
 		break;
 	}
+#ifdef USE_SDL_NET
+	case kStorageCodePassedCmd:
+		CloudMan.connectStorage(_storageId, LocalServer.indexPageHandler().code());
+		_close = true;		
+		break;
+#endif
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
 }
 
+void StorageWizardDialog::handleTickle() {
+	if (_close) {
+		setResult(1);
+		close();
+	}
+
+	Dialog::handleTickle();
+}
+
 int StorageWizardDialog::decodeHashchar(char c) {
 	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
 	for (uint32 i = 0; i < 64; ++i)
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index fa45f92..93df56d 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -33,12 +33,19 @@ class EditTextWidget;
 class StaticTextWidget;
 class ButtonWidget;
 
+#ifdef USE_SDL_NET
+enum StorageWizardDialogCommands {
+	kStorageCodePassedCmd = 'SWDC'
+};
+#endif
+
 class StorageWizardDialog : public Dialog {
 	static const uint32 CODE_FIELDS = 8;
 	uint32 _storageId;
 	EditTextWidget *_codeWidget[CODE_FIELDS];
 	StaticTextWidget *_messageWidget;
 	ButtonWidget *_connectWidget;
+	bool _close;
 
 	/**
 	 * Return the value corresponding to the given character.
@@ -69,6 +76,7 @@ public:
 	virtual void open();
 	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void handleTickle();
 };
 
 } // End of namespace GUI


Commit: 0def9c50a7d05fc1743b622ffb5fdbba58e697e6
    https://github.com/scummvm/scummvm/commit/0def9c50a7d05fc1743b622ffb5fdbba58e697e6
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Client

Cleanup in open()

Changed paths:
    backends/networking/sdl_net/client.cpp



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 24b6865..67b45b6 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -43,6 +43,12 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	_state = READING_HEADERS;
 	_socket = socket;
 	_set = set;
+	_headers = "";
+	_method = "";
+	_path = "";
+	_query = "";
+	_anchor = "";
+	_handler = nullptr;
 	if (set) {
 		int numused = SDLNet_TCP_AddSocket(set, socket);
 		if (numused == -1) {


Commit: 43071c09723472483af4b69cf454ca75a8cd4613
    https://github.com/scummvm/scummvm/commit/43071c09723472483af4b69cf454ca75a8cd4613
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update LocalWebserver

* fix handling connections;
* fix idling strategy;
* add setClientGetHandler() for SeekableReadStream;
* add determineMimeType().

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 971f00a..21ffe67 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -40,7 +40,8 @@ DECLARE_SINGLETON(Networking::LocalWebserver);
 
 namespace Networking {
 
-LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false), _stopOnIdle(false), _clients(0) {
+LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
+	_stopOnIdle(false), _clients(0), _idlingFrames(0) {
 	_indexPageHandler.addPathHandler(*this);
 }
 
@@ -135,15 +136,15 @@ void LocalWebserver::handle() {
 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
 		handleClient(i);
 
-	if (_stopOnIdle) {
-		bool idle = true;
-		for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
-			if (_client[i].state() != INVALID) {
-				idle = false;
-				break;
-			}
-		if (idle) stop();
-	}
+	_clients = 0;
+	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+		if (_client[i].state() != INVALID)
+			++_clients;
+
+	if (_clients == 0) ++_idlingFrames;
+	else _idlingFrames = 0;
+	
+	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) stop();	
 }
 
 void LocalWebserver::handleClient(uint32 i) {	
@@ -179,17 +180,47 @@ void LocalWebserver::acceptClient() {
 		SDLNet_TCP_Close(client);
 		return;
 	}
-	
-	_client[_clients++].open(_set, client);
+
+	++_clients;
+	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
+		if (_client[i].state() == INVALID) {
+			_client[i].open(_set, client);
+			break;
+		}
 }
 
-void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code) {
+void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code, const char *mimeType) {
 	byte *data = new byte[response.size()];
 	memcpy(data, response.c_str(), response.size());
 	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
-	GetClientHandler *handler = new GetClientHandler(stream);
+	setClientGetHandler(client, stream, code, mimeType);
+}
+
+void LocalWebserver::setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code, const char *mimeType) {
+	GetClientHandler *handler = new GetClientHandler(responseStream);
 	handler->setResponseCode(code);
+	if (mimeType) handler->setHeader("Content-Type", mimeType);
 	client.setHandler(handler);
 }
 
+const char *LocalWebserver::determineMimeType(Common::String &filename) {
+	// text
+	if (filename.hasSuffix(".html")) return "text/html";
+	if (filename.hasSuffix(".css")) return "text/css";
+	if (filename.hasSuffix(".txt")) return "text/plain";
+	if (filename.hasSuffix(".js")) return "application/javascript";
+
+	// images
+	if (filename.hasSuffix(".jpeg") || filename.hasSuffix(".jpg") || filename.hasSuffix(".jpe")) return "image/jpeg";
+	if (filename.hasSuffix(".gif")) return "image/gif";
+	if (filename.hasSuffix(".png")) return "image/png";
+	if (filename.hasSuffix(".svg")) return "image/svg+xml";
+	if (filename.hasSuffix(".tiff")) return "image/tiff";
+	if (filename.hasSuffix(".ico")) return "image/vnd.microsoft.icon";
+	if (filename.hasSuffix(".wbmp")) return "image/vnd.wap.wbmp";
+
+	if (filename.hasSuffix(".zip")) return "application/zip";
+	return "application/octet-stream";
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 2ad83f7..f51c33b 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -30,6 +30,10 @@
 #include "common/singleton.h"
 #include "common/scummsys.h"
 
+namespace Common {
+class SeekableReadStream;
+}
+
 typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
 typedef struct _TCPsocket *TCPsocket;
 
@@ -52,6 +56,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	bool _timerStarted, _stopOnIdle;
 	Common::HashMap<Common::String, ClientHandler> _pathHandlers;
 	IndexPageHandler _indexPageHandler;
+	uint32 _idlingFrames;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
@@ -71,7 +76,9 @@ public:
 
 	IndexPageHandler &indexPageHandler();
 
-	static void setClientGetHandler(Client &client, Common::String response, long code = 200);
+	static void setClientGetHandler(Client &client, Common::String response, long code = 200, const char *mimeType = nullptr);
+	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
+	static const char *determineMimeType(Common::String &filename);
 };
 
 /** Shortcut for accessing the local webserver. */


Commit: 5176eaba81f4cda6cd5a14108c3516abd8ba0c84
    https://github.com/scummvm/scummvm/commit/5176eaba81f4cda6cd5a14108c3516abd8ba0c84
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add wwwroot

wwwroot.zip contains ScummVM local webserver's resources, such as
template html pages, styles and images.

One can make it from wwwroot directory contents by running
make_archive.py script.

It's added to scummvm.rc, so it's included in the executable (it works
with MinGW, but I was unable to do that in VS yet).

IndexPageHandler is the one who returns these resources. It uses
index.html for "/". I'm replacing "{message}" with translated message,
so that's the way I thought the templates should work.

Changed paths:
  A backends/networking/make_archive.py
  A backends/networking/wwwroot.zip
  A backends/networking/wwwroot/favicon.ico
  A backends/networking/wwwroot/index.html
  A backends/networking/wwwroot/logo.png
  A backends/networking/wwwroot/style.css
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h
    dists/scummvm.rc



diff --git a/backends/networking/make_archive.py b/backends/networking/make_archive.py
new file mode 100644
index 0000000..72d3b01
--- /dev/null
+++ b/backends/networking/make_archive.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# encoding: utf-8
+import sys
+import re
+import os
+import zipfile
+
+ARCHIVE_FILE_EXTENSIONS = ('.html', '.css', '.js', '.ico', '.png')
+
+def buildArchive(archiveName):
+	if not os.path.isdir(archiveName):
+		print ("Invalid archive name: " + archiveName)
+		return
+
+	zf = zipfile.ZipFile(archiveName + ".zip", 'w')
+
+	print ("Building '" + archiveName + "' archive:")
+	os.chdir(archiveName)
+
+	filenames = os.listdir('.')
+	filenames.sort()
+	for filename in filenames:
+		if os.path.isfile(filename) and not filename[0] == '.' and filename.endswith(ARCHIVE_FILE_EXTENSIONS):
+			zf.write(filename, './' + filename)
+			print ("    Adding file: " + filename)
+
+	os.chdir('../')
+
+	zf.close()
+
+def main():
+	buildArchive("wwwroot")
+
+if __name__ == "__main__":
+	sys.exit(main())
diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index 58c4761..37fcac6 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -22,35 +22,135 @@
 
 #include "backends/networking/sdl_net/indexpagehandler.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "common/archive.h"
+#include "common/file.h"
+#include "common/translation.h"
+#include "common/unzip.h"
 #include "gui/storagewizarddialog.h"
 
 namespace Networking {
 
+#define ARCHIVE_NAME "wwwroot.zip"
+#define INDEX_PAGE_NAME "index.html"
+
 IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
 
 IndexPageHandler::~IndexPageHandler() {
 	LocalServer.removePathHandler("/");
+
+	Common::ArchiveMemberList fileList = listArchive();
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember const &m = **it;
+		if (m.getName() == INDEX_PAGE_NAME) continue;
+		LocalServer.removePathHandler("/" + m.getName());
+	}
 }
 
-void IndexPageHandler::handle(Client &client) {	
+void IndexPageHandler::handle(Client &client) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
 	Common::String code = client.queryParameter("code");
 
-	if (code == "") {
-		LocalWebserver::setClientGetHandler(client, "<html><head><title>ScummVM</title></head><body>This is a local webserver index page.</body></html>");
+	if (code == "") {		
+		replace(response, "{message}", _("This is a local webserver index page."));
+		LocalWebserver::setClientGetHandler(client, response);
 		return;
 	}
 
 	_code = code;
 	sendCommand(GUI::kStorageCodePassedCmd, 0);
-	LocalWebserver::setClientGetHandler(client, "<html><head><title>ScummVM</title></head><body>ScummVM got the code and already connects to your cloud storage!</body></html>");
+	replace(response, "{message}", _("ScummVM got the code and already connects to your cloud storage!"));
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+void IndexPageHandler::handleResource(Client &client) {
+	Common::String filename = client.path();
+	filename.deleteChar(0);
+	LocalWebserver::setClientGetHandler(client, getArchiveFile(filename), 200, LocalWebserver::determineMimeType(filename));
 }
 
+/// public
+
 void IndexPageHandler::addPathHandler(LocalWebserver &server) {
 	// we can't use LocalServer yet, because IndexPageHandler is created while LocalWebserver is created
 	// (thus no _instance is available and it causes stack overflow)
 	server.addPathHandler("/", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle));
+
+	Common::ArchiveMemberList fileList = listArchive();
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember const &m = **it;
+		if (m.getName() == INDEX_PAGE_NAME) continue;		
+		server.addPathHandler("/" + m.getName(), new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handleResource));		
+	}
 }
 
 Common::String IndexPageHandler::code() { return _code; }
 
+/// utils
+
+void IndexPageHandler::replace(Common::String &source, const Common::String &what, const Common::String &with) {
+	const char *cstr = source.c_str();
+	const char *position = strstr(cstr, what.c_str());
+	if (position) {
+		uint32 index = position - cstr;
+		source.replace(index, what.size(), with);
+	}
+}
+
+Common::ArchiveMemberList IndexPageHandler::listArchive() {
+	Common::ArchiveMemberList resultList;
+
+	// Find "wwwroot.zip" with SearchMan and call its listMembers()
+	Common::ArchiveMemberList fileList;
+	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember       const &m = **it;
+		Common::SeekableReadStream *const stream = m.createReadStream();
+		Common::Archive *zipArchive = Common::makeZipArchive(stream);
+		if (zipArchive) {
+			zipArchive->listMembers(resultList);
+			delete zipArchive;
+			break;
+		}
+	}
+
+	return resultList;
+}
+
+Common::SeekableReadStream *const IndexPageHandler::getArchiveFile(Common::String name) {
+	Common::SeekableReadStream *result = nullptr;
+
+	// Find "wwwroot.zip" with SearchMan and call its getMember(name)
+	Common::ArchiveMemberList fileList;
+	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember       const &m = **it;
+		Common::SeekableReadStream *const stream = m.createReadStream();
+		Common::Archive *zipArchive = Common::makeZipArchive(stream);
+		if (zipArchive) {
+			const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
+			result = ptr->createReadStream();
+			delete zipArchive;
+			break;
+		}
+	}
+
+	return result;
+}
+
+Common::String IndexPageHandler::readEverythingFromStream(Common::SeekableReadStream *const stream) {
+	Common::String result;
+	char buf[1024];
+	uint32 readBytes;
+	while (!stream->eos()) {
+		readBytes = stream->read(buf, 1024);
+		result += Common::String(buf, readBytes);
+	}
+	return result;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index 5a1d2d7..143c300 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -24,6 +24,7 @@
 #define BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
 
 #include "backends/networking/sdl_net/client.h"
+#include "common/archive.h"
 #include "gui/object.h"
 
 namespace Networking {
@@ -33,6 +34,12 @@ class IndexPageHandler: public GUI::CommandSender {
 	Common::String _code;
 
 	void handle(Client &client);
+	void handleResource(Client &client);
+
+	void replace(Common::String &source, const Common::String &what, const Common::String &with);
+	Common::ArchiveMemberList listArchive();
+	Common::SeekableReadStream *const getArchiveFile(Common::String name);
+	Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
 
 public:
 	IndexPageHandler();
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
new file mode 100644
index 0000000..4d98136
Binary files /dev/null and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/favicon.ico b/backends/networking/wwwroot/favicon.ico
new file mode 100644
index 0000000..0283e84
Binary files /dev/null and b/backends/networking/wwwroot/favicon.ico differ
diff --git a/backends/networking/wwwroot/index.html b/backends/networking/wwwroot/index.html
new file mode 100644
index 0000000..2a3d9d3
--- /dev/null
+++ b/backends/networking/wwwroot/index.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+	<head>
+		<title>ScummVM</title>
+		<meta charset="utf-8"/>
+		<link rel="stylesheet" type="text/css" href="style.css"/>
+	</head>
+	<body>
+		<div class="container">
+			<div class='header'>
+				<center><img src="logo.png"/></center>
+			</div>
+			<div class="content">
+				<p>{message}</p>
+			</div>
+		</div>
+	</body>
+</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/logo.png b/backends/networking/wwwroot/logo.png
new file mode 100644
index 0000000..9fdd2d0
Binary files /dev/null and b/backends/networking/wwwroot/logo.png differ
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
new file mode 100644
index 0000000..f092b05
--- /dev/null
+++ b/backends/networking/wwwroot/style.css
@@ -0,0 +1,21 @@
+body, .header { background: #FFCC33; }
+
+.container {
+	width: 80%;
+	margin: 0 auto;
+}
+
+.header {
+	padding: 10pt;
+	/*margin: 8pt;*/
+	margin-bottom: 0;
+}
+
+.content {
+	padding: 8pt;
+	background: rgb(251, 241, 206);
+	font-family: Tahoma;
+	font-size: 16pt;
+}
+
+.content p { margin: 0 0 6pt 0; }
\ No newline at end of file
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index 873feaa..00d71ef 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -19,6 +19,9 @@ scummmodern.zip        FILE    "gui/themes/scummmodern.zip"
 #ifdef USE_TRANSLATION
 translations.dat       FILE    "gui/themes/translations.dat"
 #endif
+#ifdef USE_SDL_NET
+wwwroot.zip       FILE    "backends/networking/wwwroot.zip"
+#endif
 
 #if ENABLE_ACCESS   == STATIC_PLUGIN
 access.dat           FILE    "dists/engine-data/access.dat"


Commit: c2c2ba908ff916e0ba680596bb1b565069255a67
    https://github.com/scummvm/scummvm/commit/c2c2ba908ff916e0ba680596bb1b565069255a67
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Hide StorageWizardDialog fields if server present

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    gui/storagewizarddialog.cpp



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 21ffe67..d6033d1 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -69,6 +69,7 @@ void LocalWebserver::stopTimer() {
 }
 
 void LocalWebserver::start() {
+	_stopOnIdle = false;
 	if (_timerStarted) return;
 	startTimer();
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index c2759f2..88826c2 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -53,11 +53,14 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	case Cloud::kStorageOneDriveId: url += "od"; break;
 	case Cloud::kStorageGoogleDriveId: url += "gd"; break;
 	}
+#ifdef USE_SDL_NET
+	url += "s";
+#endif
 
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", url);
 
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine1", _s("Obtain the code from the storage, enter it"));
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ReturnLine2", _s("in the following field and press 'Connect':"));
+	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':"));
 	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", "");
@@ -65,6 +68,16 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	// Buttons
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
+
+#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
 }
 
 void StorageWizardDialog::open() {


Commit: a56c7a3bd608d310cf0a3c3b534a20e826ecf8f0
    https://github.com/scummvm/scummvm/commit/a56c7a3bd608d310cf0a3c3b534a20e826ecf8f0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update IndexPageHandler to search wwwroot.zip

Now it also searches for that in themepath, not with SearchMan only.

Changed paths:
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index 37fcac6..a1a426d 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -23,6 +23,7 @@
 #include "backends/networking/sdl_net/indexpagehandler.h"
 #include "backends/networking/sdl_net/localwebserver.h"
 #include "common/archive.h"
+#include "common/config-manager.h"
 #include "common/file.h"
 #include "common/translation.h"
 #include "common/unzip.h"
@@ -101,44 +102,52 @@ void IndexPageHandler::replace(Common::String &source, const Common::String &wha
 	}
 }
 
-Common::ArchiveMemberList IndexPageHandler::listArchive() {
-	Common::ArchiveMemberList resultList;
+Common::Archive *IndexPageHandler::getZipArchive() {
+	// first search in themepath
+	if (ConfMan.hasKey("themepath")) {
+		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
+		if (!node.exists() || !node.isReadable() || !node.isDirectory())
+			return false;
+
+		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
+		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
+			Common::SeekableReadStream *const stream = fileNode.createReadStream();
+			Common::Archive *zipArchive = Common::makeZipArchive(stream);
+			if (zipArchive) return zipArchive;
+		}
+	}
 
-	// Find "wwwroot.zip" with SearchMan and call its listMembers()
+	// then use SearchMan to find it
 	Common::ArchiveMemberList fileList;
 	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
 	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
 		Common::ArchiveMember       const &m = **it;
 		Common::SeekableReadStream *const stream = m.createReadStream();
 		Common::Archive *zipArchive = Common::makeZipArchive(stream);
-		if (zipArchive) {
-			zipArchive->listMembers(resultList);
-			delete zipArchive;
-			break;
-		}
+		if (zipArchive) return zipArchive;
 	}
 
+	return nullptr;
+}
+
+Common::ArchiveMemberList IndexPageHandler::listArchive() {
+	Common::ArchiveMemberList resultList;
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		zipArchive->listMembers(resultList);
+		delete zipArchive;
+	}
 	return resultList;
 }
 
 Common::SeekableReadStream *const IndexPageHandler::getArchiveFile(Common::String name) {
 	Common::SeekableReadStream *result = nullptr;
-
-	// Find "wwwroot.zip" with SearchMan and call its getMember(name)
-	Common::ArchiveMemberList fileList;
-	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
-	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
-		Common::ArchiveMember       const &m = **it;
-		Common::SeekableReadStream *const stream = m.createReadStream();
-		Common::Archive *zipArchive = Common::makeZipArchive(stream);
-		if (zipArchive) {
-			const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
-			result = ptr->createReadStream();
-			delete zipArchive;
-			break;
-		}
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
+		result = ptr->createReadStream();
+		delete zipArchive;
 	}
-
 	return result;
 }
 
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index 143c300..df509a4 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -37,6 +37,7 @@ class IndexPageHandler: public GUI::CommandSender {
 	void handleResource(Client &client);
 
 	void replace(Common::String &source, const Common::String &what, const Common::String &with);
+	Common::Archive *getZipArchive();
 	Common::ArchiveMemberList listArchive();
 	Common::SeekableReadStream *const getArchiveFile(Common::String name);
 	Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);


Commit: 5e70f64e108a3b155fc14f5087a7747e5c89f581
    https://github.com/scummvm/scummvm/commit/5e70f64e108a3b155fc14f5087a7747e5c89f581
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Embed cloud icons as byte arrays

Changed paths:
  A backends/networking/curl/cloudicon_data.h
  A backends/networking/curl/cloudicon_disabled_data.h
  A dists/cloudicon.png
  A dists/cloudicon_disabled.png
  R cloudicon.png
  R cloudicon_disabled.png
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon.h



diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 8971d44..554fc79 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -22,11 +22,10 @@
 
 #include "backends/networking/curl/cloudicon.h"
 #include "backends/cloud/cloudmanager.h"
+#include "common/memstream.h"
 #include "gui/ThemeEngine.h"
 #include "gui/gui-manager.h"
 #include "image/png.h"
-#include <common/file.h>
-#include <backends/graphics/surfacesdl/surfacesdl-graphics.h>
 
 namespace Networking {
 
@@ -100,37 +99,35 @@ void CloudIcon::showDisabled() {
 	_disabledFrames = 20 * 3; //3 seconds 20 fps
 }
 
+#include "backends/networking/curl/cloudicon_data.h"
+#include "backends/networking/curl/cloudicon_disabled_data.h"
+
 void CloudIcon::initIcons() {
 	if (_iconsInited) return;
-	loadIcon(_icon, "cloudicon.png");
-	loadIcon(_disabledIcon, "cloudicon_disabled.png");
+	loadIcon(_icon, cloudicon_data, ARRAYSIZE(cloudicon_data));
+	loadIcon(_disabledIcon, cloudicon_disabled_data, ARRAYSIZE(cloudicon_disabled_data));
 	_iconsInited = true;
 }
 
-void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, const char *filename) {
-	Image::PNGDecoder decoder;
-	Common::ArchiveMemberList members;
-	Common::File file;
-	if (!file.open(filename)) warning("CloudIcon::loadIcon: unable to open %s", filename);
-	Common::SeekableReadStream *stream = &file;
-	if (stream) {
-		if (!decoder.loadStream(*stream))
-			error("CloudIcon::loadIcon: error decoding PNG");
-
-		Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
-		if (s) {
-			Graphics::PixelFormat f = g_system->getOSDFormat();
-			if (f != s->format) {
-				Graphics::TransparentSurface *s2 = s->convertTo(f);
-				if (s2) icon.copyFrom(*s2);
-				else warning("CloudIcon::loadIcon: failed converting TransparentSurface");
-				delete s2;
-			} else {
-				icon.copyFrom(*s);
-			}
-			delete s;
-		} else warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
-	}
+void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size) {	
+	Image::PNGDecoder decoder;	
+	Common::MemoryReadStream stream(data, size);
+	if (!decoder.loadStream(stream))
+		error("CloudIcon::loadIcon: error decoding PNG");
+
+	Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
+	if (s) {
+		Graphics::PixelFormat f = g_system->getOSDFormat();
+		if (f != s->format) {
+			Graphics::TransparentSurface *s2 = s->convertTo(f);
+			if (s2) icon.copyFrom(*s2);
+			else warning("CloudIcon::loadIcon: failed converting TransparentSurface");
+			delete s2;
+		} else {
+			icon.copyFrom(*s);
+		}
+		delete s;
+	} else warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
 }
 
 void CloudIcon::makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha) {
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
index e007f95..7e4d738 100644
--- a/backends/networking/curl/cloudicon.h
+++ b/backends/networking/curl/cloudicon.h
@@ -37,7 +37,7 @@ class CloudIcon {
 	int _disabledFrames;
 
 	void initIcons();
-	void loadIcon(Graphics::TransparentSurface &icon, const char *filename);
+	void loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size);
 	void makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha);
 
 public:
diff --git a/backends/networking/curl/cloudicon_data.h b/backends/networking/curl/cloudicon_data.h
new file mode 100644
index 0000000..a924dc8
--- /dev/null
+++ b/backends/networking/curl/cloudicon_data.h
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ */
+
+// This is a PNG file dumped into array.
+// $ recode data..d1 <dists/cloudicon.png >cloudicon_data.h
+// The tool is from https://github.com/pinard/Recode
+
+byte cloudicon_data[] = {
+137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
+ 82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
+122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
+124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
+ 18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
+ 69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
+101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
+  0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
+  0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
+115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4,  50,  73,  68,
+ 65,  84,  88, 133, 197, 151, 109, 104, 150, 101,  20, 199, 127, 247, 227,
+179, 105,  51,  23,  65, 181, 150, 224, 154, 214, 132, 194, 249,  33, 165,
+ 22, 189, 231, 194, 210, 250,  16, 171, 180,  55,  42, 152,  68,  65, 100,
+ 52, 233,   5, 146, 144, 144,  26, 249, 169,  62, 164,  80,  89, 152,  25,
+ 18, 226,  42,  49,  87,  88, 180,  94,  96,  96, 246, 234, 180,  70,  50,
+ 66, 214,  55, 247,  22, 133, 247, 255, 244, 225,  58, 247, 158, 107, 143,
+219, 243,  60, 186, 192,   3, 135, 251, 220, 215, 117, 223, 231, 127, 238,
+235, 252, 239, 235,  58,  39, 105, 250, 206,  56, 147, 146,  55,  85, 252,
+108,  13, 112,  59, 176,  12,  88,   2, 204,   7, 230, 248, 220,  48, 208,
+ 15, 244,   2, 221,  64,  23,  48,  86, 137, 211, 228, 146, 158, 178,  43,
+ 80,   7, 172,   3, 218, 129, 179,  43, 241,   9, 140,   0, 155, 129,  87,
+128, 193, 146,  15,  47, 248, 178, 100,   0, 237,  64,  39, 112,  14,  96,
+174,  20, 217, 153, 228, 162,   0,  50,  25,   2,  58, 128,  45,  83,   1,
+228,  53, 121,  10, 170, 129, 183, 128, 213, 126,  47, 215,  49, 224,  39,
+224,  19, 160,   7,  56,   2, 204,   0, 154, 128,  27, 128, 229, 110, 215,
+120,  64, 181, 132, 149, 184,  30, 120,   4, 248, 183,  24,  40, 185, 248,
+243, 147,  86, 160, 154, 144, 195,  91, 252,  43,   5, 140,   2,  31,   2,
+ 27, 129, 195,  83, 125, 141,  75,  19, 240,   2, 129,  47, 179,  61, 144,
+  4, 216,   7, 172,  44,  14,  34, 105, 232,  62,  41, 128, 109, 192, 189,
+ 14, 126,   2,  24,   0,  30,   3, 246, 150,   1,  46, 150,  54,  15, 184,
+  1, 200, 251, 216, 123, 192, 253,  19,   2, 152, 183, 119,  66,   0, 237,
+192,  27, 110, 159,   0, 250, 128, 187, 128,  67, 167,   8, 158, 201,  98,
+224,   3, 160, 209, 131,  72, 128,  53,  68, 156, 200, 153, 192, 181, 206,
+ 68, 167, 219,  50,  49,  96, 226,  78,  19, 135, 162, 103, 138, 117, 169,
+137,  46,  19, 163,  38,  82,  19,  63, 152, 120, 220, 196,  12, 159,  63,
+104,  98, 149, 137,  99, 238, 211,  28, 163,  46, 243, 145, 147, 192, 117,
+157,  68, 173, 219, 195,  18, 143,  74,  28, 137, 230, 139, 181,  77, 162,
+ 71,  98, 165,  68, 141,  68,  78,  98, 145, 196, 107,  18,  59, 252,  30,
+137,   3,  18, 207,  72, 140, 249, 125, 173,  99,  33,  65, 114, 209, 110,
+131, 192, 218,  65,   2, 105,   4, 108, 245, 165,  74, 125, 165, 206,   5,
+214,   2, 151,   3,   7, 128, 119, 128,  95, 253, 189, 169, 228, 105, 224,
+ 85, 183, 171, 128,  29, 192,  29,   4,  82, 142,  18, 246, 151, 177, 164,
+126, 151,   1, 220,   3, 188,  79,  32, 222,  40,  97, 167, 235, 243, 151,
+207,   2, 190,   5, 154,  35, 231, 223,   0,  45, 101, 242, 127,  12, 152,
+ 75,  97, 191,  88,  12, 124, 237, 254,  18,  96,  21, 176,  35, 227, 192,
+ 50, 207,  15,  38, 126,  49, 113,  56, 202, 243, 221,  38, 154, 139, 114,
+223,  82, 130,  23, 153, 214, 155,  88,  20, 221, 255, 104, 226, 104, 116,
+223, 106,  42, 144, 112, 169, 137, 196, 131, 248,  56,  10, 166, 222, 196,
+141,  21, 128,  77, 165, 223, 155, 120,  42,  34, 246, 158, 200, 247,  18,
+ 19, 228,  21, 178,  60, 223, 151,  41,   1, 190, 112, 251,  60, 224, 171,
+104, 238, 116,  36,  33, 240, 224,  32, 240,  25, 176,  31, 120, 194, 199,
+ 27, 161, 112,  26, 102, 167, 154,   1, 127, 186, 253,  98,   9, 240,  81,
+  2,  97,  43, 149, 231,  61, 128, 129, 104, 108,  78,  28,  64,  44,  25,
+105, 218,  74,  56, 156,  13, 252,  76, 248,  43,  42, 145, 140, 176, 249,
+226, 137, 188, 133,  20,  12, 123,  68,   9, 225, 171, 127,   7,  46,  40,
+227, 180,  82, 112, 128, 153, 126, 189, 148, 194, 105,  57,  12,   5,  18,
+246,  71,  68, 185, 214, 237, 191, 166,  65, 190,  98, 237, 243, 235, 205,
+209,  88, 127, 188,  19, 246, 250,  53,  39, 177, 194, 237, 157,  37, 118,
+193,  83, 213, 183,  37, 102,  73,  92,  39, 145, 196, 152,  57,  75, 193,
+ 82, 246, 249,  21,  75,  89, 104,  41, 205, 150, 178, 222,  82, 250, 163,
+241, 211, 213, 253, 150, 178, 201,  82, 174, 180, 148, 185, 150, 146, 248,
+120, 183, 165, 133,  20, 116, 153,  24, 113, 123, 150, 137, 103,  77,  28,
+ 55, 113, 141, 137, 173,  38, 134,  60,  61, 167, 178, 236,   3,  38,  54,
+152, 184, 213, 255, 253,  39, 221,  55, 142, 213, 101,  42, 252,   5,  99,
+132, 202, 101,  45,  97, 175,  94,  14, 172,   0, 118,   1,  15, 185,  78,
+ 71,  86,  19, 138, 217, 196, 117, 179,  99, 146, 204, 126, 125, 188,  30,
+168,  35, 236, 255, 181, 132,   3, 233,  15, 224,  54, 202,  87,  64, 229,
+164,   5, 216,   9,  92, 232, 224,  67, 192,  66, 188,  88, 205,  69, 185,
+ 26, 180, 148,  14, 207,  81, 206,  82, 230,  89, 202, 110,  75, 185, 108,
+ 26, 249, 191, 202,  82, 222, 181, 148, 243, 163, 220, 119,  56,  22, 150,
+ 78, 172,   7, 144, 216,  34, 177, 205, 237, 188, 196,   2, 137,  61,  18,
+ 15, 158,   6, 243,  31, 144, 216,  37, 209, 224, 190,  18, 137, 237, 142,
+ 49, 254,  92,  50, 115, 211, 164,  69, 233,  71,  64,  43, 140,  23, 165,
+127,  19,  26, 142, 231,   8, 117, 192,  84,  82,  69,  56, 118, 215,   3,
+ 55,  17,  54, 160, 172,  40, 253, 148,  80, 168,  78,  44,  74, 171,  59,
+ 39, 237,  11, 170, 129,  55, 129, 251,  40, 108, 205,   2, 254,   1, 126,
+243,  96, 122, 129, 163,  62, 223,   0,  92, 237, 160, 141,  17, 112,  38,
+219, 129, 135, 139, 193,   1, 146, 170, 151,  43, 106,  76, 106, 139, 198,
+229, 192,  73,  20,  96,  12, 152, 177, 253,  56, 101,  26, 147,  36, 191,
+177, 226, 214, 108,  13, 133, 214, 172, 220,  75,  35,  14,  90, 190,  53,
+203, 189,  84, 113, 119, 156,  53, 167, 173, 192,  21, 252,  95, 205, 105,
+178, 225, 204, 182, 231, 255,   1, 200,  91, 112, 221, 160, 249,  68,  42,
+  0,   0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
+};
diff --git a/backends/networking/curl/cloudicon_disabled_data.h b/backends/networking/curl/cloudicon_disabled_data.h
new file mode 100644
index 0000000..fc1638c
--- /dev/null
+++ b/backends/networking/curl/cloudicon_disabled_data.h
@@ -0,0 +1,117 @@
+/* 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.
+ *
+ */
+
+// This is a PNG file dumped into array.
+// $ recode data..d1 <dists/cloudicon_disabled.png >cloudicon_disabled_data.h
+// The tool is from https://github.com/pinard/Recode
+
+byte cloudicon_disabled_data[] = {
+137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
+ 82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
+122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
+124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
+ 18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
+ 69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
+101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
+  0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
+  0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
+115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4, 139,  73,  68,
+ 65,  84,  88, 133, 197, 215,  91, 168,  86,  69,  20,   7, 240, 223, 254,
+244, 120, 236, 120,  57,  26,  94,  10,  31,  34,  35,  11,  36, 203,  74,
+212, 160, 204, 212, 110, 166, 248, 208, 205, 172, 151, 144, 136, 158,  82,
+ 80, 168,  32, 233, 165, 160,  32, 130, 236, 165,  34, 204,  74,  18,  52,
+ 36, 169, 232,  34,  24, 221, 243, 218, 133,  74,  77, 195,  44, 200,  91,
+122, 188, 156, 172, 102, 159, 221, 195, 204, 246, 219, 126, 231, 226,  17,
+138,  22,  12, 123, 246, 236, 153, 245,  95, 107, 205, 127, 205, 172, 157,
+ 21, 254,  95, 233,  27, 122,  63, 183,   5, 179,  48,  13,  87,  98,  52,
+  6, 167, 111,  71, 176,  11,  27, 177,  14, 107, 209, 222,  27, 165, 217,
+137, 211, 207,  25, 137, 197, 152, 143,  65, 189, 209, 137, 163, 120,   1,
+ 79,  98, 111, 143, 147, 143, 247, 172, 108,  62, 158, 194,  16,  20, 169,
+105, 232, 151,  82, 171,  24,  80,  74,  27,  22, 225, 197, 110,  13, 104,
+235, 122, 188,  31,  94, 194,  93,  21, 192,  14,  49, 172, 223, 226, 109,
+124, 130,  29, 232, 131,  49, 184,  22,  55, 166, 126,  75, 131,  65, 175,
+225,  94, 252, 213, 201, 128, 131,  93, 131, 191, 137,  27,  42, 192, 199,
+241,   6, 158, 192, 246, 238, 188,  73,  50,   6, 143,  98,  78,  50, 164,
+140, 200, 123,  34, 135,  78,  49,  34, 219, 215,  89, 193, 171, 152, 151,
+192,   3, 246, 224,   1, 188, 123,  26, 224, 170,  92, 134, 143,  49, 160,
+ 50,  86,  96,   5, 238, 174,  78, 108, 204, 130, 249,  98, 216,  75, 240,
+109, 184,  13,  63, 156,   1, 248,  68,  44,  21, 189,  47, 165,  67, 140,
+196,  60, 172,  87, 225,  68, 182, 167,  62, 105, 100,   2, 106,  77,  11,
+118, 139, 123, 186, 163,  59, 164,  53,  76,  16, 195, 125,  29, 250,  15,
+227, 167,  73,  12, 237,  27,  73,  91,  75, 142,  28,  21, 185,  51,  60,
+141, 181, 225,  98,  41,  59, 106,  33, 185,  26,  88,  28, 104,  77, 253,
+163, 129, 251,   3,  59,  42, 223,  79, 105, 171, 184,  53, 240,  73, 224,
+150,  64, 203,  32, 106,  99, 185, 160, 224, 236,  16, 245, 118,   4,  54,
+  5, 166,   4,  22,   6, 218, 211, 218, 214, 132,  37,  32,  75, 238, 181,
+224,  55,  12,  76, 222, 191, 140, 251, 144,  39,  79, 135,  98,   1, 198,
+ 98,  11, 150, 227, 251,  50, 204,  67, 113, 121,  90, 156,  57, 185, 127,
+135, 154, 184,   9,  95, 160,   9,  43,  49,  59,  69, 225,  24, 206,  65,
+123, 150,  54, 247,  14, 188, 158, 214,  30,  23,  79, 186, 109,   9, 252,
+ 44, 124, 142, 113, 149, 232, 127, 134, 201, 196,  88,  79,  66, 115,   5,
+252,  24, 182, 114, 224,  32,  35, 230, 212, 207, 139,  75, 241, 169, 168,
+ 47, 195, 157,  88,  89, 110, 193, 180,  64, 145, 250, 223,   5, 182,  87,
+194, 125, 123,  96,  92, 195,  22,  76,  14, 201, 227, 177, 226,  65, 144,
+227, 111,  28, 198, 151, 113, 131, 135,   5,  46, 169, 172, 249,  38, 176,
+187, 242,  62,  35, 160, 150, 199, 197,  19, 114, 178, 156,  34, 231, 173,
+244, 180, 154, 115, 115, 166, 166,  57, 167, 180, 193,  34,   3,   7, 165,
+ 61,  11, 137, 105,  27, 112, 160,  62, 111, 235, 106,  22, 166, 126,  71,
+206,  59, 165, 238, 156,  43, 114, 100, 155,  98, 120, 218, 146, 206,   2,
+ 83, 241, 225,  26, 134, 165, 253,  27, 173,  65, 134, 138, 137, 222, 154,
+222, 139, 228, 249,  87, 233, 217, 133,  76, 159,  19,  47, 169,  89, 226,
+129, 214,  71, 188, 192, 134, 148, 231,  64, 121, 171,  21, 248,  85, 244,
+232, 177,  30, 192,  59,  90,  82,   6, 193,   9, 108, 234,  30,  28,  30,
+  9, 209, 128,  74, 214,  71, 204, 190, 121, 231, 201,   5, 228, 220, 218,
+248,  97, 136, 200, 246, 102, 106,  29, 234,  73, 190,   5, 135, 186,   7,
+135, 201,   9, 167, 111, 227, 135,  50,   2,  71, 146,  69, 153, 232, 245,
+206, 192, 136, 234, 196,  86,  49,  13, 154, 162, 113,  39, 211, 101,  51,
+126, 239,  25,  28, 154,  19, 206, 133, 234, 119, 195,  17, 234,  36, 220,
+ 85,  33, 202, 213, 169, 191, 175,  36,  92, 139, 152, 106,   3, 212,   9,
+119,  76, 100, 251, 126, 157,   9, 218,  69, 219, 150, 158, 211,  42,  99,
+187, 114, 245, 147, 112,  83, 122, 214,   2,  51,  83, 127,  85,  16,  89,
+ 62,  94,  60,  61, 202,  20,  58,  36,  38, 244, 126,  93, 159, 146,  93,
+180, 101, 129, 254, 129, 107,   2,  89,  26, 219,  24,  42,   6, 188,  95,
+153, 124,  81, 202, 251,  37,   3, 249, 101, 188, 120, 114, 148, 223, 219,
+197, 212,  56, 208, 123, 240, 245, 129, 167,   3,  19,   3, 163,  42,   6,
+172,  11, 234,  36,  92,  43, 242, 105,  32, 250, 227, 161,  89,  44,  45,
+ 98, 196, 139,  14, 178, 146, 112,  27, 210, 179,  23, 178,   7, 203, 240,
+248, 156, 152, 251,  15,  38, 221, 146, 138, 181, 212,  73, 216,  46, 214,
+112,  11,  82, 180, 103, 138,  71, 237,  40, 145,  52,  29, 216, 221, 194,
+220,  41,  49,   0, 103,  36, 129, 185, 152, 158, 116, 101,   9, 171,  29,
+178,  85, 245, 121, 213, 235, 184,  90, 215, 229, 248,  89, 172, 112, 190,
+ 62,  83, 112, 209, 145,  85, 226, 229, 147, 105, 188, 142,  43, 172, 220,
+155, 179,  40,  29, 201,  85,   6, 239, 204, 153, 155, 243, 117,  47, 216,
+222, 216,  38, 229, 188, 146,  51, 188, 162, 119,  81, 194,  82, 205, 130,
+178, 189,  24, 120, 173,  97, 108,  80,  34, 102, 111,  73,  87, 182, 123,
+  2, 107,   2, 231, 133, 184, 213,  89,  96,  69, 194,  56,  57,  47,  91,
+222,  57, 100, 253,  68, 130,  92, 175,  94, 148, 254, 129,  15, 240, 176,
+ 88,   7, 116,  39,  77, 226, 181, 187,  68, 172, 146, 154,  69,  78, 101,
+ 98,  77,  57,  91,  99,  81, 250,  82, 215, 138, 202, 178, 188,  44,  78,
+ 37,  67, 254, 196, 143, 201, 152, 141,  98, 217,  86, 224,  60,  92, 149,
+ 64, 207, 175,   0, 151, 178,  66, 119, 101, 249, 243,  61, 184, 163, 254,
+ 99, 210, 218,  48,  94,  94,   5, 101,  13, 162,   1, 176, 100, 251,  97,
+167, 249,  49, 233, 115, 179, 250, 111,  78,  23, 109, 115, 193, 178, 130,
+ 62,   5, 151,  20,  52,  23, 241,  76, 200,  10, 106, 149, 103, 173,  50,
+158,  21,  28,  45, 120, 174,  96, 110, 193,  71,  61, 232, 151,  61, 219,
+115,   4, 170,  82, 254, 156, 206,  16,  47, 197, 127, 231, 231, 244, 153,
+222,  27, 240, 159, 200,  63, 153, 185,  24, 191, 162, 246,  71, 153,   0,
+  0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
+};
diff --git a/cloudicon.png b/cloudicon.png
deleted file mode 100644
index 21373ae..0000000
Binary files a/cloudicon.png and /dev/null differ
diff --git a/cloudicon_disabled.png b/cloudicon_disabled.png
deleted file mode 100644
index 27c9600..0000000
Binary files a/cloudicon_disabled.png and /dev/null differ
diff --git a/dists/cloudicon.png b/dists/cloudicon.png
new file mode 100644
index 0000000..21373ae
Binary files /dev/null and b/dists/cloudicon.png differ
diff --git a/dists/cloudicon_disabled.png b/dists/cloudicon_disabled.png
new file mode 100644
index 0000000..27c9600
Binary files /dev/null and b/dists/cloudicon_disabled.png differ


Commit: 6ac69729d5c93899a3b1f5d82b6c5b008f030a7c
    https://github.com/scummvm/scummvm/commit/6ac69729d5c93899a3b1f5d82b6c5b008f030a7c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add some mutexes in LocalWebserver

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index d6033d1..1cfbbb8 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -69,6 +69,7 @@ void LocalWebserver::stopTimer() {
 }
 
 void LocalWebserver::start() {
+	_handleMutex.lock();
 	_stopOnIdle = false;
 	if (_timerStarted) return;
 	startTimer();
@@ -93,9 +94,11 @@ void LocalWebserver::start() {
 	if (numused == -1) {
 		error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
 	}
+	_handleMutex.unlock();
 }
 
 void LocalWebserver::stop() {
+	_handleMutex.lock();
 	if (_timerStarted) stopTimer();
 
 	if (_serverSocket) {
@@ -112,6 +115,7 @@ void LocalWebserver::stop() {
 		SDLNet_FreeSocketSet(_set);
 		_set = nullptr;
 	}
+	_handleMutex.unlock();
 }
 
 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
@@ -129,6 +133,7 @@ void LocalWebserver::removePathHandler(Common::String path) {
 IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
 
 void LocalWebserver::handle() {
+	_handleMutex.lock();
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
 		error("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
@@ -145,7 +150,13 @@ void LocalWebserver::handle() {
 	if (_clients == 0) ++_idlingFrames;
 	else _idlingFrames = 0;
 	
-	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) stop();	
+	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) {
+		_handleMutex.unlock();
+		stop();
+		return;
+	}
+
+	_handleMutex.unlock();
 }
 
 void LocalWebserver::handleClient(uint32 i) {	
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index f51c33b..2bd8daf 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -27,6 +27,7 @@
 #include "backends/networking/sdl_net/indexpagehandler.h"
 #include "common/callback.h"
 #include "common/hash-str.h"
+#include "common/mutex.h"
 #include "common/singleton.h"
 #include "common/scummsys.h"
 
@@ -57,6 +58,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	Common::HashMap<Common::String, ClientHandler> _pathHandlers;
 	IndexPageHandler _indexPageHandler;
 	uint32 _idlingFrames;
+	Common::Mutex _handleMutex;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();


Commit: 1addefad7e29ff674828c6dad5330c87a4203f29
    https://github.com/scummvm/scummvm/commit/1addefad7e29ff674828c6dad5330c87a4203f29
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use correct redirect_uris

Usage of #ifdef there (and in StorageWizardDialog) means that ScummVM
doesn't support both local webserver and scummvm.org paths at the same
time. It's either built with SDL_net (thus supporting localhost path) or
without it (thus using scummvm.org).

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 6442581..3cf14d5 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -67,7 +67,11 @@ void DropboxStorage::getAccessToken(Common::String code) {
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
+#ifdef USE_SDL_NET
 	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+#else
+	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+#endif
 	addRequest(request);
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 6d0a152..df29a87 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -87,7 +87,11 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Common::String co
 	}
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost");
+#ifdef USE_SDL_NET
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
+#else
+	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+#endif
 	addRequest(request);
 }
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 5c23036..0d2f91c 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -83,7 +83,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	}
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
+#ifdef USE_SDL_NET
 	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+#else
+	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+#endif
 	addRequest(request);
 }
 


Commit: b908b286b9767ff7a0207ce9414279ce5291762c
    https://github.com/scummvm/scummvm/commit/b908b286b9767ff7a0207ce9414279ce5291762c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "signed/unsigned integers" warning

The "comparison between signed and unsigned integer expressions" one.

Note that in UploadRequests size() and pos() are acutally signed,
because they could return -1. This commit implies that Requests are
working with such Streams which doesn't.

Changed paths:
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    gui/storagewizarddialog.cpp



diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 5765892..f1fb818 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -69,7 +69,7 @@ void DropboxUploadRequest::uploadNextPart() {
 	Common::JSONObject jsonRequestParameters;
 
 	if (_contentsStream->pos() == 0 || _sessionId == "") {
-		if (_contentsStream->size() <= UPLOAD_PER_ONE_REQUEST) {
+		if ((uint32)_contentsStream->size() <= UPLOAD_PER_ONE_REQUEST) {
 			url = "https://content.dropboxapi.com/2/files/upload";
 			jsonRequestParameters.setVal("path", new Common::JSONValue(_savePath));
 			jsonRequestParameters.setVal("mode", new Common::JSONValue("overwrite"));
@@ -80,7 +80,7 @@ void DropboxUploadRequest::uploadNextPart() {
 			jsonRequestParameters.setVal("close", new Common::JSONValue(false));
 		}
 	} else {
-		if (_contentsStream->size() - _contentsStream->pos() <= UPLOAD_PER_ONE_REQUEST) {
+		if ((uint32)(_contentsStream->size() - _contentsStream->pos()) <= UPLOAD_PER_ONE_REQUEST) {
 			url += "finish";			
 			Common::JSONObject jsonCursor, jsonCommit;			
 			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index bc54a81..08f4593 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -62,7 +62,7 @@ void OneDriveUploadRequest::start() {
 void OneDriveUploadRequest::uploadNextPart() {	
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
-	if (_uploadUrl == "" && _contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
+	if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
 		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/upload.createSession"; //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 88826c2..6734d1c 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -100,7 +100,7 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	switch (cmd) {
 	case kCodeBoxCmd: {		
 		Common::String code, message;
-		int correctFields = 0;
+		uint32 correctFields = 0;
 		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
 			Common::String subcode = _codeWidget[i]->getEditString();
 			if (subcode.size() == 0) {


Commit: 04cef8928ce0c8664bd3bb923eed3ceaa9ac17c5
    https://github.com/scummvm/scummvm/commit/04cef8928ce0c8664bd3bb923eed3ceaa9ac17c5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "type qualifiers ignored" warning

Changed paths:
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index a1a426d..e459b3a 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -140,7 +140,7 @@ Common::ArchiveMemberList IndexPageHandler::listArchive() {
 	return resultList;
 }
 
-Common::SeekableReadStream *const IndexPageHandler::getArchiveFile(Common::String name) {
+Common::SeekableReadStream *IndexPageHandler::getArchiveFile(Common::String name) {
 	Common::SeekableReadStream *result = nullptr;
 	Common::Archive *zipArchive = getZipArchive();
 	if (zipArchive) {
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index df509a4..cbcc6da 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -39,7 +39,7 @@ class IndexPageHandler: public GUI::CommandSender {
 	void replace(Common::String &source, const Common::String &what, const Common::String &with);
 	Common::Archive *getZipArchive();
 	Common::ArchiveMemberList listArchive();
-	Common::SeekableReadStream *const getArchiveFile(Common::String name);
+	Common::SeekableReadStream *getArchiveFile(Common::String name);
 	Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
 
 public:


Commit: 39eb76f8c2ea62520c29e76ee10d97c2a13f0f42
    https://github.com/scummvm/scummvm/commit/39eb76f8c2ea62520c29e76ee10d97c2a13f0f42
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "zero-length format string" warning

Changed paths:
    backends/cloud/googledrive/googledrivestorage.cpp



diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index df29a87..327c9ee 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -264,7 +264,7 @@ void GoogleDriveStorage::printFiles(FileArrayResponse response) {
 		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
 		debug("\t%s", files[i].path().c_str());
 		debug("\t%s", files[i].id().c_str());
-		debug("");
+		debug(" ");
 	}
 }
 


Commit: caa53d9f6c191eecf87dc646dac33be25cb98f16
    https://github.com/scummvm/scummvm/commit/caa53d9f6c191eecf87dc646dac33be25cb98f16
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "-Wconversion-null"

That `false` came from TranslationManager's function, which was
returning bool, not a pointer. Somehow missed that line.

Changed paths:
    backends/networking/sdl_net/indexpagehandler.cpp



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index e459b3a..2d1f2c2 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -107,7 +107,7 @@ Common::Archive *IndexPageHandler::getZipArchive() {
 	if (ConfMan.hasKey("themepath")) {
 		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
 		if (!node.exists() || !node.isReadable() || !node.isDirectory())
-			return false;
+			return nullptr;
 
 		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
 		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {


Commit: 8484273f36cba8de90e4cfd40a18b8788e50f64b
    https://github.com/scummvm/scummvm/commit/8484273f36cba8de90e4cfd40a18b8788e50f64b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix "-Wcast-qual"

The passed buffer is not changed, so could be `const`.

You might see that `postFields.c_str()` is `buffer`. Yet, as it's
`postFields`, it's used for POST in curl_easy_setopt(), which copies the
passed buffer. When `buffer` is used for upload, it's an actual bytes
buffer, kept in CurlRequest.

Changed paths:
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index d761cbb..5c37306 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -47,7 +47,7 @@ static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
-void NetworkReadStream::init(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	_eos = _requestComplete = false;
 	_sendingContentsBuffer = nullptr;
 	_sendingContentsSize = _sendingContentsPos = 0;
@@ -81,10 +81,10 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, byte *buf
 }
 
 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch) {
-	init(url, headersList, (byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
+	init(url, headersList, (const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	init(url, headersList, buffer, bufferSize, uploading, usingPatch, post);
 }
 
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 7a9d9ff..6f521cb 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -35,15 +35,15 @@ namespace Networking {
 class NetworkReadStream: public Common::MemoryReadWriteStream {	
 	CURL *_easy;
 	bool _eos, _requestComplete;
-	byte *_sendingContentsBuffer;
+	const byte *_sendingContentsBuffer;
 	uint32 _sendingContentsSize;
 	uint32 _sendingContentsPos;
 	Common::String _responseHeaders;
-	void init(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
+	void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
 
 public:	
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);
-	NetworkReadStream(const char *url, curl_slist *headersList, byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
+	NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
 	virtual ~NetworkReadStream();
 
 	/**


Commit: 81106b04440d76238da0fa0166eb3032b6db591e
    https://github.com/scummvm/scummvm/commit/81106b04440d76238da0fa0166eb3032b6db591e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update local server's style.css

Changed paths:
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 4d98136..403385b 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index f092b05..75c8378 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -1,4 +1,8 @@
-body, .header { background: #FFCC33; }
+html {
+	background: rgb(212, 117, 11);
+	background: linear-gradient(to bottom, rgb(212, 117, 11) 0%, rgb(212, 117, 11) 36%, rgb(239, 196, 24) 100%);
+	height: 100vh;
+}
 
 .container {
 	width: 80%;
@@ -7,7 +11,6 @@ body, .header { background: #FFCC33; }
 
 .header {
 	padding: 10pt;
-	/*margin: 8pt;*/
 	margin-bottom: 0;
 }
 
@@ -18,4 +21,4 @@ body, .header { background: #FFCC33; }
 	font-size: 16pt;
 }
 
-.content p { margin: 0 0 6pt 0; }
\ No newline at end of file
+.content p { margin: 0 0 6pt 0; }


Commit: aee713141b3a401f08e63cd9ccf5ce3dfe1cb06e
    https://github.com/scummvm/scummvm/commit/aee713141b3a401f08e63cd9ccf5ce3dfe1cb06e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make OutSaveFile start saves sync

It had to become a proxy class in order to do that.
finalize() starts the saves sync.

Changed paths:
  A engines/tsage/stP1kAlM
    backends/platform/ds/arm9/source/gbampsave.cpp
    backends/platform/n64/framfs_save_manager.h
    backends/platform/n64/pakfs_save_manager.h
    backends/platform/ps2/savefilemgr.cpp
    backends/saves/default/default-saves.cpp
    backends/saves/savefile.cpp
    common/savefile.h
    engines/kyra/saveload_eob.cpp
    engines/kyra/saveload_hof.cpp
    engines/kyra/saveload_lok.cpp
    engines/kyra/saveload_lol.cpp
    engines/kyra/saveload_mr.cpp



diff --git a/backends/platform/ds/arm9/source/gbampsave.cpp b/backends/platform/ds/arm9/source/gbampsave.cpp
index ef6091e..236ec55 100644
--- a/backends/platform/ds/arm9/source/gbampsave.cpp
+++ b/backends/platform/ds/arm9/source/gbampsave.cpp
@@ -56,7 +56,7 @@ Common::OutSaveFile *GBAMPSaveFileManager::openForSaving(const Common::String &f
 	Common::WriteStream *stream = DS::DSFileStream::makeFromPath(fileSpec, true);
 	// Use a write buffer
 	stream = Common::wrapBufferedWriteStream(stream, SAVE_BUFFER_SIZE);
-	return stream;
+	return new OutSaveFile(stream);
 }
 
 Common::InSaveFile *GBAMPSaveFileManager::openForLoading(const Common::String &filename) {
diff --git a/backends/platform/n64/framfs_save_manager.h b/backends/platform/n64/framfs_save_manager.h
index 9bd4ee5..aa26942 100644
--- a/backends/platform/n64/framfs_save_manager.h
+++ b/backends/platform/n64/framfs_save_manager.h
@@ -106,7 +106,7 @@ public:
 	virtual Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true) {
 		OutFRAMSave *s = new OutFRAMSave(filename.c_str());
 		if (!s->err()) {
-			return compress ? Common::wrapCompressedWriteStream(s) : s;
+			return new OutSaveFile(compress ? Common::wrapCompressedWriteStream(s) : s);
 		} else {
 			delete s;
 			return 0;
diff --git a/backends/platform/n64/pakfs_save_manager.h b/backends/platform/n64/pakfs_save_manager.h
index 0c08f0c..31aa014 100644
--- a/backends/platform/n64/pakfs_save_manager.h
+++ b/backends/platform/n64/pakfs_save_manager.h
@@ -108,7 +108,7 @@ public:
 	virtual Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true) {
 		OutPAKSave *s = new OutPAKSave(filename.c_str());
 		if (!s->err()) {
-			return compress ? Common::wrapCompressedWriteStream(s) : s;
+			return new OutSaveFile(compress ? Common::wrapCompressedWriteStream(s) : s);
 		} else {
 			delete s;
 			return NULL;
diff --git a/backends/platform/ps2/savefilemgr.cpp b/backends/platform/ps2/savefilemgr.cpp
index 4fd2b1c..569d0e1 100644
--- a/backends/platform/ps2/savefilemgr.cpp
+++ b/backends/platform/ps2/savefilemgr.cpp
@@ -192,7 +192,7 @@ Common::OutSaveFile *Ps2SaveFileManager::openForSaving(const Common::String &fil
 	}
 
 	_screen->wantAnim(false);
-	return compress ? Common::wrapCompressedWriteStream(sf) : sf;
+	return new OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf);
 }
 
 bool Ps2SaveFileManager::removeSavefile(const Common::String &filename) {
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 3fcb3cb..e20ce31 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -156,7 +156,7 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 
 	// Open the file for saving.
 	Common::WriteStream *const sf = fileNode.createWriteStream();
-	Common::OutSaveFile *const result = compress ? Common::wrapCompressedWriteStream(sf) : sf;
+	Common::OutSaveFile *const result = new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf);
 
 	// Add file to cache now that it exists.
 	_saveFileCache[filename] = Common::FSNode(fileNode.getPath());
diff --git a/backends/saves/savefile.cpp b/backends/saves/savefile.cpp
index b04c53d..1f007ca 100644
--- a/backends/saves/savefile.cpp
+++ b/backends/saves/savefile.cpp
@@ -23,9 +23,33 @@
 #include "common/util.h"
 #include "common/savefile.h"
 #include "common/str.h"
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#endif
 
 namespace Common {
 
+OutSaveFile::OutSaveFile(WriteStream *w): _wrapped(w) {}
+
+OutSaveFile::~OutSaveFile() {}
+
+bool OutSaveFile::err() const { return _wrapped->err(); }
+
+void OutSaveFile::clearErr() { _wrapped->clearErr(); }
+
+void OutSaveFile::finalize() {
+	_wrapped->finalize();
+#ifdef USE_CLOUD
+	CloudMan.syncSaves();
+#endif
+}
+
+bool OutSaveFile::flush() { return _wrapped->flush(); }
+
+uint32 OutSaveFile::write(const void *dataPtr, uint32 dataSize) {
+	return _wrapped->write(dataPtr, dataSize);
+}
+
 bool SaveFileManager::copySavefile(const String &oldFilename, const String &newFilename) {
 	InSaveFile *inFile = 0;
 	OutSaveFile *outFile = 0;
diff --git a/common/savefile.h b/common/savefile.h
index 38b21c9..d84c9e2 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -28,6 +28,7 @@
 #include "common/stream.h"
 #include "common/str-array.h"
 #include "common/error.h"
+#include "common/ptr.h"
 
 namespace Common {
 
@@ -44,8 +45,20 @@ typedef SeekableReadStream InSaveFile;
  * That typically means "save games", but also includes things like the
  * IQ points in Indy3.
  */
-typedef WriteStream OutSaveFile;
+class OutSaveFile: public WriteStream {
+protected:
+	ScopedPtr<WriteStream> _wrapped;
 
+public:
+	OutSaveFile(WriteStream *w);
+	virtual ~OutSaveFile();
+
+	virtual bool err() const;
+	virtual void clearErr();
+	virtual void finalize();
+	virtual bool flush();
+	virtual uint32 write(const void *dataPtr, uint32 dataSize);
+};
 
 /**
  * The SaveFileManager is serving as a factory for InSaveFile
diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp
index cca8f3a..6fdff0f 100644
--- a/engines/kyra/saveload_eob.cpp
+++ b/engines/kyra/saveload_eob.cpp
@@ -328,7 +328,7 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 		fileName = getSavegameFilename(slot);
 	}
 
-	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
+	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumbnail));
 	if (!out)
 		return _saveFileMan->getError();
 
@@ -995,7 +995,7 @@ bool EoBCoreEngine::saveAsOriginalSaveFile(int slot) {
 		return false;
 
 	Common::FSNode nf = nd.getChild(_flags.gameID == GI_EOB1 ? "EOBDATA.SAV" : Common::String::format("EOBDATA%d.SAV", slot));
-	Common::WriteStream *out = nf.createWriteStream();
+	Common::OutSaveFile *out = new Common::OutSaveFile(nf.createWriteStream());
 
 	if (_flags.gameID == GI_EOB2) {
 		static const char tempStr[20] = "SCUMMVM EXPORT     ";
diff --git a/engines/kyra/saveload_hof.cpp b/engines/kyra/saveload_hof.cpp
index e8e7614..60ceebd 100644
--- a/engines/kyra/saveload_hof.cpp
+++ b/engines/kyra/saveload_hof.cpp
@@ -34,7 +34,7 @@ namespace Kyra {
 Common::Error KyraEngine_HoF::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
+	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp
index 1d729d0..cb2124a 100644
--- a/engines/kyra/saveload_lok.cpp
+++ b/engines/kyra/saveload_lok.cpp
@@ -241,7 +241,7 @@ Common::Error KyraEngine_LoK::saveGameStateIntern(int slot, const char *saveName
 	if (shouldQuit())
 		return Common::kNoError;
 
-	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
+	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp
index e02b8fb..a5ecd3b 100644
--- a/engines/kyra/saveload_lol.cpp
+++ b/engines/kyra/saveload_lol.cpp
@@ -335,7 +335,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
 Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
+	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumbnail));
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_mr.cpp b/engines/kyra/saveload_mr.cpp
index a938003..3c225e6 100644
--- a/engines/kyra/saveload_mr.cpp
+++ b/engines/kyra/saveload_mr.cpp
@@ -33,7 +33,7 @@ namespace Kyra {
 Common::Error KyraEngine_MR::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
+	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/tsage/stP1kAlM b/engines/tsage/stP1kAlM
new file mode 100644
index 0000000..dfbb3b9
Binary files /dev/null and b/engines/tsage/stP1kAlM differ


Commit: 65e87c6c70fc1e5af8f0c3fb762ca13e6aa6a8e4
    https://github.com/scummvm/scummvm/commit/65e87c6c70fc1e5af8f0c3fb762ca13e6aa6a8e4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update save's timestamp on rewrite

This commit moves save/load timestamps static methods into
DefaultSaveFileManager and fixes a few related bugs.

Changed paths:
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index f059e29..4d18647 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -29,11 +29,10 @@
 #include "common/savefile.h"
 #include "common/system.h"
 #include "gui/saveload-dialog.h"
+#include <backends/saves/default/default-saves.h>
 
 namespace Cloud {
 
-const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps";
-
 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
 	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _boolCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -59,7 +58,7 @@ void SavesSyncRequest::start() {
 	_ignoreCallback = false;
 
 	//load timestamps
-	loadTimestamps();
+	_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
 
 	//list saves directory
 	Common::String dir = _storage->savesDirectoryPath();
@@ -90,25 +89,23 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 		StorageFile &file = remoteFiles[i];
 		if (file.isDirectory()) continue;
 		totalSize += file.size();
-		if (file.name() == TIMESTAMPS_FILENAME) continue;
+		if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME) continue;
 
 		Common::String name = file.name();
 		if (!_localFilesTimestamps.contains(name))
 			_filesToDownload.push_back(file);
 		else {
 			localFileNotAvailableInCloud[name] = false;
-
-			if (_localFilesTimestamps[name] != INVALID_TIMESTAMP) {
-				if (_localFilesTimestamps[name] == file.timestamp())
-					continue;
-
-				//we actually can have some files not only with timestamp < remote
-				//but also with timestamp > remote (when we have been using ANOTHER CLOUD and then switched back)
-				if (_localFilesTimestamps[name] < file.timestamp())
-					_filesToDownload.push_back(file);
-				else
-					_filesToUpload.push_back(file.name());
-			}
+			
+			if (_localFilesTimestamps[name] == file.timestamp())
+				continue;
+
+			//we actually can have some files not only with timestamp < remote
+			//but also with timestamp > remote (when we have been using ANOTHER CLOUD and then switched back)
+			if (_localFilesTimestamps[name] > file.timestamp() || _localFilesTimestamps[name] == DefaultSaveFileManager::INVALID_TIMESTAMP)
+				_filesToUpload.push_back(file.name());
+			else
+				_filesToDownload.push_back(file);
 		}
 	}
 
@@ -116,7 +113,7 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 
 	//upload files which are unavailable in cloud
 	for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
-		if (i->_key == TIMESTAMPS_FILENAME) continue;
+		if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME) continue;
 		if (i->_value) _filesToUpload.push_back(i->_key);
 	}
 
@@ -234,7 +231,7 @@ void SavesSyncRequest::downloadNextFile() {
 	///////
 	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	///////
-	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), concatWithSavesPath(_currentDownloadingFile.name()),
+	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
@@ -252,7 +249,9 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 	}
 
 	//update local timestamp for downloaded file
+	_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
 	_localFilesTimestamps[_currentDownloadingFile.name()] = _currentDownloadingFile.timestamp();
+	DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
 
 	//continue downloading files
 	downloadNextFile();
@@ -290,7 +289,9 @@ void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
 	if (_ignoreCallback) return;
 	
 	//update local timestamp for the uploaded file
+	_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
 	_localFilesTimestamps[_currentUploadingFile] = response.value.timestamp();
+	DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
 
 	//continue uploading files
 	uploadNextFile();
@@ -342,112 +343,16 @@ Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
 void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	debug("SavesSync::finishError");
 
-	//save updated timestamps (even if Request failed, there would be only valid timestamps)
-	saveTimestamps();
-
 	Request::finishError(error);
 }
 
 void SavesSyncRequest::finishSuccess(bool success) {
 	Request::finishSuccess();
 
-	//save updated timestamps (even if Request failed, there would be only valid timestamps)
-	saveTimestamps();
-
 	//update last successful sync date
 	CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
 
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
-void SavesSyncRequest::loadTimestamps() {
-	//refresh the files list
-	Common::Array<Common::String> files;
-	g_system->getSavefileManager()->updateSavefilesList(files);
-
-	//start with listing all the files in saves/ directory and setting invalid timestamp to them	
-	Common::StringArray localFiles = g_system->getSavefileManager()->listSavefiles("*");
-	for (uint32 i = 0; i < localFiles.size(); ++i)
-		_localFilesTimestamps[localFiles[i]] = INVALID_TIMESTAMP;
-
-	//now actually load timestamps from file
-	Common::InSaveFile *file = g_system->getSavefileManager()->openRawFile(TIMESTAMPS_FILENAME);
-	if (!file) {
-		warning("SavesSyncRequest: failed to open '%s' file to load timestamps", TIMESTAMPS_FILENAME);
-		return;
-	}
-	
-	while (!file->eos()) {
-		//read filename into buffer (reading until the first ' ')
-		Common::String buffer;
-		while (!file->eos()) {
-			byte b = file->readByte();
-			if (b == ' ') break;
-			buffer += (char)b;
-		}
-		
-		//read timestamp info buffer (reading until ' ' or some line ending char)
-		Common::String filename = buffer;
-		bool lineEnded = false;
-		buffer = "";
-		while (!file->eos()) {
-			byte b = file->readByte();
-			if (b == ' ' || b == '\n' || b == '\r') {
-				lineEnded = (b == '\n');
-				break;
-			}
-			buffer += (char)b;
-		}
-
-		//parse timestamp
-		uint timestamp = atol(buffer.c_str());
-		if (buffer == "" || timestamp == 0) break;
-		_localFilesTimestamps[filename] = timestamp;
-
-		//read until the end of the line
-		if (!lineEnded) {
-			while (!file->eos()) {
-				byte b = file->readByte();
-				if (b == '\n') break;
-			}
-		}
-	}
-	
-	delete file;
-}
-
-void SavesSyncRequest::saveTimestamps() {
-	Common::DumpFile f;	
-	Common::String filename = concatWithSavesPath(TIMESTAMPS_FILENAME);
-	if (!f.open(filename, true)) {
-		warning("SavesSyncRequest: failed to open '%s' file to save timestamps", filename.c_str());
-		return;
-	}
-	
-	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
-		Common::String data = i->_key + Common::String::format(" %u\n", i->_value);
-		if (f.write(data.c_str(), data.size()) != data.size()) {
-			warning("SavesSyncRequest: failed to write timestamps data into '%s'", filename.c_str());
-			return;
-		}
-	}
-
-	f.close();
-}
-
-Common::String SavesSyncRequest::concatWithSavesPath(Common::String name) {
-	Common::String path = ConfMan.get("savepath");
-	if (path.size() > 0 && (path.lastChar() == '/' || path.lastChar() == '\\'))
-		return path + name;
-
-	//simple heuristic to determine which path separator to use
-	int backslashes = 0;
-	for (uint32 i = 0; i < path.size(); ++i)
-		if (path[i] == '/') --backslashes;
-		else if (path[i] == '\\') ++backslashes;
-
-	if (backslashes) return path + '\\' + name;
-	return path + '/' + name;
-}
-
 } // End of namespace Cloud
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 105d7f7..19e67d9 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -28,14 +28,10 @@
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 #include "gui/object.h"
-#include <limits.h>
 
 namespace Cloud {
 
 class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
-	const uint32 INVALID_TIMESTAMP = UINT_MAX;
-	static const char *TIMESTAMPS_FILENAME;
-
 	Storage *_storage;
 	Storage::BoolCallback _boolCallback;
 	Common::HashMap<Common::String, uint32> _localFilesTimestamps;
@@ -61,9 +57,7 @@ class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
 	void uploadNextFile();
 	virtual void finishError(Networking::ErrorResponse error);
 	void finishSuccess(bool success);
-	void loadTimestamps();
-	void saveTimestamps();
-	Common::String concatWithSavesPath(Common::String name);
+	
 public:
 	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb);
 	virtual ~SavesSyncRequest();
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index e20ce31..54dc1c2 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -29,6 +29,7 @@
 
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
+#include "common/file.h"
 #endif
 
 #if !defined(DISABLE_DEFAULT_SAVEFILEMANAGER)
@@ -46,6 +47,10 @@
 #include <errno.h>	// for removeSavefile()
 #endif
 
+#ifdef USE_CLOUD
+const char *DefaultSaveFileManager::TIMESTAMPS_FILENAME = "timestamps";
+#endif
+
 DefaultSaveFileManager::DefaultSaveFileManager() {
 }
 
@@ -142,6 +147,13 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 		}
 	}
 
+#ifdef USE_CLOUD
+	// Update file's timestamp
+	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
+	timestamps[filename] = INVALID_TIMESTAMP;
+	saveTimestamps(timestamps);
+#endif
+
 	// Obtain node.
 	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
 	Common::FSNode fileNode;
@@ -266,4 +278,101 @@ void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
 	_cachedDirectory = savePathName;
 }
 
+#ifdef USE_CLOUD
+
+Common::HashMap<Common::String, uint32> DefaultSaveFileManager::loadTimestamps() {
+	Common::HashMap<Common::String, uint32> timestamps;
+
+	//refresh the files list
+	Common::Array<Common::String> files;
+	g_system->getSavefileManager()->updateSavefilesList(files);
+
+	//start with listing all the files in saves/ directory and setting invalid timestamp to them	
+	Common::StringArray localFiles = g_system->getSavefileManager()->listSavefiles("*");
+	for (uint32 i = 0; i < localFiles.size(); ++i)
+		timestamps[localFiles[i]] = INVALID_TIMESTAMP;
+
+	//now actually load timestamps from file
+	Common::InSaveFile *file = g_system->getSavefileManager()->openRawFile(TIMESTAMPS_FILENAME);
+	if (!file) {
+		warning("DefaultSaveFileManager: failed to open '%s' file to load timestamps", TIMESTAMPS_FILENAME);
+		return timestamps;
+	}
+
+	while (!file->eos()) {
+		//read filename into buffer (reading until the first ' ')
+		Common::String buffer;
+		while (!file->eos()) {
+			byte b = file->readByte();
+			if (b == ' ') break;
+			buffer += (char)b;
+		}
+
+		//read timestamp info buffer (reading until ' ' or some line ending char)
+		Common::String filename = buffer;
+		while (true) {
+			bool lineEnded = false;
+			buffer = "";
+			while (!file->eos()) {
+				byte b = file->readByte();
+				if (b == ' ' || b == '\n' || b == '\r') {
+					lineEnded = (b == '\n');
+					break;
+				}
+				buffer += (char)b;
+			}
+
+			if (buffer == "" && file->eos()) break;
+			if (!lineEnded) filename += " " + buffer;
+			else break;
+		}
+
+		//parse timestamp
+		uint32 timestamp = buffer.asUint64();
+		if (buffer == "" || timestamp == 0) break;		
+		timestamps[filename] = timestamp;
+	}
+
+	delete file;
+	return timestamps;
+}
+
+void DefaultSaveFileManager::saveTimestamps(Common::HashMap<Common::String, uint32> &timestamps) {
+	Common::DumpFile f;
+	Common::String filename = concatWithSavesPath(TIMESTAMPS_FILENAME);
+	if (!f.open(filename, true)) {
+		warning("DefaultSaveFileManager: failed to open '%s' file to save timestamps", filename.c_str());
+		return;
+	}
+
+	for (Common::HashMap<Common::String, uint32>::iterator i = timestamps.begin(); i != timestamps.end(); ++i) {
+		Common::String data = i->_key + Common::String::format(" %u\n", i->_value);
+		if (f.write(data.c_str(), data.size()) != data.size()) {
+			warning("DefaultSaveFileManager: failed to write timestamps data into '%s'", filename.c_str());
+			return;
+		}
+	}
+
+	f.flush();
+	f.finalize();
+	f.close();
+}
+
+Common::String DefaultSaveFileManager::concatWithSavesPath(Common::String name) {
+	Common::String path = ConfMan.get("savepath");
+	if (path.size() > 0 && (path.lastChar() == '/' || path.lastChar() == '\\'))
+		return path + name;
+
+	//simple heuristic to determine which path separator to use
+	int backslashes = 0;
+	for (uint32 i = 0; i < path.size(); ++i)
+		if (path[i] == '/') --backslashes;
+		else if (path[i] == '\\') ++backslashes;
+
+		if (backslashes) return path + '\\' + name;
+		return path + '/' + name;
+}
+
+#endif // ifdef USE_CLOUD
+
 #endif // !defined(DISABLE_DEFAULT_SAVEFILEMANAGER)
diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h
index af30cf4..e9edfb1 100644
--- a/backends/saves/default/default-saves.h
+++ b/backends/saves/default/default-saves.h
@@ -27,7 +27,8 @@
 #include "common/savefile.h"
 #include "common/str.h"
 #include "common/fs.h"
-#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include <limits.h>
 
 /**
  * Provides a default savefile manager implementation for common platforms.
@@ -44,6 +45,17 @@ public:
 	virtual Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true);
 	virtual bool removeSavefile(const Common::String &filename);
 
+#ifdef USE_CLOUD
+
+	static const uint32 INVALID_TIMESTAMP = UINT_MAX;
+	static const char *TIMESTAMPS_FILENAME;
+
+	static Common::HashMap<Common::String, uint32> loadTimestamps();
+	static void saveTimestamps(Common::HashMap<Common::String, uint32> &timestamps);
+	static Common::String concatWithSavesPath(Common::String name);
+
+#endif
+
 protected:
 	/**
 	 * Get the path to the savegame directory.


Commit: 2d3cfffa84f80578dfc0bd3c72b83587f9b8dae5
    https://github.com/scummvm/scummvm/commit/2d3cfffa84f80578dfc0bd3c72b83587f9b8dae5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
KYRA: Fix openSaveForWriting() to return OutSaveFile

Changed paths:
    engines/kyra/kyra_v1.h
    engines/kyra/saveload.cpp
    engines/kyra/saveload_eob.cpp
    engines/kyra/saveload_hof.cpp
    engines/kyra/saveload_lok.cpp
    engines/kyra/saveload_lol.cpp
    engines/kyra/saveload_mr.cpp



diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index c801829..4de7494 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -38,6 +38,7 @@
 #include "kyra/item.h"
 
 namespace Common {
+class OutSaveFile;
 class SeekableReadStream;
 class WriteStream;
 } // End of namespace Common
@@ -423,7 +424,7 @@ protected:
 	virtual Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
 
 	Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header, bool checkID = true);
-	Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const;
+	Common::OutSaveFile *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const;
 
 	// TODO: Consider moving this to Screen
 	virtual Graphics::Surface *generateSaveThumbnail() const { return 0; }
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
index 2ae6420..81ea796 100644
--- a/engines/kyra/saveload.cpp
+++ b/engines/kyra/saveload.cpp
@@ -184,7 +184,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena
 	return in;
 }
 
-Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const {
+Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const {
 	if (shouldQuit())
 		return 0;
 
@@ -226,7 +226,7 @@ Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, con
 		delete genThumbnail;
 	}
 
-	return out;
+	return new Common::OutSaveFile(out);
 }
 
 const char *KyraEngine_v1::getSavegameFilename(int num) {
diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp
index 6fdff0f..9329d14 100644
--- a/engines/kyra/saveload_eob.cpp
+++ b/engines/kyra/saveload_eob.cpp
@@ -328,7 +328,7 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 		fileName = getSavegameFilename(slot);
 	}
 
-	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumbnail));
+	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_hof.cpp b/engines/kyra/saveload_hof.cpp
index 60ceebd..e8e7614 100644
--- a/engines/kyra/saveload_hof.cpp
+++ b/engines/kyra/saveload_hof.cpp
@@ -34,7 +34,7 @@ namespace Kyra {
 Common::Error KyraEngine_HoF::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
+	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp
index cb2124a..1d729d0 100644
--- a/engines/kyra/saveload_lok.cpp
+++ b/engines/kyra/saveload_lok.cpp
@@ -241,7 +241,7 @@ Common::Error KyraEngine_LoK::saveGameStateIntern(int slot, const char *saveName
 	if (shouldQuit())
 		return Common::kNoError;
 
-	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
+	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp
index a5ecd3b..e02b8fb 100644
--- a/engines/kyra/saveload_lol.cpp
+++ b/engines/kyra/saveload_lol.cpp
@@ -335,7 +335,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
 Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumbnail));
+	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
 	if (!out)
 		return _saveFileMan->getError();
 
diff --git a/engines/kyra/saveload_mr.cpp b/engines/kyra/saveload_mr.cpp
index 3c225e6..a938003 100644
--- a/engines/kyra/saveload_mr.cpp
+++ b/engines/kyra/saveload_mr.cpp
@@ -33,7 +33,7 @@ namespace Kyra {
 Common::Error KyraEngine_MR::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
 	const char *fileName = getSavegameFilename(slot);
 
-	Common::OutSaveFile *out = new Common::OutSaveFile(openSaveForWriting(fileName, saveName, thumb));
+	Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
 	if (!out)
 		return _saveFileMan->getError();
 


Commit: 4c381dafa3aff9feeb80e1be3272eba8ca73d442
    https://github.com/scummvm/scummvm/commit/4c381dafa3aff9feeb80e1be3272eba8ca73d442
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Delete the incomplete file (when downloading)

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/savessyncrequest.cpp



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 307ea00..497509b 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -118,4 +118,9 @@ void DownloadRequest::finishSuccess(bool success) {
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
+void DownloadRequest::finishError(Networking::ErrorResponse error) {
+	if (_localFile) _localFile->close();
+	Request::finishError(error);
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index def69d4..ff7820e 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -43,6 +43,8 @@ class DownloadRequest: public Networking::Request {
 	void streamCallback(Networking::NetworkReadStreamResponse response);
 	void streamErrorCallback(Networking::ErrorResponse error);
 	void finishSuccess(bool success);
+	virtual void finishError(Networking::ErrorResponse error);
+
 public:
 	DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFileId, Common::DumpFile *dumpFile);
 	virtual ~DownloadRequest();
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 4d18647..c436253 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -244,6 +244,8 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 
 	//stop syncing if download failed
 	if (!response.value) {
+		//delete the incomplete file
+		g_system->getSavefileManager()->removeSavefile(_currentDownloadingFile.name());
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
 		return;
 	}
@@ -342,7 +344,20 @@ Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
 
 void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	debug("SavesSync::finishError");
-
+	//if we were downloading a file - remember the name
+	//and make the Request close() it, so we can delete it
+	Common::String name = _currentDownloadingFile.name();	
+	if (_workingRequest) {
+		_ignoreCallback = true;
+		_workingRequest->finish();
+		_workingRequest = nullptr;
+		_ignoreCallback = false;
+	}
+	//unlock all the files by making getFilesToDownload() return empty array
+	_currentDownloadingFile = StorageFile();
+	_filesToDownload.clear();
+	//delete the incomplete file
+	if (name != "") g_system->getSavefileManager()->removeSavefile(name);
 	Request::finishError(error);
 }
 


Commit: cff183536b62c84b6de8c83c3f249466ae3e75d6
    https://github.com/scummvm/scummvm/commit/cff183536b62c84b6de8c83c3f249466ae3e75d6
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix crash on exiting ScummVM while ConnMan is active

Changed paths:
    backends/networking/curl/connectionmanager.cpp



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 5a1e3e9..f7c8e2c 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -43,6 +43,8 @@ ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(
 }
 
 ConnectionManager::~ConnectionManager() {
+	stopTimer();
+
 	//terminate all requests
 	_handleMutex.lock();
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {


Commit: fa3ea83165d6c378348f61f8daec85f805626dbe
    https://github.com/scummvm/scummvm/commit/fa3ea83165d6c378348f61f8daec85f805626dbe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix some warnings

Mostly on format string

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/networking/sdl_net/getclienthandler.cpp
    common/json.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 9652117..b9de6d4 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -109,10 +109,10 @@ void CloudManager::save() {
 		Common::String name = getStorageConfigName(i);
 		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, ConfMan.kCloudDomain);
 		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, ConfMan.kCloudDomain);
-		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes, ConfMan.kCloudDomain));
+		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), ConfMan.kCloudDomain);
 	}
 
-	ConfMan.set("current_storage", Common::String::format("%d", _currentStorageIndex, ConfMan.kCloudDomain));
+	ConfMan.set("current_storage", Common::String::format("%u", _currentStorageIndex), ConfMan.kCloudDomain);
 	if (_activeStorage)
 		_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 	ConfMan.flushToDisk();
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 3cf14d5..6d73e52 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -80,7 +80,7 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 	if (json) {
 		Common::JSONObject result = json->asObject();
 		if (!result.contains("access_token") || !result.contains("uid")) {
-			warning(json->stringify(true).c_str());
+			warning("%s", json->stringify(true).c_str());
 			warning("Bad response, no token/uid passed");
 		} else {
 			_token = result.getVal("access_token")->asString();
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index ce1fae2..b24e02e 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -189,7 +189,7 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 	uint32 oldPos = _contentsStream->pos();
 	if (oldPos != _serverReceivedBytes) {
 		if (!_contentsStream->seek(_serverReceivedBytes)) {
-			warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%u)", _serverReceivedBytes);
+			warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%lu)", _serverReceivedBytes);
 			finishError(Networking::ErrorResponse(this, false, true, "", -1));
 			return;
 		}
diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index f3d596e..0e73a54 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -107,7 +107,7 @@ void GetClientHandler::prepareHeaders() {
 	if (!_specialHeaders.contains("Content-Length") && _stream)
 		setHeader("Content-Length", Common::String::format("%u", _stream->size()));
 
-	_headers = Common::String::format("HTTP/1.1 %d %s\r\n", _responseCode, responseMessage(_responseCode));
+	_headers = Common::String::format("HTTP/1.1 %ld %s\r\n", _responseCode, responseMessage(_responseCode));
 	for (Common::HashMap<Common::String, Common::String>::iterator i = _specialHeaders.begin(); i != _specialHeaders.end(); ++i)
 		_headers += i->_key + ": " + i->_value + "\r\n";
 	_headers += "\r\n";
diff --git a/common/json.cpp b/common/json.cpp
index 5f63d3f..f95a44f 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -988,7 +988,7 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
 
 	case JSONType_IntegerNumber: {
 		char str[80];
-		sprintf(str, "%d", _integerValue);
+		sprintf(str, "%lld", _integerValue);
 		ret_string = str;		
 		break;
 	}


Commit: f3a392359be2f6d05bac107a5f7bd168c178e428
    https://github.com/scummvm/scummvm/commit/f3a392359be2f6d05bac107a5f7bd168c178e428
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix finishSuccess() warning

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
    backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
    backends/cloud/googledrive/googledrivedownloadrequest.cpp
    backends/cloud/googledrive/googledrivedownloadrequest.h
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
    backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectoryrequest.h
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.h
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/googledrive/googledriveuploadrequest.h
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/onedrive/onedriveuploadrequest.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 497509b..c95b8b8 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -101,7 +101,7 @@ void DownloadRequest::handle() {
 			//TODO: do something about it actually			
 		}
 
-		finishSuccess(_remoteFileStream->httpResponseCode() == 200);
+		finishDownload(_remoteFileStream->httpResponseCode() == 200);
 
 		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()		
 	}
@@ -113,7 +113,7 @@ void DownloadRequest::restart() {
 	//start();
 }
 
-void DownloadRequest::finishSuccess(bool success) {
+void DownloadRequest::finishDownload(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index ff7820e..d8e18f9 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -42,7 +42,7 @@ class DownloadRequest: public Networking::Request {
 	void start();
 	void streamCallback(Networking::NetworkReadStreamResponse response);
 	void streamErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishDownload(bool success);
 	virtual void finishError(Networking::ErrorResponse error);
 
 public:
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 61cc9dd..a52d41e 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -82,7 +82,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 	}
 	
 	Common::JSONObject info = json->asObject();
-	if (info.contains("id")) finishSuccess(true);
+	if (info.contains("id")) finishCreation(true);
 	else {
 		error.response = json->stringify(true);
 		finishError(error);
@@ -104,7 +104,7 @@ void DropboxCreateDirectoryRequest::restart() { start(); }
 
 Common::String DropboxCreateDirectoryRequest::date() const { return _date; }
 
-void DropboxCreateDirectoryRequest::finishSuccess(bool success) {
+void DropboxCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
index e8599c7..4bdc6db 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
@@ -41,7 +41,7 @@ class DropboxCreateDirectoryRequest: public Networking::Request {
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishCreation(bool success);
 public:
 	DropboxCreateDirectoryRequest(Common::String token, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
 	virtual ~DropboxCreateDirectoryRequest();
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 933ea2b..dcbca31 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -125,7 +125,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 
 			_workingRequest = ConnMan.addRequest(request);
 		} else {			
-			finishSuccess(_files);
+			finishListing(_files);
 		}		
 	} else {
 		warning("null, not json");
@@ -149,7 +149,7 @@ void DropboxListDirectoryRequest::restart() { start(); }
 
 Common::String DropboxListDirectoryRequest::date() const { return _date; }
 
-void DropboxListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {	
+void DropboxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {	
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 0d96edd..62dde71 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -45,7 +45,7 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
-	void finishSuccess(Common::Array<StorageFile> &files);
+	void finishListing(Common::Array<StorageFile> &files);
 public:
 	DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
 	virtual ~DropboxListDirectoryRequest();
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index f1fb818..e530502 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -148,7 +148,7 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 				Common::String path = object.getVal("path_lower")->asString();
 				uint32 size = object.getVal("size")->asIntegerNumber();
 				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString());
-				finishSuccess(StorageFile(path, size, timestamp, false));
+				finishUpload(StorageFile(path, size, timestamp, false));
 				return;
 			}
 
@@ -163,7 +163,7 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 
 		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {			
 			warning("no file info to return");
-			finishSuccess(StorageFile(_savePath, 0, 0, false));
+			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
@@ -186,7 +186,7 @@ void DropboxUploadRequest::handle() {}
 
 void DropboxUploadRequest::restart() { start(); }
 
-void DropboxUploadRequest::finishSuccess(StorageFile file) {
+void DropboxUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
 	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
 }
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
index a85d7ef..8d9a3e2 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.h
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -44,7 +44,7 @@ class DropboxUploadRequest: public Networking::Request {
 	void uploadNextPart();
 	void partUploadedCallback(Networking::JsonResponse response);
 	void partUploadedErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(StorageFile status);
+	void finishUpload(StorageFile status);
 
 public:
 	DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 905b0c7..83296c3 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -83,7 +83,7 @@ void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorRespons
 void FolderDownloadRequest::downloadNextFile() {
 	do {
 		if (_files.empty()) {
-			finishSuccess(_failedFiles);
+			finishDownload(_failedFiles);
 			return;
 		}
 	
@@ -120,7 +120,7 @@ void FolderDownloadRequest::handle() {}
 
 void FolderDownloadRequest::restart() { start(); }
 
-void FolderDownloadRequest::finishSuccess(Common::Array<StorageFile> &files) {
+void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 8fa3b11..bf55567 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -45,7 +45,7 @@ class FolderDownloadRequest: public Networking::Request {
 	void fileDownloadedCallback(Storage::BoolResponse response);
 	void fileDownloadedErrorCallback(Networking::ErrorResponse error);
 	void downloadNextFile();
-	void finishSuccess(Common::Array<StorageFile> &files);
+	void finishDownload(Common::Array<StorageFile> &files);
 public:
 	FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive);
 	virtual ~FolderDownloadRequest();
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
index 2b7a805..9e339fd 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
@@ -90,7 +90,7 @@ void GoogleDriveCreateDirectoryRequest::idResolvedCallback(Storage::UploadRespon
 	if (response.request) _date = response.request->date();
 
 	//resolved => folder already exists
-	finishSuccess(false);
+	finishCreation(false);
 }
 
 void GoogleDriveCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
@@ -121,7 +121,7 @@ void GoogleDriveCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolRe
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	if (response.request) _date = response.request->date();
-	finishSuccess(response.value);
+	finishCreation(response.value);
 }
 
 void GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
@@ -137,7 +137,7 @@ void GoogleDriveCreateDirectoryRequest::restart() { start(); }
 
 Common::String GoogleDriveCreateDirectoryRequest::date() const { return _date; }
 
-void GoogleDriveCreateDirectoryRequest::finishSuccess(bool success) {
+void GoogleDriveCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
index f71afeb..7a6ffac 100644
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
@@ -49,7 +49,7 @@ class GoogleDriveCreateDirectoryRequest: public Networking::Request {
 	void idResolveFailedCallback(Networking::ErrorResponse error);
 	void createdDirectoryCallback(Storage::BoolResponse response);
 	void createdDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishCreation(bool success);
 public:
 	GoogleDriveCreateDirectoryRequest(GoogleDriveStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
 	virtual ~GoogleDriveCreateDirectoryRequest();
diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.cpp b/backends/cloud/googledrive/googledrivedownloadrequest.cpp
index c885352..df28c8b 100644
--- a/backends/cloud/googledrive/googledrivedownloadrequest.cpp
+++ b/backends/cloud/googledrive/googledrivedownloadrequest.cpp
@@ -69,7 +69,7 @@ void GoogleDriveDownloadRequest::idResolveFailedCallback(Networking::ErrorRespon
 void GoogleDriveDownloadRequest::downloadCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	finishSuccess(response.value);
+	finishDownload(response.value);
 }
 
 void GoogleDriveDownloadRequest::downloadErrorCallback(Networking::ErrorResponse error) {
@@ -82,7 +82,7 @@ void GoogleDriveDownloadRequest::handle() {}
 
 void GoogleDriveDownloadRequest::restart() { start(); }
 
-void GoogleDriveDownloadRequest::finishSuccess(bool success) {
+void GoogleDriveDownloadRequest::finishDownload(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.h b/backends/cloud/googledrive/googledrivedownloadrequest.h
index 89bd313..202a393 100644
--- a/backends/cloud/googledrive/googledrivedownloadrequest.h
+++ b/backends/cloud/googledrive/googledrivedownloadrequest.h
@@ -44,7 +44,7 @@ class GoogleDriveDownloadRequest: public Networking::Request {
 	void idResolveFailedCallback(Networking::ErrorResponse error);
 	void downloadCallback(Storage::BoolResponse response);
 	void downloadErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishDownload(bool success);
 public:
 	GoogleDriveDownloadRequest(GoogleDriveStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
 	virtual ~GoogleDriveDownloadRequest();
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 2530bab..582f67c 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -118,7 +118,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 			Common::String token = responseObject.getVal("nextPageToken")->asString();
 			makeRequest(token);
 		} else {			
-			finishSuccess(_files);
+			finishListing(_files);
 		}		
 	} else {
 		warning("null, not json");
@@ -142,7 +142,7 @@ void GoogleDriveListDirectoryByIdRequest::restart() { start(); }
 
 Common::String GoogleDriveListDirectoryByIdRequest::date() const { return _date; }
 
-void GoogleDriveListDirectoryByIdRequest::finishSuccess(Common::Array<StorageFile> &files) {	
+void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {	
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
index ceb533e..ecde880 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
@@ -47,7 +47,7 @@ class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
 	void makeRequest(Common::String pageToken);
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
-	void finishSuccess(Common::Array<StorageFile> &files);
+	void finishListing(Common::Array<StorageFile> &files);
 public:
 	GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb);
 	virtual ~GoogleDriveListDirectoryByIdRequest();
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
index 3387b43..f645041 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
@@ -75,7 +75,7 @@ void GoogleDriveListDirectoryRequest::idResolveErrorCallback(Networking::ErrorRe
 
 void GoogleDriveListDirectoryRequest::listNextDirectory() {
 	if (_directoriesQueue.empty()) {
-		finishSuccess(_files);
+		finishListing(_files);
 		return;
 	}
 
@@ -120,7 +120,7 @@ void GoogleDriveListDirectoryRequest::restart() { start(); }
 
 Common::String GoogleDriveListDirectoryRequest::date() const { return _date; }
 
-void GoogleDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
+void GoogleDriveListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
index b3d8ff6..d76338b 100644
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
@@ -51,7 +51,7 @@ class GoogleDriveListDirectoryRequest: public Networking::Request {
 	void listNextDirectory();
 	void listedDirectoryCallback(Storage::FileArrayResponse response);
 	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(Common::Array<StorageFile> &files);
+	void finishListing(Common::Array<StorageFile> &files);
 public:
 	GoogleDriveListDirectoryRequest(GoogleDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
 	virtual ~GoogleDriveListDirectoryRequest();
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 3fae830..3dfb843 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -56,10 +56,10 @@ void GoogleDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
 	retry(0);
 }
 
-void GoogleDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
+void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishSuccess(nullptr);
+		CurlJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -101,7 +101,7 @@ void GoogleDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishSuccess(json);
+	CurlJsonRequest::finishJson(json);
 }
 
 void GoogleDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h
index 3dcb56b..3bc5fbd 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.h
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.h
@@ -37,7 +37,7 @@ class GoogleDriveTokenRefresher: public Networking::CurlJsonRequest {
 	
 	void tokenRefreshed(Storage::BoolResponse response);
 
-	virtual void finishSuccess(Common::JSONValue *json);
+	virtual void finishJson(Common::JSONValue *json);
 public:	
 	GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~GoogleDriveTokenRefresher();
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index b24e02e..d9ba281 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -279,14 +279,14 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 					timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
 
 				//as we list directory by id, we can't determine full path for the file, so we leave it empty
-				finishSuccess(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
+				finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
 				return;
 			}
 		}
 
 		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
 			warning("no file info to return");
-			finishSuccess(StorageFile(_savePath, 0, 0, false));
+			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
@@ -320,7 +320,7 @@ void GoogleDriveUploadRequest::handle() {}
 
 void GoogleDriveUploadRequest::restart() { start(); }
 
-void GoogleDriveUploadRequest::finishSuccess(StorageFile file) {
+void GoogleDriveUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
 	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
 }
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.h b/backends/cloud/googledrive/googledriveuploadrequest.h
index e417403..8cc4079 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.h
+++ b/backends/cloud/googledrive/googledriveuploadrequest.h
@@ -54,7 +54,7 @@ class GoogleDriveUploadRequest: public Networking::Request {
 	void partUploadedCallback(Networking::JsonResponse response);
 	void partUploadedErrorCallback(Networking::ErrorResponse error);
 	bool handleHttp308(const Networking::NetworkReadStream *stream);
-	void finishSuccess(StorageFile status);
+	void finishUpload(StorageFile status);
 
 public:
 	GoogleDriveUploadRequest(GoogleDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index 2c644c4..6173465 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -101,7 +101,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 	}
 	
 	Common::JSONObject info = json->asObject();
-	if (info.contains("id")) finishSuccess(true);
+	if (info.contains("id")) finishCreation(true);
 	else {
 		error.response = json->stringify(true);
 		finishError(error);
@@ -123,7 +123,7 @@ void OneDriveCreateDirectoryRequest::restart() { start(); }
 
 Common::String OneDriveCreateDirectoryRequest::date() const { return _date; }
 
-void OneDriveCreateDirectoryRequest::finishSuccess(bool success) {
+void OneDriveCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
index 880e94e..83c72c4 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -43,7 +43,7 @@ class OneDriveCreateDirectoryRequest: public Networking::Request {
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishCreation(bool success);
 public:
 	OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
 	virtual ~OneDriveCreateDirectoryRequest();
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index be6fcb7..baccdf4 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -60,7 +60,7 @@ void OneDriveListDirectoryRequest::start() {
 
 void OneDriveListDirectoryRequest::listNextDirectory() {
 	if (_directoriesQueue.empty()) {
-		finishSuccess(_files);
+		finishListing(_files);
 		return;
 	}
 
@@ -150,7 +150,7 @@ void OneDriveListDirectoryRequest::restart() { start(); }
 
 Common::String OneDriveListDirectoryRequest::date() const { return _date; }
 
-void OneDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) {
+void OneDriveListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index 5e80f4f..eb510ab 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -50,7 +50,7 @@ class OneDriveListDirectoryRequest: public Networking::Request {
 	void listedDirectoryCallback(Networking::JsonResponse response);
 	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
 	void makeRequest(Common::String url);
-	void finishSuccess(Common::Array<StorageFile> &files);
+	void finishListing(Common::Array<StorageFile> &files);
 public:
 	OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
 	virtual ~OneDriveListDirectoryRequest();
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index bf849f7..04e155c 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -56,10 +56,10 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
 	retry(0);
 }
 
-void OneDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
+void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishSuccess(nullptr);
+		CurlJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -105,7 +105,7 @@ void OneDriveTokenRefresher::finishSuccess(Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishSuccess(json);
+	CurlJsonRequest::finishJson(json);
 }
 
 void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 04b0bf2..4be1fa7 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -37,7 +37,7 @@ class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
 	
 	void tokenRefreshed(Storage::BoolResponse response);
 
-	virtual void finishSuccess(Common::JSONValue *json);
+	virtual void finishJson(Common::JSONValue *json);
 public:	
 	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~OneDriveTokenRefresher();
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 08f4593..fb32443 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -126,7 +126,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 				Common::String path = _savePath; //object.getVal("name")->asString();; //object.getVal("id")->asString();
 				uint32 size = object.getVal("size")->asIntegerNumber();
 				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
-				finishSuccess(StorageFile(path, size, timestamp, false));
+				finishUpload(StorageFile(path, size, timestamp, false));
 				return;
 			}
 
@@ -140,7 +140,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 
 		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
 			warning("no file info to return");
-			finishSuccess(StorageFile(_savePath, 0, 0, false));
+			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
@@ -162,7 +162,7 @@ void OneDriveUploadRequest::handle() {}
 
 void OneDriveUploadRequest::restart() { start(); }
 
-void OneDriveUploadRequest::finishSuccess(StorageFile file) {
+void OneDriveUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
 	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
 }
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.h b/backends/cloud/onedrive/onedriveuploadrequest.h
index 09419d8..f613c41 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.h
+++ b/backends/cloud/onedrive/onedriveuploadrequest.h
@@ -45,7 +45,7 @@ class OneDriveUploadRequest: public Networking::Request {
 	void uploadNextPart();
 	void partUploadedCallback(Networking::JsonResponse response);
 	void partUploadedErrorCallback(Networking::ErrorResponse error);
-	void finishSuccess(StorageFile status);
+	void finishUpload(StorageFile status);
 
 public:
 	OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index c436253..e12f5af 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -269,7 +269,7 @@ void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse err
 
 void SavesSyncRequest::uploadNextFile() {
 	if (_filesToUpload.empty()) {
-		finishSuccess(true);
+		finishSync(true);
 		return;
 	}
 
@@ -361,7 +361,7 @@ void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	Request::finishError(error);
 }
 
-void SavesSyncRequest::finishSuccess(bool success) {
+void SavesSyncRequest::finishSync(bool success) {
 	Request::finishSuccess();
 
 	//update last successful sync date
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 19e67d9..d1a9d4a4 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -56,7 +56,7 @@ class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
 	void downloadNextFile();
 	void uploadNextFile();
 	virtual void finishError(Networking::ErrorResponse error);
-	void finishSuccess(bool success);
+	void finishSync(bool success);
 	
 public:
 	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb);
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 46d8865..3bfc823 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -71,10 +71,10 @@ void CurlJsonRequest::handle() {
 			char *contents = getPreparedContents();
 			Common::JSONValue *json = Common::JSON::parse(contents);
 			if (json) {
-				finishSuccess(json); //it's JSON even if's not 200 OK? That's fine!..
+				finishJson(json); //it's JSON even if's not 200 OK? That's fine!..
 			} else {
 				if (_stream->httpResponseCode() == 200) //no JSON, but 200 OK? That's fine!..
-					finishSuccess(nullptr);
+					finishJson(nullptr);
 				else
 					finishError(ErrorResponse(this, false, true, contents, _stream->httpResponseCode()));
 			}
@@ -89,7 +89,7 @@ void CurlJsonRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
-void CurlJsonRequest::finishSuccess(Common::JSONValue *json) {
+void CurlJsonRequest::finishJson(Common::JSONValue *json) {
 	Request::finishSuccess();
 	if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
 	else delete json;
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 83005a7..bd6f135 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -41,7 +41,7 @@ protected:
 	char *getPreparedContents();
 
 	/** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */
-	virtual void finishSuccess(Common::JSONValue *json);
+	virtual void finishJson(Common::JSONValue *json);
 
 public:
 	CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url);


Commit: acce1c89ab2b9b362fa9f2fc32ae813c62038705
    https://github.com/scummvm/scummvm/commit/acce1c89ab2b9b362fa9f2fc32ae813c62038705
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix saves sync

Tested that on actual unix system and found out a few minor bugs related
to paths.

Changed paths:
    backends/saves/default/default-saves.cpp
    common/file.cpp



diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 54dc1c2..06d4047 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -359,7 +359,8 @@ void DefaultSaveFileManager::saveTimestamps(Common::HashMap<Common::String, uint
 }
 
 Common::String DefaultSaveFileManager::concatWithSavesPath(Common::String name) {
-	Common::String path = ConfMan.get("savepath");
+	DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
+	Common::String path = (manager ? manager->getSavePath() : ConfMan.get("savepath"));
 	if (path.size() > 0 && (path.lastChar() == '/' || path.lastChar() == '\\'))
 		return path + name;
 
@@ -369,8 +370,8 @@ Common::String DefaultSaveFileManager::concatWithSavesPath(Common::String name)
 		if (path[i] == '/') --backslashes;
 		else if (path[i] == '\\') ++backslashes;
 
-		if (backslashes) return path + '\\' + name;
-		return path + '/' + name;
+	if (backslashes > 0) return path + '\\' + name;
+	return path + '/' + name;
 }
 
 #endif // ifdef USE_CLOUD
diff --git a/common/file.cpp b/common/file.cpp
index 52b66bd..5f3402e 100644
--- a/common/file.cpp
+++ b/common/file.cpp
@@ -160,6 +160,7 @@ bool DumpFile::open(const String &filename, bool createPath) {
 			if (filename[i] == '/' || filename[i] == '\\') {
 				Common::String subpath = filename;
 				subpath.erase(i);
+				if (subpath.empty()) continue;
 				AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(subpath);				
 				if (node->exists()) continue;				
 				if (!node->create(true)) warning("DumpFile: unable to create directories from path prefix");


Commit: 97c0bbd2388ac049970fc3c99ebdc072c75724f1
    https://github.com/scummvm/scummvm/commit/97c0bbd2388ac049970fc3c99ebdc072c75724f1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add DownloadDialog sketch

Changed paths:
  A gui/downloaddialog.cpp
  A gui/downloaddialog.h
    gui/module.mk
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
new file mode 100644
index 0000000..b449be5
--- /dev/null
+++ b/gui/downloaddialog.cpp
@@ -0,0 +1,88 @@
+/* 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/downloaddialog.h"
+#include "gui/widgets/list.h"
+#include "gui/widget.h"
+#include "gui/gui-manager.h"
+#include "backends/cloud/cloudmanager.h"
+#include "common/translation.h"
+#include "widgets/edittext.h"
+
+namespace GUI {
+
+enum {
+	kDownloadDialogButtonCmd = 'Dldb'	
+};
+
+DownloadDialog::DownloadDialog(uint32 storageId):
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _wasInProgress(true), _inProgress(false), _close(false) {
+	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
+
+	_messageText = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DialogDesc", _("Press the button to download a directory"));
+	_mainButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Start download"), 0, kDownloadDialogButtonCmd);
+	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
+	updateButtons();
+}
+
+void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kDownloadDialogButtonCmd: {
+		_inProgress = !_inProgress;
+		reflowLayout();
+		break;
+	}
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+void DownloadDialog::handleTickle() {
+	if (_close) {
+		setResult(1);
+		close();
+	}
+
+	Dialog::handleTickle();
+}
+
+void DownloadDialog::reflowLayout() {
+	Dialog::reflowLayout();
+	updateButtons();
+}
+
+void DownloadDialog::updateButtons() {
+	if (_wasInProgress == _inProgress) return;
+
+	if (_inProgress) {		
+		_messageText->setLabel(_("Press the button to cancel the download"));
+		_mainButton->setLabel(_("Cancel the download"));
+	} else {
+		_messageText->setLabel(_("Press the button to download a directory"));
+		_mainButton->setLabel(_("Start download"));
+	}	
+	
+	_wasInProgress = _inProgress;	
+}
+
+
+} // End of namespace GUI
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
new file mode 100644
index 0000000..333ce0e
--- /dev/null
+++ b/gui/downloaddialog.h
@@ -0,0 +1,56 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_DOWNLOADDIALOG_H
+#define GUI_DOWNLOADDIALOG_H
+
+#include "gui/dialog.h"
+#include "common/str.h"
+
+namespace GUI {
+
+class CommandSender;
+class EditTextWidget;
+class StaticTextWidget;
+class ButtonWidget;
+
+class DownloadDialog : public Dialog {	
+	StaticTextWidget *_messageText;
+	ButtonWidget *_mainButton;
+	ButtonWidget *_closeButton;
+
+	bool _wasInProgress, _inProgress;
+	bool _close;
+
+	void updateButtons();
+
+public:
+	DownloadDialog(uint32 storageId);
+
+	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void handleTickle();
+	virtual void reflowLayout();
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/module.mk b/gui/module.mk
index ef00531..2d71c45 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	console.o \
 	debugger.o \
 	dialog.o \
+	downloaddialog.o \
 	error.o \
 	EventRecorder.o \
 	filebrowser-dialog.o \
diff --git a/gui/options.cpp b/gui/options.cpp
index 2ab6b1e..1ea64ca 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -42,6 +42,7 @@
 #include "audio/musicplugin.h"
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
+#include "downloaddialog.h"
 
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
@@ -92,7 +93,8 @@ enum {
 #ifdef USE_CLOUD
 enum {
 	kConfigureStorageCmd = 'cfst',
-	kRefreshStorageCmd = 'rfst'
+	kRefreshStorageCmd = 'rfst',
+	kDownloadStorageCmd = 'dlst'
 };
 #endif
 
@@ -1292,6 +1294,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 
 	_storageConnectButton = new ButtonWidget(tab, "GlobalOptions_Cloud.ConnectButton", _("Connect"), _("Open wizard dialog to connect your cloud storage account"), kConfigureStorageCmd);
 	_storageRefreshButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RefreshButton", _("Refresh"), _("Refresh current cloud storage information (username and usage)"), kRefreshStorageCmd);
+	_storageDownloadButton = new ButtonWidget(tab, "GlobalOptions_Cloud.DownloadButton", _("Downloads"), _("Open downloads manager dialog"), kDownloadStorageCmd);
 
 	setupCloudTab();
 	_redrawCloudTab = false;
@@ -1598,6 +1601,12 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		CloudMan.listDirectory(dir, new Common::Callback<GlobalOptionsDialog, Cloud::Storage::ListDirectoryResponse>(this, &GlobalOptionsDialog::storageListDirectoryCallback), nullptr);
 		break;
 	}
+	case kDownloadStorageCmd:
+	{
+		DownloadDialog dialog(_selectedStorageIndex);
+		dialog.runModal();
+		break;
+	}
 #endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
@@ -1696,6 +1705,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	}
 	if (_storageConnectButton) _storageConnectButton->setVisible(shown);
 	if (_storageRefreshButton) _storageRefreshButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
+	if (_storageDownloadButton) _storageDownloadButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
 }
 
 void GlobalOptionsDialog::storageInfoCallback(Cloud::Storage::StorageInfoResponse response) {
diff --git a/gui/options.h b/gui/options.h
index 1454ddb..fa7a1d2 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -262,6 +262,7 @@ protected:
 	StaticTextWidget *_storageLastSync;
 	ButtonWidget	 *_storageConnectButton;
 	ButtonWidget	 *_storageRefreshButton;
+	ButtonWidget	 *_storageDownloadButton;
 	bool _redrawCloudTab;
 
 	void setupCloudTab();
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 5954196..f9ec30b 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -579,6 +579,27 @@
 				<widget name = 'RefreshButton'
 						type = 'Button'
 				/>
+				<widget name = 'DownloadButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 8' spacing = '8'>
+			<widget name = 'DialogDesc'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'MainButton'
+					type = 'Button'
+			/>
+			<space/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10'>
+				<space/>
+				<widget name = 'CloseButton'
+						type = 'Button'
+				/>
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index ad9a3b6..fe15dc2 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -576,6 +576,27 @@
 				<widget name = 'RefreshButton'
 						type = 'Button'
 				/>
+				<widget name = 'DownloadButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '8, 8, 8, 4' spacing = '8'>
+			<widget name = 'DialogDesc'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'MainButton'
+					type = 'Button'
+			/>
+			<space/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6'>
+				<space/>
+				<widget name = 'CloseButton'
+						type = 'Button'
+				/>
 			</layout>
 		</layout>
 	</dialog>


Commit: 72b82bd2aa66223f6c740e7bf6dce316b2145b15
    https://github.com/scummvm/scummvm/commit/72b82bd2aa66223f6c740e7bf6dce316b2145b15
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add RemoteBrowserDialog

WIP. Tested with Dropbox.

Changed paths:
  A gui/remotebrowser.cpp
  A gui/remotebrowser.h
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/module.mk



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index b449be5..e94b196 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -27,6 +27,9 @@
 #include "backends/cloud/cloudmanager.h"
 #include "common/translation.h"
 #include "widgets/edittext.h"
+#include "message.h"
+#include "browser.h"
+#include "remotebrowser.h"
 
 namespace GUI {
 
@@ -38,6 +41,9 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	Dialog("GlobalOptions_Cloud_DownloadDialog"), _wasInProgress(true), _inProgress(false), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
+	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
+	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"), true);
+
 	_messageText = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DialogDesc", _("Press the button to download a directory"));
 	_mainButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Start download"), 0, kDownloadDialogButtonCmd);
 	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
@@ -47,6 +53,10 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kDownloadDialogButtonCmd: {
+		if (!_inProgress) {
+			selectDirectories();
+		}
+
 		_inProgress = !_inProgress;
 		reflowLayout();
 		break;
@@ -56,6 +66,36 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
+void DownloadDialog::selectDirectories() {
+	//first user should select remote directory to download	
+	if (_remoteBrowser->runModal() <= 0) return;
+
+	/*
+	Common::FSNode dir(_browser->getResult());
+	Common::FSList files;
+	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
+		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
+		alert.runModal();
+		return;
+	}
+	*/
+
+	//now user should select local directory to download into
+	if (_browser->runModal() <= 0) return;
+
+	Common::FSNode dir(_browser->getResult());
+	Common::FSList files;
+	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
+		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
+		alert.runModal();
+		return;
+	}
+
+	//TODO: require empty directory?
+
+	//TODO: initiate download
+}
+
 void DownloadDialog::handleTickle() {
 	if (_close) {
 		setResult(1);
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 333ce0e..ca702ef 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -32,8 +32,13 @@ class CommandSender;
 class EditTextWidget;
 class StaticTextWidget;
 class ButtonWidget;
+class BrowserDialog;
+class RemoteBrowserDialog;
+
+class DownloadDialog : public Dialog {
+	BrowserDialog *_browser;
+	RemoteBrowserDialog *_remoteBrowser;
 
-class DownloadDialog : public Dialog {	
 	StaticTextWidget *_messageText;
 	ButtonWidget *_mainButton;
 	ButtonWidget *_closeButton;
@@ -42,6 +47,7 @@ class DownloadDialog : public Dialog {
 	bool _close;
 
 	void updateButtons();
+	void selectDirectories();
 
 public:
 	DownloadDialog(uint32 storageId);
diff --git a/gui/module.mk b/gui/module.mk
index 2d71c45..1fdd0d4 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -17,6 +17,7 @@ MODULE_OBJS := \
 	object.o \
 	options.o \
 	predictivedialog.o \
+	remotebrowser.o \
 	saveload.o \
 	saveload-dialog.o \
 	storagewizarddialog.o \
diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
new file mode 100644
index 0000000..37a51d5
--- /dev/null
+++ b/gui/remotebrowser.cpp
@@ -0,0 +1,220 @@
+/* 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/remotebrowser.h"
+#include "gui/widgets/list.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/algorithm.h"
+
+#include "common/translation.h"
+#include <backends/networking/curl/request.h>
+#include <backends/cloud/storage.h>
+#include <backends/cloud/cloudmanager.h>
+
+namespace GUI {
+
+enum {
+	kChooseCmd = 'Chos',
+	kGoUpCmd = 'GoUp',
+	kHiddenCmd = 'Hidd'
+};
+
+/* We want to use this as a general directory selector at some point... possible uses
+ * - to select the data dir for a game
+ * - to select the place where save games are stored
+ * - others???
+ */
+
+RemoteBrowserDialog::RemoteBrowserDialog(const char *title, bool dirRemoteBrowser)
+	: Dialog("Browser"), _navigationLocked(false), _updateList(false) {
+
+	_isDirRemoteBrowser = dirRemoteBrowser;
+	_fileList = NULL;
+	_currentPath = NULL;
+
+	// Headline - TODO: should be customizable during creation time
+	new StaticTextWidget(this, "Browser.Headline", title);
+
+	// Current path - TODO: handle long paths ?
+	_currentPath = new StaticTextWidget(this, "Browser.Path", "DUMMY");
+
+	// Add file list
+	_fileList = new ListWidget(this, "Browser.List");
+	_fileList->setNumberingMode(kListNumberingOff);
+	_fileList->setEditable(false);
+
+	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
+
+	// hide checkbox for the "show hidden files" state.
+	//_showHiddenWidget = new CheckboxWidget(this, "Browser.Hidden", _("Show hidden files"), _("Show files marked with the hidden attribute"), kHiddenCmd);
+
+	// Buttons
+	if (g_system->getOverlayWidth() > 320)
+		new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd);
+	else
+		new ButtonWidget(this, "Browser.Up", _c("Go up", "lowres"), _("Go to previous directory level"), kGoUpCmd);
+	new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd);
+	new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd);
+}
+
+void RemoteBrowserDialog::open() {	
+	Dialog::open();
+	listDirectory(Cloud::StorageFile());
+}
+
+void RemoteBrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kChooseCmd:
+		if (_isDirRemoteBrowser) {
+			// If nothing is selected in the list widget, choose the current dir.
+			// Else, choose the dir that is selected.
+			int selection = _fileList->getSelected();
+			if (selection >= 0)
+				_choice = _nodeContent[selection];
+			else
+				_choice = _node;
+			setResult(1);
+			close();
+		} else {
+			int selection = _fileList->getSelected();
+			if (selection < 0)
+				break;
+			if (_nodeContent[selection].isDirectory()) {
+				_node = _nodeContent[selection];
+				updateListing();
+			} else {
+				_choice = _nodeContent[selection];
+				setResult(1);
+				close();
+			}
+		}
+		break;
+	case kGoUpCmd:
+		goUp();
+		break;
+	case kListItemActivatedCmd:
+	case kListItemDoubleClickedCmd:
+		if (_nodeContent[data].isDirectory()) {
+			_node = _nodeContent[data];
+			listDirectory(_node);
+		} else if (!_isDirRemoteBrowser) { //TODO: ????
+			_choice = _nodeContent[data];
+			setResult(1);
+			close();
+		}
+		break;
+	case kListSelectionChangedCmd:
+		// We do not allow selecting directories in directory
+		// RemoteBrowser mode, thus we will invalidate the selection
+		// when the user selects an directory over here.
+		if (data != (uint32)-1 && _isDirRemoteBrowser && !_nodeContent[data].isDirectory())
+			_fileList->setSelected(-1);
+		break;
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+void RemoteBrowserDialog::handleTickle() {
+	if (_updateList) {
+		updateListing();
+		_updateList = false;
+	}
+
+	Dialog::handleTickle();
+}
+
+void RemoteBrowserDialog::updateListing() {
+	// Update the path display
+	Common::String path = _node.path();
+	if (path.empty()) path = "/"; //root
+	_currentPath->setLabel(path);
+
+	if (!_navigationLocked) {
+		// Populate the ListWidget
+		ListWidget::StringArray list;
+		ListWidget::ColorList colors;
+		for (Common::Array<Cloud::StorageFile>::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
+			if (i->isDirectory()) {
+				list.push_back(i->name() + "/");
+				colors.push_back(ThemeEngine::kFontColorNormal);
+			} else {
+				list.push_back(i->name());
+				colors.push_back(ThemeEngine::kFontColorAlternate);
+			}
+		}
+
+		_fileList->setList(list, &colors);
+		_fileList->scrollTo(0);
+	}
+
+	_fileList->setEnabled(!_navigationLocked);
+
+	// Finally, redraw
+	draw();
+}
+
+void RemoteBrowserDialog::goUp() {
+	Common::String path = _node.path();
+	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
+	if (path.empty()) {
+		draw();
+		return;
+	}
+	for (int i = path.size()-1; i >= 0; --i)
+		if (path[i] == '/' || path[i] == '\\') {
+			path.erase(i);
+			break;
+		}
+	listDirectory(Cloud::StorageFile(path, 0, 0, true));
+}
+
+void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
+	if (_navigationLocked) return;
+	_navigationLocked = true;
+
+	_workingRequest = CloudMan.listDirectory(
+		node.path(),
+		new Common::Callback<RemoteBrowserDialog, Cloud::Storage::ListDirectoryResponse>(this, &RemoteBrowserDialog::directoryListedCallback),
+		new Common::Callback<RemoteBrowserDialog, Networking::ErrorResponse>(this, &RemoteBrowserDialog::directoryListedErrorCallback),
+		false
+	);
+
+	_node = node;
+	updateListing();
+}
+
+void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryResponse response) {
+	_navigationLocked = false;
+	//TODO: list files from response
+	_nodeContent = response.value;
+	_updateList = true;
+}
+
+void RemoteBrowserDialog::directoryListedErrorCallback(Networking::ErrorResponse error) {
+	_navigationLocked = false;
+	//TODO: show error message
+}
+
+} // End of namespace GUI
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
new file mode 100644
index 0000000..55be119
--- /dev/null
+++ b/gui/remotebrowser.h
@@ -0,0 +1,71 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_REMOTEBROWSER_DIALOG_H
+#define GUI_REMOTEBROWSER_DIALOG_H
+
+#include "gui/dialog.h"
+#include "common/fs.h"
+#include <backends/cloud/storagefile.h>
+#include <backends/networking/curl/request.h>
+#include <backends/cloud/storage.h>
+
+namespace GUI {
+
+class ListWidget;
+class StaticTextWidget;
+class CheckboxWidget;
+class CommandSender;
+
+class RemoteBrowserDialog : public Dialog {
+public:
+	RemoteBrowserDialog(const char *title, bool dirRemoteBrowser);
+
+	virtual void open();
+	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void handleTickle();
+
+	const Cloud::StorageFile	&getResult() { return _choice; }
+
+protected:
+	ListWidget		*_fileList;
+	StaticTextWidget	*_currentPath;
+	Cloud::StorageFile _node;
+	Common::Array<Cloud::StorageFile> _nodeContent;
+	Cloud::StorageFile _choice;
+	bool _isDirRemoteBrowser;
+	bool _navigationLocked;
+	bool _updateList;
+
+	Networking::Request *_workingRequest;
+	bool _ignoreCallback; //?
+
+	void updateListing();
+	void goUp();
+	void listDirectory(Cloud::StorageFile node);
+	void directoryListedCallback(Cloud::Storage::ListDirectoryResponse response);
+	void directoryListedErrorCallback(Networking::ErrorResponse error);
+};
+
+} // End of namespace GUI
+
+#endif


Commit: e388accda3d4af43c4ae5f060719c4f31bc5cce5
    https://github.com/scummvm/scummvm/commit/e388accda3d4af43c4ae5f060719c4f31bc5cce5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix "Go up"

OneDrive and Google Drive paths do not start with '/', so one was unable
to go up to root.

Changed paths:
    gui/remotebrowser.cpp



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index 37a51d5..a1b0e3f 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -183,7 +183,7 @@ void RemoteBrowserDialog::goUp() {
 		return;
 	}
 	for (int i = path.size()-1; i >= 0; --i)
-		if (path[i] == '/' || path[i] == '\\') {
+		if (i == 0 || path[i] == '/' || path[i] == '\\') {
 			path.erase(i);
 			break;
 		}


Commit: 4aa8e23ea24e275f6b68fe6ed773ee2129d4de26
    https://github.com/scummvm/scummvm/commit/4aa8e23ea24e275f6b68fe6ed773ee2129d4de26
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Make RemoteBrowser show "Loading..."

Changed paths:
    gui/remotebrowser.cpp



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index a1b0e3f..ffa24ec 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -149,6 +149,7 @@ void RemoteBrowserDialog::updateListing() {
 	// Update the path display
 	Common::String path = _node.path();
 	if (path.empty()) path = "/"; //root
+	if (_navigationLocked) path = "Loading... " + path;
 	_currentPath->setLabel(path);
 
 	if (!_navigationLocked) {


Commit: 73bb2e20afdd625998a1438adf29e5d9dbaa2929
    https://github.com/scummvm/scummvm/commit/73bb2e20afdd625998a1438adf29e5d9dbaa2929
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Clean up in RemoteBrowser

Changed paths:
    gui/downloaddialog.cpp
    gui/remotebrowser.cpp
    gui/remotebrowser.h



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index e94b196..6e9410d 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -42,7 +42,7 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
-	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"), true);
+	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"));
 
 	_messageText = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DialogDesc", _("Press the button to download a directory"));
 	_mainButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Start download"), 0, kDownloadDialogButtonCmd);
diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index ffa24ec..fac0b02 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -36,40 +36,20 @@ namespace GUI {
 
 enum {
 	kChooseCmd = 'Chos',
-	kGoUpCmd = 'GoUp',
-	kHiddenCmd = 'Hidd'
+	kGoUpCmd = 'GoUp'
 };
 
-/* We want to use this as a general directory selector at some point... possible uses
- * - to select the data dir for a game
- * - to select the place where save games are stored
- * - others???
- */
-
-RemoteBrowserDialog::RemoteBrowserDialog(const char *title, bool dirRemoteBrowser)
+RemoteBrowserDialog::RemoteBrowserDialog(const char *title)
 	: Dialog("Browser"), _navigationLocked(false), _updateList(false) {
+	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
-	_isDirRemoteBrowser = dirRemoteBrowser;
-	_fileList = NULL;
-	_currentPath = NULL;
-
-	// Headline - TODO: should be customizable during creation time
 	new StaticTextWidget(this, "Browser.Headline", title);
-
-	// Current path - TODO: handle long paths ?
 	_currentPath = new StaticTextWidget(this, "Browser.Path", "DUMMY");
 
-	// Add file list
 	_fileList = new ListWidget(this, "Browser.List");
 	_fileList->setNumberingMode(kListNumberingOff);
 	_fileList->setEditable(false);
 
-	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
-
-	// hide checkbox for the "show hidden files" state.
-	//_showHiddenWidget = new CheckboxWidget(this, "Browser.Hidden", _("Show hidden files"), _("Show files marked with the hidden attribute"), kHiddenCmd);
-
-	// Buttons
 	if (g_system->getOverlayWidth() > 320)
 		new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd);
 	else
@@ -85,50 +65,32 @@ void RemoteBrowserDialog::open() {
 
 void RemoteBrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
-	case kChooseCmd:
-		if (_isDirRemoteBrowser) {
-			// If nothing is selected in the list widget, choose the current dir.
-			// Else, choose the dir that is selected.
-			int selection = _fileList->getSelected();
-			if (selection >= 0)
-				_choice = _nodeContent[selection];
-			else
-				_choice = _node;
-			setResult(1);
-			close();
-		} else {
-			int selection = _fileList->getSelected();
-			if (selection < 0)
-				break;
-			if (_nodeContent[selection].isDirectory()) {
-				_node = _nodeContent[selection];
-				updateListing();
-			} else {
-				_choice = _nodeContent[selection];
-				setResult(1);
-				close();
-			}
-		}
+	case kChooseCmd: {
+		// If nothing is selected in the list widget, choose the current dir.
+		// Else, choose the dir that is selected.
+		int selection = _fileList->getSelected();
+		if (selection >= 0)
+			_choice = _nodeContent[selection];
+		else
+			_choice = _node;
+		setResult(1);
+		close();
 		break;
+	}
 	case kGoUpCmd:
 		goUp();
 		break;
 	case kListItemActivatedCmd:
 	case kListItemDoubleClickedCmd:
-		if (_nodeContent[data].isDirectory()) {
-			_node = _nodeContent[data];
-			listDirectory(_node);
-		} else if (!_isDirRemoteBrowser) { //TODO: ????
-			_choice = _nodeContent[data];
-			setResult(1);
-			close();
+		if (_nodeContent[data].isDirectory()) {			
+			listDirectory(_nodeContent[data]);
 		}
 		break;
 	case kListSelectionChangedCmd:
-		// We do not allow selecting directories in directory
-		// RemoteBrowser mode, thus we will invalidate the selection
-		// when the user selects an directory over here.
-		if (data != (uint32)-1 && _isDirRemoteBrowser && !_nodeContent[data].isDirectory())
+		// We do not allow selecting directories,
+		// thus we will invalidate the selection
+		// when the user selects a directory over here.
+		if (data != (uint32)-1 && !_nodeContent[data].isDirectory())
 			_fileList->setSelected(-1);
 		break;
 	default:
@@ -202,19 +164,21 @@ void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
 		false
 	);
 
+	_backupNode = _node;
 	_node = node;
 	updateListing();
 }
 
 void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryResponse response) {
 	_navigationLocked = false;
-	//TODO: list files from response
 	_nodeContent = response.value;
 	_updateList = true;
 }
 
 void RemoteBrowserDialog::directoryListedErrorCallback(Networking::ErrorResponse error) {
 	_navigationLocked = false;
+	_node = _backupNode;
+	_updateList = true;
 	//TODO: show error message
 }
 
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index 55be119..ca1f937 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -38,7 +38,7 @@ class CommandSender;
 
 class RemoteBrowserDialog : public Dialog {
 public:
-	RemoteBrowserDialog(const char *title, bool dirRemoteBrowser);
+	RemoteBrowserDialog(const char *title);
 
 	virtual void open();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
@@ -49,10 +49,9 @@ public:
 protected:
 	ListWidget		*_fileList;
 	StaticTextWidget	*_currentPath;
-	Cloud::StorageFile _node;
+	Cloud::StorageFile _node, _backupNode;
 	Common::Array<Cloud::StorageFile> _nodeContent;
 	Cloud::StorageFile _choice;
-	bool _isDirRemoteBrowser;
 	bool _navigationLocked;
 	bool _updateList;
 


Commit: 51a7232c73b1f6c04103716cae88b3781fd8ab33
    https://github.com/scummvm/scummvm/commit/51a7232c73b1f6c04103716cae88b3781fd8ab33
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix RemoteBrowser Request handling

Init with NULL, ignore callbacks, and such.

Changed paths:
    gui/remotebrowser.cpp
    gui/remotebrowser.h



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index fac0b02..995e6a6 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -40,7 +40,7 @@ enum {
 };
 
 RemoteBrowserDialog::RemoteBrowserDialog(const char *title)
-	: Dialog("Browser"), _navigationLocked(false), _updateList(false) {
+	: Dialog("Browser"), _navigationLocked(false), _updateList(false), _workingRequest(nullptr), _ignoreCallback(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	new StaticTextWidget(this, "Browser.Headline", title);
@@ -58,11 +58,27 @@ RemoteBrowserDialog::RemoteBrowserDialog(const char *title)
 	new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd);
 }
 
+RemoteBrowserDialog::~RemoteBrowserDialog() {
+	if (_workingRequest) {
+		_ignoreCallback = true;
+		_workingRequest->finish();
+	}
+}
+
 void RemoteBrowserDialog::open() {	
 	Dialog::open();
 	listDirectory(Cloud::StorageFile());
 }
 
+void RemoteBrowserDialog::close() {
+	Dialog::close();
+	if (_workingRequest) {
+		_ignoreCallback = true;
+		_workingRequest->finish();
+		_ignoreCallback = false;
+	}
+}
+
 void RemoteBrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kChooseCmd: {
@@ -154,7 +170,7 @@ void RemoteBrowserDialog::goUp() {
 }
 
 void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
-	if (_navigationLocked) return;
+	if (_navigationLocked || _workingRequest) return;
 	_navigationLocked = true;
 
 	_workingRequest = CloudMan.listDirectory(
@@ -170,12 +186,18 @@ void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
 }
 
 void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
 	_navigationLocked = false;
 	_nodeContent = response.value;
 	_updateList = true;
 }
 
 void RemoteBrowserDialog::directoryListedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
 	_navigationLocked = false;
 	_node = _backupNode;
 	_updateList = true;
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index ca1f937..e13ee9d 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -39,8 +39,10 @@ class CommandSender;
 class RemoteBrowserDialog : public Dialog {
 public:
 	RemoteBrowserDialog(const char *title);
+	virtual ~RemoteBrowserDialog();
 
 	virtual void open();
+	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
 
@@ -56,7 +58,7 @@ protected:
 	bool _updateList;
 
 	Networking::Request *_workingRequest;
-	bool _ignoreCallback; //?
+	bool _ignoreCallback;
 
 	void updateListing();
 	void goUp();


Commit: 6faf2c26173e0a1305e4cea07a04c2857586bf6f
    https://github.com/scummvm/scummvm/commit/6faf2c26173e0a1305e4cea07a04c2857586bf6f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add RemoteBrowser parent directories remembering

No wait when "Go up" is pressed. These contents could be invalid,
though. In order to refresh contents, one has to go up one more time and
then get back inside (in root folder - just press "Go up" to refresh
it).

Changed paths:
    gui/remotebrowser.cpp
    gui/remotebrowser.h



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index 995e6a6..efeabb6 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -98,7 +98,8 @@ void RemoteBrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	case kListItemActivatedCmd:
 	case kListItemDoubleClickedCmd:
-		if (_nodeContent[data].isDirectory()) {			
+		if (_nodeContent[data].isDirectory()) {
+			_rememberedNodeContents[_node.path()] = _nodeContent;
 			listDirectory(_nodeContent[data]);
 		}
 		break;
@@ -155,30 +156,39 @@ void RemoteBrowserDialog::updateListing() {
 }
 
 void RemoteBrowserDialog::goUp() {
+	if (_rememberedNodeContents.contains(_node.path()))
+		_rememberedNodeContents.erase(_node.path());
+
 	Common::String path = _node.path();
 	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
 	if (path.empty()) {
-		draw();
-		return;
+		_rememberedNodeContents.erase("");
+	} else {
+		for (int i = path.size() - 1; i >= 0; --i)
+			if (i == 0 || path[i] == '/' || path[i] == '\\') {
+				path.erase(i);
+				break;
+			}		
 	}
-	for (int i = path.size()-1; i >= 0; --i)
-		if (i == 0 || path[i] == '/' || path[i] == '\\') {
-			path.erase(i);
-			break;
-		}
+
 	listDirectory(Cloud::StorageFile(path, 0, 0, true));
 }
 
 void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
 	if (_navigationLocked || _workingRequest) return;
-	_navigationLocked = true;
-
-	_workingRequest = CloudMan.listDirectory(
-		node.path(),
-		new Common::Callback<RemoteBrowserDialog, Cloud::Storage::ListDirectoryResponse>(this, &RemoteBrowserDialog::directoryListedCallback),
-		new Common::Callback<RemoteBrowserDialog, Networking::ErrorResponse>(this, &RemoteBrowserDialog::directoryListedErrorCallback),
-		false
-	);
+
+	if (_rememberedNodeContents.contains(node.path())) {
+		_nodeContent = _rememberedNodeContents[node.path()];
+	} else {
+		_navigationLocked = true;
+
+		_workingRequest = CloudMan.listDirectory(
+			node.path(),
+			new Common::Callback<RemoteBrowserDialog, Cloud::Storage::ListDirectoryResponse>(this, &RemoteBrowserDialog::directoryListedCallback),
+			new Common::Callback<RemoteBrowserDialog, Networking::ErrorResponse>(this, &RemoteBrowserDialog::directoryListedErrorCallback),
+			false
+		);
+	}
 
 	_backupNode = _node;
 	_node = node;
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index e13ee9d..0137c32 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -53,6 +53,7 @@ protected:
 	StaticTextWidget	*_currentPath;
 	Cloud::StorageFile _node, _backupNode;
 	Common::Array<Cloud::StorageFile> _nodeContent;
+	Common::HashMap<Common::String, Common::Array<Cloud::StorageFile> > _rememberedNodeContents;
 	Cloud::StorageFile _choice;
 	bool _navigationLocked;
 	bool _updateList;


Commit: a37c63998671ad8a341e1b88aead758f408e5fc2
    https://github.com/scummvm/scummvm/commit/a37c63998671ad8a341e1b88aead758f408e5fc2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make Google Drive sort files list

GoogleDriveListDirectoryByIdRequest now uses "orderBy" field to specify
that we want the commonly used "alphabetical, folders first" order.

That's mostly needed for RemoteBrowserDialog, because Requests don't
care about the order, and this one is more user-friendly.

Changed paths:
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp



diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 582f67c..36bc390 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -55,7 +55,7 @@ void GoogleDriveListDirectoryByIdRequest::start() {
 }
 
 void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken) {
-	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken";
+	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken&orderBy=folder,name";
 	//files(id,mimeType,modifiedTime,name,size),nextPageToken
 	if (pageToken != "") url += "&pageToken=" + pageToken;
 	url += "&q=%27" + _requestedId + "%27+in+parents";


Commit: 8972f28bc17b6d2f6fee69adcb025c9a561e851f
    https://github.com/scummvm/scummvm/commit/8972f28bc17b6d2f6fee69adcb025c9a561e851f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add RemoteBrowser file list sorting

Because Dropbox has no means to specify files order.

Changed paths:
    gui/remotebrowser.cpp
    gui/remotebrowser.h



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index efeabb6..e8921b9 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -201,6 +201,7 @@ void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryR
 
 	_navigationLocked = false;
 	_nodeContent = response.value;
+	Common::sort(_nodeContent.begin(), _nodeContent.end(), FileListOrder());
 	_updateList = true;
 }
 
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index 0137c32..be41615 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -66,6 +66,16 @@ protected:
 	void listDirectory(Cloud::StorageFile node);
 	void directoryListedCallback(Cloud::Storage::ListDirectoryResponse response);
 	void directoryListedErrorCallback(Networking::ErrorResponse error);
+
+	struct FileListOrder : public Common::BinaryFunction<Cloud::StorageFile, Cloud::StorageFile, bool> {
+		bool operator()(const Cloud::StorageFile &x, const Cloud::StorageFile &y) const {
+			if (x.isDirectory() != y.isDirectory()) {
+				return x.isDirectory(); //x < y (directory < not directory) or x > y (not directory > directory)
+			}
+
+			return x.name() < y.name();
+		}
+	};
 };
 
 } // End of namespace GUI


Commit: c32d6fa047d03ca7be0e060aadcf33e5ee7d6d4a
    https://github.com/scummvm/scummvm/commit/c32d6fa047d03ca7be0e060aadcf33e5ee7d6d4a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add error message in RemoteBrowser

For the error callback case.

Changed paths:
    gui/remotebrowser.cpp
    gui/remotebrowser.h



diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index e8921b9..d176d29 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -31,6 +31,7 @@
 #include <backends/networking/curl/request.h>
 #include <backends/cloud/storage.h>
 #include <backends/cloud/cloudmanager.h>
+#include "message.h"
 
 namespace GUI {
 
@@ -39,8 +40,9 @@ enum {
 	kGoUpCmd = 'GoUp'
 };
 
-RemoteBrowserDialog::RemoteBrowserDialog(const char *title)
-	: Dialog("Browser"), _navigationLocked(false), _updateList(false), _workingRequest(nullptr), _ignoreCallback(false) {
+RemoteBrowserDialog::RemoteBrowserDialog(const char *title):
+	Dialog("Browser"), _navigationLocked(false), _updateList(false), _showError(false),
+	_workingRequest(nullptr), _ignoreCallback(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	new StaticTextWidget(this, "Browser.Headline", title);
@@ -121,6 +123,12 @@ void RemoteBrowserDialog::handleTickle() {
 		_updateList = false;
 	}
 
+	if (_showError) {
+		_showError = false;
+		MessageDialog alert(_("ScummVM couldn't list the directory!"));
+		alert.runModal();
+	}
+
 	Dialog::handleTickle();
 }
 
@@ -212,7 +220,7 @@ void RemoteBrowserDialog::directoryListedErrorCallback(Networking::ErrorResponse
 	_navigationLocked = false;
 	_node = _backupNode;
 	_updateList = true;
-	//TODO: show error message
+	_showError = true;
 }
 
 } // End of namespace GUI
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index be41615..190d8c6 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -57,6 +57,7 @@ protected:
 	Cloud::StorageFile _choice;
 	bool _navigationLocked;
 	bool _updateList;
+	bool _showError;
 
 	Networking::Request *_workingRequest;
 	bool _ignoreCallback;


Commit: d776b5397198835e1538071fd4fe53ab491c5da4
    https://github.com/scummvm/scummvm/commit/d776b5397198835e1538071fd4fe53ab491c5da4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Use RemoteBrowser's result in DownloadDialog

It now checks the selected local directory, and shows different
MessageDialogs to notify user of mistake or ambiguous situation.

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 6e9410d..34eddbe 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -70,15 +70,7 @@ void DownloadDialog::selectDirectories() {
 	//first user should select remote directory to download	
 	if (_remoteBrowser->runModal() <= 0) return;
 
-	/*
-	Common::FSNode dir(_browser->getResult());
-	Common::FSList files;
-	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
-		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
-		alert.runModal();
-		return;
-	}
-	*/
+	Cloud::StorageFile remoteDirectory = _remoteBrowser->getResult();
 
 	//now user should select local directory to download into
 	if (_browser->runModal() <= 0) return;
@@ -91,7 +83,24 @@ void DownloadDialog::selectDirectories() {
 		return;
 	}
 
-	//TODO: require empty directory?
+	//check that there is no file with the remote directory's name in the local one
+	for (Common::FSList::iterator i = files.begin(); i != files.end(); ++i) {
+		if (i->getName().equalsIgnoreCase(remoteDirectory.name())) {
+			//if there is, ask user whether it's OK
+			if (!i->isDirectory()) {
+				GUI::MessageDialog alert(_("Cannot create a directory to download - the specified directory has a file with the same name."), _("OK"));
+				alert.runModal();
+				return;
+			}
+			GUI::MessageDialog alert(
+				Common::String::format(_("The \"%s\" already exists in the specified directory.\nDo you really want to download files into that directory?"), remoteDirectory.name().c_str()),
+				_("Yes"),
+				_("No")
+			);
+			if (alert.runModal() != GUI::kMessageOK) return;
+			break;
+		}
+	}
 
 	//TODO: initiate download
 }


Commit: dc0a95617227c2a2489150aec69ec0c464cd30de
    https://github.com/scummvm/scummvm/commit/dc0a95617227c2a2489150aec69ec0c464cd30de
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add CloudManager::downloadFolder()

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index b9de6d4..0a5006c 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -226,6 +226,17 @@ Networking::Request *CloudManager::listDirectory(Common::String path, Storage::L
 	return nullptr;
 }
 
+Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Common::String localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
+	else {
+		delete callback;
+		delete errorCallback;
+		//TODO: should we call errorCallback?
+	}
+	return nullptr;
+}
+
 Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->info(callback, errorCallback);
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 70b32f0..574c51a 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -184,6 +184,9 @@ public:
 	/** Returns ListDirectoryResponse with list of files. */
 	Networking::Request *listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
+	/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
+	Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+
 	/** Return the StorageInfo struct. */
 	Networking::Request *info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 


Commit: 10250af2516c8f7d41cc79e3c55b59f2eeecfa92
    https://github.com/scummvm/scummvm/commit/10250af2516c8f7d41cc79e3c55b59f2eeecfa92
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CloudManager's methods

Were not returning created Request.

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 0a5006c..68c7e9a 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -217,7 +217,7 @@ void CloudManager::printBool(Storage::BoolResponse response) const {
 
 Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->listDirectory(path, callback, errorCallback, recursive);
+	if (storage) return storage->listDirectory(path, callback, errorCallback, recursive);
 	else {
 		delete callback;
 		delete errorCallback;
@@ -228,7 +228,7 @@ Networking::Request *CloudManager::listDirectory(Common::String path, Storage::L
 
 Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Common::String localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
+	if (storage) return storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
 	else {
 		delete callback;
 		delete errorCallback;


Commit: 71a326493b351b845ea800ae88495238b1a61066
    https://github.com/scummvm/scummvm/commit/71a326493b351b845ea800ae88495238b1a61066
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Initiate download in DownloadDialog

Changed paths:
    gui/downloaddialog.cpp
    gui/downloaddialog.h



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 34eddbe..d4c44e5 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -38,7 +38,8 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _wasInProgress(true), _inProgress(false), _close(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false),
+	_workingRequest(nullptr), _ignoreCallback(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -50,15 +51,35 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	updateButtons();
 }
 
+DownloadDialog::~DownloadDialog() {
+	if (_workingRequest) {
+		_ignoreCallback = true;
+		_workingRequest->finish();
+	}
+}
+
+void DownloadDialog::close() {
+	if (_workingRequest) {
+		_ignoreCallback = true;
+		_workingRequest->finish();
+		_ignoreCallback = false;
+	}
+	Dialog::close();
+}
+
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kDownloadDialogButtonCmd: {
-		if (!_inProgress) {
+		if (_workingRequest == nullptr) {
 			selectDirectories();
+		} else {
+			_ignoreCallback = true;
+			_workingRequest->finish();
+			_ignoreCallback = false;
 		}
 
-		_inProgress = !_inProgress;
 		reflowLayout();
+		draw();
 		break;
 	}
 	default:
@@ -102,7 +123,38 @@ void DownloadDialog::selectDirectories() {
 		}
 	}
 
-	//TODO: initiate download
+	//make a local path
+	Common::String localPath = dir.getPath();
+
+	//simple heuristic to determine which path separator to use
+	int backslashes = 0;
+	for (uint32 i = 0; i < localPath.size(); ++i)
+		if (localPath[i] == '/') --backslashes;
+		else if (localPath[i] == '\\') ++backslashes;
+
+	if (backslashes > 0) localPath += '\\' + remoteDirectory.name();
+	else localPath += '/' + remoteDirectory.name();
+
+	_workingRequest = CloudMan.downloadFolder(
+		remoteDirectory.path(), localPath,
+		new Common::Callback<DownloadDialog, Cloud::Storage::FileArrayResponse>(this, &DownloadDialog::directoryDownloadedCallback),
+		new Common::Callback<DownloadDialog, Networking::ErrorResponse>(this, &DownloadDialog::directoryDownloadedErrorCallback),
+		true
+	);
+}
+
+void DownloadDialog::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//TODO: show response.value (if not empty), show message on OSD
+}
+
+void DownloadDialog::directoryDownloadedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//TODO: _showError = true;
 }
 
 void DownloadDialog::handleTickle() {
@@ -119,19 +171,14 @@ void DownloadDialog::reflowLayout() {
 	updateButtons();
 }
 
-void DownloadDialog::updateButtons() {
-	if (_wasInProgress == _inProgress) return;
-
-	if (_inProgress) {		
+void DownloadDialog::updateButtons() {	
+	if (_workingRequest != nullptr) {		
 		_messageText->setLabel(_("Press the button to cancel the download"));
 		_mainButton->setLabel(_("Cancel the download"));
 	} else {
 		_messageText->setLabel(_("Press the button to download a directory"));
 		_mainButton->setLabel(_("Start download"));
-	}	
-	
-	_wasInProgress = _inProgress;	
+	}
 }
 
-
 } // End of namespace GUI
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index ca702ef..6836b38 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -25,6 +25,7 @@
 
 #include "gui/dialog.h"
 #include "common/str.h"
+#include <backends/cloud/storage.h>
 
 namespace GUI {
 
@@ -43,15 +44,21 @@ class DownloadDialog : public Dialog {
 	ButtonWidget *_mainButton;
 	ButtonWidget *_closeButton;
 
-	bool _wasInProgress, _inProgress;
 	bool _close;
 
+	Networking::Request *_workingRequest;
+	bool _ignoreCallback;
+
 	void updateButtons();
 	void selectDirectories();
+	void directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response);
+	void directoryDownloadedErrorCallback(Networking::ErrorResponse error);
 
 public:
 	DownloadDialog(uint32 storageId);
+	virtual ~DownloadDialog();
 
+	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
 	virtual void reflowLayout();


Commit: b8ee9d4e7d32d0cc0dd832cbd0ffec5c5d08db34
    https://github.com/scummvm/scummvm/commit/b8ee9d4e7d32d0cc0dd832cbd0ffec5c5d08db34
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add FolderDownload-related methods in Storage

CloudManager's shortcuts are added too.

The idea is to keep FolderDownload request within Storage, and provide
necessary means to access it. The download is started and cancelled
through the DownloadDialog.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    gui/downloaddialog.cpp
    gui/downloaddialog.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 68c7e9a..a1756ed 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -274,6 +274,8 @@ bool CloudManager::isWorking() {
 	return false;
 }
 
+///// SavesSyncRequest-related /////
+
 bool CloudManager::isSyncing() {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->isSyncing();
@@ -308,4 +310,34 @@ void CloudManager::setSyncTarget(GUI::CommandReceiver *target) {
 	if (storage) storage->setSyncTarget(target);
 }
 
+///// DownloadFolderRequest-related /////
+
+bool CloudManager::startDownload(Common::String remotePath, Common::String localPath) {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->startDownload(remotePath, localPath);
+	return false;
+}
+
+void CloudManager::cancelDownload() {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->cancelDownload();
+}
+
+void CloudManager::setDownloadTarget(GUI::CommandReceiver *target) {
+	Storage *storage = getCurrentStorage();
+	if (storage) storage->setDownloadTarget(target);
+}
+
+bool CloudManager::isDownloading() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->isDownloading();
+	return false;
+}
+
+double CloudManager::getDownloadingProgress() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadingProgress();
+	return 1;
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 574c51a..2617a9c 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -206,6 +206,8 @@ public:
 	/** Returns whether there are any requests running. */
 	bool isWorking();
 
+	///// SavesSyncRequest-related /////
+
 	/** Returns whether there is a SavesSyncRequest running. */
 	bool isSyncing();
 
@@ -223,6 +225,23 @@ public:
 
 	/** Sets SavesSyncRequest's target to given CommandReceiver. */
 	void setSyncTarget(GUI::CommandReceiver *target);
+
+	///// DownloadFolderRequest-related /////
+
+	/** Starts a folder download. */
+	bool startDownload(Common::String remotePath, Common::String localPath);
+
+	/** Cancels running download. */
+	void cancelDownload();
+
+	/** Sets FolderDownloadRequest's target to given CommandReceiver. */
+	void setDownloadTarget(GUI::CommandReceiver *target);
+
+	/** Returns whether there is a FolderDownloadRequest running. */
+	bool isDownloading();
+
+	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
+	double getDownloadingProgress();
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 83296c3..e00f851 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -26,7 +26,7 @@
 namespace Cloud {
 
 FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive):
-	Request(nullptr, ecb), _storage(storage), _fileArrayCallback(callback),
+	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _fileArrayCallback(callback),
 	_remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive),
 	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
@@ -125,4 +125,6 @@ void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
+double FolderDownloadRequest::getProgress() { return 0; } //TODO
+
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index bf55567..83d3432 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -26,10 +26,11 @@
 #include "backends/networking/curl/request.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/cloud/storage.h"
+#include "gui/object.h"
 
 namespace Cloud {
 
-class FolderDownloadRequest: public Networking::Request {
+class FolderDownloadRequest: public Networking::Request, public GUI::CommandSender {
 	Storage *_storage;
 	Storage::FileArrayCallback _fileArrayCallback;
 	Common::String _remoteDirectoryPath, _localDirectoryPath;
@@ -51,7 +52,10 @@ public:
 	virtual ~FolderDownloadRequest();
 
 	virtual void handle();
-	virtual void restart();	
+	virtual void restart();
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getProgress();
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index b98f213..a08fe11 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -30,7 +30,9 @@
 
 namespace Cloud {
 
-Storage::Storage(): _runningRequestsCount(0), _savesSyncRequest(nullptr) {}
+Storage::Storage():
+	_runningRequestsCount(0), _savesSyncRequest(nullptr), _syncRestartRequestsed(false),
+	_downloadFolderRequest(nullptr) {}
 
 Storage::~Storage() {}
 
@@ -135,6 +137,8 @@ bool Storage::isWorking() {
 	return working;
 }
 
+///// SavesSyncRequest-related /////
+
 bool Storage::isSyncing() {
 	_runningRequestsMutex.lock();
 	bool syncing = _savesSyncRequest != nullptr;
@@ -183,5 +187,70 @@ void Storage::setSyncTarget(GUI::CommandReceiver *target) {
 	_runningRequestsMutex.unlock();
 }
 
+///// DownloadFolderRequest-related /////
+
+bool Storage::startDownload(Common::String remotePath, Common::String localPath) {
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest) {
+		warning("Storage::startDownload: there is a download in progress already");
+		_runningRequestsMutex.unlock();
+		return false;
+	}
+	_downloadFolderRequest = (FolderDownloadRequest *)downloadFolder(
+		remotePath, localPath,
+		new Common::Callback<Storage, Cloud::Storage::FileArrayResponse>(this, &Storage::directoryDownloadedCallback),
+		new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::directoryDownloadedErrorCallback),
+		true
+	);
+	_runningRequestsMutex.unlock();
+	return true;
+}
+
+void Storage::cancelDownload() {
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		_downloadFolderRequest->finish();
+	_runningRequestsMutex.unlock();
+}
+
+void Storage::setDownloadTarget(GUI::CommandReceiver *target) {
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		_downloadFolderRequest->setTarget(target);
+	_runningRequestsMutex.unlock();
+}
+
+bool Storage::isDownloading() {
+	_runningRequestsMutex.lock();
+	bool syncing = _downloadFolderRequest != nullptr;
+	_runningRequestsMutex.unlock();
+	return syncing;
+}
+
+double Storage::getDownloadingProgress() {
+	double result = 1;
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getProgress();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
+void Storage::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response) {
+	_runningRequestsMutex.lock();
+	_downloadFolderRequest = nullptr;
+	_runningRequestsMutex.unlock();
+
+	//TODO: show response.value (if not empty), show message on OSD
+}
+
+void Storage::directoryDownloadedErrorCallback(Networking::ErrorResponse error) {
+	_runningRequestsMutex.lock();
+	_downloadFolderRequest = nullptr;
+	_runningRequestsMutex.unlock();
+
+	//TODO: _showError = true;
+}
+
 } // End of namespace Cloud
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index ace2f30..28a2072 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -42,6 +42,7 @@ class CommandReceiver;
 namespace Cloud {
 
 class SavesSyncRequest;
+class FolderDownloadRequest;
 
 class Storage {
 public:
@@ -63,6 +64,7 @@ protected:
 	Common::Mutex _runningRequestsMutex;
 	SavesSyncRequest *_savesSyncRequest;
 	bool _syncRestartRequestsed;
+	FolderDownloadRequest *_downloadFolderRequest;
 
 	/** Returns default error callback (printErrorResponse). */
 	virtual Networking::ErrorCallback getErrorPrintingCallback();
@@ -159,6 +161,8 @@ public:
 	/** Returns whether there are any requests running. */
 	virtual bool isWorking();
 
+	///// SavesSyncRequest-related /////
+
 	/** Returns whether there is a SavesSyncRequest running. */
 	virtual bool isSyncing();
 
@@ -176,6 +180,30 @@ public:
 
 	/** Sets SavesSyncRequest's target to given CommandReceiver. */
 	virtual void setSyncTarget(GUI::CommandReceiver *target);
+
+	///// DownloadFolderRequest-related /////
+
+	/** Starts a folder download. */
+	virtual bool startDownload(Common::String remotePath, Common::String localPath);
+
+	/** Cancels running download. */
+	virtual void cancelDownload();
+
+	/** Sets FolderDownloadRequest's target to given CommandReceiver. */
+	virtual void setDownloadTarget(GUI::CommandReceiver *target);
+
+	/** Returns whether there is a FolderDownloadRequest running. */
+	virtual bool isDownloading();
+
+	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
+	virtual double getDownloadingProgress();
+
+protected:
+	/** Finishes the download. Shows an OSD message. */
+	virtual void directoryDownloadedCallback(FileArrayResponse response);
+
+	/** Finishes the download. Shows an OSD message. */
+	virtual void directoryDownloadedErrorCallback(Networking::ErrorResponse error);
 };
 
 } // End of namespace Cloud
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index d4c44e5..ac3b3d3 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -38,8 +38,7 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false),
-	_workingRequest(nullptr), _ignoreCallback(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -51,31 +50,13 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	updateButtons();
 }
 
-DownloadDialog::~DownloadDialog() {
-	if (_workingRequest) {
-		_ignoreCallback = true;
-		_workingRequest->finish();
-	}
-}
-
-void DownloadDialog::close() {
-	if (_workingRequest) {
-		_ignoreCallback = true;
-		_workingRequest->finish();
-		_ignoreCallback = false;
-	}
-	Dialog::close();
-}
-
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kDownloadDialogButtonCmd: {
-		if (_workingRequest == nullptr) {
-			selectDirectories();
+		if (CloudMan.isDownloading()) {
+			CloudMan.cancelDownload();
 		} else {
-			_ignoreCallback = true;
-			_workingRequest->finish();
-			_ignoreCallback = false;
+			selectDirectories();
 		}
 
 		reflowLayout();
@@ -135,26 +116,7 @@ void DownloadDialog::selectDirectories() {
 	if (backslashes > 0) localPath += '\\' + remoteDirectory.name();
 	else localPath += '/' + remoteDirectory.name();
 
-	_workingRequest = CloudMan.downloadFolder(
-		remoteDirectory.path(), localPath,
-		new Common::Callback<DownloadDialog, Cloud::Storage::FileArrayResponse>(this, &DownloadDialog::directoryDownloadedCallback),
-		new Common::Callback<DownloadDialog, Networking::ErrorResponse>(this, &DownloadDialog::directoryDownloadedErrorCallback),
-		true
-	);
-}
-
-void DownloadDialog::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-
-	//TODO: show response.value (if not empty), show message on OSD
-}
-
-void DownloadDialog::directoryDownloadedErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-
-	//TODO: _showError = true;
+	CloudMan.startDownload(remoteDirectory.path(), localPath);
 }
 
 void DownloadDialog::handleTickle() {
@@ -172,7 +134,7 @@ void DownloadDialog::reflowLayout() {
 }
 
 void DownloadDialog::updateButtons() {	
-	if (_workingRequest != nullptr) {		
+	if (CloudMan.isDownloading()) {
 		_messageText->setLabel(_("Press the button to cancel the download"));
 		_mainButton->setLabel(_("Cancel the download"));
 	} else {
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 6836b38..508e91a 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -46,19 +46,12 @@ class DownloadDialog : public Dialog {
 
 	bool _close;
 
-	Networking::Request *_workingRequest;
-	bool _ignoreCallback;
-
 	void updateButtons();
 	void selectDirectories();
-	void directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response);
-	void directoryDownloadedErrorCallback(Networking::ErrorResponse error);
 
 public:
 	DownloadDialog(uint32 storageId);
-	virtual ~DownloadDialog();
 
-	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
 	virtual void reflowLayout();


Commit: ddb1a6ccb6238aaed599b271506a94a7c0f18844
    https://github.com/scummvm/scummvm/commit/ddb1a6ccb6238aaed599b271506a94a7c0f18844
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Upgrade DownloadDialog

It now shows the remote and local directories and a progress bar.

Storage now shows OSD messages on download success and failure.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a1756ed..dfe65e7 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -340,4 +340,16 @@ double CloudManager::getDownloadingProgress() {
 	return 1;
 }
 
+Common::String CloudManager::getDownloadRemoteDirectory() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadRemoteDirectory();
+	return "";
+}
+
+Common::String CloudManager::getDownloadLocalDirectory() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadLocalDirectory();
+	return "";
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 2617a9c..1cdbbcc 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -242,6 +242,12 @@ public:
 
 	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
 	double getDownloadingProgress();
+
+	/** Returns remote directory path. */
+	virtual Common::String getDownloadRemoteDirectory();
+
+	/** Returns local directory path. */
+	virtual Common::String getDownloadLocalDirectory();
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index e00f851..d57da6b 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -22,17 +22,19 @@
 
 #include "backends/cloud/folderdownloadrequest.h"
 #include "common/debug.h"
+#include "gui/downloaddialog.h"
 
 namespace Cloud {
 
 FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive):
 	Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _fileArrayCallback(callback),
 	_remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive),
-	_workingRequest(nullptr), _ignoreCallback(false) {
+	_workingRequest(nullptr), _ignoreCallback(false), _totalFiles(0) {
 	start();
 }
 
 FolderDownloadRequest::~FolderDownloadRequest() {
+	sendCommand(GUI::kDownloadEndedCmd, 0);
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
 	delete _fileArrayCallback;
@@ -46,6 +48,7 @@ void FolderDownloadRequest::start() {
 	_files.clear();
 	_failedFiles.clear();
 	_ignoreCallback = false;
+	_totalFiles = 0;
 
 	//list directory first
 	_workingRequest = _storage->listDirectory(
@@ -60,6 +63,7 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	_files = response.value;
+	_totalFiles = _files.size();
 	downloadNextFile();
 }
 
@@ -91,6 +95,8 @@ void FolderDownloadRequest::downloadNextFile() {
 		_files.pop_back();
 	} while (_currentFile.isDirectory()); //TODO: may be create these directories (in case those are empty)
 
+	sendCommand(GUI::kDownloadProgressCmd, (int)(getProgress() * 100));
+
 	Common::String remotePath = _currentFile.path();
 	Common::String localPath = remotePath;
 	if (_remoteDirectoryPath == "" || remotePath.hasPrefix(_remoteDirectoryPath)) {
@@ -125,6 +131,9 @@ void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
-double FolderDownloadRequest::getProgress() { return 0; } //TODO
+double FolderDownloadRequest::getProgress() {
+	if (_totalFiles == 0) return 0;	
+	return (double)(_totalFiles - _files.size()) / (double)(_totalFiles);
+}
 
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 83d3432..41eacc2 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -39,6 +39,7 @@ class FolderDownloadRequest: public Networking::Request, public GUI::CommandSend
 	StorageFile _currentFile;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	uint32 _totalFiles;
 
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
@@ -56,6 +57,12 @@ public:
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
 	double getProgress();
+
+	/** Returns remote directory path. */
+	Common::String getRemotePath() { return _remoteDirectoryPath; }
+
+	/** Returns local directory path. */
+	Common::String getLocalPath() { return _localDirectoryPath; }
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index a08fe11..4e3dc43 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -27,6 +27,7 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "common/debug.h"
 #include "common/file.h"
+#include <common/translation.h>
 
 namespace Cloud {
 
@@ -198,7 +199,7 @@ bool Storage::startDownload(Common::String remotePath, Common::String localPath)
 	}
 	_downloadFolderRequest = (FolderDownloadRequest *)downloadFolder(
 		remotePath, localPath,
-		new Common::Callback<Storage, Cloud::Storage::FileArrayResponse>(this, &Storage::directoryDownloadedCallback),
+		new Common::Callback<Storage, FileArrayResponse>(this, &Storage::directoryDownloadedCallback),
 		new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::directoryDownloadedErrorCallback),
 		true
 	);
@@ -236,12 +237,36 @@ double Storage::getDownloadingProgress() {
 	return result;
 }
 
-void Storage::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response) {
+Common::String Storage::getDownloadRemoteDirectory() {
+	Common::String result = "";
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getRemotePath();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
+Common::String Storage::getDownloadLocalDirectory() {
+	Common::String result = "";
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getLocalPath();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
+void Storage::directoryDownloadedCallback(FileArrayResponse response) {
 	_runningRequestsMutex.lock();
 	_downloadFolderRequest = nullptr;
 	_runningRequestsMutex.unlock();
 
-	//TODO: show response.value (if not empty), show message on OSD
+	Common::String message;
+	if (response.value.size()) {
+		message = Common::String::format(_("Download complete.\nFailed to download %u files."), response.value.size());
+	} else {
+		message = _("Download complete.");
+	}
+	g_system->displayMessageOnOSD(message.c_str());
 }
 
 void Storage::directoryDownloadedErrorCallback(Networking::ErrorResponse error) {
@@ -249,7 +274,7 @@ void Storage::directoryDownloadedErrorCallback(Networking::ErrorResponse error)
 	_downloadFolderRequest = nullptr;
 	_runningRequestsMutex.unlock();
 
-	//TODO: _showError = true;
+	g_system->displayMessageOnOSD(_("Download failed."));
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 28a2072..62b4269 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -198,6 +198,12 @@ public:
 	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
 	virtual double getDownloadingProgress();
 
+	/** Returns remote directory path. */
+	virtual Common::String getDownloadRemoteDirectory();
+
+	/** Returns local directory path. */
+	virtual Common::String getDownloadLocalDirectory();
+
 protected:
 	/** Finishes the download. Shows an OSD message. */
 	virtual void directoryDownloadedCallback(FileArrayResponse response);
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index ac3b3d3..28a29fc 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -38,7 +38,7 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _reflow(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -46,14 +46,35 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 
 	_messageText = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DialogDesc", _("Press the button to download a directory"));
 	_mainButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Start download"), 0, kDownloadDialogButtonCmd);
+	_remoteDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.RemoteDirectory", _("From: "));
+	_localDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.LocalDirectory", _("To: "));
+	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
+	_progressBar = new SliderWidget(this, "GlobalOptions_Cloud_DownloadDialog.ProgressBar");
+	_progressBar->setMinValue(0);
+	_progressBar->setMaxValue(100);
+	_progressBar->setValue(progress);
+	_progressBar->setEnabled(false);
+	_percentLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.PercentText", Common::String::format("%u %%", progress));
 	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
 	updateButtons();
+
+	CloudMan.setDownloadTarget(this);
+}
+
+DownloadDialog::~DownloadDialog() {
+	CloudMan.setDownloadTarget(nullptr);
+}
+
+void DownloadDialog::close() {
+	CloudMan.setDownloadTarget(nullptr);
+	Dialog::close();
 }
 
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kDownloadDialogButtonCmd: {
 		if (CloudMan.isDownloading()) {
+			CloudMan.setDownloadTarget(nullptr);
 			CloudMan.cancelDownload();
 		} else {
 			selectDirectories();
@@ -63,6 +84,14 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		draw();
 		break;
 	}
+	case kDownloadProgressCmd:		
+		_percentLabel->setLabel(Common::String::format("%u %%", data));
+		_progressBar->setValue(data);
+		_reflow = true;
+		break;
+	case kDownloadEndedCmd:
+		_reflow = true;
+		break;
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
@@ -117,12 +146,14 @@ void DownloadDialog::selectDirectories() {
 	else localPath += '/' + remoteDirectory.name();
 
 	CloudMan.startDownload(remoteDirectory.path(), localPath);
+	CloudMan.setDownloadTarget(this);
 }
 
 void DownloadDialog::handleTickle() {
-	if (_close) {
-		setResult(1);
-		close();
+	if (_reflow) {
+		reflowLayout();
+		draw();
+		_reflow = false;
 	}
 
 	Dialog::handleTickle();
@@ -133,14 +164,24 @@ void DownloadDialog::reflowLayout() {
 	updateButtons();
 }
 
-void DownloadDialog::updateButtons() {	
-	if (CloudMan.isDownloading()) {
+void DownloadDialog::updateButtons() {
+	bool downloading = CloudMan.isDownloading();
+	if (downloading) {
 		_messageText->setLabel(_("Press the button to cancel the download"));
 		_mainButton->setLabel(_("Cancel the download"));
+		_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
+		_localDirectoryLabel->setLabel(_("To: ") + CloudMan.getDownloadLocalDirectory());
+		uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
+		_percentLabel->setLabel(Common::String::format("%u %%", progress));
+		_progressBar->setValue(progress);
 	} else {
 		_messageText->setLabel(_("Press the button to download a directory"));
 		_mainButton->setLabel(_("Start download"));
 	}
+	_remoteDirectoryLabel->setVisible(downloading);
+	_localDirectoryLabel->setVisible(downloading);
+	_percentLabel->setVisible(downloading);
+	_progressBar->setVisible(downloading);	
 }
 
 } // End of namespace GUI
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 508e91a..429e20a 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -33,25 +33,37 @@ class CommandSender;
 class EditTextWidget;
 class StaticTextWidget;
 class ButtonWidget;
+class SliderWidget;
 class BrowserDialog;
 class RemoteBrowserDialog;
 
+enum DownloadProgress {
+	kDownloadProgressCmd = 'DLPR',
+	kDownloadEndedCmd = 'DLEN'
+};
+
 class DownloadDialog : public Dialog {
 	BrowserDialog *_browser;
 	RemoteBrowserDialog *_remoteBrowser;
 
 	StaticTextWidget *_messageText;
 	ButtonWidget *_mainButton;
+	StaticTextWidget *_remoteDirectoryLabel;
+	StaticTextWidget *_localDirectoryLabel;
+	StaticTextWidget *_percentLabel;
+	SliderWidget *_progressBar;
 	ButtonWidget *_closeButton;
 
-	bool _close;
+	bool _reflow;
 
 	void updateButtons();
 	void selectDirectories();
 
 public:
 	DownloadDialog(uint32 storageId);
+	virtual ~DownloadDialog();
 
+	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
 	virtual void reflowLayout();
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index f9ec30b..e6493ba 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -594,6 +594,20 @@
 			<widget name = 'MainButton'
 					type = 'Button'
 			/>
+			<widget name = 'RemoteDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'LocalDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'ProgressBar'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10'>
 				<space/>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index fe15dc2..ab78d3a 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -591,6 +591,20 @@
 			<widget name = 'MainButton'
 					type = 'Button'
 			/>
+			<widget name = 'RemoteDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'LocalDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'ProgressBar'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6'>
 				<space/>


Commit: 458bfcec79e76b927414ed1b2151a4d59503ff86
    https://github.com/scummvm/scummvm/commit/458bfcec79e76b927414ed1b2151a4d59503ff86
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix DownloadDialog path creation

Was adding a path separator even when none is required.

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 28a29fc..05d87c2 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -137,13 +137,15 @@ void DownloadDialog::selectDirectories() {
 	Common::String localPath = dir.getPath();
 
 	//simple heuristic to determine which path separator to use
-	int backslashes = 0;
-	for (uint32 i = 0; i < localPath.size(); ++i)
-		if (localPath[i] == '/') --backslashes;
-		else if (localPath[i] == '\\') ++backslashes;
-
-	if (backslashes > 0) localPath += '\\' + remoteDirectory.name();
-	else localPath += '/' + remoteDirectory.name();
+	if (localPath.size() && localPath.lastChar() != '/' && localPath.lastChar() != '\\') {
+		int backslashes = 0;
+		for (uint32 i = 0; i < localPath.size(); ++i)
+			if (localPath[i] == '/') --backslashes;
+			else if (localPath[i] == '\\') ++backslashes;
+
+			if (backslashes > 0) localPath += '\\' + remoteDirectory.name();
+			else localPath += '/' + remoteDirectory.name();
+	} else localPath += remoteDirectory.name();
 
 	CloudMan.startDownload(remoteDirectory.path(), localPath);
 	CloudMan.setDownloadTarget(this);


Commit: 052d8bf0aec65f8fa2feb19e9780574e79196203
    https://github.com/scummvm/scummvm/commit/052d8bf0aec65f8fa2feb19e9780574e79196203
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Forbid using download directory in "Add Game"

Changed paths:
    gui/launcher.cpp



diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index dee7f17..1ea3169 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -51,6 +51,9 @@
 #include "gui/ThemeEval.h"
 
 #include "graphics/cursorman.h"
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#endif
 
 using Common::ConfigManager;
 
@@ -844,6 +847,20 @@ void LauncherDialog::addGame() {
 		if (_browser->runModal() > 0) {
 			// User made his choice...
 			Common::FSNode dir(_browser->getResult());
+#ifdef USE_CLOUD
+			String selectedDirectory = dir.getPath();
+			String bannedDirectory = CloudMan.getDownloadLocalDirectory();
+			if (selectedDirectory.size() && selectedDirectory.lastChar() != '/' && selectedDirectory.lastChar() != '\\')
+				selectedDirectory += '/';
+			if (bannedDirectory.size() && bannedDirectory.lastChar() != '/' && bannedDirectory.lastChar() != '\\')
+				if (selectedDirectory.size()) bannedDirectory += selectedDirectory.lastChar();
+				else bannedDirectory += '/';
+			if (selectedDirectory.equalsIgnoreCase(bannedDirectory)) {
+				MessageDialog alert(_("This directory cannot be used yet, it is being downloaded into!"));
+				alert.runModal();
+				return;
+			}
+#endif
 			Common::FSList files;
 			if (!dir.getChildren(files, Common::FSNode::kListAll)) {
 				MessageDialog alert(_("ScummVM couldn't open the specified directory!"));


Commit: 659dcd9702a82b3fdf5b6c6c87f11267fe313744
    https://github.com/scummvm/scummvm/commit/659dcd9702a82b3fdf5b6c6c87f11267fe313744
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix SaveLoadDialog

It was SavesSyncRequest's target even when closed.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 3ac6074..eff4a5a 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -165,6 +165,11 @@ void SaveLoadChooserDialog::open() {
 	_dialogWasShown = false;
 }
 
+void SaveLoadChooserDialog::close() {
+	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
+	Dialog::close();
+}
+
 int SaveLoadChooserDialog::run(const Common::String &target, const MetaEngine *metaEngine) {
 	_metaEngine = metaEngine;
 	_target = target;
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 890169c..9c601fd 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -73,6 +73,7 @@ public:
 	virtual ~SaveLoadChooserDialog();
 
 	virtual void open();
+	virtual void close();
 
 	virtual void reflowLayout();
 


Commit: 1cfdb9661678a5cabb77c2cf60d2fa357121d584
    https://github.com/scummvm/scummvm/commit/1cfdb9661678a5cabb77c2cf60d2fa357121d584
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix FolderDownloadRequest

Actually, I'm not completely sure, but this fixed the segfault when user
closes ScummVM during the download. Even if that's not a fix, these
lines must be in this method anyway.

Changed paths:
    backends/cloud/folderdownloadrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index d57da6b..6cf55b2 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -81,6 +81,8 @@ void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse respons
 }
 
 void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
 	fileDownloadedCallback(Storage::BoolResponse(error.request, false));
 }
 


Commit: a5765a339e150952dca27035357bbfb1f88a4718
    https://github.com/scummvm/scummvm/commit/a5765a339e150952dca27035357bbfb1f88a4718
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Update DownloadDialog

It now less empty, because if there is no download in progress, user
sees the RemoteBrowser instead of empty dialog. The cancel button is now
in the left bottom corner.

Changed paths:
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 05d87c2..9c48111 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -38,14 +38,12 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _reflow(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false), _reflow(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
 	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"));
-
-	_messageText = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DialogDesc", _("Press the button to download a directory"));
-	_mainButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Start download"), 0, kDownloadDialogButtonCmd);
+		
 	_remoteDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.RemoteDirectory", _("From: "));
 	_localDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.LocalDirectory", _("To: "));
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
@@ -55,9 +53,10 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	_progressBar->setValue(progress);
 	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.PercentText", Common::String::format("%u %%", progress));
+	_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Cancel download"), 0, kDownloadDialogButtonCmd);
 	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
-	updateButtons();
-
+	refreshWidgets();
+	
 	CloudMan.setDownloadTarget(this);
 }
 
@@ -65,6 +64,15 @@ DownloadDialog::~DownloadDialog() {
 	CloudMan.setDownloadTarget(nullptr);
 }
 
+void DownloadDialog::open() {
+	Dialog::open();
+	if (!CloudMan.isDownloading())
+		if (!selectDirectories())
+			close();
+	reflowLayout();
+	draw();
+}
+
 void DownloadDialog::close() {
 	CloudMan.setDownloadTarget(nullptr);
 	Dialog::close();
@@ -72,16 +80,10 @@ void DownloadDialog::close() {
 
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
-	case kDownloadDialogButtonCmd: {
-		if (CloudMan.isDownloading()) {
-			CloudMan.setDownloadTarget(nullptr);
-			CloudMan.cancelDownload();
-		} else {
-			selectDirectories();
-		}
-
-		reflowLayout();
-		draw();
+	case kDownloadDialogButtonCmd: {		
+		CloudMan.setDownloadTarget(nullptr);
+		CloudMan.cancelDownload();
+		close();
 		break;
 	}
 	case kDownloadProgressCmd:		
@@ -90,28 +92,28 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		_reflow = true;
 		break;
 	case kDownloadEndedCmd:
-		_reflow = true;
+		_close = true;
 		break;
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
 }
 
-void DownloadDialog::selectDirectories() {
+bool DownloadDialog::selectDirectories() {
 	//first user should select remote directory to download	
-	if (_remoteBrowser->runModal() <= 0) return;
+	if (_remoteBrowser->runModal() <= 0) return false;
 
 	Cloud::StorageFile remoteDirectory = _remoteBrowser->getResult();
 
 	//now user should select local directory to download into
-	if (_browser->runModal() <= 0) return;
+	if (_browser->runModal() <= 0) return false;
 
 	Common::FSNode dir(_browser->getResult());
 	Common::FSList files;
 	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
 		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
 		alert.runModal();
-		return;
+		return false;
 	}
 
 	//check that there is no file with the remote directory's name in the local one
@@ -121,14 +123,14 @@ void DownloadDialog::selectDirectories() {
 			if (!i->isDirectory()) {
 				GUI::MessageDialog alert(_("Cannot create a directory to download - the specified directory has a file with the same name."), _("OK"));
 				alert.runModal();
-				return;
+				return false;
 			}
 			GUI::MessageDialog alert(
 				Common::String::format(_("The \"%s\" already exists in the specified directory.\nDo you really want to download files into that directory?"), remoteDirectory.name().c_str()),
 				_("Yes"),
 				_("No")
 			);
-			if (alert.runModal() != GUI::kMessageOK) return;
+			if (alert.runModal() != GUI::kMessageOK) return false;
 			break;
 		}
 	}
@@ -149,9 +151,16 @@ void DownloadDialog::selectDirectories() {
 
 	CloudMan.startDownload(remoteDirectory.path(), localPath);
 	CloudMan.setDownloadTarget(this);
+	return true;
 }
 
 void DownloadDialog::handleTickle() {
+	if (_close) {
+		close();
+		_close = false;
+		return;
+	}
+
 	if (_reflow) {
 		reflowLayout();
 		draw();
@@ -163,27 +172,15 @@ void DownloadDialog::handleTickle() {
 
 void DownloadDialog::reflowLayout() {
 	Dialog::reflowLayout();
-	updateButtons();
+	refreshWidgets();
 }
 
-void DownloadDialog::updateButtons() {
-	bool downloading = CloudMan.isDownloading();
-	if (downloading) {
-		_messageText->setLabel(_("Press the button to cancel the download"));
-		_mainButton->setLabel(_("Cancel the download"));
-		_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
-		_localDirectoryLabel->setLabel(_("To: ") + CloudMan.getDownloadLocalDirectory());
-		uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
-		_percentLabel->setLabel(Common::String::format("%u %%", progress));
-		_progressBar->setValue(progress);
-	} else {
-		_messageText->setLabel(_("Press the button to download a directory"));
-		_mainButton->setLabel(_("Start download"));
-	}
-	_remoteDirectoryLabel->setVisible(downloading);
-	_localDirectoryLabel->setVisible(downloading);
-	_percentLabel->setVisible(downloading);
-	_progressBar->setVisible(downloading);	
+void DownloadDialog::refreshWidgets() {	
+	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
+	_localDirectoryLabel->setLabel(_("To: ") + CloudMan.getDownloadLocalDirectory());
+	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
+	_percentLabel->setLabel(Common::String::format("%u %%", progress));
+	_progressBar->setValue(progress);
 }
 
 } // End of namespace GUI
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 429e20a..74dd1e2 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -45,24 +45,24 @@ enum DownloadProgress {
 class DownloadDialog : public Dialog {
 	BrowserDialog *_browser;
 	RemoteBrowserDialog *_remoteBrowser;
-
-	StaticTextWidget *_messageText;
-	ButtonWidget *_mainButton;
+	
 	StaticTextWidget *_remoteDirectoryLabel;
 	StaticTextWidget *_localDirectoryLabel;
 	StaticTextWidget *_percentLabel;
 	SliderWidget *_progressBar;
+	ButtonWidget *_cancelButton;
 	ButtonWidget *_closeButton;
 
-	bool _reflow;
+	bool _close, _reflow;
 
-	void updateButtons();
-	void selectDirectories();
+	void refreshWidgets();
+	bool selectDirectories();
 
 public:
 	DownloadDialog(uint32 storageId);
 	virtual ~DownloadDialog();
 
+	virtual void open();
 	virtual void close();
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index e6493ba..d2e60d7 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -588,12 +588,6 @@
 
 	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
 		<layout type = 'vertical' padding = '16, 16, 16, 8' spacing = '8'>
-			<widget name = 'DialogDesc'
-					height = 'Globals.Line.Height'
-			/>
-			<widget name = 'MainButton'
-					type = 'Button'
-			/>
 			<widget name = 'RemoteDirectory'
 					height = 'Globals.Line.Height'
 			/>
@@ -610,6 +604,9 @@
 			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10'>
+				<widget name = 'MainButton'
+						type = 'Button'
+				/>
 				<space/>
 				<widget name = 'CloseButton'
 						type = 'Button'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index ab78d3a..1ff2e9f 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -585,12 +585,6 @@
 
 	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
 		<layout type = 'vertical' padding = '8, 8, 8, 4' spacing = '8'>
-			<widget name = 'DialogDesc'
-					height = 'Globals.Line.Height'
-			/>
-			<widget name = 'MainButton'
-					type = 'Button'
-			/>
 			<widget name = 'RemoteDirectory'
 					height = 'Globals.Line.Height'
 			/>
@@ -607,6 +601,9 @@
 			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6'>
+				<widget name = 'MainButton'
+						type = 'Button'
+				/>
 				<space/>
 				<widget name = 'CloseButton'
 						type = 'Button'


Commit: 2284333b1c9348638ce0255668f4c0abb85aeb84
    https://github.com/scummvm/scummvm/commit/2284333b1c9348638ce0255668f4c0abb85aeb84
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add lowres support for DownloadDialog's button

No actual translations, though. Should be just "Cancel", because "Cancel
download" is too long for lowres mode.

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 9c48111..d926030 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -53,7 +53,11 @@ DownloadDialog::DownloadDialog(uint32 storageId):
 	_progressBar->setValue(progress);
 	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.PercentText", Common::String::format("%u %%", progress));
-	_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Cancel download"), 0, kDownloadDialogButtonCmd);
+	if (g_system->getOverlayWidth() > 320)		
+		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Cancel download"), 0, kDownloadDialogButtonCmd);
+	else
+		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _c("Cancel download", "lowres"), 0, kDownloadDialogButtonCmd);
+	
 	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
 	refreshWidgets();
 	


Commit: ad069f442ce1a042361620f30d5b313668df23c4
    https://github.com/scummvm/scummvm/commit/ad069f442ce1a042361620f30d5b313668df23c4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add "Run server" button in Cloud tab

Changed paths:
    gui/object.h
    gui/options.cpp
    gui/options.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/object.h b/gui/object.h
index 776c941..1541c35 100644
--- a/gui/object.h
+++ b/gui/object.h
@@ -76,6 +76,8 @@ public:
 
 	virtual void setTextDrawableArea(const Common::Rect &r) { _textDrawableArea = r; }
 
+	virtual int16	getRelX() const		{ return _x; }
+	virtual int16	getRelY() const		{ return _y; }
 	virtual int16	getAbsX() const		{ return _x; }
 	virtual int16	getAbsY() const		{ return _y; }
 	virtual int16	getChildX() const	{ return getAbsX(); }
diff --git a/gui/options.cpp b/gui/options.cpp
index 1ea64ca..4332784 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -90,11 +90,12 @@ enum {
 };
 #endif
 
-#ifdef USE_CLOUD
+#if defined USE_CLOUD || defined USE_SDL_NET
 enum {
 	kConfigureStorageCmd = 'cfst',
 	kRefreshStorageCmd = 'rfst',
-	kDownloadStorageCmd = 'dlst'
+	kDownloadStorageCmd = 'dlst',
+	kRunServerCmd = 'rnsv'
 };
 #endif
 
@@ -1265,7 +1266,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	new ButtonWidget(tab, "GlobalOptions_Misc.UpdatesCheckManuallyButton", _("Check now"), 0, kUpdatesCheckCmd);
 #endif
 
-#ifdef USE_CLOUD
+#if defined USE_CLOUD || defined USE_SDL_NET
 	//
 	// 7) The cloud tab
 	//
@@ -1274,13 +1275,21 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	else
 		tab->addTab(_c("Cloud", "lowres"));
 
+#ifdef USE_CLOUD
 	_selectedStorageIndex = CloudMan.getStorageIndex();
+#else
+	_selectedStorageIndex = 0;
+#endif
 
 	_storagePopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StoragePopupDesc", _("Storage:"), _("Active cloud storage"));
 	_storagePopUp = new PopUpWidget(tab, "GlobalOptions_Cloud.StoragePopup");
+#ifdef USE_CLOUD
 	Common::StringArray list = CloudMan.listStorages();
 	for (uint32 i = 0; i < list.size(); ++i)
 		_storagePopUp->appendEntry(list[i], i);
+#else
+	_storagePopUp->appendEntry(_("<none>"), 0);
+#endif
 	_storagePopUp->setSelected(_selectedStorageIndex);
 
 	_storageUsernameDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
@@ -1296,6 +1305,9 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	_storageRefreshButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RefreshButton", _("Refresh"), _("Refresh current cloud storage information (username and usage)"), kRefreshStorageCmd);
 	_storageDownloadButton = new ButtonWidget(tab, "GlobalOptions_Cloud.DownloadButton", _("Downloads"), _("Open downloads manager dialog"), kDownloadStorageCmd);
 
+	_runServerButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
+	_serverInfoLabel = new StaticTextWidget(tab, "GlobalOptions_Cloud.ServerInfoLabel", "Not running");
+
 	setupCloudTab();
 	_redrawCloudTab = false;
 #endif
@@ -1608,6 +1620,15 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 #endif
+#ifdef USE_SDL_NET
+	case kRunServerCmd:
+	{
+		//TODO
+		//DownloadDialog dialog(_selectedStorageIndex);
+		//dialog.runModal();
+		break;
+	}
+#endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
 		_keysDialog->runModal();
@@ -1675,8 +1696,10 @@ void GlobalOptionsDialog::reflowLayout() {
 	OptionsDialog::reflowLayout();
 }
 
-#ifdef USE_CLOUD
+#if defined USE_CLOUD || defined USE_SDL_NET
 void GlobalOptionsDialog::setupCloudTab() {
+	int serverLabelPosition = -1; //no override
+#ifdef USE_CLOUD
 	_selectedStorageIndex = _storagePopUp->getSelectedTag();
 
 	bool shown = (_selectedStorageIndex != Cloud::kStorageNoneId);
@@ -1706,8 +1729,47 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageConnectButton) _storageConnectButton->setVisible(shown);
 	if (_storageRefreshButton) _storageRefreshButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
 	if (_storageDownloadButton) _storageDownloadButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
+	if (!shown) serverLabelPosition = (_storageUsernameDesc ? _storageUsernameDesc->getRelY() : 0);
+#else
+	_selectedStorageIndex = 0;
+	
+	if (_storagePopUpDesc) _storagePopUpDesc->setVisible(false);
+	if (_storagePopUp) _storagePopUp->setVisible(false);
+	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(false);
+	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(false);
+	if (_storageUsername) _storageUsername->setVisible(false);
+	if (_storageUsedSpaceDesc) _storageUsedSpaceDesc->setVisible(false);
+	if (_storageUsedSpace) _storageUsedSpace->setVisible(false);
+	if (_storageLastSyncDesc) _storageLastSyncDesc->setVisible(false);
+	if (_storageLastSync) _storageLastSync->setVisible(false);	
+	if (_storageConnectButton) _storageConnectButton->setVisible(false);
+	if (_storageRefreshButton) _storageRefreshButton->setVisible(false);
+	if (_storageDownloadButton) _storageDownloadButton->setVisible(false);
+
+	serverButtonPosition = (_storagePopUpDesc ? _storagePopUpDesc->getRelY() : 0);
+#endif
+#ifdef USE_SDL_NET
+	//determine original widget's positions
+	int16 x, y;
+	uint16 w, h;
+	int serverButtonY, serverInfoY;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud.RunServerButton", x, y, w, h))
+		warning("GlobalOptions_Cloud.RunServerButton's position is undefined");
+	serverButtonY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud.ServerInfoLabel", x, y, w, h))
+		warning("GlobalOptions_Cloud.ServerInfoLabel's position is undefined");
+	serverInfoY = y;
+		
+	if (serverLabelPosition < 0) serverLabelPosition = serverInfoY;
+	if (_runServerButton) _runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
+	if (_serverInfoLabel) _serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition);
+#else
+	if (_runServerButton) _runServerButton->setVisible(false);
+	if (_serverInfoLabel) _serverInfoLabel->setVisible(false);
+#endif
 }
-
+#endif
+#ifdef USE_CLOUD
 void GlobalOptionsDialog::storageInfoCallback(Cloud::Storage::StorageInfoResponse response) {
 	//we could've used response.value.email()
 	//but Storage already notified CloudMan
diff --git a/gui/options.h b/gui/options.h
index fa7a1d2..77470ae 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -247,7 +247,7 @@ protected:
 	PopUpWidget *_updatesPopUp;
 #endif
 
-#ifdef USE_CLOUD
+#if defined USE_CLOUD || defined USE_SDL_NET
 	//
 	// Cloud controls
 	//
@@ -263,9 +263,13 @@ protected:
 	ButtonWidget	 *_storageConnectButton;
 	ButtonWidget	 *_storageRefreshButton;
 	ButtonWidget	 *_storageDownloadButton;
+	ButtonWidget	 *_runServerButton;
+	StaticTextWidget *_serverInfoLabel;
 	bool _redrawCloudTab;
 
 	void setupCloudTab();
+#endif
+#ifdef USE_CLOUD
 	void storageInfoCallback(Cloud::Storage::StorageInfoResponse response);
 	void storageListDirectoryCallback(Cloud::Storage::ListDirectoryResponse response);
 #endif
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index d2e60d7..9e79ac2 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -583,6 +583,14 @@
 						type = 'Button'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'RunServerButton'
+						type = 'Button'
+				/>
+				<widget name = 'ServerInfoLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 1ff2e9f..9f00d2e 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -580,6 +580,14 @@
 						type = 'Button'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'RunServerButton'
+						type = 'Button'
+				/>
+				<widget name = 'ServerInfoLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 


Commit: 81c85b6651fcb4ba2f1dc0ba2955e34cb541fc35
    https://github.com/scummvm/scummvm/commit/81c85b6651fcb4ba2f1dc0ba2955e34cb541fc35
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix SaveLoadDialogs to check USE_CLOUD

Linking was failing when disabling curl support.

Changed paths:
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/saveload.cpp



diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index eff4a5a..faf6fa9 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -22,9 +22,11 @@
 
 #include "gui/saveload-dialog.h"
 
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
+#endif
 
 #include "common/translation.h"
 #include "common/config-manager.h"
@@ -39,6 +41,8 @@
 
 namespace GUI {
 
+#ifdef USE_CLOUD
+
 enum {
 	kCancelSyncCmd = 'PDCS',
 	kBackgroundSyncCmd = 'PDBS'
@@ -93,6 +97,7 @@ void SaveLoadCloudSyncProgressDialog::handleTickle() {
 
 	Dialog::handleTickle();
 }
+#endif
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) {
@@ -152,7 +157,9 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const b
 }
 
 SaveLoadChooserDialog::~SaveLoadChooserDialog() {
+#ifdef USE_CLOUD
 	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
+#endif
 }
 
 void SaveLoadChooserDialog::open() {
@@ -166,7 +173,9 @@ void SaveLoadChooserDialog::open() {
 }
 
 void SaveLoadChooserDialog::close() {
+#ifdef USE_CLOUD
 	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
+#endif
 	Dialog::close();
 }
 
@@ -206,14 +215,17 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	}
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
+#ifdef USE_CLOUD
 	if (cmd == kSavesSyncProgressCmd || cmd == kSavesSyncEndedCmd) {
 		//this dialog only gets these commands if the progress dialog was shown and user clicked "run in background"
 		return updateSaveList();
 	}
+#endif
 
 	return Dialog::handleCommand(sender, cmd, data);
 }
 
+#ifdef USE_CLOUD
 void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 	if (!CloudMan.isSyncing()) {
 		if (hasSavepathOverride) {
@@ -224,8 +236,10 @@ void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 		}
 	}
 }
+#endif
 
 void SaveLoadChooserDialog::handleTickle() {
+#ifdef USE_CLOUD
 	if (!_dialogWasShown && CloudMan.isSyncing()) {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
@@ -243,6 +257,7 @@ void SaveLoadChooserDialog::handleTickle() {
 			updateSaveList();
 		}
 	}
+#endif
 	Dialog::handleTickle();
 }
 
@@ -263,9 +278,11 @@ void SaveLoadChooserDialog::reflowLayout() {
 	Dialog::reflowLayout();
 }
 
-void SaveLoadChooserDialog::updateSaveList() {	
+void SaveLoadChooserDialog::updateSaveList() {
+#ifdef USE_CLOUD
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
 	g_system->getSavefileManager()->updateSavefilesList(files);
+#endif
 	listSaves();
 }
 
@@ -273,6 +290,8 @@ void SaveLoadChooserDialog::listSaves() {
 	if (!_metaEngine) return; //very strange
 	_saveList = _metaEngine->listSaves(_target.c_str());
 
+#ifdef USE_CLOUD
+	//if there is Cloud support, add currently synced files as "locked" saves in the list
 	if (_metaEngine->simpleSaveNames()) {
 		Common::String pattern = _target + ".###";
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
@@ -294,6 +313,7 @@ void SaveLoadChooserDialog::listSaves() {
 
 		Common::sort(_saveList.begin(), _saveList.end(), SaveStateDescriptorSlotComparator());
 	}
+#endif
 }
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 9c601fd..ba2c2be 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -30,6 +30,7 @@
 
 namespace GUI {
 
+#ifdef USE_CLOUD
 enum SaveLoadCloudSyncProgress {
 	kSavesSyncProgressCmd = 'SSPR',
 	kSavesSyncEndedCmd = 'SSEN'
@@ -46,6 +47,7 @@ public:
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 	virtual void handleTickle();
 };
+#endif
 
 #define kSwitchSaveLoadDialog -2
 
@@ -79,7 +81,9 @@ public:
 
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 
+#ifdef USE_CLOUD
 	virtual void runSaveSync(bool hasSavepathOverride);
+#endif
 	
 	virtual void handleTickle();
 
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index b3e6698..b08ab7f 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -87,7 +87,9 @@ int SaveLoadChooser::runModalWithPluginAndTarget(const EnginePlugin *plugin, con
 	if (!_impl)
 		return -1;
 
+#ifdef USE_CLOUD
 	_impl->runSaveSync(ConfMan.hasKey("savepath", target));
+#endif
 
 	// Set up the game domain as newly active domain, so
 	// target specific savepath will be checked


Commit: 211d9ed5f67712f5e0a1b23e7b3fb492b5256f89
    https://github.com/scummvm/scummvm/commit/211d9ed5f67712f5e0a1b23e7b3fb492b5256f89
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix Options' Cloud tab reflowing

Changed paths:
    gui/options.cpp



diff --git a/gui/options.cpp b/gui/options.cpp
index 4332784..bf692f8 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1694,6 +1694,7 @@ void GlobalOptionsDialog::reflowLayout() {
 
 	_tabWidget->setActiveTab(activeTab);
 	OptionsDialog::reflowLayout();
+	setupCloudTab();
 }
 
 #if defined USE_CLOUD || defined USE_SDL_NET
@@ -1746,7 +1747,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageRefreshButton) _storageRefreshButton->setVisible(false);
 	if (_storageDownloadButton) _storageDownloadButton->setVisible(false);
 
-	serverButtonPosition = (_storagePopUpDesc ? _storagePopUpDesc->getRelY() : 0);
+	serverLabelPosition = (_storagePopUpDesc ? _storagePopUpDesc->getRelY() : 0);
 #endif
 #ifdef USE_SDL_NET
 	//determine original widget's positions


Commit: 3da38ca60b65d3f1bd67b049f3c4b2a90a4d6a19
    https://github.com/scummvm/scummvm/commit/3da38ca60b65d3f1bd67b049f3c4b2a90a4d6a19
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Replace USE_CLOUD with USE_LIBCURL

In most cases that's the right one to check. USE_CLOUD is defined when
either USE_LIBCURL or USE_SDL_NET are, which means if there is no curl,
USE_CLOUD still could be defined and linking errors would appear.

Changed paths:
    backends/module.mk
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h
    backends/saves/savefile.cpp
    base/main.cpp
    gui/launcher.cpp
    gui/options.cpp
    gui/options.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/saveload.cpp



diff --git a/backends/module.mk b/backends/module.mk
index ecdca2e..ece3128 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -19,7 +19,7 @@ MODULE_OBJS := \
 	saves/default/default-saves.o \
 	timer/default/default-timer.o
 
-ifdef USE_CLOUD
+ifdef USE_LIBCURL
 MODULE_OBJS += \
 	cloud/cloudmanager.o \
 	cloud/iso8601.o \
@@ -45,11 +45,7 @@ MODULE_OBJS += \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
 	cloud/onedrive/onedrivelistdirectoryrequest.o \
-	cloud/onedrive/onedriveuploadrequest.o
-endif
-
-ifdef USE_LIBCURL
-MODULE_OBJS += \
+	cloud/onedrive/onedriveuploadrequest.o \
 	networking/curl/connectionmanager.o \
 	networking/curl/networkreadstream.o \
 	networking/curl/cloudicon.o \
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 06d4047..7f96868 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -27,7 +27,7 @@
 
 #include "common/scummsys.h"
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #include "common/file.h"
 #endif
@@ -47,7 +47,7 @@
 #include <errno.h>	// for removeSavefile()
 #endif
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 const char *DefaultSaveFileManager::TIMESTAMPS_FILENAME = "timestamps";
 #endif
 
@@ -147,7 +147,7 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 		}
 	}
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	// Update file's timestamp
 	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
 	timestamps[filename] = INVALID_TIMESTAMP;
@@ -237,7 +237,7 @@ void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
 	// Check that path exists and is usable.
 	checkPath(Common::FSNode(savePathName));
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
 	if (!files.empty()) updateSavefilesList(files); //makes this cache invalid
 	else _lockedFiles = files;
@@ -278,7 +278,7 @@ void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
 	_cachedDirectory = savePathName;
 }
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 
 Common::HashMap<Common::String, uint32> DefaultSaveFileManager::loadTimestamps() {
 	Common::HashMap<Common::String, uint32> timestamps;
@@ -374,6 +374,6 @@ Common::String DefaultSaveFileManager::concatWithSavesPath(Common::String name)
 	return path + '/' + name;
 }
 
-#endif // ifdef USE_CLOUD
+#endif // ifdef USE_LIBCURL
 
 #endif // !defined(DISABLE_DEFAULT_SAVEFILEMANAGER)
diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h
index e9edfb1..f245381 100644
--- a/backends/saves/default/default-saves.h
+++ b/backends/saves/default/default-saves.h
@@ -45,7 +45,7 @@ public:
 	virtual Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true);
 	virtual bool removeSavefile(const Common::String &filename);
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 
 	static const uint32 INVALID_TIMESTAMP = UINT_MAX;
 	static const char *TIMESTAMPS_FILENAME;
diff --git a/backends/saves/savefile.cpp b/backends/saves/savefile.cpp
index 1f007ca..5e98347 100644
--- a/backends/saves/savefile.cpp
+++ b/backends/saves/savefile.cpp
@@ -23,7 +23,7 @@
 #include "common/util.h"
 #include "common/savefile.h"
 #include "common/str.h"
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #endif
 
@@ -39,7 +39,7 @@ void OutSaveFile::clearErr() { _wrapped->clearErr(); }
 
 void OutSaveFile::finalize() {
 	_wrapped->finalize();
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	CloudMan.syncSaves();
 #endif
 }
diff --git a/base/main.cpp b/base/main.cpp
index 6c91305..dd2c3f5 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -66,10 +66,8 @@
 #endif
 
 #include "backends/keymapper/keymapper.h"
-#ifdef USE_CLOUD
-#include "backends/cloud/cloudmanager.h"
-#endif
 #ifdef USE_LIBCURL
+#include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #endif
 #ifdef USE_SDL_NET
@@ -485,7 +483,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	}
 #endif
 		
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	CloudMan.init();
 	CloudMan.syncSaves();
 	CloudMan.testFeature(); //TODO: remove later
@@ -604,8 +602,6 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #endif
 #ifdef USE_LIBCURL
 	Networking::ConnectionManager::destroy();
-#endif
-#ifdef USE_CLOUD
 	//I think it's important to destroy it after ConnectionManager
 	Cloud::CloudManager::destroy();
 #endif
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 1ea3169..309c747 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -51,7 +51,7 @@
 #include "gui/ThemeEval.h"
 
 #include "graphics/cursorman.h"
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #endif
 
@@ -566,7 +566,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 			// User made his choice...
 			Common::FSNode dir(browser.getResult());
 			_savePathWidget->setLabel(dir.getPath());
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 			MessageDialog warningMessage(_("Saves sync feature doesn't work with non-default directories. If you want your saves to sync, use default directory."));
 			warningMessage.runModal();
 #endif
@@ -847,7 +847,7 @@ void LauncherDialog::addGame() {
 		if (_browser->runModal() > 0) {
 			// User made his choice...
 			Common::FSNode dir(_browser->getResult());
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 			String selectedDirectory = dir.getPath();
 			String bannedDirectory = CloudMan.getDownloadLocalDirectory();
 			if (selectedDirectory.size() && selectedDirectory.lastChar() != '/' && selectedDirectory.lastChar() != '\\')
diff --git a/gui/options.cpp b/gui/options.cpp
index bf692f8..9d98334 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -44,7 +44,7 @@
 #include "audio/fmopl.h"
 #include "downloaddialog.h"
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #include "gui/storagewizarddialog.h"
 #endif
@@ -90,7 +90,7 @@ enum {
 };
 #endif
 
-#if defined USE_CLOUD || defined USE_SDL_NET
+#ifdef USE_CLOUD
 enum {
 	kConfigureStorageCmd = 'cfst',
 	kRefreshStorageCmd = 'rfst',
@@ -1266,7 +1266,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	new ButtonWidget(tab, "GlobalOptions_Misc.UpdatesCheckManuallyButton", _("Check now"), 0, kUpdatesCheckCmd);
 #endif
 
-#if defined USE_CLOUD || defined USE_SDL_NET
+#ifdef USE_CLOUD
 	//
 	// 7) The cloud tab
 	//
@@ -1275,7 +1275,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	else
 		tab->addTab(_c("Cloud", "lowres"));
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	_selectedStorageIndex = CloudMan.getStorageIndex();
 #else
 	_selectedStorageIndex = 0;
@@ -1283,7 +1283,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 
 	_storagePopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StoragePopupDesc", _("Storage:"), _("Active cloud storage"));
 	_storagePopUp = new PopUpWidget(tab, "GlobalOptions_Cloud.StoragePopup");
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	Common::StringArray list = CloudMan.listStorages();
 	for (uint32 i = 0; i < list.size(); ++i)
 		_storagePopUp->appendEntry(list[i], i);
@@ -1463,7 +1463,7 @@ void GlobalOptionsDialog::close() {
 		}
 #endif
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 		if (CloudMan.getStorageIndex() != _selectedStorageIndex) {
 			if (!CloudMan.switchStorage(_selectedStorageIndex)) {
 				bool anotherStorageIsWorking = CloudMan.isWorking();
@@ -1590,7 +1590,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		}
 		break;
 	}
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	case kPopUpItemSelectedCmd:
 	{
 		setupCloudTab();
@@ -1694,13 +1694,15 @@ void GlobalOptionsDialog::reflowLayout() {
 
 	_tabWidget->setActiveTab(activeTab);
 	OptionsDialog::reflowLayout();
+#ifdef USE_CLOUD
 	setupCloudTab();
+#endif
 }
 
-#if defined USE_CLOUD || defined USE_SDL_NET
+#ifdef USE_CLOUD
 void GlobalOptionsDialog::setupCloudTab() {
 	int serverLabelPosition = -1; //no override
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	_selectedStorageIndex = _storagePopUp->getSelectedTag();
 
 	bool shown = (_selectedStorageIndex != Cloud::kStorageNoneId);
@@ -1770,7 +1772,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 #endif
 }
 #endif
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 void GlobalOptionsDialog::storageInfoCallback(Cloud::Storage::StorageInfoResponse response) {
 	//we could've used response.value.email()
 	//but Storage already notified CloudMan
diff --git a/gui/options.h b/gui/options.h
index 77470ae..c4c70aa 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -37,7 +37,7 @@
 #include "gui/fluidsynth-dialog.h"
 #endif
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/storage.h"
 #endif
 
@@ -247,7 +247,7 @@ protected:
 	PopUpWidget *_updatesPopUp;
 #endif
 
-#if defined USE_CLOUD || defined USE_SDL_NET
+#ifdef USE_CLOUD
 	//
 	// Cloud controls
 	//
@@ -269,7 +269,7 @@ protected:
 
 	void setupCloudTab();
 #endif
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	void storageInfoCallback(Cloud::Storage::StorageInfoResponse response);
 	void storageListDirectoryCallback(Cloud::Storage::ListDirectoryResponse response);
 #endif
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index faf6fa9..222f637 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -22,7 +22,7 @@
 
 #include "gui/saveload-dialog.h"
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
@@ -41,7 +41,7 @@
 
 namespace GUI {
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 
 enum {
 	kCancelSyncCmd = 'PDCS',
@@ -157,7 +157,7 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const b
 }
 
 SaveLoadChooserDialog::~SaveLoadChooserDialog() {
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
 #endif
 }
@@ -173,7 +173,7 @@ void SaveLoadChooserDialog::open() {
 }
 
 void SaveLoadChooserDialog::close() {
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	CloudMan.setSyncTarget(nullptr); //not that dialog, at least
 #endif
 	Dialog::close();
@@ -215,7 +215,7 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	}
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	if (cmd == kSavesSyncProgressCmd || cmd == kSavesSyncEndedCmd) {
 		//this dialog only gets these commands if the progress dialog was shown and user clicked "run in background"
 		return updateSaveList();
@@ -225,7 +225,7 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	return Dialog::handleCommand(sender, cmd, data);
 }
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 	if (!CloudMan.isSyncing()) {
 		if (hasSavepathOverride) {
@@ -239,7 +239,7 @@ void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 #endif
 
 void SaveLoadChooserDialog::handleTickle() {
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	if (!_dialogWasShown && CloudMan.isSyncing()) {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
@@ -279,7 +279,7 @@ void SaveLoadChooserDialog::reflowLayout() {
 }
 
 void SaveLoadChooserDialog::updateSaveList() {
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
 	g_system->getSavefileManager()->updateSavefilesList(files);
 #endif
@@ -290,7 +290,7 @@ void SaveLoadChooserDialog::listSaves() {
 	if (!_metaEngine) return; //very strange
 	_saveList = _metaEngine->listSaves(_target.c_str());
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	//if there is Cloud support, add currently synced files as "locked" saves in the list
 	if (_metaEngine->simpleSaveNames()) {
 		Common::String pattern = _target + ".###";
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index ba2c2be..4e375bf 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -30,7 +30,7 @@
 
 namespace GUI {
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 enum SaveLoadCloudSyncProgress {
 	kSavesSyncProgressCmd = 'SSPR',
 	kSavesSyncEndedCmd = 'SSEN'
@@ -81,7 +81,7 @@ public:
 
 	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	virtual void runSaveSync(bool hasSavepathOverride);
 #endif
 	
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index b08ab7f..3072aa6 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -87,7 +87,7 @@ int SaveLoadChooser::runModalWithPluginAndTarget(const EnginePlugin *plugin, con
 	if (!_impl)
 		return -1;
 
-#ifdef USE_CLOUD
+#ifdef USE_LIBCURL
 	_impl->runSaveSync(ConfMan.hasKey("savepath", target));
 #endif
 


Commit: e601c39802adbb64dce4b449c617e1786ef584ed
    https://github.com/scummvm/scummvm/commit/e601c39802adbb64dce4b449c617e1786ef584ed
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make "Run server" button active

It should show the real server's IP over there, but that doesn't work
yet.

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    gui/options.cpp
    gui/options.h
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 1cfbbb8..134ccd8 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -71,7 +71,10 @@ void LocalWebserver::stopTimer() {
 void LocalWebserver::start() {
 	_handleMutex.lock();
 	_stopOnIdle = false;
-	if (_timerStarted) return;
+	if (_timerStarted) {
+		_handleMutex.unlock();
+		return;
+	}
 	startTimer();
 
 	// Create a listening TCP socket
@@ -79,6 +82,11 @@ void LocalWebserver::start() {
 	if (SDLNet_ResolveHost(&ip, NULL, SERVER_PORT) == -1) {
 		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
+	_address = Common::String::format(
+		"http://%u.%u.%u.%u:%u/",
+		(ip.host>>24)&0xFF, (ip.host >> 16) & 0xFF, (ip.host >> 8) & 0xFF, ip.host & 0xFF,
+		SERVER_PORT
+	);
 	_serverSocket = SDLNet_TCP_Open(&ip);
 	if (!_serverSocket) {
 		error("SDLNet_TCP_Open: %s\n", SDLNet_GetError());		
@@ -130,8 +138,18 @@ void LocalWebserver::removePathHandler(Common::String path) {
 	_pathHandlers.erase(path);
 }
 
+Common::String LocalWebserver::getAddress() { return _address;  }
+
 IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
 
+bool LocalWebserver::isRunning() {
+	bool result = false;
+	_handleMutex.lock();
+	result = _timerStarted;
+	_handleMutex.unlock();
+	return result;
+}
+
 void LocalWebserver::handle() {
 	_handleMutex.lock();
 	int numready = SDLNet_CheckSockets(_set, 0);
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 2bd8daf..f73d1b2 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -59,6 +59,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	IndexPageHandler _indexPageHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
+	Common::String _address;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
@@ -76,7 +77,9 @@ public:
 	void addPathHandler(Common::String path, ClientHandler handler);
 	void removePathHandler(Common::String path);
 
+	Common::String getAddress();
 	IndexPageHandler &indexPageHandler();
+	bool isRunning();
 
 	static void setClientGetHandler(Client &client, Common::String response, long code = 200, const char *mimeType = nullptr);
 	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
diff --git a/gui/options.cpp b/gui/options.cpp
index 9d98334..bb9bf4d 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -49,6 +49,10 @@
 #include "gui/storagewizarddialog.h"
 #endif
 
+#ifdef USE_SDL_NET
+#include "backends/networking/sdl_net/localwebserver.h"
+#endif
+
 namespace GUI {
 
 enum {
@@ -1306,10 +1310,13 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	_storageDownloadButton = new ButtonWidget(tab, "GlobalOptions_Cloud.DownloadButton", _("Downloads"), _("Open downloads manager dialog"), kDownloadStorageCmd);
 
 	_runServerButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
-	_serverInfoLabel = new StaticTextWidget(tab, "GlobalOptions_Cloud.ServerInfoLabel", "Not running");
+	_serverInfoLabel = new StaticTextWidget(tab, "GlobalOptions_Cloud.ServerInfoLabel", _("Not running"));
 
 	setupCloudTab();
 	_redrawCloudTab = false;
+#ifdef USE_SDL_NET
+	_serverWasRunning = false;
+#endif
 #endif
 
 	// Activate the first tab
@@ -1623,9 +1630,8 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 #ifdef USE_SDL_NET
 	case kRunServerCmd:
 	{
-		//TODO
-		//DownloadDialog dialog(_selectedStorageIndex);
-		//dialog.runModal();
+		if (LocalServer.isRunning()) LocalServer.stopOnIdle();
+		else LocalServer.start();
 		break;
 	}
 #endif
@@ -1653,6 +1659,12 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 void GlobalOptionsDialog::handleTickle() {
 	OptionsDialog::handleTickle();
 #ifdef USE_CLOUD
+#ifdef USE_SDL_NET
+	if (LocalServer.isRunning() != _serverWasRunning) {
+		_serverWasRunning = !_serverWasRunning;
+		_redrawCloudTab = true;
+	}
+#endif
 	if (_redrawCloudTab) {
 		setupCloudTab();
 		draw();
@@ -1762,10 +1774,19 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud.ServerInfoLabel", x, y, w, h))
 		warning("GlobalOptions_Cloud.ServerInfoLabel's position is undefined");
 	serverInfoY = y;
+
+	bool serverIsRunning = LocalServer.isRunning();
 		
 	if (serverLabelPosition < 0) serverLabelPosition = serverInfoY;
-	if (_runServerButton) _runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
-	if (_serverInfoLabel) _serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition);
+	if (_runServerButton) {
+		_runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
+		_runServerButton->setLabel(_(serverIsRunning ? "Stop server" : "Run server"));
+	}
+	if (_serverInfoLabel) {
+		_serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition);
+		if (serverIsRunning) _serverInfoLabel->setLabel(LocalServer.getAddress());
+		else _serverInfoLabel->setLabel(_("Not running"));
+	}
 #else
 	if (_runServerButton) _runServerButton->setVisible(false);
 	if (_serverInfoLabel) _serverInfoLabel->setVisible(false);
diff --git a/gui/options.h b/gui/options.h
index c4c70aa..1d7f9ff 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -266,6 +266,9 @@ protected:
 	ButtonWidget	 *_runServerButton;
 	StaticTextWidget *_serverInfoLabel;
 	bool _redrawCloudTab;
+#ifdef USE_SDL_NET
+	bool _serverWasRunning;
+#endif
 
 	void setupCloudTab();
 #endif
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 6734d1c..7871017 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -39,7 +39,7 @@ enum {
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false) {
+	Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false), _stopServerOnClose(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	Common::String headline = Common::String::format(_("%s Storage Connection Wizard"), CloudMan.listStorages()[_storageId].c_str());
@@ -83,6 +83,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 void StorageWizardDialog::open() {
 	Dialog::open();
 #ifdef USE_SDL_NET
+	_stopServerOnClose = !LocalServer.isRunning();
 	LocalServer.start();
 	LocalServer.indexPageHandler().setTarget(this);
 #endif
@@ -90,7 +91,7 @@ void StorageWizardDialog::open() {
 
 void StorageWizardDialog::close() {
 #ifdef USE_SDL_NET
-	LocalServer.stopOnIdle();
+	if (_stopServerOnClose) LocalServer.stopOnIdle();
 	LocalServer.indexPageHandler().setTarget(nullptr);
 #endif
 	Dialog::close();
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 93df56d..ade3c57 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -46,6 +46,7 @@ class StorageWizardDialog : public Dialog {
 	StaticTextWidget *_messageWidget;
 	ButtonWidget *_connectWidget;
 	bool _close;
+	bool _stopServerOnClose;
 
 	/**
 	 * Return the value corresponding to the given character.


Commit: eae57728d17beb74e4631c488ad8d1b4aaea7aea
    https://github.com/scummvm/scummvm/commit/eae57728d17beb74e4631c488ad8d1b4aaea7aea
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Resolve local machine's IP

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 134ccd8..d1dda16 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -82,11 +82,25 @@ void LocalWebserver::start() {
 	if (SDLNet_ResolveHost(&ip, NULL, SERVER_PORT) == -1) {
 		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
-	_address = Common::String::format(
-		"http://%u.%u.%u.%u:%u/",
-		(ip.host>>24)&0xFF, (ip.host >> 16) & 0xFF, (ip.host >> 8) & 0xFF, ip.host & 0xFF,
-		SERVER_PORT
-	);
+
+	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", SERVER_PORT);
+
+	const char *name = SDLNet_ResolveIP(&ip);
+	if (name == NULL) {
+		warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+	} else {
+		IPaddress localIp;
+		if (SDLNet_ResolveHost(&localIp, name, SERVER_PORT) == -1) {
+			warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		} else {
+			_address = Common::String::format(
+				"http://%u.%u.%u.%u:%u/",
+				localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
+				SERVER_PORT
+			);
+		}
+	}
+
 	_serverSocket = SDLNet_TCP_Open(&ip);
 	if (!_serverSocket) {
 		error("SDLNet_TCP_Open: %s\n", SDLNet_GetError());		


Commit: c409d29f66057a1a2c0e405e60cc8aa5623bc52f
    https://github.com/scummvm/scummvm/commit/c409d29f66057a1a2c0e405e60cc8aa5623bc52f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "/files" handler

Shows the page with controls, but doesn't actually list the directories,
create the specified ones or allows to upload files yet.

Changed paths:
  A backends/networking/wwwroot/files.html
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index 2d1f2c2..c2ea470 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -33,11 +33,13 @@ namespace Networking {
 
 #define ARCHIVE_NAME "wwwroot.zip"
 #define INDEX_PAGE_NAME "index.html"
+#define FILES_PAGE_NAME "files.html"
 
 IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
 
 IndexPageHandler::~IndexPageHandler() {
 	LocalServer.removePathHandler("/");
+	LocalServer.removePathHandler("/files/");
 
 	Common::ArchiveMemberList fileList = listArchive();
 	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
@@ -68,6 +70,28 @@ void IndexPageHandler::handle(Client &client) {
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
+void IndexPageHandler::handleFiles(Client &client) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{content}</body></html>"; //TODO
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(FILES_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	// TODO: list specified directory
+	Common::String path = client.queryParameter("path");
+	Common::String content = "";
+
+	//these occur twice:
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{upload_files_button}", _("Upload files")); //tab
+	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
+	replace(response, "{create_directory_desc}", _("Type new directory name:"));
+	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
+	replace(response, "{content}", content);	
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
 void IndexPageHandler::handleResource(Client &client) {
 	Common::String filename = client.path();
 	filename.deleteChar(0);
@@ -80,11 +104,13 @@ void IndexPageHandler::addPathHandler(LocalWebserver &server) {
 	// we can't use LocalServer yet, because IndexPageHandler is created while LocalWebserver is created
 	// (thus no _instance is available and it causes stack overflow)
 	server.addPathHandler("/", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle));
+	server.addPathHandler("/files", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handleFiles));
 
 	Common::ArchiveMemberList fileList = listArchive();
 	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
 		Common::ArchiveMember const &m = **it;
-		if (m.getName() == INDEX_PAGE_NAME) continue;		
+		if (m.getName() == INDEX_PAGE_NAME) continue;
+		if (m.getName() == FILES_PAGE_NAME) continue;
 		server.addPathHandler("/" + m.getName(), new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handleResource));		
 	}
 }
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index cbcc6da..e70ffd5 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -34,6 +34,7 @@ class IndexPageHandler: public GUI::CommandSender {
 	Common::String _code;
 
 	void handle(Client &client);
+	void handleFiles(Client &client);
 	void handleResource(Client &client);
 
 	void replace(Common::String &source, const Common::String &what, const Common::String &with);
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 403385b..1a301f5 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/files.html b/backends/networking/wwwroot/files.html
new file mode 100644
index 0000000..8a27889
--- /dev/null
+++ b/backends/networking/wwwroot/files.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+	<head>
+		<title>ScummVM</title>
+		<meta charset="utf-8"/>
+		<link rel="stylesheet" type="text/css" href="style.css"/>
+	</head>
+	<body>
+		<div class="container">
+			<div class='header'>
+				<center><img src="logo.png"/></center>
+			</div>
+			<div class="controls">
+				<table class="buttons"><tr>
+					<td><a href="javascript:show('create_directory');">{create_directory_button}</a></td>
+					<td><a href="javascript:show('upload_file');">{upload_files_button}</a></td>
+				</tr></table>
+				<div id="create_directory" class="modal">
+					<p>{create_directory_desc}</p>
+					<form action="create">
+						<input type="text" name="directory_name" value=""/>
+						<input type="submit" value="{create_directory_button}"/>
+					</form>
+				</div>
+				<div id="upload_file" class="modal">
+					<p>{upload_file_desc}</p>
+					<form action="upload" method="post">
+						<input type="file" name="upload_file"/>
+						<input type="submit" value="{upload_file_button}"/>
+					</form>
+				</div>
+			</div>
+			<div class="content">
+				{content}
+			</div>
+		</div>
+		<script>
+			function show(id) {
+				var e = document.getElementById(id);
+				var visible = (e.style.display == "block");
+				if (visible) id = ""; //hide
+
+				e = document.getElementById("create_directory");
+				e.style.display = (e.id == id ? "block" : "none");
+				e = document.getElementById("upload_file");
+				e.style.display = (e.id == id ? "block" : "none");
+			}
+		</script>
+	</body>
+</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index 75c8378..59d9667 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -22,3 +22,67 @@ html {
 }
 
 .content p { margin: 0 0 6pt 0; }
+
+.controls {
+	padding: 8pt;
+	background: #FFF;
+	font-family: Tahoma;
+	font-size: 16pt;
+}
+
+.controls .buttons {
+	width: 100%;
+	max-width: 500pt;
+	margin: -8pt auto;
+	border: 0;
+	border-spacing: 0;
+}
+
+.controls .buttons td {
+	width: 50%;
+	text-align: center;
+	margin: 0;
+	padding: 0;
+}
+
+.controls .buttons a {
+	display: block;
+	height: 40pt;
+	line-height: 38pt;
+	vertical-align: middle;
+	color: #000;
+	text-decoration: none;
+}
+
+.controls .buttons a:hover {
+	background: #F3F3F3;
+}
+
+.modal {
+	margin-top: 10pt;
+	display: none;
+}
+
+.modal p { margin: 0 0 6pt 0; }
+
+#create_directory input[type="text"], #upload_file input[type="file"] {
+	width: calc(100% - 2 * 5pt);
+}
+
+.modal input {
+	border: 1px solid #EEE;
+	padding: 5pt;
+	font-size: 12pt;
+}
+
+.modal input[type="submit"] {
+	display: block;
+	margin: 6pt auto;
+	background: #DDD;
+	border: 0;
+}
+
+.modal input[type="submit"]:hover {
+	background: #F3F3F3;
+	cursor: pointer;
+}
\ No newline at end of file


Commit: e47b6c04b30565b1db238c2784e562f3f4472d70
    https://github.com/scummvm/scummvm/commit/e47b6c04b30565b1db238c2784e562f3f4472d70
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make "/files" list directories

Including both virtual "/root" and "/saves" ones.

Changed paths:
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/files.html



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index c2ea470..c44c3d4 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -22,9 +22,11 @@
 
 #include "backends/networking/sdl_net/indexpagehandler.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/saves/default/default-saves.h"
 #include "common/archive.h"
 #include "common/config-manager.h"
 #include "common/file.h"
+#include "common/savefile.h"
 #include "common/translation.h"
 #include "common/unzip.h"
 #include "gui/storagewizarddialog.h"
@@ -71,15 +73,28 @@ void IndexPageHandler::handle(Client &client) {
 }
 
 void IndexPageHandler::handleFiles(Client &client) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{content}</body></html>"; //TODO
+	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
+	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = getArchiveFile(FILES_PAGE_NAME);
 	if (stream) response = readEverythingFromStream(stream);
 
-	// TODO: list specified directory
 	Common::String path = client.queryParameter("path");
 	Common::String content = "";
+	
+	// show an error message if failed to list directory
+	if (!listDirectory(path, content, itemTemplate)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=/\">%s</a>",
+				_("ScummVM couldn't list the directory you specified."),
+				_("Back to the files manager")
+			)
+		);
+		return;
+	}
 
 	//these occur twice:
 	replace(response, "{create_directory_button}", _("Create directory"));
@@ -98,6 +113,121 @@ void IndexPageHandler::handleResource(Client &client) {
 	LocalWebserver::setClientGetHandler(client, getArchiveFile(filename), 200, LocalWebserver::determineMimeType(filename));
 }
 
+void IndexPageHandler::handleErrorMessage(Client &client, Common::String message) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+/// "files/"-related
+
+Common::String IndexPageHandler::parentPath(Common::String path) {
+	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
+	if (!path.empty()) {
+		for (int i = path.size() - 1; i >= 0; --i)
+			if (i == 0 || path[i] == '/' || path[i] == '\\') {
+				path.erase(i);
+				break;
+			}
+	}
+	if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	return path;
+}
+
+void IndexPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size) {
+	Common::String item = itemTemplate;
+	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + path);
+	replace(item, "{name}", name);
+	replace(item, "{size}", size);
+	content += item;
+}
+
+bool IndexPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
+	if (path == "" || path == "/") {
+		addItem(content, itemTemplate, true, "/root/", _("File system root"));
+		addItem(content, itemTemplate, true, "/saves/", _("Saved games"));
+		return true;
+	}
+
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd)) return false;
+
+	Common::FSNode node = Common::FSNode(path);
+	if (path == "/") node = node.getParent(); // absolute root
+	if (!node.isDirectory()) return false;
+
+	// list directory
+	Common::FSList _nodeContent;
+	if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files
+		_nodeContent.clear();
+	else
+		Common::sort(_nodeContent.begin(), _nodeContent.end());
+
+	// add parent directory link
+	{
+		Common::String filePath = path;
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		if (filePath == "" || filePath == "/" || filePath == "\\")
+			filePath = "/";
+		else
+			filePath = parentPath(prefixToAdd + filePath);
+		addItem(content, itemTemplate, true, filePath, _("Parent directory"));
+	}
+
+	// fill the content
+	for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
+		Common::String name = i->getDisplayName();
+		if (i->isDirectory()) name += "/";
+
+		Common::String filePath = i->getPath();
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		filePath = prefixToAdd + filePath;
+
+		addItem(content, itemTemplate, i->isDirectory(), filePath, name);
+	}
+
+	return true;
+}
+
+bool IndexPageHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd) {
+	// <path> is not empty, but could lack the trailing slash	
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+
+	if (path.hasPrefix("/root")) {
+		prefixToAdd = "/root/";
+		prefixToRemove = "";
+		path.erase(0, 5);
+		if (path == "") path = "/"; // absolute root is '/'
+		if (path != "/") path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
+		return true;
+	}
+
+	if (path.hasPrefix("/saves")) {
+		prefixToAdd = "/saves/";
+
+		// determine savepath (prefix to remove)
+		DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
+		prefixToRemove = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
+		if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
+			prefixToRemove += '/';
+
+		path.erase(0, 6);
+		if (path.size() && (path[0] == '/' || path[0] == '\\'))
+			path.deleteChar(0);
+		path = prefixToRemove + path;
+		return true;
+	}
+
+	return false;
+}
+
 /// public
 
 void IndexPageHandler::addPathHandler(LocalWebserver &server) {
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index e70ffd5..0ccbf32 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -36,6 +36,30 @@ class IndexPageHandler: public GUI::CommandSender {
 	void handle(Client &client);
 	void handleFiles(Client &client);
 	void handleResource(Client &client);
+	void handleErrorMessage(Client &client, Common::String message);
+
+	// "files/"-related
+	Common::String parentPath(Common::String path);
+
+	/** Helper method for adding items into the files list. */
+	void addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size = "");
+
+	/**
+	 * Lists the directory <path>.
+	 *
+	 * Returns true on success.
+	 */
+	bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate);
+
+	/**
+	 * Transforms virtual <path> into actual file system path.
+	 *
+	 * Fills prefixes with actual file system prefix ("to remove")
+	 * and virtual path prefix ("to add").
+	 *
+	 * Returns true on success.
+	 */
+	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd);
 
 	void replace(Common::String &source, const Common::String &what, const Common::String &with);
 	Common::Archive *getZipArchive();
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 1a301f5..da031c8 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/files.html b/backends/networking/wwwroot/files.html
index 8a27889..2baf4a0 100644
--- a/backends/networking/wwwroot/files.html
+++ b/backends/networking/wwwroot/files.html
@@ -31,7 +31,9 @@
 				</div>
 			</div>
 			<div class="content">
-				{content}
+				<table class="files_list">
+					{content}
+				</table>
 			</div>
 		</div>
 		<script>


Commit: 627bda9d82c178641e5b5253379c21ce556eb3c2
    https://github.com/scummvm/scummvm/commit/627bda9d82c178641e5b5253379c21ce556eb3c2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
COMMON: Add replace(String, String, String)

Searches for a substring in the string and replaces it with the other
string.

Changed paths:
    backends/networking/sdl_net/indexpagehandler.cpp
    backends/networking/sdl_net/indexpagehandler.h
    common/str.cpp
    common/str.h



diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
index c44c3d4..b3790a8 100644
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/indexpagehandler.cpp
@@ -249,15 +249,6 @@ Common::String IndexPageHandler::code() { return _code; }
 
 /// utils
 
-void IndexPageHandler::replace(Common::String &source, const Common::String &what, const Common::String &with) {
-	const char *cstr = source.c_str();
-	const char *position = strstr(cstr, what.c_str());
-	if (position) {
-		uint32 index = position - cstr;
-		source.replace(index, what.size(), with);
-	}
-}
-
 Common::Archive *IndexPageHandler::getZipArchive() {
 	// first search in themepath
 	if (ConfMan.hasKey("themepath")) {
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
index 0ccbf32..a75c7a0 100644
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ b/backends/networking/sdl_net/indexpagehandler.h
@@ -61,7 +61,6 @@ class IndexPageHandler: public GUI::CommandSender {
 	 */
 	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd);
 
-	void replace(Common::String &source, const Common::String &what, const Common::String &with);
 	Common::Archive *getZipArchive();
 	Common::ArchiveMemberList listArchive();
 	Common::SeekableReadStream *getArchiveFile(Common::String name);
diff --git a/common/str.cpp b/common/str.cpp
index 326b4a8..90bd539 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -838,6 +838,15 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMod
 	}
 }
 
+void replace(Common::String &source, const Common::String &what, const Common::String &with) {
+	const char *cstr = source.c_str();
+	const char *position = strstr(cstr, what.c_str());
+	if (position) {
+		uint32 index = position - cstr;
+		source.replace(index, what.size(), with);
+	}
+}
+
 String tag2string(uint32 tag) {
 	char str[5];
 	str[0] = (char)(tag >> 24);
diff --git a/common/str.h b/common/str.h
index 89b832d..1293d89 100644
--- a/common/str.h
+++ b/common/str.h
@@ -390,6 +390,15 @@ String normalizePath(const String &path, const char sep);
  */
 bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
 
+/**
+ * Function which replaces substring with the other. It happens in place.
+ * If there is no substring found, original string is not changed.
+ *
+ * @param source String to search and replace substring in.
+ * @param what Substring to replace.
+ * @param with String to replace with.
+ */
+void replace(Common::String &source, const Common::String &what, const Common::String &with);
 
 /**
  * Take a 32 bit value and turn it into a four character string, where each of


Commit: 48e3fff6bcea94da5bd46ee2def17eb6bdca716c
    https://github.com/scummvm/scummvm/commit/48e3fff6bcea94da5bd46ee2def17eb6bdca716c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Refactor LocalWebserver

Its handlers are now more compact. This commit moves Handler classes in
handlers\ directory.

ResourceHandler ignores "hidden" files in the archive, and these are
used as markup templates in IndexPageHandler and FilesPageHandler.

Changed paths:
  A backends/networking/sdl_net/handlers/basehandler.cpp
  A backends/networking/sdl_net/handlers/basehandler.h
  A backends/networking/sdl_net/handlers/filesbasehandler.cpp
  A backends/networking/sdl_net/handlers/filesbasehandler.h
  A backends/networking/sdl_net/handlers/filespagehandler.cpp
  A backends/networking/sdl_net/handlers/filespagehandler.h
  A backends/networking/sdl_net/handlers/indexpagehandler.cpp
  A backends/networking/sdl_net/handlers/indexpagehandler.h
  A backends/networking/sdl_net/handlers/resourcehandler.cpp
  A backends/networking/sdl_net/handlers/resourcehandler.h
  A backends/networking/wwwroot/.files.html
  A backends/networking/wwwroot/.index.html
  R backends/networking/sdl_net/indexpagehandler.cpp
  R backends/networking/sdl_net/indexpagehandler.h
  R backends/networking/wwwroot/files.html
  R backends/networking/wwwroot/index.html
    backends/module.mk
    backends/networking/make_archive.py
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/wwwroot.zip



diff --git a/backends/module.mk b/backends/module.mk
index ece3128..7dd29be 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -58,7 +58,11 @@ ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/client.o \
 	networking/sdl_net/getclienthandler.o \
-	networking/sdl_net/indexpagehandler.o \
+	networking/sdl_net/handlers/basehandler.o \
+	networking/sdl_net/handlers/filesbasehandler.o \
+	networking/sdl_net/handlers/filespagehandler.o \
+	networking/sdl_net/handlers/indexpagehandler.o \
+	networking/sdl_net/handlers/resourcehandler.o \
 	networking/sdl_net/localwebserver.o
 endif
 
diff --git a/backends/networking/make_archive.py b/backends/networking/make_archive.py
index 72d3b01..a55a4bd 100644
--- a/backends/networking/make_archive.py
+++ b/backends/networking/make_archive.py
@@ -20,7 +20,7 @@ def buildArchive(archiveName):
 	filenames = os.listdir('.')
 	filenames.sort()
 	for filename in filenames:
-		if os.path.isfile(filename) and not filename[0] == '.' and filename.endswith(ARCHIVE_FILE_EXTENSIONS):
+		if os.path.isfile(filename) and filename.endswith(ARCHIVE_FILE_EXTENSIONS):
 			zf.write(filename, './' + filename)
 			print ("    Adding file: " + filename)
 
diff --git a/backends/networking/sdl_net/handlers/basehandler.cpp b/backends/networking/sdl_net/handlers/basehandler.cpp
new file mode 100644
index 0000000..da90bb4
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/basehandler.cpp
@@ -0,0 +1,100 @@
+/* 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 "backends/networking/sdl_net/handlers/basehandler.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/unzip.h"
+
+namespace Networking {
+
+#define ARCHIVE_NAME "wwwroot.zip"
+
+BaseHandler::BaseHandler() {}
+
+BaseHandler::~BaseHandler() {}
+
+/// utils
+
+Common::Archive *BaseHandler::getZipArchive() const {
+	// first search in themepath
+	if (ConfMan.hasKey("themepath")) {
+		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
+		if (!node.exists() || !node.isReadable() || !node.isDirectory())
+			return nullptr;
+
+		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
+		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
+			Common::SeekableReadStream *const stream = fileNode.createReadStream();
+			Common::Archive *zipArchive = Common::makeZipArchive(stream);
+			if (zipArchive) return zipArchive;
+		}
+	}
+
+	// then use SearchMan to find it
+	Common::ArchiveMemberList fileList;
+	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember       const &m = **it;
+		Common::SeekableReadStream *const stream = m.createReadStream();
+		Common::Archive *zipArchive = Common::makeZipArchive(stream);
+		if (zipArchive) return zipArchive;
+	}
+
+	return nullptr;
+}
+
+Common::ArchiveMemberList BaseHandler::listArchive() const {
+	Common::ArchiveMemberList resultList;
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		zipArchive->listMembers(resultList);
+		delete zipArchive;
+	}
+	return resultList;
+}
+
+Common::SeekableReadStream *BaseHandler::getArchiveFile(Common::String name) const {
+	Common::SeekableReadStream *result = nullptr;
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
+		if (ptr.get() == nullptr) return nullptr;
+		result = ptr->createReadStream();
+		delete zipArchive;
+	}
+	return result;
+}
+
+Common::String BaseHandler::readEverythingFromStream(Common::SeekableReadStream *const stream) {
+	Common::String result;
+	char buf[1024];
+	uint32 readBytes;
+	while (!stream->eos()) {
+		readBytes = stream->read(buf, 1024);
+		result += Common::String(buf, readBytes);
+	}
+	return result;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/basehandler.h b/backends/networking/sdl_net/handlers/basehandler.h
new file mode 100644
index 0000000..feea83a
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/basehandler.h
@@ -0,0 +1,50 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_BASEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_BASEHANDLER_H
+
+#include "backends/networking/sdl_net/client.h"
+#include "common/archive.h"
+#include "common/callback.h"
+
+namespace Networking {
+
+typedef Common::BaseCallback<Client &> *ClientHandlerCallback;
+
+class BaseHandler {
+protected:
+	Common::Archive *getZipArchive() const;
+	Common::ArchiveMemberList listArchive() const;
+	Common::SeekableReadStream *getArchiveFile(Common::String name) const;
+	static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
+
+public:
+	BaseHandler();
+	virtual ~BaseHandler();
+
+	virtual ClientHandlerCallback getHandler() = 0;
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
new file mode 100644
index 0000000..4575e4d
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -0,0 +1,79 @@
+/* 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 "backends/networking/sdl_net/handlers/filesbasehandler.h"
+#include "backends/saves/default/default-saves.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+
+namespace Networking {
+
+FilesBaseHandler::FilesBaseHandler() {}
+
+FilesBaseHandler::~FilesBaseHandler() {}
+
+Common::String FilesBaseHandler::parentPath(Common::String path) {
+	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
+	if (!path.empty()) {
+		for (int i = path.size() - 1; i >= 0; --i)
+			if (i == 0 || path[i] == '/' || path[i] == '\\') {
+				path.erase(i);
+				break;
+			}
+	}
+	if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	return path;
+}
+
+bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd) {
+	// <path> is not empty, but could lack the trailing slash	
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+
+	if (path.hasPrefix("/root")) {
+		prefixToAdd = "/root/";
+		prefixToRemove = "";
+		path.erase(0, 5);
+		if (path == "") path = "/"; // absolute root is '/'
+		if (path != "/") path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
+		return true;
+	}
+
+	if (path.hasPrefix("/saves")) {
+		prefixToAdd = "/saves/";
+
+		// determine savepath (prefix to remove)
+		DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
+		prefixToRemove = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
+		if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
+			prefixToRemove += '/';
+
+		path.erase(0, 6);
+		if (path.size() && (path[0] == '/' || path[0] == '\\'))
+			path.deleteChar(0);
+		path = prefixToRemove + path;
+		return true;
+	}
+
+	return false;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.h b/backends/networking/sdl_net/handlers/filesbasehandler.h
new file mode 100644
index 0000000..3b631a9
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.h
@@ -0,0 +1,50 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_FILESBASEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_FILESBASEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/basehandler.h"
+
+namespace Networking {
+
+class FilesBaseHandler: public BaseHandler {
+protected:
+	Common::String parentPath(Common::String path);
+
+	/**
+	* Transforms virtual <path> into actual file system path.
+	*
+	* Fills prefixes with actual file system prefix ("to remove")
+	* and virtual path prefix ("to add").
+	*
+	* Returns true on success.
+	*/
+	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd);
+public:
+	FilesBaseHandler();
+	virtual ~FilesBaseHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
new file mode 100644
index 0000000..360fe20
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -0,0 +1,146 @@
+/* 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 "backends/networking/sdl_net/handlers/filespagehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/file.h"
+#include "common/translation.h"
+
+namespace Networking {
+
+#define INDEX_PAGE_NAME ".index.html"
+#define FILES_PAGE_NAME ".files.html"
+
+FilesPageHandler::FilesPageHandler() {}
+
+FilesPageHandler::~FilesPageHandler() {}
+
+void FilesPageHandler::handle(Client &client) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
+	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(FILES_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	Common::String path = client.queryParameter("path");
+	Common::String content = "";
+	
+	// show an error message if failed to list directory
+	if (!listDirectory(path, content, itemTemplate)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=/\">%s</a>",
+				_("ScummVM couldn't list the directory you specified."),
+				_("Back to the files manager")
+			)
+		);
+		return;
+	}
+
+	//these occur twice:
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{upload_files_button}", _("Upload files")); //tab
+	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
+	replace(response, "{create_directory_desc}", _("Type new directory name:"));
+	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
+	replace(response, "{content}", content);	
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+void FilesPageHandler::handleErrorMessage(Client &client, Common::String message) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
+	if (path == "" || path == "/") {
+		addItem(content, itemTemplate, true, "/root/", _("File system root"));
+		addItem(content, itemTemplate, true, "/saves/", _("Saved games"));
+		return true;
+	}
+
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd)) return false;
+
+	Common::FSNode node = Common::FSNode(path);
+	if (path == "/") node = node.getParent(); // absolute root
+	if (!node.isDirectory()) return false;
+
+	// list directory
+	Common::FSList _nodeContent;
+	if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files
+		_nodeContent.clear();
+	else
+		Common::sort(_nodeContent.begin(), _nodeContent.end());
+
+	// add parent directory link
+	{
+		Common::String filePath = path;
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		if (filePath == "" || filePath == "/" || filePath == "\\")
+			filePath = "/";
+		else
+			filePath = parentPath(prefixToAdd + filePath);
+		addItem(content, itemTemplate, true, filePath, _("Parent directory"));
+	}
+
+	// fill the content
+	for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
+		Common::String name = i->getDisplayName();
+		if (i->isDirectory()) name += "/";
+
+		Common::String filePath = i->getPath();
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		filePath = prefixToAdd + filePath;
+
+		addItem(content, itemTemplate, i->isDirectory(), filePath, name);
+	}
+
+	return true;
+}
+
+void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size) {
+	Common::String item = itemTemplate;
+	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + path);
+	replace(item, "{name}", name);
+	replace(item, "{size}", size);
+	content += item;
+}
+
+/// public
+
+ClientHandlerCallback FilesPageHandler::getHandler() {
+	return new Common::Callback<FilesPageHandler, Client &>(this, &FilesPageHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
new file mode 100644
index 0000000..6205dcd
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -0,0 +1,53 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_FILESPAGEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_FILESPAGEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+
+namespace Networking {
+
+class FilesPageHandler: public FilesBaseHandler {
+	void handle(Client &client);
+	void handleErrorMessage(Client &client, Common::String message);
+
+	/**
+	 * Lists the directory <path>.
+	 *
+	 * Returns true on success.
+	 */
+	bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate);
+
+	/** Helper method for adding items into the files list. */
+	void addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size = "");
+
+public:
+	FilesPageHandler();
+	virtual ~FilesPageHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
new file mode 100644
index 0000000..0d7256c
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -0,0 +1,70 @@
+/* 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 "backends/networking/sdl_net/handlers/indexpagehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/saves/default/default-saves.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/translation.h"
+#include "gui/storagewizarddialog.h"
+
+namespace Networking {
+
+#define INDEX_PAGE_NAME ".index.html"
+
+IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
+
+IndexPageHandler::~IndexPageHandler() {}
+
+void IndexPageHandler::handle(Client &client) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	Common::String code = client.queryParameter("code");
+
+	if (code == "") {		
+		replace(response, "{message}", _("This is a local webserver index page."));
+		LocalWebserver::setClientGetHandler(client, response);
+		return;
+	}
+
+	_code = code;
+	sendCommand(GUI::kStorageCodePassedCmd, 0);
+	replace(response, "{message}", _("ScummVM got the code and already connects to your cloud storage!"));
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+/// public
+
+Common::String IndexPageHandler::code() { return _code; }
+
+ClientHandlerCallback IndexPageHandler::getHandler() {
+	return new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.h b/backends/networking/sdl_net/handlers/indexpagehandler.h
new file mode 100644
index 0000000..f10ab63
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.h
@@ -0,0 +1,46 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/basehandler.h"
+#include "gui/object.h"
+
+namespace Networking {
+class LocalWebserver;
+
+class IndexPageHandler: public BaseHandler, public GUI::CommandSender {
+	Common::String _code;
+
+	void handle(Client &client);
+public:
+	IndexPageHandler();
+	virtual ~IndexPageHandler();
+
+	Common::String code();
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
new file mode 100644
index 0000000..160f78f
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -0,0 +1,52 @@
+/* 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 "backends/networking/sdl_net/handlers/resourcehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+
+namespace Networking {
+
+ResourceHandler::ResourceHandler() {}
+
+ResourceHandler::~ResourceHandler() {}
+
+void ResourceHandler::handle(Client &client) {
+	Common::String filename = client.path();
+	filename.deleteChar(0);
+
+	// if archive hidden file is requested, ignore
+	if (filename.size() && filename[0] == '.') return;
+
+	// if file not found, don't set handler either
+	Common::SeekableReadStream *file = getArchiveFile(filename);
+	if (file == nullptr) return;
+
+	LocalWebserver::setClientGetHandler(client, file, 200, LocalWebserver::determineMimeType(filename));
+}
+
+/// public
+
+ClientHandlerCallback ResourceHandler::getHandler() {
+	return new Common::Callback<ResourceHandler, Client &>(this, &ResourceHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.h b/backends/networking/sdl_net/handlers/resourcehandler.h
new file mode 100644
index 0000000..1b4ceec
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/resourcehandler.h
@@ -0,0 +1,41 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_RESOURCEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_RESOURCEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/basehandler.h"
+
+namespace Networking {
+
+class ResourceHandler: public BaseHandler {
+	void handle(Client &client);
+public:
+	ResourceHandler();
+	virtual ~ResourceHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp
deleted file mode 100644
index b3790a8..0000000
--- a/backends/networking/sdl_net/indexpagehandler.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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 "backends/networking/sdl_net/indexpagehandler.h"
-#include "backends/networking/sdl_net/localwebserver.h"
-#include "backends/saves/default/default-saves.h"
-#include "common/archive.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/savefile.h"
-#include "common/translation.h"
-#include "common/unzip.h"
-#include "gui/storagewizarddialog.h"
-
-namespace Networking {
-
-#define ARCHIVE_NAME "wwwroot.zip"
-#define INDEX_PAGE_NAME "index.html"
-#define FILES_PAGE_NAME "files.html"
-
-IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
-
-IndexPageHandler::~IndexPageHandler() {
-	LocalServer.removePathHandler("/");
-	LocalServer.removePathHandler("/files/");
-
-	Common::ArchiveMemberList fileList = listArchive();
-	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
-		Common::ArchiveMember const &m = **it;
-		if (m.getName() == INDEX_PAGE_NAME) continue;
-		LocalServer.removePathHandler("/" + m.getName());
-	}
-}
-
-void IndexPageHandler::handle(Client &client) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	Common::String code = client.queryParameter("code");
-
-	if (code == "") {		
-		replace(response, "{message}", _("This is a local webserver index page."));
-		LocalWebserver::setClientGetHandler(client, response);
-		return;
-	}
-
-	_code = code;
-	sendCommand(GUI::kStorageCodePassedCmd, 0);
-	replace(response, "{message}", _("ScummVM got the code and already connects to your cloud storage!"));
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
-void IndexPageHandler::handleFiles(Client &client) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
-	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(FILES_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	Common::String path = client.queryParameter("path");
-	Common::String content = "";
-	
-	// show an error message if failed to list directory
-	if (!listDirectory(path, content, itemTemplate)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=/\">%s</a>",
-				_("ScummVM couldn't list the directory you specified."),
-				_("Back to the files manager")
-			)
-		);
-		return;
-	}
-
-	//these occur twice:
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{upload_files_button}", _("Upload files")); //tab
-	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
-	replace(response, "{create_directory_desc}", _("Type new directory name:"));
-	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
-	replace(response, "{content}", content);	
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
-void IndexPageHandler::handleResource(Client &client) {
-	Common::String filename = client.path();
-	filename.deleteChar(0);
-	LocalWebserver::setClientGetHandler(client, getArchiveFile(filename), 200, LocalWebserver::determineMimeType(filename));
-}
-
-void IndexPageHandler::handleErrorMessage(Client &client, Common::String message) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", message);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
-/// "files/"-related
-
-Common::String IndexPageHandler::parentPath(Common::String path) {
-	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
-	if (!path.empty()) {
-		for (int i = path.size() - 1; i >= 0; --i)
-			if (i == 0 || path[i] == '/' || path[i] == '\\') {
-				path.erase(i);
-				break;
-			}
-	}
-	if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-	return path;
-}
-
-void IndexPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size) {
-	Common::String item = itemTemplate;
-	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + path);
-	replace(item, "{name}", name);
-	replace(item, "{size}", size);
-	content += item;
-}
-
-bool IndexPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
-	if (path == "" || path == "/") {
-		addItem(content, itemTemplate, true, "/root/", _("File system root"));
-		addItem(content, itemTemplate, true, "/saves/", _("Saved games"));
-		return true;
-	}
-
-	Common::String prefixToRemove = "", prefixToAdd = "";
-	if (!transformPath(path, prefixToRemove, prefixToAdd)) return false;
-
-	Common::FSNode node = Common::FSNode(path);
-	if (path == "/") node = node.getParent(); // absolute root
-	if (!node.isDirectory()) return false;
-
-	// list directory
-	Common::FSList _nodeContent;
-	if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files
-		_nodeContent.clear();
-	else
-		Common::sort(_nodeContent.begin(), _nodeContent.end());
-
-	// add parent directory link
-	{
-		Common::String filePath = path;
-		if (filePath.hasPrefix(prefixToRemove))
-			filePath.erase(0, prefixToRemove.size());
-		if (filePath == "" || filePath == "/" || filePath == "\\")
-			filePath = "/";
-		else
-			filePath = parentPath(prefixToAdd + filePath);
-		addItem(content, itemTemplate, true, filePath, _("Parent directory"));
-	}
-
-	// fill the content
-	for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
-		Common::String name = i->getDisplayName();
-		if (i->isDirectory()) name += "/";
-
-		Common::String filePath = i->getPath();
-		if (filePath.hasPrefix(prefixToRemove))
-			filePath.erase(0, prefixToRemove.size());
-		filePath = prefixToAdd + filePath;
-
-		addItem(content, itemTemplate, i->isDirectory(), filePath, name);
-	}
-
-	return true;
-}
-
-bool IndexPageHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd) {
-	// <path> is not empty, but could lack the trailing slash	
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-
-	if (path.hasPrefix("/root")) {
-		prefixToAdd = "/root/";
-		prefixToRemove = "";
-		path.erase(0, 5);
-		if (path == "") path = "/"; // absolute root is '/'
-		if (path != "/") path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
-		return true;
-	}
-
-	if (path.hasPrefix("/saves")) {
-		prefixToAdd = "/saves/";
-
-		// determine savepath (prefix to remove)
-		DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
-		prefixToRemove = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
-		if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
-			prefixToRemove += '/';
-
-		path.erase(0, 6);
-		if (path.size() && (path[0] == '/' || path[0] == '\\'))
-			path.deleteChar(0);
-		path = prefixToRemove + path;
-		return true;
-	}
-
-	return false;
-}
-
-/// public
-
-void IndexPageHandler::addPathHandler(LocalWebserver &server) {
-	// we can't use LocalServer yet, because IndexPageHandler is created while LocalWebserver is created
-	// (thus no _instance is available and it causes stack overflow)
-	server.addPathHandler("/", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle));
-	server.addPathHandler("/files", new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handleFiles));
-
-	Common::ArchiveMemberList fileList = listArchive();
-	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
-		Common::ArchiveMember const &m = **it;
-		if (m.getName() == INDEX_PAGE_NAME) continue;
-		if (m.getName() == FILES_PAGE_NAME) continue;
-		server.addPathHandler("/" + m.getName(), new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handleResource));		
-	}
-}
-
-Common::String IndexPageHandler::code() { return _code; }
-
-/// utils
-
-Common::Archive *IndexPageHandler::getZipArchive() {
-	// first search in themepath
-	if (ConfMan.hasKey("themepath")) {
-		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
-		if (!node.exists() || !node.isReadable() || !node.isDirectory())
-			return nullptr;
-
-		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
-		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
-			Common::SeekableReadStream *const stream = fileNode.createReadStream();
-			Common::Archive *zipArchive = Common::makeZipArchive(stream);
-			if (zipArchive) return zipArchive;
-		}
-	}
-
-	// then use SearchMan to find it
-	Common::ArchiveMemberList fileList;
-	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
-	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
-		Common::ArchiveMember       const &m = **it;
-		Common::SeekableReadStream *const stream = m.createReadStream();
-		Common::Archive *zipArchive = Common::makeZipArchive(stream);
-		if (zipArchive) return zipArchive;
-	}
-
-	return nullptr;
-}
-
-Common::ArchiveMemberList IndexPageHandler::listArchive() {
-	Common::ArchiveMemberList resultList;
-	Common::Archive *zipArchive = getZipArchive();
-	if (zipArchive) {
-		zipArchive->listMembers(resultList);
-		delete zipArchive;
-	}
-	return resultList;
-}
-
-Common::SeekableReadStream *IndexPageHandler::getArchiveFile(Common::String name) {
-	Common::SeekableReadStream *result = nullptr;
-	Common::Archive *zipArchive = getZipArchive();
-	if (zipArchive) {
-		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
-		result = ptr->createReadStream();
-		delete zipArchive;
-	}
-	return result;
-}
-
-Common::String IndexPageHandler::readEverythingFromStream(Common::SeekableReadStream *const stream) {
-	Common::String result;
-	char buf[1024];
-	uint32 readBytes;
-	while (!stream->eos()) {
-		readBytes = stream->read(buf, 1024);
-		result += Common::String(buf, readBytes);
-	}
-	return result;
-}
-
-} // End of namespace Networking
diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h
deleted file mode 100644
index a75c7a0..0000000
--- a/backends/networking/sdl_net/indexpagehandler.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
-#define BACKENDS_NETWORKING_SDL_NET_INDEXPAGEHANDLER_H
-
-#include "backends/networking/sdl_net/client.h"
-#include "common/archive.h"
-#include "gui/object.h"
-
-namespace Networking {
-class LocalWebserver;
-
-class IndexPageHandler: public GUI::CommandSender {
-	Common::String _code;
-
-	void handle(Client &client);
-	void handleFiles(Client &client);
-	void handleResource(Client &client);
-	void handleErrorMessage(Client &client, Common::String message);
-
-	// "files/"-related
-	Common::String parentPath(Common::String path);
-
-	/** Helper method for adding items into the files list. */
-	void addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size = "");
-
-	/**
-	 * Lists the directory <path>.
-	 *
-	 * Returns true on success.
-	 */
-	bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate);
-
-	/**
-	 * Transforms virtual <path> into actual file system path.
-	 *
-	 * Fills prefixes with actual file system prefix ("to remove")
-	 * and virtual path prefix ("to add").
-	 *
-	 * Returns true on success.
-	 */
-	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd);
-
-	Common::Archive *getZipArchive();
-	Common::ArchiveMemberList listArchive();
-	Common::SeekableReadStream *getArchiveFile(Common::String name);
-	Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
-
-public:
-	IndexPageHandler();
-	virtual ~IndexPageHandler();
-
-	void addPathHandler(LocalWebserver &server);
-	Common::String code();
-};
-
-} // End of namespace Networking
-
-#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index d1dda16..c3df9f8 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -42,7 +42,9 @@ namespace Networking {
 
 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
 	_stopOnIdle(false), _clients(0), _idlingFrames(0) {
-	_indexPageHandler.addPathHandler(*this);
+	addPathHandler("/", _indexPageHandler.getHandler());
+	addPathHandler("/files", _filesPageHandler.getHandler());
+	_defaultHandler = _resourceHandler.getHandler();
 }
 
 LocalWebserver::~LocalWebserver() {
@@ -142,7 +144,7 @@ void LocalWebserver::stop() {
 
 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
 
-void LocalWebserver::addPathHandler(Common::String path, ClientHandler handler) {
+void LocalWebserver::addPathHandler(Common::String path, ClientHandlerCallback handler) {
 	if (_pathHandlers.contains(path)) warning("LocalWebserver::addPathHandler: path already had a handler");
 	_pathHandlers[path] = handler;
 }
@@ -199,7 +201,9 @@ void LocalWebserver::handleClient(uint32 i) {
 		//if GET, check whether we know a handler for such URL
 		//if PUT, check whether we know a handler for that URL
 		if (_pathHandlers.contains(_client[i].path()))
-			(*_pathHandlers[_client[i].path()])(_client[i]);		
+			(*_pathHandlers[_client[i].path()])(_client[i]);
+		else if (_defaultHandler)
+			(*_defaultHandler)(_client[i]); //try default handler
 		
 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break;
 
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index f73d1b2..1d397e6 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -24,8 +24,10 @@
 #define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H
 
 #include "backends/networking/sdl_net/client.h"
-#include "backends/networking/sdl_net/indexpagehandler.h"
-#include "common/callback.h"
+#include "backends/networking/sdl_net/handlers/basehandler.h"
+#include "backends/networking/sdl_net/handlers/filespagehandler.h"
+#include "backends/networking/sdl_net/handlers/indexpagehandler.h"
+#include "backends/networking/sdl_net/handlers/resourcehandler.h"
 #include "common/hash-str.h"
 #include "common/mutex.h"
 #include "common/singleton.h"
@@ -46,8 +48,6 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	static const uint32 SERVER_PORT = 12345;
 	static const uint32 MAX_CONNECTIONS = 10;
 
-	typedef Common::BaseCallback<Client &> *ClientHandler;
-
 	friend void localWebserverTimer(void *); //calls handle()
 
 	SDLNet_SocketSet _set;
@@ -55,8 +55,11 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	Client _client[MAX_CONNECTIONS];
 	int _clients;
 	bool _timerStarted, _stopOnIdle;
-	Common::HashMap<Common::String, ClientHandler> _pathHandlers;
+	Common::HashMap<Common::String, ClientHandlerCallback> _pathHandlers;
+	ClientHandlerCallback _defaultHandler;
 	IndexPageHandler _indexPageHandler;
+	FilesPageHandler _filesPageHandler;
+	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
 	Common::String _address;
@@ -74,7 +77,7 @@ public:
 	void start();
 	void stop();
 	void stopOnIdle();
-	void addPathHandler(Common::String path, ClientHandler handler);
+	void addPathHandler(Common::String path, ClientHandlerCallback handler);
 	void removePathHandler(Common::String path);
 
 	Common::String getAddress();
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index da031c8..4d1be97 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
new file mode 100644
index 0000000..2baf4a0
--- /dev/null
+++ b/backends/networking/wwwroot/.files.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+	<head>
+		<title>ScummVM</title>
+		<meta charset="utf-8"/>
+		<link rel="stylesheet" type="text/css" href="style.css"/>
+	</head>
+	<body>
+		<div class="container">
+			<div class='header'>
+				<center><img src="logo.png"/></center>
+			</div>
+			<div class="controls">
+				<table class="buttons"><tr>
+					<td><a href="javascript:show('create_directory');">{create_directory_button}</a></td>
+					<td><a href="javascript:show('upload_file');">{upload_files_button}</a></td>
+				</tr></table>
+				<div id="create_directory" class="modal">
+					<p>{create_directory_desc}</p>
+					<form action="create">
+						<input type="text" name="directory_name" value=""/>
+						<input type="submit" value="{create_directory_button}"/>
+					</form>
+				</div>
+				<div id="upload_file" class="modal">
+					<p>{upload_file_desc}</p>
+					<form action="upload" method="post">
+						<input type="file" name="upload_file"/>
+						<input type="submit" value="{upload_file_button}"/>
+					</form>
+				</div>
+			</div>
+			<div class="content">
+				<table class="files_list">
+					{content}
+				</table>
+			</div>
+		</div>
+		<script>
+			function show(id) {
+				var e = document.getElementById(id);
+				var visible = (e.style.display == "block");
+				if (visible) id = ""; //hide
+
+				e = document.getElementById("create_directory");
+				e.style.display = (e.id == id ? "block" : "none");
+				e = document.getElementById("upload_file");
+				e.style.display = (e.id == id ? "block" : "none");
+			}
+		</script>
+	</body>
+</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/.index.html b/backends/networking/wwwroot/.index.html
new file mode 100644
index 0000000..2a3d9d3
--- /dev/null
+++ b/backends/networking/wwwroot/.index.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+	<head>
+		<title>ScummVM</title>
+		<meta charset="utf-8"/>
+		<link rel="stylesheet" type="text/css" href="style.css"/>
+	</head>
+	<body>
+		<div class="container">
+			<div class='header'>
+				<center><img src="logo.png"/></center>
+			</div>
+			<div class="content">
+				<p>{message}</p>
+			</div>
+		</div>
+	</body>
+</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/files.html b/backends/networking/wwwroot/files.html
deleted file mode 100644
index 2baf4a0..0000000
--- a/backends/networking/wwwroot/files.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!doctype html>
-<html>
-	<head>
-		<title>ScummVM</title>
-		<meta charset="utf-8"/>
-		<link rel="stylesheet" type="text/css" href="style.css"/>
-	</head>
-	<body>
-		<div class="container">
-			<div class='header'>
-				<center><img src="logo.png"/></center>
-			</div>
-			<div class="controls">
-				<table class="buttons"><tr>
-					<td><a href="javascript:show('create_directory');">{create_directory_button}</a></td>
-					<td><a href="javascript:show('upload_file');">{upload_files_button}</a></td>
-				</tr></table>
-				<div id="create_directory" class="modal">
-					<p>{create_directory_desc}</p>
-					<form action="create">
-						<input type="text" name="directory_name" value=""/>
-						<input type="submit" value="{create_directory_button}"/>
-					</form>
-				</div>
-				<div id="upload_file" class="modal">
-					<p>{upload_file_desc}</p>
-					<form action="upload" method="post">
-						<input type="file" name="upload_file"/>
-						<input type="submit" value="{upload_file_button}"/>
-					</form>
-				</div>
-			</div>
-			<div class="content">
-				<table class="files_list">
-					{content}
-				</table>
-			</div>
-		</div>
-		<script>
-			function show(id) {
-				var e = document.getElementById(id);
-				var visible = (e.style.display == "block");
-				if (visible) id = ""; //hide
-
-				e = document.getElementById("create_directory");
-				e.style.display = (e.id == id ? "block" : "none");
-				e = document.getElementById("upload_file");
-				e.style.display = (e.id == id ? "block" : "none");
-			}
-		</script>
-	</body>
-</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/index.html b/backends/networking/wwwroot/index.html
deleted file mode 100644
index 2a3d9d3..0000000
--- a/backends/networking/wwwroot/index.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!doctype html>
-<html>
-	<head>
-		<title>ScummVM</title>
-		<meta charset="utf-8"/>
-		<link rel="stylesheet" type="text/css" href="style.css"/>
-	</head>
-	<body>
-		<div class="container">
-			<div class='header'>
-				<center><img src="logo.png"/></center>
-			</div>
-			<div class="content">
-				<p>{message}</p>
-			</div>
-		</div>
-	</body>
-</html>
\ No newline at end of file


Commit: ab4361a76b40b56348ec46311121a0552f0c9d6b
    https://github.com/scummvm/scummvm/commit/ab4361a76b40b56348ec46311121a0552f0c9d6b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make "/create" work

One can now create directories through browser.

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 67b45b6..f567891 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -23,6 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/sdl_net/client.h"
+#include "backends/networking/sdl_net/localwebserver.h"
 #include "common/textconsole.h"
 #include <SDL/SDL_net.h>
 
@@ -201,13 +202,13 @@ Common::String Client::queryParameter(Common::String name) const {
 			} else key += _query[i];
 		} else {
 			if (_query[i] == '&') {
-				if (key == name) return value;
+				if (key == name) return LocalWebserver::urlDecode(value);
 				readingKey = true;
 				key = "";
 			} else value += _query[i];
 		}
 	}
-	if (key == name) return value; //the last key doesn't have an '&' in the end of the query
+	if (key == name) return LocalWebserver::urlDecode(value); //the last key doesn't have an '&' in the end of the query
 	return "";
 }
 
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 360fe20..327071f 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -22,6 +22,7 @@
 
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/fs/fs-factory.h"
 #include "common/file.h"
 #include "common/translation.h"
 
@@ -35,6 +36,11 @@ FilesPageHandler::FilesPageHandler() {}
 FilesPageHandler::~FilesPageHandler() {}
 
 void FilesPageHandler::handle(Client &client) {
+	if (client.path() == "/files") handleFiles(client);
+	else handleCreateDirectory(client);
+}
+
+void FilesPageHandler::handleFiles(Client &client) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
 	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
@@ -61,6 +67,8 @@ void FilesPageHandler::handle(Client &client) {
 	//these occur twice:
 	replace(response, "{create_directory_button}", _("Create directory"));
 	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{path}", client.queryParameter("path"));
+	replace(response, "{path}", client.queryParameter("path"));
 	replace(response, "{upload_files_button}", _("Upload files")); //tab
 	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
 	replace(response, "{create_directory_desc}", _("Type new directory name:"));
@@ -69,6 +77,27 @@ void FilesPageHandler::handle(Client &client) {
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
+void FilesPageHandler::handleCreateDirectory(Client &client) {
+	Common::String path = client.queryParameter("path");
+	Common::String name = client.queryParameter("directory_name");
+	Common::String errorMessage = "";
+
+	// show an error message if failed to create directory	
+	if (!createDirectory(path, name, errorMessage)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=/\">%s</a>",
+				errorMessage.c_str(),
+				_("Back to the files manager")
+				)
+			);
+		return;
+	}
+
+	handleFiles(client);
+}
+
 void FilesPageHandler::handleErrorMessage(Client &client, Common::String message) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
 
@@ -80,6 +109,50 @@ void FilesPageHandler::handleErrorMessage(Client &client, Common::String message
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
+bool FilesPageHandler::createDirectory(Common::String path, Common::String name, Common::String &errorMessage) {
+	// check that <path> is not an absolute root
+	if (path == "" || path == "/") {
+		errorMessage = _("Can't create directory here!");
+		return false;
+	}
+
+	// transform virtual path to actual file system one
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// check that <path> exists and is directory
+	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!node->exists()) {
+		errorMessage = _("Parent directory doesn't exists!");
+		return false;
+	}
+	if (!node->isDirectory()) {
+		errorMessage = _("Can't create a directory within a file!");
+		return false;
+	}
+	
+	// check that <directory_name> doesn't exist or is directory
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
+	if (node->exists()) {
+		if (!node->isDirectory()) {
+			errorMessage = _("There is a file with that name in the parent directory!");
+			return false;
+		} else return true;
+	}
+	
+	// create the <directory_name> in <path>
+	if (!node->create(true)) {
+		errorMessage = _("Failed to create the directory!");
+		return false;
+	}
+
+	return true;
+}
+
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
 		addItem(content, itemTemplate, true, "/root/", _("File system root"));
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index 6205dcd..e5a32c9 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -29,9 +29,20 @@ namespace Networking {
 
 class FilesPageHandler: public FilesBaseHandler {
 	void handle(Client &client);
+	void handleFiles(Client &client);
+	void handleCreateDirectory(Client &client);
 	void handleErrorMessage(Client &client, Common::String message);
 
 	/**
+	 * Creates the directory <name> in <path>.
+	 *
+	 * Fills <errorMessage> on failure.
+	 *
+	 * Returns true on success.
+	 */
+	bool createDirectory(Common::String path, Common::String name, Common::String &errorMessage);
+
+	/**
 	 * Lists the directory <path>.
 	 *
 	 * Returns true on success.
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index c3df9f8..c8f322d 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -44,6 +44,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	_stopOnIdle(false), _clients(0), _idlingFrames(0) {
 	addPathHandler("/", _indexPageHandler.getHandler());
 	addPathHandler("/files", _filesPageHandler.getHandler());
+	addPathHandler("/create", _filesPageHandler.getHandler()); //"Create directory" feature
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
@@ -271,4 +272,32 @@ const char *LocalWebserver::determineMimeType(Common::String &filename) {
 	return "application/octet-stream";
 }
 
+namespace {
+int hexDigit(char c) {
+	if ('0' <= c && c <= '9') return c - '0';
+	if ('A' <= c && c <= 'F') return c - 'A' + 10;
+	if ('a' <= c && c <= 'f') return c - 'a' + 10;
+	return -1;
+}
+}
+
+Common::String LocalWebserver::urlDecode(Common::String value) {
+	Common::String result = "";
+	uint32 size = value.size();
+	for (uint32 i = 0; i < size; ++i) {
+		if (value[i] == '%' && i+2 < size) {
+			int d1 = hexDigit(value[i+1]);
+			int d2 = hexDigit(value[i+2]);
+			if (0 <= d1 && d1 < 16 && 0 <= d2 && d2 < 16) {
+				result += (char)(d1 * 16 + d2);
+				i = i + 2;
+				continue;
+			}
+		}
+
+		result += value[i];
+	}
+	return result;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 1d397e6..2e56209 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -87,6 +87,7 @@ public:
 	static void setClientGetHandler(Client &client, Common::String response, long code = 200, const char *mimeType = nullptr);
 	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
 	static const char *determineMimeType(Common::String &filename);
+	static Common::String urlDecode(Common::String value);
 };
 
 /** Shortcut for accessing the local webserver. */
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 4d1be97..de683a0 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index 2baf4a0..de29ac3 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -18,6 +18,7 @@
 				<div id="create_directory" class="modal">
 					<p>{create_directory_desc}</p>
 					<form action="create">
+						<input type="hidden" name="path" value="{path}"/>
 						<input type="text" name="directory_name" value=""/>
 						<input type="submit" value="{create_directory_button}"/>
 					</form>
@@ -25,6 +26,7 @@
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
 					<form action="upload" method="post">
+						<input type="hidden" name="path" value="{path}"/>
 						<input type="file" name="upload_file"/>
 						<input type="submit" value="{upload_file_button}"/>
 					</form>


Commit: 8a9d126152f3e0ed1e91daa15dd90f0c18060492
    https://github.com/scummvm/scummvm/commit/8a9d126152f3e0ed1e91daa15dd90f0c18060492
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Move "/create" to separate Handler

It does redirect to "/files" on success, so user doesn't even see the
strange "/create" URL at all.

This commit is for keeping these handlers small, not making one
(FilesPageHandler in this case) do everything.

Changed paths:
  A backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
  A backends/networking/sdl_net/handlers/createdirectoryhandler.h
    backends/module.mk
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/module.mk b/backends/module.mk
index 7dd29be..43057a4 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -59,6 +59,7 @@ MODULE_OBJS += \
 	networking/sdl_net/client.o \
 	networking/sdl_net/getclienthandler.o \
 	networking/sdl_net/handlers/basehandler.o \
+	networking/sdl_net/handlers/createdirectoryhandler.o \
 	networking/sdl_net/handlers/filesbasehandler.o \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
new file mode 100644
index 0000000..ad8a67a
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -0,0 +1,132 @@
+/* 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 "backends/networking/sdl_net/handlers/createdirectoryhandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/fs/fs-factory.h"
+#include "common/file.h"
+#include "common/translation.h"
+
+namespace Networking {
+
+#define INDEX_PAGE_NAME ".index.html"
+
+CreateDirectoryHandler::CreateDirectoryHandler() {}
+
+CreateDirectoryHandler::~CreateDirectoryHandler() {}
+
+void CreateDirectoryHandler::handle(Client &client) {
+	Common::String path = client.queryParameter("path");
+	Common::String name = client.queryParameter("directory_name");
+	Common::String errorMessage = "";
+
+	// show an error message if failed to create directory	
+	if (!createDirectory(path, name, errorMessage)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=/\">%s</a>",
+				errorMessage.c_str(),
+				_("Back to the files manager")
+				)
+			);
+		return;
+	}
+
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", Common::String::format(
+			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			_("Directory created successfully!"),
+			client.queryParameter("path").c_str(),
+			_("Back to parent directory")
+		)
+	);
+	LocalWebserver::setClientRedirectHandler(client, response, "/files?path=" + client.queryParameter("path"));
+}
+
+void CreateDirectoryHandler::handleErrorMessage(Client &client, Common::String message) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+bool CreateDirectoryHandler::createDirectory(Common::String path, Common::String name, Common::String &errorMessage) {
+	// check that <path> is not an absolute root
+	if (path == "" || path == "/") {
+		errorMessage = _("Can't create directory here!");
+		return false;
+	}
+
+	// transform virtual path to actual file system one
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// check that <path> exists and is directory
+	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!node->exists()) {
+		errorMessage = _("Parent directory doesn't exists!");
+		return false;
+	}
+	if (!node->isDirectory()) {
+		errorMessage = _("Can't create a directory within a file!");
+		return false;
+	}
+	
+	// check that <directory_name> doesn't exist or is directory
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
+	if (node->exists()) {
+		if (!node->isDirectory()) {
+			errorMessage = _("There is a file with that name in the parent directory!");
+			return false;
+		} else return true;
+	}
+	
+	// create the <directory_name> in <path>
+	if (!node->create(true)) {
+		errorMessage = _("Failed to create the directory!");
+		return false;
+	}
+
+	return true;
+}
+
+/// public
+
+ClientHandlerCallback CreateDirectoryHandler::getHandler() {
+	return new Common::Callback<CreateDirectoryHandler, Client &>(this, &CreateDirectoryHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.h b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
new file mode 100644
index 0000000..023c3e7
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
@@ -0,0 +1,51 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_CREATEDIRECTORYHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_CREATEDIRECTORYHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+
+namespace Networking {
+
+class CreateDirectoryHandler: public FilesBaseHandler {
+	void handle(Client &client);
+	void handleErrorMessage(Client &client, Common::String message);
+
+	/**
+	 * Creates the directory <name> in <path>.
+	 *
+	 * Fills <errorMessage> on failure.
+	 *
+	 * Returns true on success.
+	 */
+	bool createDirectory(Common::String path, Common::String name, Common::String &errorMessage);
+public:
+	CreateDirectoryHandler();
+	virtual ~CreateDirectoryHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 327071f..2ecb448 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -22,7 +22,6 @@
 
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "backends/fs/fs-factory.h"
 #include "common/file.h"
 #include "common/translation.h"
 
@@ -36,11 +35,6 @@ FilesPageHandler::FilesPageHandler() {}
 FilesPageHandler::~FilesPageHandler() {}
 
 void FilesPageHandler::handle(Client &client) {
-	if (client.path() == "/files") handleFiles(client);
-	else handleCreateDirectory(client);
-}
-
-void FilesPageHandler::handleFiles(Client &client) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
 	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
@@ -77,27 +71,6 @@ void FilesPageHandler::handleFiles(Client &client) {
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
-void FilesPageHandler::handleCreateDirectory(Client &client) {
-	Common::String path = client.queryParameter("path");
-	Common::String name = client.queryParameter("directory_name");
-	Common::String errorMessage = "";
-
-	// show an error message if failed to create directory	
-	if (!createDirectory(path, name, errorMessage)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=/\">%s</a>",
-				errorMessage.c_str(),
-				_("Back to the files manager")
-				)
-			);
-		return;
-	}
-
-	handleFiles(client);
-}
-
 void FilesPageHandler::handleErrorMessage(Client &client, Common::String message) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
 
@@ -109,50 +82,6 @@ void FilesPageHandler::handleErrorMessage(Client &client, Common::String message
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
-bool FilesPageHandler::createDirectory(Common::String path, Common::String name, Common::String &errorMessage) {
-	// check that <path> is not an absolute root
-	if (path == "" || path == "/") {
-		errorMessage = _("Can't create directory here!");
-		return false;
-	}
-
-	// transform virtual path to actual file system one
-	Common::String prefixToRemove = "", prefixToAdd = "";
-	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
-		errorMessage = _("Invalid path!");
-		return false;
-	}
-
-	// check that <path> exists and is directory
-	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
-	if (!node->exists()) {
-		errorMessage = _("Parent directory doesn't exists!");
-		return false;
-	}
-	if (!node->isDirectory()) {
-		errorMessage = _("Can't create a directory within a file!");
-		return false;
-	}
-	
-	// check that <directory_name> doesn't exist or is directory
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
-	if (node->exists()) {
-		if (!node->isDirectory()) {
-			errorMessage = _("There is a file with that name in the parent directory!");
-			return false;
-		} else return true;
-	}
-	
-	// create the <directory_name> in <path>
-	if (!node->create(true)) {
-		errorMessage = _("Failed to create the directory!");
-		return false;
-	}
-
-	return true;
-}
-
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
 		addItem(content, itemTemplate, true, "/root/", _("File system root"));
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index e5a32c9..6205dcd 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -29,20 +29,9 @@ namespace Networking {
 
 class FilesPageHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	void handleFiles(Client &client);
-	void handleCreateDirectory(Client &client);
 	void handleErrorMessage(Client &client, Common::String message);
 
 	/**
-	 * Creates the directory <name> in <path>.
-	 *
-	 * Fills <errorMessage> on failure.
-	 *
-	 * Returns true on success.
-	 */
-	bool createDirectory(Common::String path, Common::String name, Common::String &errorMessage);
-
-	/**
 	 * Lists the directory <path>.
 	 *
 	 * Returns true on success.
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index c8f322d..b2eff16 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -44,7 +44,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	_stopOnIdle(false), _clients(0), _idlingFrames(0) {
 	addPathHandler("/", _indexPageHandler.getHandler());
 	addPathHandler("/files", _filesPageHandler.getHandler());
-	addPathHandler("/create", _filesPageHandler.getHandler()); //"Create directory" feature
+	addPathHandler("/create", _createDirectoryHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
@@ -252,6 +252,21 @@ void LocalWebserver::setClientGetHandler(Client &client, Common::SeekableReadStr
 	client.setHandler(handler);
 }
 
+void LocalWebserver::setClientRedirectHandler(Client &client, Common::String response, Common::String location, const char *mimeType) {
+	byte *data = new byte[response.size()];
+	memcpy(data, response.c_str(), response.size());
+	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
+	setClientRedirectHandler(client, stream, location, mimeType);
+}
+
+void LocalWebserver::setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType) {
+	GetClientHandler *handler = new GetClientHandler(responseStream);
+	handler->setResponseCode(302); //redirect
+	handler->setHeader("Location", location);
+	if (mimeType) handler->setHeader("Content-Type", mimeType);
+	client.setHandler(handler);
+}
+
 const char *LocalWebserver::determineMimeType(Common::String &filename) {
 	// text
 	if (filename.hasSuffix(".html")) return "text/html";
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 2e56209..ed1860a 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -25,6 +25,7 @@
 
 #include "backends/networking/sdl_net/client.h"
 #include "backends/networking/sdl_net/handlers/basehandler.h"
+#include "backends/networking/sdl_net/handlers/createdirectoryhandler.h"
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
 #include "backends/networking/sdl_net/handlers/resourcehandler.h"
@@ -59,6 +60,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	ClientHandlerCallback _defaultHandler;
 	IndexPageHandler _indexPageHandler;
 	FilesPageHandler _filesPageHandler;
+	CreateDirectoryHandler _createDirectoryHandler;
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
@@ -86,6 +88,8 @@ public:
 
 	static void setClientGetHandler(Client &client, Common::String response, long code = 200, const char *mimeType = nullptr);
 	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
+	static void setClientRedirectHandler(Client &client, Common::String response, Common::String location, const char *mimeType = nullptr);
+	static void setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType = nullptr);
 	static const char *determineMimeType(Common::String &filename);
 	static Common::String urlDecode(Common::String value);
 };


Commit: b40bfaa046370a2eeadcce1236f518315e8ada01
    https://github.com/scummvm/scummvm/commit/b40bfaa046370a2eeadcce1236f518315e8ada01
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "/download" handler

Now one can download files from the device through browser!

Changed paths:
  A backends/networking/sdl_net/handlers/downloadfilehandler.cpp
  A backends/networking/sdl_net/handlers/downloadfilehandler.h
    backends/module.mk
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlers/filesbasehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/module.mk b/backends/module.mk
index 43057a4..b1aced4 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -60,6 +60,7 @@ MODULE_OBJS += \
 	networking/sdl_net/getclienthandler.o \
 	networking/sdl_net/handlers/basehandler.o \
 	networking/sdl_net/handlers/createdirectoryhandler.o \
+	networking/sdl_net/handlers/downloadfilehandler.o \
 	networking/sdl_net/handlers/filesbasehandler.o \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
new file mode 100644
index 0000000..59eb7df
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -0,0 +1,111 @@
+/* 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 "backends/networking/sdl_net/handlers/downloadfilehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/fs/fs-factory.h"
+#include "common/file.h"
+#include "common/translation.h"
+#include "../getclienthandler.h"
+
+namespace Networking {
+
+#define INDEX_PAGE_NAME ".index.html"
+
+DownloadFileHandler::DownloadFileHandler() {}
+
+DownloadFileHandler::~DownloadFileHandler() {}
+
+void DownloadFileHandler::handle(Client &client) {
+	Common::String path = client.queryParameter("path");
+	Common::String errorMessage = "";
+
+	// show an error message if failed to download the file
+	if (!downloadFile(client, path, errorMessage)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=/\">%s</a>",
+				errorMessage.c_str(),
+				_("Back to the files manager")
+				)
+			);
+	}
+}
+
+void DownloadFileHandler::handleErrorMessage(Client &client, Common::String message) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+bool DownloadFileHandler::downloadFile(Client &client, Common::String path, Common::String &errorMessage) {
+	// check that <path> is not an absolute root
+	if (path == "" || path == "/") {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// transform virtual path to actual file system one
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// check that <path> exists and is directory
+	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!node->exists()) {
+		errorMessage = _("The file doesn't exist!");
+		return false;
+	}
+	if (node->isDirectory()) {
+		errorMessage = _("Can't download a directory!");
+		return false;
+	}
+	Common::SeekableReadStream *stream = node->createReadStream();
+	if (stream == nullptr) {
+		errorMessage = _("Failed to read the file!");
+		return false;
+	}
+
+	GetClientHandler *handler = new GetClientHandler(stream);
+	handler->setResponseCode(200);
+	handler->setHeader("Content-Type", "application/force-download");	
+	handler->setHeader("Content-Disposition", "attachment; filename=\"" + node->getDisplayName() + "\"");
+	handler->setHeader("Content-Transfer-Encoding", "binary");	
+	client.setHandler(handler);
+	return true;
+}
+
+/// public
+
+ClientHandlerCallback DownloadFileHandler::getHandler() {
+	return new Common::Callback<DownloadFileHandler, Client &>(this, &DownloadFileHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.h b/backends/networking/sdl_net/handlers/downloadfilehandler.h
new file mode 100644
index 0000000..6b69664
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.h
@@ -0,0 +1,51 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_DOWNLOADFILEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_DOWNLOADFILEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+
+namespace Networking {
+
+class DownloadFileHandler: public FilesBaseHandler {
+	void handle(Client &client);
+	void handleErrorMessage(Client &client, Common::String message);
+
+	/**
+	 * Creates a client handler for downloading file <path>.
+	 *
+	 * Fills <errorMessage> on failure.
+	 *
+	 * Returns true on success.
+	 */
+	bool downloadFile(Client &client, Common::String path, Common::String &errorMessage);
+public:
+	DownloadFileHandler();
+	virtual ~DownloadFileHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index 4575e4d..d4b5c3b 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -44,9 +44,9 @@ Common::String FilesBaseHandler::parentPath(Common::String path) {
 	return path;
 }
 
-bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd) {
+bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd, bool isDirectory) {
 	// <path> is not empty, but could lack the trailing slash	
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	if (isDirectory && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
 
 	if (path.hasPrefix("/root")) {
 		prefixToAdd = "/root/";
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.h b/backends/networking/sdl_net/handlers/filesbasehandler.h
index 3b631a9..a960181 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.h
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.h
@@ -39,7 +39,7 @@ protected:
 	*
 	* Returns true on success.
 	*/
-	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd);
+	bool transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd, bool isDirectory = true);
 public:
 	FilesBaseHandler();
 	virtual ~FilesBaseHandler();
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index b2eff16..cc3f67e 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -45,6 +45,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/", _indexPageHandler.getHandler());
 	addPathHandler("/files", _filesPageHandler.getHandler());
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
+	addPathHandler("/download", _downloadFileHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index ed1860a..97fe2aa 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -26,6 +26,7 @@
 #include "backends/networking/sdl_net/client.h"
 #include "backends/networking/sdl_net/handlers/basehandler.h"
 #include "backends/networking/sdl_net/handlers/createdirectoryhandler.h"
+#include "backends/networking/sdl_net/handlers/downloadfilehandler.h"
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
 #include "backends/networking/sdl_net/handlers/resourcehandler.h"
@@ -61,6 +62,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	IndexPageHandler _indexPageHandler;
 	FilesPageHandler _filesPageHandler;
 	CreateDirectoryHandler _createDirectoryHandler;
+	DownloadFileHandler _downloadFileHandler;
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;


Commit: 89a1a549822dee5b18d799cd9b8e5a58190aa49d
    https://github.com/scummvm/scummvm/commit/89a1a549822dee5b18d799cd9b8e5a58190aa49d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GetClientHandler's buffer

It's not static now and it's increased to 1 MB.

Changed paths:
    backends/networking/sdl_net/getclienthandler.cpp
    backends/networking/sdl_net/getclienthandler.h



diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index 0e73a54..cdbcd96 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -25,9 +25,14 @@
 
 namespace Networking {
 
-GetClientHandler::GetClientHandler(Common::SeekableReadStream *stream): _responseCode(200), _headersPrepared(false), _stream(stream) {}
+GetClientHandler::GetClientHandler(Common::SeekableReadStream *stream):
+	_responseCode(200), _headersPrepared(false),
+	_stream(stream), _buffer(new byte[CLIENT_HANDLER_BUFFER_SIZE]) {}
 
-GetClientHandler::~GetClientHandler() { delete _stream; }
+GetClientHandler::~GetClientHandler() {
+	delete _stream;
+	delete[] _buffer;
+}
 
 const char *GetClientHandler::responseMessage(long responseCode) {
 	switch (responseCode) {
@@ -118,16 +123,14 @@ void GetClientHandler::prepareHeaders() {
 void GetClientHandler::handle(Client *client) {
 	if (!client) return;
 	if (!_headersPrepared) prepareHeaders();
-	
-	const int kBufSize = 16 * 1024;
-	char buf[kBufSize];
+
 	uint32 readBytes;
 
 	// send headers first
 	if (_headers.size() > 0) {
 		readBytes = _headers.size();
-		if (readBytes > kBufSize) readBytes = kBufSize;
-		memcpy(buf, _headers.c_str(), readBytes);
+		if (readBytes > CLIENT_HANDLER_BUFFER_SIZE) readBytes = CLIENT_HANDLER_BUFFER_SIZE;
+		memcpy(_buffer, _headers.c_str(), readBytes);
 		_headers.erase(0, readBytes);
 	} else {
 		if (!_stream) {
@@ -135,11 +138,11 @@ void GetClientHandler::handle(Client *client) {
 			return;
 		}
 
-		readBytes = _stream->read(buf, kBufSize);
+		readBytes = _stream->read(_buffer, CLIENT_HANDLER_BUFFER_SIZE);
 	}
 
 	if (readBytes != 0)
-		if (client->send(buf, readBytes) != readBytes) {
+		if (client->send(_buffer, readBytes) != readBytes) {
 			warning("GetClientHandler: unable to send all bytes to the client");
 			client->close();
 			return;
diff --git a/backends/networking/sdl_net/getclienthandler.h b/backends/networking/sdl_net/getclienthandler.h
index f434df1..3486cee 100644
--- a/backends/networking/sdl_net/getclienthandler.h
+++ b/backends/networking/sdl_net/getclienthandler.h
@@ -30,12 +30,15 @@
 
 namespace Networking {
 
+#define CLIENT_HANDLER_BUFFER_SIZE 1 * 1024 * 1024
+
 class GetClientHandler: public ClientHandler {
 	Common::HashMap<Common::String, Common::String> _specialHeaders;
 	long _responseCode;
 	bool _headersPrepared;
 	Common::String _headers;
 	Common::SeekableReadStream *_stream;
+	byte *_buffer;
 
 	static const char *responseMessage(long responseCode);
 	void prepareHeaders();


Commit: 4d88f51de9583d9fcdebe8fc5e90fed61416fe96
    https://github.com/scummvm/scummvm/commit/4d88f51de9583d9fcdebe8fc5e90fed61416fe96
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add query parameters URL encoding

In local webserver's links.

Fixed URL decoding to understand '+', by the way. Firefox sends these
instead of spaces and "%2B" instead of '+'.

Changed paths:
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index ad8a67a..7b7d9ed 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -44,8 +44,9 @@ void CreateDirectoryHandler::handle(Client &client) {
 		handleErrorMessage(
 			client,
 			Common::String::format(
-				"%s<br/><a href=\"files?path=/\">%s</a>",
+				"%s<br/><a href=\"files?path=%s\">%s</a>",
 				errorMessage.c_str(),
+				"%2F", //that's encoded "/"
 				_("Back to the files manager")
 				)
 			);
@@ -65,7 +66,10 @@ void CreateDirectoryHandler::handle(Client &client) {
 			_("Back to parent directory")
 		)
 	);
-	LocalWebserver::setClientRedirectHandler(client, response, "/files?path=" + client.queryParameter("path"));
+	LocalWebserver::setClientRedirectHandler(
+		client, response,
+		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+	);
 }
 
 void CreateDirectoryHandler::handleErrorMessage(Client &client, Common::String message) {
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 59eb7df..8974cb7 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -44,8 +44,9 @@ void DownloadFileHandler::handle(Client &client) {
 		handleErrorMessage(
 			client,
 			Common::String::format(
-				"%s<br/><a href=\"files?path=/\">%s</a>",
+				"%s<br/><a href=\"files?path=%s\">%s</a>",
 				errorMessage.c_str(),
+				"%2F", //that's encoded "/"
 				_("Back to the files manager")
 				)
 			);
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 2ecb448..c21575d 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -50,8 +50,9 @@ void FilesPageHandler::handle(Client &client) {
 		handleErrorMessage(
 			client,
 			Common::String::format(
-				"%s<br/><a href=\"files?path=/\">%s</a>",
+				"%s<br/><a href=\"files?path=%s\">%s</a>",
 				_("ScummVM couldn't list the directory you specified."),
+				"%2F", //that's encoded "/"
 				_("Back to the files manager")
 			)
 		);
@@ -133,7 +134,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 
 void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size) {
 	Common::String item = itemTemplate;
-	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + path);
+	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + LocalWebserver::urlEncodeQueryParameterValue(path));
 	replace(item, "{name}", name);
 	replace(item, "{size}", size);
 	content += item;
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index cc3f67e..2880c67 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -301,6 +301,11 @@ Common::String LocalWebserver::urlDecode(Common::String value) {
 	Common::String result = "";
 	uint32 size = value.size();
 	for (uint32 i = 0; i < size; ++i) {
+		if (value[i] == '+') {
+			result += ' ';
+			continue;
+		}
+
 		if (value[i] == '%' && i+2 < size) {
 			int d1 = hexDigit(value[i+1]);
 			int d2 = hexDigit(value[i+2]);
@@ -316,4 +321,36 @@ Common::String LocalWebserver::urlDecode(Common::String value) {
 	return result;
 }
 
+namespace {
+bool isQueryUnreserved(char c) {
+	return (
+		('0' <= c && c <= '9') ||
+		('A' <= c && c <= 'Z') ||
+		('a' <= c && c <= 'z') ||
+		c == '-' || c == '_' || c == '.' || c == '!' ||
+		c == '~' || c == '*' || c == '\'' || c == '(' || c == ')'
+	);
+}
+}
+
+Common::String LocalWebserver::urlEncodeQueryParameterValue(Common::String value) {
+	//OK chars = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+	//reserved for query are ";", "/", "?", ":", "@", "&", "=", "+", ","
+	//that means these must be encoded too or otherwise they could malform the query
+	Common::String result = "";
+	char hexChar[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+	for (uint32 i = 0; i < value.size(); ++i) {
+		char c = value[i];
+		if (isQueryUnreserved(c))
+			result += c;
+		else {
+			result += '%';
+			result += hexChar[(c >> 4) & 0xF];
+			result += hexChar[c & 0xF];
+		}
+	}
+	return result;
+}
+
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 97fe2aa..24a4ecf 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -94,6 +94,7 @@ public:
 	static void setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType = nullptr);
 	static const char *determineMimeType(Common::String &filename);
 	static Common::String urlDecode(Common::String value);
+	static Common::String urlEncodeQueryParameterValue(Common::String value);
 };
 
 /** Shortcut for accessing the local webserver. */


Commit: 79b39bf0d080d43dc1c5569272b8091017dfa3e3
    https://github.com/scummvm/scummvm/commit/79b39bf0d080d43dc1c5569272b8091017dfa3e3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Reader sketch

That should be part of the Client, I guess. Reader is not ready to
continue reading from place it stopped, but it already works as it
should for the case when whole content is available.

Changed paths:
  A backends/networking/sdl_net/reader.cpp
  A backends/networking/sdl_net/reader.h
    backends/networking/sdl_net/localwebserver.cpp



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 2880c67..cac7dab 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -30,6 +30,7 @@
 #include "common/timer.h"
 #include "common/textconsole.h"
 #include <SDL/SDL_net.h>
+#include "reader.h"
 
 namespace Common {
 class MemoryReadWriteStream;
@@ -47,6 +48,9 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
 	addPathHandler("/download", _downloadFileHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
+
+	Reader reader;
+	reader.readResponse();
 }
 
 LocalWebserver::~LocalWebserver() {
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
new file mode 100644
index 0000000..edfaf81
--- /dev/null
+++ b/backends/networking/sdl_net/reader.cpp
@@ -0,0 +1,220 @@
+/* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/sdl_net/reader.h"
+#include <common/debug.h>
+
+namespace Networking {
+
+Reader::Reader() {
+	_contentLength = 0;
+	_availableBytes = 0;
+
+	_window = nullptr;
+	_windowUsed = 0;
+	_windowSize = 0;
+
+	_content =
+		"POST /upload HTTP/1.1\r\n" \
+		"Host: 127.0.0.1:12345\r\n" \
+		"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0\r\n" \
+		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" \
+		"Accept-Language: ru,en-US;q=0.7,en;q=0.3\r\n" \
+		"Accept-Encoding: gzip, deflate\r\n" \
+		"Referer: http://127.0.0.1:12345/files\r\n" \
+		"Connection: keep-alive\r\n" \
+		"Content-Type: multipart/form-data; boundary=---------------------------93411339527546\r\n" \
+		"Content-Length: 319\r\n" \
+		"\r\n" \
+		"-----------------------------93411339527546\r\n" \
+		"Content-Disposition: form-data; name=\"path\"\r\n" \
+		"\r\n" \
+		"/root\r\n" \
+		"-----------------------------93411339527546\r\n" \
+		"Content-Disposition: form-data; name=\"upload_file\"; filename=\"irc.txt\"\r\n" \
+		"Content-Type: text/plain\r\n" \
+		"\r\n" \
+		"shells.fnordserver.eu/1400\r\n" \
+		"-----------------------------93411339527546--";
+}
+
+Reader::~Reader() {}
+
+void Reader::readResponse() {
+	while (true) {
+		readHeaders(); //til "\r\n\r\n"
+		readContent(); //til "--" + _boundary
+		if (_availableBytes >= 2) {
+			Common::String bts;
+			bts += readOne();
+			bts += readOne();
+			if (bts == "--") break;
+			if (bts == "\r\n") continue;
+			warning("strange bytes: \"%s\"", bts);
+		} else {
+			warning("strange ending");
+			break;
+		}
+	}	
+	if (_availableBytes > 0) debug("STRANGE END: %llu bytes left", _availableBytes);
+	else debug("END");
+}
+
+void Reader::readHeaders() {
+	Common::String boundary = "\r\n\r\n";
+	makeWindow(boundary.size());
+
+	Common::String headers = "";
+	while (readOneByteInString(headers, boundary));
+	handleHeaders(headers);
+}
+
+namespace {
+void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) {
+	const char *position = strstr(cstr, needle.c_str());
+
+	if (position) {
+		char c;
+		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
+			if (c == '\n' || c == '\r') break;
+			result += c;
+		}
+	}
+}
+}
+
+void Reader::handleHeaders(Common::String headers) {
+	debug("\nHANDLE HEADERS:\n>>%s<<", headers.c_str());
+	if (_boundary.empty()) {
+		//TODO: parse method, path, query, fragment
+
+		//find boundary
+		_boundary = "";
+		readFromThatUntilLineEnd(headers.c_str(), "boundary=", _boundary);
+
+		//find content length
+		Common::String contentLength = "";
+		readFromThatUntilLineEnd(headers.c_str(), "Content-Length: ", contentLength);
+		_contentLength = contentLength.asUint64();
+		_availableBytes = _contentLength;
+		debug("BOUNDARY: %s", _boundary.c_str());
+		debug("LENGTH: %llu", _contentLength);
+	} else {
+		//find field name
+		_currentFieldName = "";
+		readFromThatUntilLineEnd(headers.c_str(), "name=\"", _currentFieldName);
+		for (uint32 i = 0; i < _currentFieldName.size(); ++i)
+			if (_currentFieldName[i] == '\"') {
+				_currentFieldName.erase(i);
+				break;
+			}
+		debug("FIELD NAME: >>%s<<", _currentFieldName.c_str());
+
+		//find out field type
+		//_fieldIsFile = true;
+	}
+}
+
+void Reader::readContent() {
+	Common::String boundary = "--" + _boundary;
+	makeWindow(boundary.size());
+
+	/*
+	if (_fieldIsFile) {
+		//create temporary file
+		tempFileName = generateTempFileName();
+		stream = openFileStream(tempFileName);
+		//read till "--" + _boundary
+		while (readOneByteInStream(stream));
+		handleFileContent(tempFileName);
+	} else {
+	*/
+		Common::String buffer = "";
+		while (readOneByteInString(buffer, boundary)) ;
+		handleValueContent(buffer);
+	//}
+}
+
+void Reader::handleFileContent(Common::String filename) {
+	_attachedFiles[_currentFieldName] = filename;
+}
+
+void Reader::handleValueContent(Common::String value) {
+	debug("\nHANDLE CONTENT:\n>>%s<<", value.c_str());
+	_fields[_currentFieldName] = value;
+}
+
+void Reader::makeWindow(uint32 size) {
+	delete[] _window;
+	_window = nullptr;
+
+	_window = new byte[size];
+	_windowUsed = 0;
+	_windowSize = size;
+}
+
+/*
+bool Reader::readOneByteInStream(stream) {
+	b = read(1);
+	_window[_windowUsed++] = b;
+	if (_windowUsed < _windowSize) return true;
+
+	//when window is filled, check whether that's the boundary
+	if (_window == "--" + _boundary)
+		return false;
+
+	//if not, write the first byte of the window to the stream
+	stream.write(_window[0]);
+	for (uint32 i = 1; i < _windowSize; ++i)
+		_window[i - 1] = _window[i];
+	--_windowUsed;
+	return true;
+}
+*/
+
+bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary) {
+	byte b = readOne();
+	_window[_windowUsed++] = b;
+	if (_windowUsed < _windowSize) return true;
+
+	//when window is filled, check whether that's the boundary
+	if (Common::String((char *)_window, _windowSize) == boundary)
+		return false;
+
+	//if not, add the first byte of the window to the string
+	buffer += _window[0];
+	for (uint32 i = 1; i < _windowSize; ++i)
+		_window[i - 1] = _window[i];
+	--_windowUsed;
+	return true;
+}
+
+byte Reader::readOne() {
+	byte b = _content[0];
+	_content.deleteChar(0);
+	--_availableBytes;
+	return b;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
new file mode 100644
index 0000000..d4f9e11
--- /dev/null
+++ b/backends/networking/sdl_net/reader.h
@@ -0,0 +1,70 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_READER_H
+#define BACKENDS_NETWORKING_SDL_NET_READER_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Networking {
+
+class Reader {
+	///Common::String _headers;
+	///Common::String _method, _path, _query, _anchor;
+	Common::String _content;
+
+	Common::String _boundary;
+	uint32 _contentLength;
+	uint32 _availableBytes;
+
+	Common::String _currentFieldName;
+	Common::HashMap<Common::String, Common::String> _fields;
+	Common::HashMap<Common::String, Common::String> _attachedFiles;
+
+	byte *_window;
+	uint32 _windowUsed, _windowSize;
+
+	void readHeaders();
+	void readContent();
+	void handleHeaders(Common::String headers);
+	void handleFileContent(Common::String filename);
+	void handleValueContent(Common::String value);
+
+	void makeWindow(uint32 size);
+	///bool Reader::readOneByteInStream(stream);
+	bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary);
+
+	byte readOne();
+
+public:
+	Reader();
+	~Reader();
+
+	void readResponse();
+};
+
+} // End of namespace Networking
+
+#endif


Commit: bb67b81d04101d5f8457cf2e1fc1e67d09ad29c3
    https://github.com/scummvm/scummvm/commit/bb67b81d04101d5f8457cf2e1fc1e67d09ad29c3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update Reader to support pausing

That means that if current buffer is over, reader will stop reading and
it's safe to call readResponse() again, so it would continue from the
place it left.

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index cac7dab..d8f12b8 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -50,7 +50,8 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	_defaultHandler = _resourceHandler.getHandler();
 
 	Reader reader;
-	reader.readResponse();
+	reader.setBytesLeft(128);
+	while (!reader.readResponse()) reader.setBytesLeft(20);
 }
 
 LocalWebserver::~LocalWebserver() {
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index edfaf81..b6bf9e5 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -28,6 +28,9 @@
 namespace Networking {
 
 Reader::Reader() {
+	_state = RS_NONE;
+	_bytesLeft = 0;
+
 	_contentLength = 0;
 	_availableBytes = 0;
 
@@ -35,6 +38,9 @@ Reader::Reader() {
 	_windowUsed = 0;
 	_windowSize = 0;
 
+	_headers = "";
+	_buffer = "";
+
 	_content =
 		"POST /upload HTTP/1.1\r\n" \
 		"Host: 127.0.0.1:12345\r\n" \
@@ -61,10 +67,20 @@ Reader::Reader() {
 
 Reader::~Reader() {}
 
-void Reader::readResponse() {
+bool Reader::readResponse() {
+	if (_state == RS_NONE) _state = RS_READING_HEADERS;
+
 	while (true) {
-		readHeaders(); //til "\r\n\r\n"
-		readContent(); //til "--" + _boundary
+		if (!bytesLeft()) return false;
+
+		if (_state == RS_READING_HEADERS)
+			if (!readHeaders())
+				return false;
+
+		if (_state == RS_READING_CONTENT)
+			if (!readContent())
+				return false;
+
 		if (_availableBytes >= 2) {
 			Common::String bts;
 			bts += readOne();
@@ -79,15 +95,25 @@ void Reader::readResponse() {
 	}	
 	if (_availableBytes > 0) debug("STRANGE END: %llu bytes left", _availableBytes);
 	else debug("END");
+
+	return true;
 }
 
-void Reader::readHeaders() {
+bool Reader::readHeaders() {
 	Common::String boundary = "\r\n\r\n";
-	makeWindow(boundary.size());
+	if (_window == nullptr) {
+		makeWindow(boundary.size());
+		_headers = "";
+	}
 
-	Common::String headers = "";
-	while (readOneByteInString(headers, boundary));
-	handleHeaders(headers);
+	while (readOneByteInString(_headers, boundary)) {
+		if (!bytesLeft()) return false;
+	}
+	handleHeaders(_headers);
+
+	freeWindow();
+	_state = RS_READING_CONTENT;
+	return true;
 }
 
 namespace {
@@ -136,9 +162,12 @@ void Reader::handleHeaders(Common::String headers) {
 	}
 }
 
-void Reader::readContent() {
+bool Reader::readContent() {
 	Common::String boundary = "--" + _boundary;
-	makeWindow(boundary.size());
+	if (_window == nullptr) {
+		makeWindow(boundary.size());
+		_buffer = "";
+	}
 
 	/*
 	if (_fieldIsFile) {
@@ -150,10 +179,15 @@ void Reader::readContent() {
 		handleFileContent(tempFileName);
 	} else {
 	*/
-		Common::String buffer = "";
-		while (readOneByteInString(buffer, boundary)) ;
-		handleValueContent(buffer);
+		while (readOneByteInString(_buffer, boundary)) {		
+			if (!bytesLeft()) return false;
+		}
+		handleValueContent(_buffer);
 	//}
+
+	freeWindow();
+	_state = RS_READING_HEADERS;
+	return true;
 }
 
 void Reader::handleFileContent(Common::String filename) {
@@ -166,14 +200,19 @@ void Reader::handleValueContent(Common::String value) {
 }
 
 void Reader::makeWindow(uint32 size) {
-	delete[] _window;
-	_window = nullptr;
+	freeWindow();
 
 	_window = new byte[size];
 	_windowUsed = 0;
 	_windowSize = size;
 }
 
+void Reader::freeWindow() {
+	delete[] _window;
+	_window = nullptr;
+	_windowUsed = _windowSize = 0;
+}
+
 /*
 bool Reader::readOneByteInStream(stream) {
 	b = read(1);
@@ -214,7 +253,12 @@ byte Reader::readOne() {
 	byte b = _content[0];
 	_content.deleteChar(0);
 	--_availableBytes;
+	--_bytesLeft;
 	return b;
 }
 
+uint32 Reader::bytesLeft() { return _bytesLeft; }
+
+void Reader::setBytesLeft(uint32 b) { _bytesLeft = b; }
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index d4f9e11..b91973e 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -30,15 +30,26 @@
 
 namespace Networking {
 
+enum ReaderState {
+	RS_NONE,
+	RS_READING_HEADERS,
+	RS_READING_CONTENT
+};
+
 class Reader {
 	///Common::String _headers;
 	///Common::String _method, _path, _query, _anchor;
+	ReaderState _state;
 	Common::String _content;
+	uint32 _bytesLeft;
 
 	Common::String _boundary;
 	uint32 _contentLength;
 	uint32 _availableBytes;
 
+	Common::String _headers;
+	Common::String _buffer;
+
 	Common::String _currentFieldName;
 	Common::HashMap<Common::String, Common::String> _fields;
 	Common::HashMap<Common::String, Common::String> _attachedFiles;
@@ -46,23 +57,26 @@ class Reader {
 	byte *_window;
 	uint32 _windowUsed, _windowSize;
 
-	void readHeaders();
-	void readContent();
+	bool readHeaders(); //true when ended reading
+	bool readContent(); //true when ended reading
 	void handleHeaders(Common::String headers);
 	void handleFileContent(Common::String filename);
 	void handleValueContent(Common::String value);
 
 	void makeWindow(uint32 size);
+	void freeWindow();
 	///bool Reader::readOneByteInStream(stream);
 	bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary);
 
 	byte readOne();
+	uint32 bytesLeft();
 
 public:
 	Reader();
 	~Reader();
 
-	void readResponse();
+	bool readResponse(); //true when ended reading
+	void setBytesLeft(uint32 b);
 };
 
 } // End of namespace Networking


Commit: 589b4cd45741ff73ba2d1260a14a14351cfba914
    https://github.com/scummvm/scummvm/commit/589b4cd45741ff73ba2d1260a14a14351cfba914
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use "multipart/form-data" in upload form

Changed paths:
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html



diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index de683a0..7049289 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index de29ac3..0651346 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -25,7 +25,7 @@
 				</div>
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
-					<form action="upload" method="post">
+					<form action="upload" method="post" enctype="multipart/form-data">
 						<input type="hidden" name="path" value="{path}"/>
 						<input type="file" name="upload_file"/>
 						<input type="submit" value="{upload_file_button}"/>


Commit: f91bb39192cd11a0cb30a39bfb630d2fd9fad89e
    https://github.com/scummvm/scummvm/commit/f91bb39192cd11a0cb30a39bfb630d2fd9fad89e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use Reader in Client

Instead of copy-pasting it I'm just "integrating" it in.

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index f567891..adcbafd 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -45,6 +45,7 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	_socket = socket;
 	_set = set;
 	_headers = "";
+	_reader = Reader();
 	_method = "";
 	_path = "";
 	_query = "";
@@ -63,88 +64,25 @@ void Client::readHeaders() {
 	if (!SDLNet_SocketReady(_socket)) return;
 
 	const uint32 BUFFER_SIZE = 16 * 1024;
-	char buffer[BUFFER_SIZE];
+	byte buffer[BUFFER_SIZE];
 	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);
 	if (bytes <= 0) {
 		warning("Client::readHeaders recv fail");
 		close();
 		return;
 	}
-	_headers += Common::String(buffer, bytes);
-	checkIfHeadersEnded();
-	checkIfBadRequest();
-}
-
-void Client::checkIfHeadersEnded() {
-	const char *cstr = _headers.c_str();
-	const char *position = strstr(cstr, "\r\n\r\n");
-	if (position) _state = READ_HEADERS;
-}
-
-void Client::checkIfBadRequest() {
-	uint32 headersSize = _headers.size();
-	bool bad = false;
-
-	const uint32 SUSPICIOUS_HEADERS_SIZE = 128 * 1024;
-	if (headersSize > SUSPICIOUS_HEADERS_SIZE) bad = true;
-
-	if (!bad) {
-		if (headersSize > 0) {
-			const char *cstr = _headers.c_str();
-			const char *position = strstr(cstr, "\r\n");
-			if (position) { //we have at least one line - and we want the first one
-				//"<METHOD> <path> HTTP/<VERSION>\r\n"
-				Common::String method, path, http, buf;
-				uint32 length = position - cstr;
-				if (headersSize > length) headersSize = length;
-				for (uint32 i = 0; i < headersSize; ++i) {
-					if (_headers[i] != ' ') buf += _headers[i];
-					if (_headers[i] == ' ' || i == headersSize - 1) {
-						if (method == "") method = buf;
-						else if (path == "") path = buf;
-						else if (http == "") http = buf;
-						else {
-							bad = true;
-							break;
-						}
-						buf = "";
-					}
-				}
-
-				//check that method is supported
-				if (method != "GET" && method != "PUT" && method != "POST") bad = true;
-
-				//check that HTTP/<VERSION> is OK
-				if (!http.hasPrefix("HTTP/")) bad = true;
-
-				_method = method;
-				parsePathQueryAndAnchor(path);
-			}
+	
+	_reader.setContent(buffer, bytes);
+	if (_reader.readResponse()) {
+		if (_reader.badRequest()) _state = BAD_REQUEST;
+		else {
+			_state = READ_HEADERS;
+			_method = _reader.method();
+			_path = _reader.path();
+			_query = _reader.query();
+			_anchor = _reader.anchor();
 		}
 	}
-
-	if (bad) _state = BAD_REQUEST;
-}
-
-void Client::parsePathQueryAndAnchor(Common::String path) {
-	//<path>[?query][#anchor]
-	bool readingPath = true;
-	bool readingQuery = false;
-	_path = "";
-	_query = "";
-	_anchor = "";
-	for (uint32 i = 0; i < path.size(); ++i) {
-		if (readingPath) {
-			if (path[i] == '?') {
-				readingPath = false;
-				readingQuery = true;
-			} else _path += path[i];
-		} else if(readingQuery) {
-			if (path[i] == '#') {
-				readingQuery = false;
-			} else _query += path[i];
-		} else _anchor += path[i];
-	}
 }
 
 void Client::setHandler(ClientHandler *handler) {	
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index eba5dab..a54f72a 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "common/str.h"
+#include "reader.h"
 
 typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
 typedef struct _TCPsocket *TCPsocket;
@@ -51,14 +52,11 @@ class Client {
 	ClientState _state;
 	SDLNet_SocketSet _set;
 	TCPsocket _socket;
+	Reader _reader;
 	Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
 	ClientHandler *_handler;
 
-	void checkIfHeadersEnded();
-	void checkIfBadRequest();
-	void parsePathQueryAndAnchor(Common::String path);
-
 public:
 	Client();
 	Client(SDLNet_SocketSet set, TCPsocket socket);
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index d8f12b8..8f9c30d 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -48,10 +48,6 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
 	addPathHandler("/download", _downloadFileHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
-
-	Reader reader;
-	reader.setBytesLeft(128);
-	while (!reader.readResponse()) reader.setBytesLeft(20);
 }
 
 LocalWebserver::~LocalWebserver() {
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index b6bf9e5..3f79835 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -29,11 +29,9 @@ namespace Networking {
 
 Reader::Reader() {
 	_state = RS_NONE;
+	_content = nullptr;
 	_bytesLeft = 0;
 
-	_contentLength = 0;
-	_availableBytes = 0;
-
 	_window = nullptr;
 	_windowUsed = 0;
 	_windowSize = 0;
@@ -41,28 +39,9 @@ Reader::Reader() {
 	_headers = "";
 	_buffer = "";
 
-	_content =
-		"POST /upload HTTP/1.1\r\n" \
-		"Host: 127.0.0.1:12345\r\n" \
-		"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0\r\n" \
-		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" \
-		"Accept-Language: ru,en-US;q=0.7,en;q=0.3\r\n" \
-		"Accept-Encoding: gzip, deflate\r\n" \
-		"Referer: http://127.0.0.1:12345/files\r\n" \
-		"Connection: keep-alive\r\n" \
-		"Content-Type: multipart/form-data; boundary=---------------------------93411339527546\r\n" \
-		"Content-Length: 319\r\n" \
-		"\r\n" \
-		"-----------------------------93411339527546\r\n" \
-		"Content-Disposition: form-data; name=\"path\"\r\n" \
-		"\r\n" \
-		"/root\r\n" \
-		"-----------------------------93411339527546\r\n" \
-		"Content-Disposition: form-data; name=\"upload_file\"; filename=\"irc.txt\"\r\n" \
-		"Content-Type: text/plain\r\n" \
-		"\r\n" \
-		"shells.fnordserver.eu/1400\r\n" \
-		"-----------------------------93411339527546--";
+	_contentLength = 0;
+	_availableBytes = 0;
+	_isBadRequest = false;
 }
 
 Reader::~Reader() {}
@@ -77,6 +56,10 @@ bool Reader::readResponse() {
 			if (!readHeaders())
 				return false;
 
+		if (_boundary.empty()) return true; //not POST multipart
+
+		if (!bytesLeft()) return false;
+
 		if (_state == RS_READING_CONTENT)
 			if (!readContent())
 				return false;
@@ -133,7 +116,8 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
 void Reader::handleHeaders(Common::String headers) {
 	debug("\nHANDLE HEADERS:\n>>%s<<", headers.c_str());
 	if (_boundary.empty()) {
-		//TODO: parse method, path, query, fragment
+		//parse method, path, query, fragment
+		parseFirstLine(headers);
 
 		//find boundary
 		_boundary = "";
@@ -162,6 +146,72 @@ void Reader::handleHeaders(Common::String headers) {
 	}
 }
 
+void Reader::parseFirstLine(const Common::String &headers) {
+	uint32 headersSize = headers.size();
+	bool bad = false;
+
+	const uint32 SUSPICIOUS_HEADERS_SIZE = 128 * 1024;
+	if (headersSize > SUSPICIOUS_HEADERS_SIZE) bad = true;
+
+	if (!bad) {
+		if (headersSize > 0) {
+			const char *cstr = headers.c_str();
+			const char *position = strstr(cstr, "\r\n");
+			if (position) { //we have at least one line - and we want the first one
+							//"<METHOD> <path> HTTP/<VERSION>\r\n"
+				Common::String method, path, http, buf;
+				uint32 length = position - cstr;
+				if (headersSize > length) headersSize = length;
+				for (uint32 i = 0; i < headersSize; ++i) {
+					if (headers[i] != ' ') buf += headers[i];
+					if (headers[i] == ' ' || i == headersSize - 1) {
+						if (method == "") method = buf;
+						else if (path == "") path = buf;
+						else if (http == "") http = buf;
+						else {
+							bad = true;
+							break;
+						}
+						buf = "";
+					}
+				}
+
+				//check that method is supported
+				if (method != "GET" && method != "PUT" && method != "POST") bad = true;
+
+				//check that HTTP/<VERSION> is OK
+				if (!http.hasPrefix("HTTP/")) bad = true;
+
+				_method = method;
+				parsePathQueryAndAnchor(path);
+			}
+		}
+	}
+
+	if (bad) _isBadRequest = true;
+}
+
+void Reader::parsePathQueryAndAnchor(Common::String path) {
+	//<path>[?query][#anchor]
+	bool readingPath = true;
+	bool readingQuery = false;
+	_path = "";
+	_query = "";
+	_anchor = "";
+	for (uint32 i = 0; i < path.size(); ++i) {
+		if (readingPath) {
+			if (path[i] == '?') {
+				readingPath = false;
+				readingQuery = true;
+			} else _path += path[i];
+		} else if (readingQuery) {
+			if (path[i] == '#') {
+				readingQuery = false;
+			} else _query += path[i];
+		} else _anchor += path[i];
+	}
+}
+
 bool Reader::readContent() {
 	Common::String boundary = "--" + _boundary;
 	if (_window == nullptr) {
@@ -251,7 +301,7 @@ bool Reader::readOneByteInString(Common::String &buffer, const Common::String &b
 
 byte Reader::readOne() {
 	byte b = _content[0];
-	_content.deleteChar(0);
+	++_content;
 	--_availableBytes;
 	--_bytesLeft;
 	return b;
@@ -259,6 +309,19 @@ byte Reader::readOne() {
 
 uint32 Reader::bytesLeft() { return _bytesLeft; }
 
-void Reader::setBytesLeft(uint32 b) { _bytesLeft = b; }
+void Reader::setContent(byte *buffer, uint32 size) {
+	_content = buffer;
+	_bytesLeft = size;
+}
+
+bool Reader::badRequest() { return _isBadRequest; }
+
+Common::String Reader::method() const { return _method; }
+
+Common::String Reader::path() const { return _path; }
+
+Common::String Reader::query() const { return _query; }
+
+Common::String Reader::anchor() const { return _anchor; }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index b91973e..245f63a 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -37,25 +37,25 @@ enum ReaderState {
 };
 
 class Reader {
-	///Common::String _headers;
-	///Common::String _method, _path, _query, _anchor;
 	ReaderState _state;
-	Common::String _content;
+	byte *_content;
 	uint32 _bytesLeft;
 
-	Common::String _boundary;
-	uint32 _contentLength;
-	uint32 _availableBytes;
+	byte *_window;
+	uint32 _windowUsed, _windowSize;
 
 	Common::String _headers;
 	Common::String _buffer;
 
-	Common::String _currentFieldName;
+	///Common::String _headers;
+	Common::String _method, _path, _query, _anchor;
 	Common::HashMap<Common::String, Common::String> _fields;
 	Common::HashMap<Common::String, Common::String> _attachedFiles;
-
-	byte *_window;
-	uint32 _windowUsed, _windowSize;
+	uint32 _contentLength;
+	Common::String _boundary;
+	uint32 _availableBytes;
+	Common::String _currentFieldName;
+	bool _isBadRequest;
 
 	bool readHeaders(); //true when ended reading
 	bool readContent(); //true when ended reading
@@ -63,6 +63,9 @@ class Reader {
 	void handleFileContent(Common::String filename);
 	void handleValueContent(Common::String value);
 
+	void parseFirstLine(const Common::String &headers);
+	void parsePathQueryAndAnchor(Common::String path);
+
 	void makeWindow(uint32 size);
 	void freeWindow();
 	///bool Reader::readOneByteInStream(stream);
@@ -76,7 +79,13 @@ public:
 	~Reader();
 
 	bool readResponse(); //true when ended reading
-	void setBytesLeft(uint32 b);
+	void setContent(byte *buffer, uint32 size);
+	bool badRequest();
+
+	Common::String method() const;
+	Common::String path() const;
+	Common::String query() const;
+	Common::String anchor() const;
 };
 
 } // End of namespace Networking


Commit: c82ed40fdd95761310855355c7c2a032512afd84
    https://github.com/scummvm/scummvm/commit/c82ed40fdd95761310855355c7c2a032512afd84
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Move method/path/etc info in Reader

Query parameters are now parsed once and then just searched in the
HashMap.

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index adcbafd..3edf9c3 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -44,12 +44,7 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	_state = READING_HEADERS;
 	_socket = socket;
 	_set = set;
-	_headers = "";
 	_reader = Reader();
-	_method = "";
-	_path = "";
-	_query = "";
-	_anchor = "";
 	_handler = nullptr;
 	if (set) {
 		int numused = SDLNet_TCP_AddSocket(set, socket);
@@ -73,16 +68,8 @@ void Client::readHeaders() {
 	}
 	
 	_reader.setContent(buffer, bytes);
-	if (_reader.readResponse()) {
-		if (_reader.badRequest()) _state = BAD_REQUEST;
-		else {
-			_state = READ_HEADERS;
-			_method = _reader.method();
-			_path = _reader.path();
-			_query = _reader.query();
-			_anchor = _reader.anchor();
-		}
-	}
+	if (_reader.readRequest())
+		_state = (_reader.badRequest() ? BAD_REQUEST : READ_HEADERS);
 }
 
 void Client::setHandler(ClientHandler *handler) {	
@@ -118,39 +105,17 @@ void Client::close() {
 
 ClientState Client::state() const { return _state; }
 
-Common::String Client::headers() const { return _headers; }
-
-Common::String Client::method() const { return _method; }
-
-Common::String Client::path() const { return _path; }
-
-Common::String Client::query() const { return _query; }
-
-Common::String Client::queryParameter(Common::String name) const {
-	// this approach is a bit slower than searching for the <name>
-	// yet I believe it to be the right one, because we probably can have "<name>=" in the value of other key
-	Common::String key = "";
-	Common::String value = "";
-	bool readingKey = true;
-	for (uint32 i = 0; i < _query.size(); ++i) {
-		if (readingKey) {
-			if (_query[i] == '=') {
-				readingKey = false;
-				value = "";
-			} else key += _query[i];
-		} else {
-			if (_query[i] == '&') {
-				if (key == name) return LocalWebserver::urlDecode(value);
-				readingKey = true;
-				key = "";
-			} else value += _query[i];
-		}
-	}
-	if (key == name) return LocalWebserver::urlDecode(value); //the last key doesn't have an '&' in the end of the query
-	return "";
-}
+//Common::String Client::headers() const { return _headers; }
+
+Common::String Client::method() const { return _reader.method(); }
+
+Common::String Client::path() const { return _reader.path(); }
+
+Common::String Client::query() const { return _reader.query(); }
+
+Common::String Client::queryParameter(Common::String name) const { return _reader.queryParameter(name); }
 
-Common::String Client::anchor() const { return _anchor; }
+Common::String Client::anchor() const { return _reader.anchor(); }
 
 bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
 
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index a54f72a..9764dac 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -53,8 +53,6 @@ class Client {
 	SDLNet_SocketSet _set;
 	TCPsocket _socket;
 	Reader _reader;
-	Common::String _headers;
-	Common::String _method, _path, _query, _anchor;
 	ClientHandler *_handler;
 
 public:
@@ -69,7 +67,7 @@ public:
 	void close();
 
 	ClientState state() const;
-	Common::String headers() const;
+	//Common::String headers() const;
 	Common::String method() const;
 	Common::String path() const;
 	Common::String query() const;
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 3f79835..452520a 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -23,7 +23,8 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/networking/sdl_net/reader.h"
-#include <common/debug.h>
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/debug.h"
 
 namespace Networking {
 
@@ -46,7 +47,7 @@ Reader::Reader() {
 
 Reader::~Reader() {}
 
-bool Reader::readResponse() {
+bool Reader::readRequest() {
 	if (_state == RS_NONE) _state = RS_READING_HEADERS;
 
 	while (true) {
@@ -210,6 +211,34 @@ void Reader::parsePathQueryAndAnchor(Common::String path) {
 			} else _query += path[i];
 		} else _anchor += path[i];
 	}
+
+	parseQueryParameters();
+}
+
+void Reader::parseQueryParameters() {
+	Common::String key = "";
+	Common::String value = "";
+	bool readingKey = true;
+	for (uint32 i = 0; i < _query.size(); ++i) {
+		if (readingKey) {
+			if (_query[i] == '=') {
+				readingKey = false;
+				value = "";
+			} else key += _query[i];
+		} else {
+			if (_query[i] == '&') {				
+				if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!");
+				else _queryParameters[key] = LocalWebserver::urlDecode(value);
+				readingKey = true;
+				key = "";
+			} else value += _query[i];
+		}
+	}
+
+	if (!key.empty()) {
+		if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!");
+		else _queryParameters[key] = LocalWebserver::urlDecode(value);
+	}
 }
 
 bool Reader::readContent() {
@@ -246,7 +275,7 @@ void Reader::handleFileContent(Common::String filename) {
 
 void Reader::handleValueContent(Common::String value) {
 	debug("\nHANDLE CONTENT:\n>>%s<<", value.c_str());
-	_fields[_currentFieldName] = value;
+	_queryParameters[_currentFieldName] = value;
 }
 
 void Reader::makeWindow(uint32 size) {
@@ -322,6 +351,8 @@ Common::String Reader::path() const { return _path; }
 
 Common::String Reader::query() const { return _query; }
 
+Common::String Reader::queryParameter(Common::String name) const { return _queryParameters[name]; }
+
 Common::String Reader::anchor() const { return _anchor; }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 245f63a..e5ef523 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -49,7 +49,7 @@ class Reader {
 
 	///Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
-	Common::HashMap<Common::String, Common::String> _fields;
+	Common::HashMap<Common::String, Common::String> _queryParameters;
 	Common::HashMap<Common::String, Common::String> _attachedFiles;
 	uint32 _contentLength;
 	Common::String _boundary;
@@ -65,6 +65,7 @@ class Reader {
 
 	void parseFirstLine(const Common::String &headers);
 	void parsePathQueryAndAnchor(Common::String path);
+	void parseQueryParameters();
 
 	void makeWindow(uint32 size);
 	void freeWindow();
@@ -78,13 +79,14 @@ public:
 	Reader();
 	~Reader();
 
-	bool readResponse(); //true when ended reading
+	bool readRequest(); //true when ended reading
 	void setContent(byte *buffer, uint32 size);
 	bool badRequest();
 
 	Common::String method() const;
 	Common::String path() const;
 	Common::String query() const;
+	Common::String queryParameter(Common::String name) const;
 	Common::String anchor() const;
 };
 


Commit: 83957c9666fce2e4b536c0974f22fc70f57b119c
    https://github.com/scummvm/scummvm/commit/83957c9666fce2e4b536c0974f22fc70f57b119c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Determine file's name in POST

Changed paths:
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 452520a..025f534 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -42,6 +42,7 @@ Reader::Reader() {
 
 	_contentLength = 0;
 	_availableBytes = 0;
+	_isFileField = false;
 	_isBadRequest = false;
 }
 
@@ -143,7 +144,21 @@ void Reader::handleHeaders(Common::String headers) {
 		debug("FIELD NAME: >>%s<<", _currentFieldName.c_str());
 
 		//find out field type
-		//_fieldIsFile = true;
+		_currentFileName = "";
+		readFromThatUntilLineEnd(headers.c_str(), "filename=\"", _currentFileName);
+		for (uint32 i = 0; i < _currentFileName.size(); ++i)
+			if (_currentFileName[i] == '\"') {
+				_currentFileName.erase(i);
+				break;
+			}
+
+		if (!_currentFileName.empty()) {
+			_isFileField = true;
+			_queryParameters[_currentFieldName] = _currentFileName;
+			debug("FILE NAME: >>%s<<", _currentFileName.c_str());
+		} else {
+			_isFileField = false;
+		}
 	}
 }
 
@@ -270,7 +285,7 @@ bool Reader::readContent() {
 }
 
 void Reader::handleFileContent(Common::String filename) {
-	_attachedFiles[_currentFieldName] = filename;
+	_attachedFiles[_currentFileName] = filename;
 }
 
 void Reader::handleValueContent(Common::String value) {
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index e5ef523..74fcd43 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -54,7 +54,8 @@ class Reader {
 	uint32 _contentLength;
 	Common::String _boundary;
 	uint32 _availableBytes;
-	Common::String _currentFieldName;
+	Common::String _currentFieldName, _currentFileName;
+	bool _isFileField;
 	bool _isBadRequest;
 
 	bool readHeaders(); //true when ended reading


Commit: 80cc3469e7a00c7c7eab5d8bfad0dcae8f05875f
    https://github.com/scummvm/scummvm/commit/80cc3469e7a00c7c7eab5d8bfad0dcae8f05875f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Reader::readOneByteInStream()

Changed paths:
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 025f534..19c5b0a 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -25,6 +25,8 @@
 #include "backends/networking/sdl_net/reader.h"
 #include "backends/networking/sdl_net/localwebserver.h"
 #include "common/debug.h"
+#include "common/stream.h"
+#include "common/memstream.h"
 
 namespace Networking {
 
@@ -39,6 +41,7 @@ Reader::Reader() {
 
 	_headers = "";
 	_buffer = "";
+	_stream = nullptr;
 
 	_contentLength = 0;
 	_availableBytes = 0;
@@ -46,7 +49,11 @@ Reader::Reader() {
 	_isBadRequest = false;
 }
 
-Reader::~Reader() {}
+Reader::~Reader() {
+	//TODO: free everything
+	if (_window != nullptr) freeWindow();
+	delete _stream;
+}
 
 bool Reader::readRequest() {
 	if (_state == RS_NONE) _state = RS_READING_HEADERS;
@@ -261,6 +268,9 @@ bool Reader::readContent() {
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
 		_buffer = "";
+
+		if (_stream) delete _stream;
+		_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
 	}
 
 	/*
@@ -273,10 +283,20 @@ bool Reader::readContent() {
 		handleFileContent(tempFileName);
 	} else {
 	*/
-		while (readOneByteInString(_buffer, boundary)) {		
+		while (readOneByteInStream(_stream, boundary)) {
 			if (!bytesLeft()) return false;
 		}
-		handleValueContent(_buffer);
+		Common::MemoryReadWriteStream *dynamicStream = dynamic_cast<Common::MemoryReadWriteStream *>(_stream);
+		if (dynamicStream != nullptr)
+			if (dynamicStream->size() == 0)
+				handleValueContent("");
+			else
+				handleValueContent(Common::String((char *)dynamicStream->getData(), dynamicStream->size()));
+		else
+			if (_stream != nullptr)
+				warning("Stream somehow changed its type from MemoryReadWriteStream!");
+			else
+				warning("No stream was created!");
 	//}
 
 	freeWindow();
@@ -307,24 +327,22 @@ void Reader::freeWindow() {
 	_windowUsed = _windowSize = 0;
 }
 
-/*
-bool Reader::readOneByteInStream(stream) {
-	b = read(1);
+bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
+	byte b = readOne();
 	_window[_windowUsed++] = b;
 	if (_windowUsed < _windowSize) return true;
 
 	//when window is filled, check whether that's the boundary
-	if (_window == "--" + _boundary)
+	if (Common::String((char *)_window, _windowSize) == boundary)
 		return false;
 
-	//if not, write the first byte of the window to the stream
-	stream.write(_window[0]);
+	//if not, add the first byte of the window to the string
+	stream->writeByte(_window[0]);
 	for (uint32 i = 1; i < _windowSize; ++i)
 		_window[i - 1] = _window[i];
 	--_windowUsed;
 	return true;
 }
-*/
 
 bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary) {
 	byte b = readOne();
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 74fcd43..5f57701 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -28,6 +28,10 @@
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 
+namespace Common {
+class WriteStream;
+}
+
 namespace Networking {
 
 enum ReaderState {
@@ -46,6 +50,7 @@ class Reader {
 
 	Common::String _headers;
 	Common::String _buffer;
+	Common::WriteStream *_stream;
 
 	///Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
@@ -70,7 +75,7 @@ class Reader {
 
 	void makeWindow(uint32 size);
 	void freeWindow();
-	///bool Reader::readOneByteInStream(stream);
+	bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary);
 	bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary);
 
 	byte readOne();


Commit: bca05b2720298b17f9078465cd0896cc5d627819
    https://github.com/scummvm/scummvm/commit/bca05b2720298b17f9078465cd0896cc5d627819
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Save files fields into temp files

Instead of keeping them in memory. Temp file name is generated to point
into ScummVM's working directory. That means that if user wanted to
upload file to the place with sufficient size, it would instead be
uploaded to ScummVM's working directory. Yet it's too early to parse the
target directory, so there is no way to generate temp file name within
that directory.

Changed paths:
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 19c5b0a..2037ef7 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -27,6 +27,7 @@
 #include "common/debug.h"
 #include "common/stream.h"
 #include "common/memstream.h"
+#include "backends/fs/fs-factory.h"
 
 namespace Networking {
 
@@ -40,7 +41,6 @@ Reader::Reader() {
 	_windowSize = 0;
 
 	_headers = "";
-	_buffer = "";
 	_stream = nullptr;
 
 	_contentLength = 0;
@@ -263,29 +263,78 @@ void Reader::parseQueryParameters() {
 	}
 }
 
+namespace {
+char generateRandomChar() {
+	int r = rand() % 36;
+	char c = '0' + r;
+	if (r > 9) c = 'a' + r - 10;
+	return c;
+}
+
+Common::String generateTempFileName(Common::String originalFilename) {
+	//generates "./<originalFilename>-<uniqueSequence>.scummtmp"
+	//normalize <originalFilename>
+	Common::String prefix = "./";
+	for (uint32 i = 0; i < originalFilename.size(); ++i) {
+		char c = originalFilename[i];
+		if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '.' || c == '_' || c == '-') {
+			prefix += c;
+		} else {
+			prefix += '_';
+		}
+	}
+	prefix += '-';
+
+	//generate initial sequence
+	Common::String uniqueSequence;
+	for (uint32 i = 0; i < 5; ++i)
+		uniqueSequence += generateRandomChar();
+
+	//update sequence while generate path exists
+	AbstractFSNode *node;
+	Common::String path;
+	do {
+		uniqueSequence += generateRandomChar();
+		path = prefix + uniqueSequence + ".scummtmp";
+		node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	} while (node->exists());
+
+	return path;
+}
+}
+
 bool Reader::readContent() {
 	Common::String boundary = "--" + _boundary;
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
-		_buffer = "";
 
 		if (_stream) delete _stream;
-		_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
+		if (_isFileField) {
+			//create temporary file
+			_currentTempFileName = generateTempFileName(_currentFileName);
+			AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(_currentTempFileName);
+			_stream = node->createWriteStream();
+			if (_stream == nullptr)
+				error("Unable to open temp file to write into!");
+		} else {
+			_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
+		}
 	}
 
-	/*
-	if (_fieldIsFile) {
-		//create temporary file
-		tempFileName = generateTempFileName();
-		stream = openFileStream(tempFileName);
-		//read till "--" + _boundary
-		while (readOneByteInStream(stream));
-		handleFileContent(tempFileName);
-	} else {
-	*/
-		while (readOneByteInStream(_stream, boundary)) {
-			if (!bytesLeft()) return false;
+	while (readOneByteInStream(_stream, boundary)) {
+		if (!bytesLeft()) return false;
+	}
+
+	if (_isFileField) {
+		if (_stream != nullptr) {
+			_stream->flush();
+			delete _stream;
+			_stream = nullptr;
+		} else {
+			warning("No stream was created!");
 		}
+		handleFileContent(_currentTempFileName);
+	} else {
 		Common::MemoryReadWriteStream *dynamicStream = dynamic_cast<Common::MemoryReadWriteStream *>(_stream);
 		if (dynamicStream != nullptr)
 			if (dynamicStream->size() == 0)
@@ -297,7 +346,7 @@ bool Reader::readContent() {
 				warning("Stream somehow changed its type from MemoryReadWriteStream!");
 			else
 				warning("No stream was created!");
-	//}
+	}
 
 	freeWindow();
 	_state = RS_READING_HEADERS;
@@ -305,6 +354,7 @@ bool Reader::readContent() {
 }
 
 void Reader::handleFileContent(Common::String filename) {
+	debug("\nHANDLE FILE CONTENT:\nFILE >>%s<< SAVED INTO >>%s<<", _currentFileName.c_str(), filename.c_str());
 	_attachedFiles[_currentFileName] = filename;
 }
 
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 5f57701..20b2a61 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -49,7 +49,6 @@ class Reader {
 	uint32 _windowUsed, _windowSize;
 
 	Common::String _headers;
-	Common::String _buffer;
 	Common::WriteStream *_stream;
 
 	///Common::String _headers;
@@ -59,7 +58,7 @@ class Reader {
 	uint32 _contentLength;
 	Common::String _boundary;
 	uint32 _availableBytes;
-	Common::String _currentFieldName, _currentFileName;
+	Common::String _currentFieldName, _currentFileName, _currentTempFileName;
 	bool _isFileField;
 	bool _isBadRequest;
 


Commit: b69cc3effbbe2cccefc29eb2db097945d78cf89b
    https://github.com/scummvm/scummvm/commit/b69cc3effbbe2cccefc29eb2db097945d78cf89b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update Reader to delete temp files

Changed paths:
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 2037ef7..7fe9d7b 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -29,9 +29,18 @@
 #include "common/memstream.h"
 #include "backends/fs/fs-factory.h"
 
+// This define lets us use the system function remove() on Symbian, which
+// is disabled by default due to a macro conflict.
+// See backends/platform/symbian/src/portdefs.h .
+#define SYMBIAN_USE_SYSTEM_REMOVE
+
+#ifndef _WIN32_WCE
+#include <errno.h>	// for removeFile()
+#endif
+
 namespace Networking {
 
-Reader::Reader() {
+Reader::Reader(): _randomSource("Networking::Reader") {
 	_state = RS_NONE;
 	_content = nullptr;
 	_bytesLeft = 0;
@@ -49,10 +58,79 @@ Reader::Reader() {
 	_isBadRequest = false;
 }
 
+namespace {
+bool removeFile(const char *filename) {
+	// FIXME: remove does not exist on all systems. If your port fails to
+	// compile because of this, please let us know (scummvm-devel).
+	// There is a nicely portable workaround, too: Make this method overloadable.
+	if (remove(filename) != 0) {
+#ifndef _WIN32_WCE
+		if (errno == EACCES)
+			error("Reader: removeFile(): Search or write permission denied: %s", filename);
+
+		if (errno == ENOENT)
+			error("Reader: removeFile(): '%s' does not exist or path is invalid", filename);
+#endif
+		return false;
+	} else {
+		return true;
+	}
+}
+}
+
 Reader::~Reader() {
-	//TODO: free everything
+	cleanup();
+}
+
+Reader &Reader::operator=(Reader &r) {
+	if (this == &r) return *this;
+	cleanup();
+
+	_state = r._state;
+	_content = r._content;
+	_bytesLeft = r._bytesLeft;
+	r._state = RS_NONE;
+
+	_window = r._window;
+	_windowUsed = r._windowUsed;
+	_windowSize = r._windowSize;
+	r._window = nullptr;
+
+	_headers = r._headers;
+	_stream = r._stream;
+	r._stream = nullptr;
+
+	_headers = r._headers;
+	_method = r._method;
+	_path = r._path;
+	_query = r._query;
+	_anchor = r._anchor;
+	_queryParameters = r._queryParameters;
+	_attachedFiles = r._attachedFiles;
+	r._attachedFiles.clear();
+	_contentLength = r._contentLength;
+	_boundary = r._boundary;
+	_availableBytes = r._availableBytes;
+	_currentFieldName = r._currentFieldName;
+	_currentFileName = r._currentFileName;
+	_currentTempFileName = r._currentTempFileName;
+	_isFileField = r._isFileField;
+	_isBadRequest = r._isBadRequest;
+
+	return *this;
+}
+
+void Reader::cleanup() {
+	//_content is not to be freed, it's not owned by Reader
+
 	if (_window != nullptr) freeWindow();
 	delete _stream;
+
+	//delete temp files (by the time Reader is destucted those must be renamed or read)
+	for (Common::HashMap<Common::String, Common::String>::iterator i = _attachedFiles.begin(); i != _attachedFiles.end(); ++i) {
+		AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(i->_value);
+		if (node->exists()) removeFile(node->getPath().c_str());
+	}
 }
 
 bool Reader::readRequest() {
@@ -264,14 +342,14 @@ void Reader::parseQueryParameters() {
 }
 
 namespace {
-char generateRandomChar() {
-	int r = rand() % 36;
+char generateRandomChar(Common::RandomSource &random) {
+	int r = random.getRandomNumber(36);
 	char c = '0' + r;
 	if (r > 9) c = 'a' + r - 10;
 	return c;
 }
 
-Common::String generateTempFileName(Common::String originalFilename) {
+Common::String generateTempFileName(Common::String originalFilename, Common::RandomSource &random) {
 	//generates "./<originalFilename>-<uniqueSequence>.scummtmp"
 	//normalize <originalFilename>
 	Common::String prefix = "./";
@@ -288,13 +366,13 @@ Common::String generateTempFileName(Common::String originalFilename) {
 	//generate initial sequence
 	Common::String uniqueSequence;
 	for (uint32 i = 0; i < 5; ++i)
-		uniqueSequence += generateRandomChar();
+		uniqueSequence += generateRandomChar(random);
 
 	//update sequence while generate path exists
 	AbstractFSNode *node;
 	Common::String path;
 	do {
-		uniqueSequence += generateRandomChar();
+		uniqueSequence += generateRandomChar(random);
 		path = prefix + uniqueSequence + ".scummtmp";
 		node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	} while (node->exists());
@@ -311,7 +389,7 @@ bool Reader::readContent() {
 		if (_stream) delete _stream;
 		if (_isFileField) {
 			//create temporary file
-			_currentTempFileName = generateTempFileName(_currentFileName);
+			_currentTempFileName = generateTempFileName(_currentFileName, _randomSource);
 			AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(_currentTempFileName);
 			_stream = node->createWriteStream();
 			if (_stream == nullptr)
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 20b2a61..09550e0 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -27,6 +27,7 @@
 #include "common/str.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
+#include "common/random.h"
 
 namespace Common {
 class WriteStream;
@@ -41,6 +42,8 @@ enum ReaderState {
 };
 
 class Reader {
+	Common::RandomSource _randomSource; //for temp file names
+
 	ReaderState _state;
 	byte *_content;
 	uint32 _bytesLeft;
@@ -62,6 +65,8 @@ class Reader {
 	bool _isFileField;
 	bool _isBadRequest;
 
+	void cleanup();
+
 	bool readHeaders(); //true when ended reading
 	bool readContent(); //true when ended reading
 	void handleHeaders(Common::String headers);
@@ -84,6 +89,8 @@ public:
 	Reader();
 	~Reader();
 
+	Reader &operator=(Reader &r);
+
 	bool readRequest(); //true when ended reading
 	void setContent(byte *buffer, uint32 size);
 	bool badRequest();


Commit: f3ee9e3272c76236a5f234d00cb9ba1afa81ef60
    https://github.com/scummvm/scummvm/commit/f3ee9e3272c76236a5f234d00cb9ba1afa81ef60
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix minor Reader-related bugs

Changed paths:
    backends/module.mk
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/module.mk b/backends/module.mk
index b1aced4..3c6f5e5 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -65,7 +65,8 @@ MODULE_OBJS += \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
 	networking/sdl_net/handlers/resourcehandler.o \
-	networking/sdl_net/localwebserver.o
+	networking/sdl_net/localwebserver.o \
+	networking/sdl_net/reader.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 3edf9c3..32aa1e4 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -44,7 +44,8 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	_state = READING_HEADERS;
 	_socket = socket;
 	_set = set;
-	_reader = Reader();
+	Reader cleanReader;
+	_reader = cleanReader;
 	_handler = nullptr;
 	if (set) {
 		int numused = SDLNet_TCP_AddSocket(set, socket);
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 7fe9d7b..698c209 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -157,7 +157,7 @@ bool Reader::readRequest() {
 			bts += readOne();
 			if (bts == "--") break;
 			if (bts == "\r\n") continue;
-			warning("strange bytes: \"%s\"", bts);
+			warning("strange bytes: \"%s\"", bts.c_str());
 		} else {
 			warning("strange ending");
 			break;
@@ -327,7 +327,7 @@ void Reader::parseQueryParameters() {
 			} else key += _query[i];
 		} else {
 			if (_query[i] == '&') {				
-				if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!");
+				if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!", key.c_str());
 				else _queryParameters[key] = LocalWebserver::urlDecode(value);
 				readingKey = true;
 				key = "";
@@ -336,7 +336,7 @@ void Reader::parseQueryParameters() {
 	}
 
 	if (!key.empty()) {
-		if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!");
+		if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!", key.c_str());
 		else _queryParameters[key] = LocalWebserver::urlDecode(value);
 	}
 }
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 09550e0..de0f3b6 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -79,8 +79,8 @@ class Reader {
 
 	void makeWindow(uint32 size);
 	void freeWindow();
-	bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary);
-	bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary);
+	bool readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary);
+	bool readOneByteInString(Common::String &buffer, const Common::String &boundary);
 
 	byte readOne();
 	uint32 bytesLeft();


Commit: f0fc18d2ee02c61524486aa1e73b781a6f64ef23
    https://github.com/scummvm/scummvm/commit/f0fc18d2ee02c61524486aa1e73b781a6f64ef23
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add UploadFileHandler

Changed paths:
  A backends/networking/sdl_net/handlers/uploadfilehandler.cpp
  A backends/networking/sdl_net/handlers/uploadfilehandler.h
    backends/module.mk
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/module.mk b/backends/module.mk
index 3c6f5e5..0647d36 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -65,6 +65,7 @@ MODULE_OBJS += \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
 	networking/sdl_net/handlers/resourcehandler.o \
+	networking/sdl_net/handlers/uploadfilehandler.o \
 	networking/sdl_net/localwebserver.o \
 	networking/sdl_net/reader.o
 endif
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 32aa1e4..13543a6 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -116,6 +116,8 @@ Common::String Client::query() const { return _reader.query(); }
 
 Common::String Client::queryParameter(Common::String name) const { return _reader.queryParameter(name); }
 
+Common::String Client::attachedFile(Common::String name) const { return _reader.attachedFile(name); }
+
 Common::String Client::anchor() const { return _reader.anchor(); }
 
 bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 9764dac..cd10adc 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -72,6 +72,7 @@ public:
 	Common::String path() const;
 	Common::String query() const;
 	Common::String queryParameter(Common::String name) const;
+	Common::String attachedFile(Common::String name) const;
 	Common::String anchor() const;
 
 	/**
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
new file mode 100644
index 0000000..1eabf1d
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -0,0 +1,187 @@
+/* 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 "backends/networking/sdl_net/handlers/uploadfilehandler.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/fs/fs-factory.h"
+#include "common/file.h"
+#include "common/translation.h"
+
+namespace Networking {
+
+#define INDEX_PAGE_NAME ".index.html"
+
+UploadFileHandler::UploadFileHandler() {}
+
+UploadFileHandler::~UploadFileHandler() {}
+
+void UploadFileHandler::handle(Client &client) {
+	Common::String errorMessage = "";
+
+	// show an error message if failed to upload the file
+	if (!uploadFile(client, errorMessage)) {
+		handleErrorMessage(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=%s\">%s</a>",
+				errorMessage.c_str(),
+				"%2F", //that's encoded "/"
+				_("Back to the files manager")
+				)
+			);
+		return;
+	}
+
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", Common::String::format(
+			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			_("Uploaded successfully!"),
+			client.queryParameter("path").c_str(),
+			_("Back to parent directory")
+		)
+	);
+	LocalWebserver::setClientRedirectHandler(
+		client, response,
+		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+	);
+}
+
+void UploadFileHandler::handleErrorMessage(Client &client, Common::String message) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+namespace {
+bool copyStream(Common::ReadStream *from, Common::WriteStream *into) {
+	assert(from);
+	assert(into);
+	const uint32 BUFFER_SIZE = 1 * 1024 * 1024;
+	void *buffer = malloc(BUFFER_SIZE);
+	bool success = true;
+	assert(buffer);
+
+	while (!from->eos()) {
+		uint32 readBytes = from->read(buffer, BUFFER_SIZE);
+
+		if (from->err()) {
+			warning("copyStream: failed to read bytes from the stream");
+			success = false;
+			break;
+		}
+
+		if (readBytes != 0)
+			if (into->write(buffer, readBytes) != readBytes || into->err()) {
+				warning("copyStream: failed to write all bytes into the file");
+				success = false;
+				break;
+			}
+	}
+
+	free(buffer);
+	return success;
+}
+}
+
+bool UploadFileHandler::uploadFile(Client &client, Common::String &errorMessage) {
+	Common::String path = client.queryParameter("path");
+	Common::String originalFilename = client.queryParameter("upload_file");
+	Common::String tempFilename = client.attachedFile(originalFilename);
+	debug("path = <%s>", path.c_str());
+	debug("filename = <%s>", originalFilename.c_str());
+	debug("tempfile = <%s>", tempFilename.c_str());
+
+	// check that <path> is not an absolute root
+	if (path == "" || path == "/" || path == "\\") {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// transform virtual path to actual file system one
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
+		errorMessage = _("Invalid path!");
+		return false;
+	}
+
+	// check that <path> exists and is directory
+	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!node->exists()) {
+		errorMessage = _("The parent directory doesn't exist!");
+		return false;
+	}
+	if (!node->isDirectory()) {
+		errorMessage = _("Can't upload into a file!");
+		return false;
+	}
+
+	// check that <path>/<originalFilename> doesn't exist
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + originalFilename);
+	if (originalNode->exists()) {
+		errorMessage = _("There is a file with that name in the parent directory!");
+		return false;
+	}
+
+	// check that <tempFilename> exists
+	AbstractFSNode *tempNode = g_system->getFilesystemFactory()->makeFileNodePath(tempFilename);
+	if (!tempNode->exists() || tempNode->isDirectory()) {
+		errorMessage = _("Failed to upload the file!");
+		return false;
+	}
+
+	// copy <tempFilename> into <path>/<originalFilename>
+	// FIXME: I think we should move/rename file with some system call
+	// even though that might be less portable, that is much better than
+	// making an actual copy of data (because user might had enough place
+	// for one copy of the file, but not for two of them)
+	Common::ReadStream *tempStream = tempNode->createReadStream();
+	Common::WriteStream *fileStream = originalNode->createWriteStream();
+	if (tempStream == nullptr || fileStream == nullptr || !copyStream(tempStream, fileStream)) {
+		delete tempStream;
+		delete fileStream;
+		errorMessage = _("Failed to upload the file!");
+		return false;
+	}
+
+	delete tempStream;
+	delete fileStream;
+	return true;
+}
+
+/// public
+
+ClientHandlerCallback UploadFileHandler::getHandler() {
+	return new Common::Callback<UploadFileHandler, Client &>(this, &UploadFileHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h
new file mode 100644
index 0000000..6300a13
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h
@@ -0,0 +1,51 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_UPLOADFILEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_UPLOADFILEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+
+namespace Networking {
+
+class UploadFileHandler: public FilesBaseHandler {
+	void handle(Client &client);
+	void handleErrorMessage(Client &client, Common::String message);
+
+	/**
+	 * Uploads file.
+	 *
+	 * Fills <errorMessage> on failure.
+	 *
+	 * Returns true on success.
+	 */
+	bool uploadFile(Client &client, Common::String &errorMessage);
+public:
+	UploadFileHandler();
+	virtual ~UploadFileHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 8f9c30d..86a1d1a 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -47,6 +47,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/files", _filesPageHandler.getHandler());
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
 	addPathHandler("/download", _downloadFileHandler.getHandler());
+	addPathHandler("/upload", _uploadFileHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 24a4ecf..124b1b3 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -30,6 +30,7 @@
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
 #include "backends/networking/sdl_net/handlers/resourcehandler.h"
+#include "backends/networking/sdl_net/handlers/uploadfilehandler.h"
 #include "common/hash-str.h"
 #include "common/mutex.h"
 #include "common/singleton.h"
@@ -63,6 +64,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	FilesPageHandler _filesPageHandler;
 	CreateDirectoryHandler _createDirectoryHandler;
 	DownloadFileHandler _downloadFileHandler;
+	UploadFileHandler _uploadFileHandler;
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 698c209..e08d5db 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -51,6 +51,7 @@ Reader::Reader(): _randomSource("Networking::Reader") {
 
 	_headers = "";
 	_stream = nullptr;
+	_firstBlock = true;
 
 	_contentLength = 0;
 	_availableBytes = 0;
@@ -98,6 +99,7 @@ Reader &Reader::operator=(Reader &r) {
 
 	_headers = r._headers;
 	_stream = r._stream;
+	_firstBlock = r._firstBlock;
 	r._stream = nullptr;
 
 	_headers = r._headers;
@@ -383,6 +385,7 @@ Common::String generateTempFileName(Common::String originalFilename, Common::Ran
 
 bool Reader::readContent() {
 	Common::String boundary = "--" + _boundary;
+	if (!_firstBlock) boundary = "\r\n" + boundary;
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
 
@@ -403,6 +406,7 @@ bool Reader::readContent() {
 		if (!bytesLeft()) return false;
 	}
 
+	_firstBlock = false;
 	if (_isFileField) {
 		if (_stream != nullptr) {
 			_stream->flush();
@@ -514,6 +518,8 @@ Common::String Reader::query() const { return _query; }
 
 Common::String Reader::queryParameter(Common::String name) const { return _queryParameters[name]; }
 
+Common::String Reader::attachedFile(Common::String name) const { return _attachedFiles[name]; }
+
 Common::String Reader::anchor() const { return _anchor; }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index de0f3b6..244d619 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -53,6 +53,7 @@ class Reader {
 
 	Common::String _headers;
 	Common::WriteStream *_stream;
+	bool _firstBlock;
 
 	///Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
@@ -99,6 +100,7 @@ public:
 	Common::String path() const;
 	Common::String query() const;
 	Common::String queryParameter(Common::String name) const;
+	Common::String attachedFile(Common::String name) const;
 	Common::String anchor() const;
 };
 


Commit: eaa5fb175939c819785095d23bd67e3514144ec7
    https://github.com/scummvm/scummvm/commit/eaa5fb175939c819785095d23bd67e3514144ec7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Put "/upload" "path" parameter in the URL

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index c21575d..228bf76 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -34,6 +34,17 @@ FilesPageHandler::FilesPageHandler() {}
 
 FilesPageHandler::~FilesPageHandler() {}
 
+namespace {
+Common::String encodeDoubleQuotes(Common::String s) {
+	Common::String result = "";
+	for (uint32 i = 0; i < s.size(); ++i)
+		if (s[i] == '"') {
+			result += "\\\"";
+		} else result += s[i];
+	return result;
+}
+}
+
 void FilesPageHandler::handle(Client &client) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
 	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
@@ -62,8 +73,8 @@ void FilesPageHandler::handle(Client &client) {
 	//these occur twice:
 	replace(response, "{create_directory_button}", _("Create directory"));
 	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{path}", client.queryParameter("path"));
-	replace(response, "{path}", client.queryParameter("path"));
+	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
+	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
 	replace(response, "{upload_files_button}", _("Upload files")); //tab
 	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
 	replace(response, "{create_directory_desc}", _("Type new directory name:"));
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 7049289..a57355e 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index 0651346..d14acfc 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -25,8 +25,7 @@
 				</div>
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
-					<form action="upload" method="post" enctype="multipart/form-data">
-						<input type="hidden" name="path" value="{path}"/>
+					<form action="upload?path={path}" method="post" enctype="multipart/form-data">
 						<input type="file" name="upload_file"/>
 						<input type="submit" value="{upload_file_button}"/>
 					</form>


Commit: a424ebbc28549c0e06b9a8ca4985bf799179c404
    https://github.com/scummvm/scummvm/commit/a424ebbc28549c0e06b9a8ca4985bf799179c404
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix quotes encoding

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 228bf76..4b18ac3 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -40,6 +40,8 @@ Common::String encodeDoubleQuotes(Common::String s) {
 	for (uint32 i = 0; i < s.size(); ++i)
 		if (s[i] == '"') {
 			result += "\\\"";
+		} else if (s[i] == '\\') {
+			result += "\\\\";
 		} else result += s[i];
 	return result;
 }


Commit: e4bb7c4e750d53434140a8d1a401e7fa6ee221ab
    https://github.com/scummvm/scummvm/commit/e4bb7c4e750d53434140a8d1a401e7fa6ee221ab
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add UploadFileClientHandler

Now Client reads the first headers block, then LocalWebserver decides
which Handler to use. In case of "/upload", UploadFileHandler is used.

But now it only knows the "path" parameter. If that's valid, actual
UploadFileClientHandler is created, which reads the contents of the
request and, when finds there  an "upload_file" field, starts saving it
in the directory specified by "path".

With that we don't need temp files approach from Reader class.

Changed paths:
  A backends/networking/sdl_net/handlerutils.cpp
  A backends/networking/sdl_net/handlerutils.h
  A backends/networking/sdl_net/uploadfileclienthandler.cpp
  A backends/networking/sdl_net/uploadfileclienthandler.h
    backends/module.mk
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/module.mk b/backends/module.mk
index 0647d36..8b51a28 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -66,8 +66,10 @@ MODULE_OBJS += \
 	networking/sdl_net/handlers/indexpagehandler.o \
 	networking/sdl_net/handlers/resourcehandler.o \
 	networking/sdl_net/handlers/uploadfilehandler.o \
+	networking/sdl_net/handlerutils.o \
 	networking/sdl_net/localwebserver.o \
-	networking/sdl_net/reader.o
+	networking/sdl_net/reader.o \
+	networking/sdl_net/uploadfileclienthandler.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 13543a6..89b50fa 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -26,12 +26,13 @@
 #include "backends/networking/sdl_net/localwebserver.h"
 #include "common/textconsole.h"
 #include <SDL/SDL_net.h>
+#include <common/memstream.h>
 
 namespace Networking {
 
-Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {}
+Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) {}
 
-Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {
+Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) {
 	open(set, socket);
 }
 
@@ -46,7 +47,11 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	_set = set;
 	Reader cleanReader;
 	_reader = cleanReader;
+	if (_handler) delete _handler;
 	_handler = nullptr;
+	if (_previousHandler) delete _previousHandler;
+	_previousHandler = nullptr;
+	_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
 	if (set) {
 		int numused = SDLNet_TCP_AddSocket(set, socket);
 		if (numused == -1) {
@@ -55,9 +60,11 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	}
 }
 
-void Client::readHeaders() {
-	if (!_socket) return;
-	if (!SDLNet_SocketReady(_socket)) return;
+bool Client::readMoreIfNeeded() {
+	if (_stream == nullptr) return false; //nothing to read into
+	if (_stream->size() - _stream->pos() > 0) return true; //not needed, some data left in the stream
+	if (!_socket) return false;
+	if (!SDLNet_SocketReady(_socket)) return false;
 
 	const uint32 BUFFER_SIZE = 16 * 1024;
 	byte buffer[BUFFER_SIZE];
@@ -65,16 +72,48 @@ void Client::readHeaders() {
 	if (bytes <= 0) {
 		warning("Client::readHeaders recv fail");
 		close();
-		return;
+		return false;
+	}
+
+	if (_stream->write(buffer, bytes) != bytes) {
+		warning("failed to write() into MemoryReadWriteStream");
+		close();
+		return false;
 	}
-	
-	_reader.setContent(buffer, bytes);
-	if (_reader.readRequest())
+
+	return true;
+}
+
+void Client::readHeaders() {
+	if (!readMoreIfNeeded()) return;
+	_reader.setContent(_stream);
+	if (_reader.readFirstHeaders())
 		_state = (_reader.badRequest() ? BAD_REQUEST : READ_HEADERS);
 }
 
+bool Client::readContent(Common::WriteStream *stream) {
+	if (!readMoreIfNeeded()) return false;
+	_reader.setContent(_stream);
+	return _reader.readFirstContent(stream);
+}
+
+bool Client::readBlockHeaders(Common::WriteStream *stream) {
+	if (!readMoreIfNeeded()) return false;
+	_reader.setContent(_stream);
+	return _reader.readBlockHeaders(stream);
+}
+
+bool Client::readBlockContent(Common::WriteStream *stream) {
+	if (!readMoreIfNeeded()) return false;
+	_reader.setContent(_stream);
+	return _reader.readBlockContent(stream);
+}
+
 void Client::setHandler(ClientHandler *handler) {	
-	if (_handler) delete _handler;
+	if (_handler) {
+		if (_previousHandler) delete _previousHandler;
+		_previousHandler = _handler; //can't just delete it, as setHandler() could've been called by handler itself
+	}
 	_state = BEING_HANDLED;
 	_handler = handler;
 }
@@ -100,6 +139,11 @@ void Client::close() {
 		_socket = nullptr;
 	}
 
+	if (_stream) {
+		delete _stream;
+		_stream = nullptr;
+	}
+
 	_state = INVALID;
 }
 
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index cd10adc..6a0dea3 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -27,6 +27,10 @@
 #include "common/str.h"
 #include "reader.h"
 
+namespace Common {
+class MemoryReadWriteStream;
+}
+
 typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
 typedef struct _TCPsocket *TCPsocket;
 
@@ -53,7 +57,10 @@ class Client {
 	SDLNet_SocketSet _set;
 	TCPsocket _socket;
 	Reader _reader;
-	ClientHandler *_handler;
+	ClientHandler *_handler, *_previousHandler;
+	Common::MemoryReadWriteStream *_stream;
+
+	bool readMoreIfNeeded();
 
 public:
 	Client();
@@ -62,7 +69,11 @@ public:
 
 	void open(SDLNet_SocketSet set, TCPsocket socket);
 	void readHeaders();
+	bool readContent(Common::WriteStream *stream);
+	bool readBlockHeaders(Common::WriteStream *stream);
+	bool readBlockContent(Common::WriteStream *stream);
 	void setHandler(ClientHandler *handler);
+	void dropHandler();
 	void handle();
 	void close();
 
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index 1eabf1d..eeb4fad 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -21,161 +21,49 @@
 */
 
 #include "backends/networking/sdl_net/handlers/uploadfilehandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/networking/sdl_net/uploadfileclienthandler.h"
 #include "backends/fs/fs-factory.h"
 #include "common/file.h"
 #include "common/translation.h"
 
 namespace Networking {
 
-#define INDEX_PAGE_NAME ".index.html"
-
 UploadFileHandler::UploadFileHandler() {}
 
 UploadFileHandler::~UploadFileHandler() {}
 
 void UploadFileHandler::handle(Client &client) {
-	Common::String errorMessage = "";
-
-	// show an error message if failed to upload the file
-	if (!uploadFile(client, errorMessage)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=%s\">%s</a>",
-				errorMessage.c_str(),
-				"%2F", //that's encoded "/"
-				_("Back to the files manager")
-				)
-			);
-		return;
-	}
-
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", Common::String::format(
-			"%s<br/><a href=\"files?path=%s\">%s</a>",
-			_("Uploaded successfully!"),
-			client.queryParameter("path").c_str(),
-			_("Back to parent directory")
-		)
-	);
-	LocalWebserver::setClientRedirectHandler(
-		client, response,
-		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
-	);
-}
-
-void UploadFileHandler::handleErrorMessage(Client &client, Common::String message) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", message);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
-namespace {
-bool copyStream(Common::ReadStream *from, Common::WriteStream *into) {
-	assert(from);
-	assert(into);
-	const uint32 BUFFER_SIZE = 1 * 1024 * 1024;
-	void *buffer = malloc(BUFFER_SIZE);
-	bool success = true;
-	assert(buffer);
-
-	while (!from->eos()) {
-		uint32 readBytes = from->read(buffer, BUFFER_SIZE);
-
-		if (from->err()) {
-			warning("copyStream: failed to read bytes from the stream");
-			success = false;
-			break;
-		}
-
-		if (readBytes != 0)
-			if (into->write(buffer, readBytes) != readBytes || into->err()) {
-				warning("copyStream: failed to write all bytes into the file");
-				success = false;
-				break;
-			}
-	}
-
-	free(buffer);
-	return success;
-}
-}
-
-bool UploadFileHandler::uploadFile(Client &client, Common::String &errorMessage) {
 	Common::String path = client.queryParameter("path");
-	Common::String originalFilename = client.queryParameter("upload_file");
-	Common::String tempFilename = client.attachedFile(originalFilename);
 	debug("path = <%s>", path.c_str());
-	debug("filename = <%s>", originalFilename.c_str());
-	debug("tempfile = <%s>", tempFilename.c_str());
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/" || path == "\\") {
-		errorMessage = _("Invalid path!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
-		errorMessage = _("Invalid path!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
 	}
 
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
-		errorMessage = _("The parent directory doesn't exist!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!"));
+		return;
 	}
 	if (!node->isDirectory()) {
-		errorMessage = _("Can't upload into a file!");
-		return false;
-	}
-
-	// check that <path>/<originalFilename> doesn't exist
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + originalFilename);
-	if (originalNode->exists()) {
-		errorMessage = _("There is a file with that name in the parent directory!");
-		return false;
-	}
-
-	// check that <tempFilename> exists
-	AbstractFSNode *tempNode = g_system->getFilesystemFactory()->makeFileNodePath(tempFilename);
-	if (!tempNode->exists() || tempNode->isDirectory()) {
-		errorMessage = _("Failed to upload the file!");
-		return false;
-	}
-
-	// copy <tempFilename> into <path>/<originalFilename>
-	// FIXME: I think we should move/rename file with some system call
-	// even though that might be less portable, that is much better than
-	// making an actual copy of data (because user might had enough place
-	// for one copy of the file, but not for two of them)
-	Common::ReadStream *tempStream = tempNode->createReadStream();
-	Common::WriteStream *fileStream = originalNode->createWriteStream();
-	if (tempStream == nullptr || fileStream == nullptr || !copyStream(tempStream, fileStream)) {
-		delete tempStream;
-		delete fileStream;
-		errorMessage = _("Failed to upload the file!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!"));
+		return;
 	}
-
-	delete tempStream;
-	delete fileStream;
-	return true;
+	
+	// if all OK, set special handler
+	client.setHandler(new UploadFileClientHandler(path));
 }
 
 /// public
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h
index 6300a13..bba7fdf 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.h
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h
@@ -29,16 +29,7 @@ namespace Networking {
 
 class UploadFileHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	void handleErrorMessage(Client &client, Common::String message);
-
-	/**
-	 * Uploads file.
-	 *
-	 * Fills <errorMessage> on failure.
-	 *
-	 * Returns true on success.
-	 */
-	bool uploadFile(Client &client, Common::String &errorMessage);
+	
 public:
 	UploadFileHandler();
 	virtual ~UploadFileHandler();
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
new file mode 100644
index 0000000..f249ee1
--- /dev/null
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -0,0 +1,121 @@
+/* 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 "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/translation.h"
+#include "common/unzip.h"
+
+namespace Networking {
+
+#define ARCHIVE_NAME "wwwroot.zip"
+
+#define INDEX_PAGE_NAME ".index.html"
+
+Common::Archive *HandlerUtils::getZipArchive() {
+	// first search in themepath
+	if (ConfMan.hasKey("themepath")) {
+		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
+		if (!node.exists() || !node.isReadable() || !node.isDirectory())
+			return nullptr;
+
+		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
+		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
+			Common::SeekableReadStream *const stream = fileNode.createReadStream();
+			Common::Archive *zipArchive = Common::makeZipArchive(stream);
+			if (zipArchive) return zipArchive;
+		}
+	}
+
+	// then use SearchMan to find it
+	Common::ArchiveMemberList fileList;
+	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
+	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+		Common::ArchiveMember       const &m = **it;
+		Common::SeekableReadStream *const stream = m.createReadStream();
+		Common::Archive *zipArchive = Common::makeZipArchive(stream);
+		if (zipArchive) return zipArchive;
+	}
+
+	return nullptr;
+}
+
+Common::ArchiveMemberList HandlerUtils::listArchive() {
+	Common::ArchiveMemberList resultList;
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		zipArchive->listMembers(resultList);
+		delete zipArchive;
+	}
+	return resultList;
+}
+
+Common::SeekableReadStream *HandlerUtils::getArchiveFile(Common::String name) {
+	Common::SeekableReadStream *result = nullptr;
+	Common::Archive *zipArchive = getZipArchive();
+	if (zipArchive) {
+		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
+		if (ptr.get() == nullptr) return nullptr;
+		result = ptr->createReadStream();
+		delete zipArchive;
+	}
+	return result;
+}
+
+Common::String HandlerUtils::readEverythingFromStream(Common::SeekableReadStream *const stream) {
+	Common::String result;
+	char buf[1024];
+	uint32 readBytes;
+	while (!stream->eos()) {
+		readBytes = stream->read(buf, 1024);
+		result += Common::String(buf, readBytes);
+	}
+	return result;
+}
+
+void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
+	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
+	if (stream) response = readEverythingFromStream(stream);
+
+	replace(response, "{message}", message);
+	if (redirectTo.empty()) LocalWebserver::setClientGetHandler(client, response);
+	else LocalWebserver::setClientRedirectHandler(client, response, redirectTo);
+}
+
+void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
+	setMessageHandler(client,
+		Common::String::format(
+			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			message.c_str(),
+			"%2F", //that's encoded "/"
+			_("Back to the files manager")
+		), redirectTo
+	);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlerutils.h b/backends/networking/sdl_net/handlerutils.h
new file mode 100644
index 0000000..c672268
--- /dev/null
+++ b/backends/networking/sdl_net/handlerutils.h
@@ -0,0 +1,44 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_HANDLERUTILS_H
+#define BACKENDS_NETWORKING_SDL_NET_HANDLERUTILS_H
+
+#include "backends/networking/sdl_net/client.h"
+#include "common/archive.h"
+
+namespace Networking {
+
+class HandlerUtils {
+public:
+	static Common::Archive *getZipArchive();
+	static Common::ArchiveMemberList listArchive();
+	static Common::SeekableReadStream *getArchiveFile(Common::String name);
+	static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
+
+	static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
+	static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index e08d5db..d61eff2 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -135,14 +135,14 @@ void Reader::cleanup() {
 	}
 }
 
-bool Reader::readRequest() {
+bool Reader::readWholeRequest() {
 	if (_state == RS_NONE) _state = RS_READING_HEADERS;
 
 	while (true) {
 		if (!bytesLeft()) return false;
 
 		if (_state == RS_READING_HEADERS)
-			if (!readHeaders())
+			if (!readWholeHeaders())
 				return false;
 
 		if (_boundary.empty()) return true; //not POST multipart
@@ -150,7 +150,7 @@ bool Reader::readRequest() {
 		if (!bytesLeft()) return false;
 
 		if (_state == RS_READING_CONTENT)
-			if (!readContent())
+			if (!readWholeContent())
 				return false;
 
 		if (_availableBytes >= 2) {
@@ -171,7 +171,69 @@ bool Reader::readRequest() {
 	return true;
 }
 
-bool Reader::readHeaders() {
+bool Reader::readFirstHeaders() {
+	if (_state == RS_NONE) _state = RS_READING_HEADERS;
+
+	if (!bytesLeft()) return false;
+
+	if (_state == RS_READING_HEADERS)
+		return readWholeHeaders();
+
+	warning("Reader::readFirstHeaders(): bad state");
+	return false;
+}
+
+bool Reader::readFirstContent(Common::WriteStream *stream) {
+	if (_state != RS_READING_CONTENT) {
+		warning("Reader::readFirstContent(): bad state");
+		return false;
+	}
+
+	if (!bytesLeft()) return false;
+
+	return readWholeContentIntoStream(stream);
+}
+
+bool Reader::readBlockHeaders(Common::WriteStream *stream) {
+	if (_state != RS_READING_HEADERS) {
+		warning("Reader::readBlockHeaders(): bad state");
+		return false;
+	}
+
+	if (!bytesLeft()) return false;
+
+	return readWholeHeadersIntoStream(stream);
+}
+
+bool Reader::readBlockContent(Common::WriteStream *stream) {
+	if (_state != RS_READING_CONTENT) {
+		warning("Reader::readBlockContent(): bad state");
+		return false;
+	}
+
+	if (!bytesLeft()) return false;
+
+	if (!readWholeContentIntoStream(stream))
+		return false;
+
+	/*
+	if (_availableBytes >= 2) {
+		Common::String bts;
+		bts += readOne();
+		bts += readOne();
+		if (bts == "--") _isOver = true;
+		else if (bts != "\r\n")
+			warning("strange bytes: \"%s\"", bts.c_str());
+	} else {
+		warning("strange ending");
+		_isOver = true;
+	}
+	*/
+
+	return true;
+}
+
+bool Reader::readWholeHeaders() {
 	Common::String boundary = "\r\n\r\n";
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
@@ -188,6 +250,20 @@ bool Reader::readHeaders() {
 	return true;
 }
 
+bool Reader::readWholeHeadersIntoStream(Common::WriteStream *stream) {
+	Common::String boundary = "\r\n\r\n";
+	if (_window == nullptr) makeWindow(boundary.size());
+
+	while (readOneByteInStream(stream, boundary)) {
+		if (!bytesLeft()) return false;
+	}
+	if (stream) stream->flush();
+
+	freeWindow();
+	_state = RS_READING_CONTENT;
+	return true;
+}
+
 namespace {
 void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) {
 	const char *position = strstr(cstr, needle.c_str());
@@ -383,7 +459,7 @@ Common::String generateTempFileName(Common::String originalFilename, Common::Ran
 }
 }
 
-bool Reader::readContent() {
+bool Reader::readWholeContent() {
 	Common::String boundary = "--" + _boundary;
 	if (!_firstBlock) boundary = "\r\n" + boundary;
 	if (_window == nullptr) {
@@ -435,6 +511,23 @@ bool Reader::readContent() {
 	return true;
 }
 
+bool Reader::readWholeContentIntoStream(Common::WriteStream *stream) {
+	Common::String boundary = "--" + _boundary;
+	if (!_firstBlock) boundary = "\r\n" + boundary;
+	if (_window == nullptr) makeWindow(boundary.size());
+
+	while (readOneByteInStream(stream, boundary)) {
+		if (!bytesLeft()) return false;
+	}
+
+	_firstBlock = false;
+	if (stream) stream->flush();
+
+	freeWindow();
+	_state = RS_READING_HEADERS;
+	return true;
+}
+
 void Reader::handleFileContent(Common::String filename) {
 	debug("\nHANDLE FILE CONTENT:\nFILE >>%s<< SAVED INTO >>%s<<", _currentFileName.c_str(), filename.c_str());
 	_attachedFiles[_currentFileName] = filename;
@@ -469,7 +562,7 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri
 		return false;
 
 	//if not, add the first byte of the window to the string
-	stream->writeByte(_window[0]);
+	if (stream) stream->writeByte(_window[0]);
 	for (uint32 i = 1; i < _windowSize; ++i)
 		_window[i - 1] = _window[i];
 	--_windowUsed;
@@ -494,8 +587,8 @@ bool Reader::readOneByteInString(Common::String &buffer, const Common::String &b
 }
 
 byte Reader::readOne() {
-	byte b = _content[0];
-	++_content;
+	byte b;
+	_content->read(&b, 1);
 	--_availableBytes;
 	--_bytesLeft;
 	return b;
@@ -503,9 +596,9 @@ byte Reader::readOne() {
 
 uint32 Reader::bytesLeft() { return _bytesLeft; }
 
-void Reader::setContent(byte *buffer, uint32 size) {
-	_content = buffer;
-	_bytesLeft = size;
+void Reader::setContent(Common::MemoryReadWriteStream *stream) {
+	_content = stream;
+	_bytesLeft = stream->size() - stream->pos();
 }
 
 bool Reader::badRequest() { return _isBadRequest; }
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 244d619..5f951a3 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -30,6 +30,7 @@
 #include "common/random.h"
 
 namespace Common {
+class MemoryReadWriteStream;
 class WriteStream;
 }
 
@@ -45,7 +46,7 @@ class Reader {
 	Common::RandomSource _randomSource; //for temp file names
 
 	ReaderState _state;
-	byte *_content;
+	Common::MemoryReadWriteStream *_content;
 	uint32 _bytesLeft;
 
 	byte *_window;
@@ -68,8 +69,10 @@ class Reader {
 
 	void cleanup();
 
-	bool readHeaders(); //true when ended reading
-	bool readContent(); //true when ended reading
+	bool readWholeHeaders(); //true when ended reading
+	bool readWholeContent(); //true when ended reading
+	bool readWholeHeadersIntoStream(Common::WriteStream *stream); //true when ended reading
+	bool readWholeContentIntoStream(Common::WriteStream *stream); //true when ended reading
 	void handleHeaders(Common::String headers);
 	void handleFileContent(Common::String filename);
 	void handleValueContent(Common::String value);
@@ -92,8 +95,12 @@ public:
 
 	Reader &operator=(Reader &r);
 
-	bool readRequest(); //true when ended reading
-	void setContent(byte *buffer, uint32 size);
+	bool readWholeRequest(); //true when ended reading
+	bool readFirstHeaders(); //true when ended reading
+	bool readFirstContent(Common::WriteStream *stream); //true when ended reading
+	bool readBlockHeaders(Common::WriteStream *stream); //true when ended reading
+	bool readBlockContent(Common::WriteStream *stream); //true when ended reading
+	void setContent(Common::MemoryReadWriteStream *stream);
 	bool badRequest();
 
 	Common::String method() const;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
new file mode 100644
index 0000000..ae795da
--- /dev/null
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -0,0 +1,176 @@
+/* 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 "backends/networking/sdl_net/uploadfileclienthandler.h"
+#include "common/textconsole.h"
+#include <common/memstream.h>
+#include <common/translation.h>
+#include <common/system.h>
+#include <backends/fs/fs-factory.h>
+#include <common/debug.h>
+#include "handlerutils.h"
+#include "localwebserver.h"
+
+namespace Networking {
+
+UploadFileClientHandler::UploadFileClientHandler(Common::String parentDirectoryPath):
+	_state(UFH_READING_CONTENT), _headersStream(nullptr), _contentStream(nullptr),
+	_parentDirectoryPath(parentDirectoryPath) {}
+
+UploadFileClientHandler::~UploadFileClientHandler() {
+	delete _headersStream;
+	delete _contentStream;
+}
+
+namespace {
+void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) {
+	const char *position = strstr(cstr, needle.c_str());
+
+	if (position) {
+		char c;
+		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
+			if (c == '\n' || c == '\r') break;
+			result += c;
+		}
+	}
+}
+
+void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Common::String &result) {
+	const char *position = strstr(cstr, needle.c_str());
+
+	if (position) {
+		char c;
+		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
+			if (c == '"') break;
+			result += c;
+		}
+	}
+}
+
+Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) {
+	Common::String result;
+	char buf[1024];
+	uint32 readBytes;
+	while (true) {
+		readBytes = stream->read(buf, 1024);
+		if (readBytes == 0) break;
+		result += Common::String(buf, readBytes);
+	}
+	return result;
+}
+}
+
+void UploadFileClientHandler::handleBlockHeaders(Client *client) {
+	// search for "upload_file" field
+	Common::String headers = readEverythingFromMemoryStream(_headersStream);
+	Common::String fieldName = "";
+	readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
+	if (fieldName != "upload_file") return;
+
+	Common::String filename = "";
+	readFromThatUntilDoubleQuote(headers.c_str(), "filename=\"", filename);
+
+	// check that <filename> is not empty
+	if (filename.empty()) {
+		setErrorMessageHandler(*client, _("Invalid filename!"));
+		return;
+	}
+
+	// check that <path>/<filename> doesn't exist
+	Common::String path = _parentDirectoryPath;
+	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
+	if (originalNode->exists()) {
+		setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
+		return;
+	}
+
+	// create file stream
+	_contentStream = originalNode->createWriteStream();
+	if (_contentStream == nullptr) {
+		setErrorMessageHandler(*client, _("Failed to upload the file!"));
+		return;
+	}
+}
+
+void UploadFileClientHandler::handle(Client *client) {
+	if (client == nullptr) {
+		warning("UploadFileClientHandler::handle(): empty client pointer");
+		return;
+	}
+
+	while (true) {
+		switch (_state) {
+		case UFH_READING_CONTENT:
+			if (client->readContent(nullptr)) {
+				_state = UFH_READING_BLOCK_HEADERS;
+				continue;
+			}
+			break;
+
+		case UFH_READING_BLOCK_HEADERS:
+			if (_headersStream == nullptr)
+				_headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
+
+			if (client->readBlockHeaders(_headersStream)) {
+				handleBlockHeaders(client);
+				_state = UFH_READING_BLOCK_CONTENT;
+				continue;
+			}
+			break;
+
+		case UFH_READING_BLOCK_CONTENT:
+			if (client->readBlockContent(_contentStream)) {
+				if (_contentStream) {
+					_contentStream->flush();
+					// success - redirect back to directory listing
+					HandlerUtils::setMessageHandler(*client,
+						Common::String::format(
+							"%s<br/><a href=\"files?path=%s\">%s</a>",
+							_("Uploaded successfully!"),
+							client->queryParameter("path").c_str(),
+							_("Back to parent directory")
+						),
+						"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
+					);
+					return;
+				}
+				_state = UFH_READING_BLOCK_HEADERS;
+				continue;
+			}
+			break;
+
+		case UFH_ERROR:
+			return;
+		}
+
+		break;
+	}
+}
+
+
+void UploadFileClientHandler::setErrorMessageHandler(Client &client, Common::String message) {
+	HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
+	_state = UFH_ERROR;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h
new file mode 100644
index 0000000..4f2ce62
--- /dev/null
+++ b/backends/networking/sdl_net/uploadfileclienthandler.h
@@ -0,0 +1,57 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_UPLOADFILECLIENTHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_UPLOADFILECLIENTHANDLER_H
+
+#include "backends/networking/sdl_net/client.h"
+#include "common/stream.h"
+
+namespace Networking {
+
+enum UploadFileHandlerState {
+	UFH_READING_CONTENT,
+	UFH_READING_BLOCK_HEADERS,
+	UFH_READING_BLOCK_CONTENT,
+	UFH_ERROR
+};
+
+class UploadFileClientHandler: public ClientHandler {
+	UploadFileHandlerState _state;
+	Common::MemoryReadWriteStream *_headersStream;
+	Common::WriteStream *_contentStream;
+	Common::String _parentDirectoryPath;
+
+	void handleBlockHeaders(Client *client);
+	bool validFilename(Client &client, Common::String filename, Common::String &errorMessage);
+	void setErrorMessageHandler(Client &client, Common::String message);
+
+public:
+	UploadFileClientHandler(Common::String parentDirectoryPath);
+	virtual ~UploadFileClientHandler();
+
+	virtual void handle(Client *client);
+};
+
+} // End of namespace Networking
+
+#endif


Commit: abae5c437142b53d9099653fe5f14b3ac1967b18
    https://github.com/scummvm/scummvm/commit/abae5c437142b53d9099653fe5f14b3ac1967b18
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in UploadFileHandler

Changed paths:
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index eeb4fad..e2f6143 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -22,10 +22,9 @@
 
 #include "backends/networking/sdl_net/handlers/uploadfilehandler.h"
 #include "backends/networking/sdl_net/handlerutils.h"
-#include "backends/networking/sdl_net/localwebserver.h"
 #include "backends/networking/sdl_net/uploadfileclienthandler.h"
 #include "backends/fs/fs-factory.h"
-#include "common/file.h"
+#include "common/system.h"
 #include "common/translation.h"
 
 namespace Networking {
@@ -36,7 +35,6 @@ UploadFileHandler::~UploadFileHandler() {}
 
 void UploadFileHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
-	debug("path = <%s>", path.c_str());
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/" || path == "\\") {


Commit: 7fcdcc10cb8f6dad370e942ef917ff00e888e2ec
    https://github.com/scummvm/scummvm/commit/7fcdcc10cb8f6dad370e942ef917ff00e888e2ec
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in UploadFileClientHandler

Adds Client::noMoreContent() and Reader::noMoreContent(), which return
true when whole client's request was read.

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 89b50fa..ef46e81 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -164,6 +164,8 @@ Common::String Client::attachedFile(Common::String name) const { return _reader.
 
 Common::String Client::anchor() const { return _reader.anchor(); }
 
+bool Client::noMoreContent() const { return _reader.noMoreContent(); }
+
 bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
 
 int Client::recv(void *data, int maxlen) { return SDLNet_TCP_Recv(_socket, data, maxlen); }
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 6a0dea3..a60545e 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -85,6 +85,7 @@ public:
 	Common::String queryParameter(Common::String name) const;
 	Common::String attachedFile(Common::String name) const;
 	Common::String anchor() const;
+	bool noMoreContent() const;
 
 	/**
 	 * Return SDLNet_SocketReady(_socket).
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index d61eff2..143ab93 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -57,6 +57,7 @@ Reader::Reader(): _randomSource("Networking::Reader") {
 	_availableBytes = 0;
 	_isFileField = false;
 	_isBadRequest = false;
+	_allContentRead = false;
 }
 
 namespace {
@@ -118,6 +119,7 @@ Reader &Reader::operator=(Reader &r) {
 	_currentTempFileName = r._currentTempFileName;
 	_isFileField = r._isFileField;
 	_isBadRequest = r._isBadRequest;
+	_allContentRead = r._allContentRead;
 
 	return *this;
 }
@@ -216,19 +218,17 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
 	if (!readWholeContentIntoStream(stream))
 		return false;
 
-	/*
 	if (_availableBytes >= 2) {
 		Common::String bts;
 		bts += readOne();
 		bts += readOne();
-		if (bts == "--") _isOver = true;
+		if (bts == "--") _allContentRead = true;
 		else if (bts != "\r\n")
 			warning("strange bytes: \"%s\"", bts.c_str());
 	} else {
 		warning("strange ending");
-		_isOver = true;
+		_allContentRead = true;
 	}
-	*/
 
 	return true;
 }
@@ -601,7 +601,9 @@ void Reader::setContent(Common::MemoryReadWriteStream *stream) {
 	_bytesLeft = stream->size() - stream->pos();
 }
 
-bool Reader::badRequest() { return _isBadRequest; }
+bool Reader::badRequest() const { return _isBadRequest; }
+
+bool Reader::noMoreContent() const { return _allContentRead; }
 
 Common::String Reader::method() const { return _method; }
 
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 5f951a3..0ed0d8c 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -66,6 +66,7 @@ class Reader {
 	Common::String _currentFieldName, _currentFileName, _currentTempFileName;
 	bool _isFileField;
 	bool _isBadRequest;
+	bool _allContentRead;
 
 	void cleanup();
 
@@ -101,7 +102,8 @@ public:
 	bool readBlockHeaders(Common::WriteStream *stream); //true when ended reading
 	bool readBlockContent(Common::WriteStream *stream); //true when ended reading
 	void setContent(Common::MemoryReadWriteStream *stream);
-	bool badRequest();
+	bool badRequest() const;
+	bool noMoreContent() const;
 
 	Common::String method() const;
 	Common::String path() const;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index ae795da..733a87b 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -21,14 +21,11 @@
 */
 
 #include "backends/networking/sdl_net/uploadfileclienthandler.h"
-#include "common/textconsole.h"
-#include <common/memstream.h>
-#include <common/translation.h>
-#include <common/system.h>
-#include <backends/fs/fs-factory.h>
-#include <common/debug.h>
-#include "handlerutils.h"
-#include "localwebserver.h"
+#include "backends/fs/fs-factory.h"
+#include "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/memstream.h"
+#include "common/translation.h"
 
 namespace Networking {
 
@@ -41,19 +38,50 @@ UploadFileClientHandler::~UploadFileClientHandler() {
 	delete _contentStream;
 }
 
-namespace {
-void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::String &result) {
-	const char *position = strstr(cstr, needle.c_str());
+void UploadFileClientHandler::handle(Client *client) {
+	if (client == nullptr) {
+		warning("UploadFileClientHandler::handle(): empty client pointer");
+		return;
+	}
 
-	if (position) {
-		char c;
-		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
-			if (c == '\n' || c == '\r') break;
-			result += c;
+	while (true) {
+		switch (_state) {
+		case UFH_READING_CONTENT:
+			if (client->readContent(nullptr)) {
+				_state = UFH_READING_BLOCK_HEADERS;
+				continue;
+			}
+			break;
+
+		case UFH_READING_BLOCK_HEADERS:
+			if (_headersStream == nullptr)
+				_headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
+
+			if (client->readBlockHeaders(_headersStream)) {
+				handleBlockHeaders(client);
+				continue;
+			}
+			break;
+
+		case UFH_READING_BLOCK_CONTENT:
+			// _contentStream is created by handleBlockHeaders() if needed
+
+			if (client->readBlockContent(_contentStream)) {
+				handleBlockContent(client);
+				continue;
+			}
+			break;
+
+		case UFH_ERROR:
+		case UFH_STOP:
+			return;
 		}
+
+		break;
 	}
 }
 
+namespace {
 void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Common::String &result) {
 	const char *position = strstr(cstr, needle.c_str());
 
@@ -80,6 +108,8 @@ Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *str
 }
 
 void UploadFileClientHandler::handleBlockHeaders(Client *client) {
+	_state = UFH_READING_BLOCK_CONTENT;
+
 	// search for "upload_file" field
 	Common::String headers = readEverythingFromMemoryStream(_headersStream);
 	Common::String fieldName = "";
@@ -112,62 +142,33 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	}
 }
 
-void UploadFileClientHandler::handle(Client *client) {
-	if (client == nullptr) {
-		warning("UploadFileClientHandler::handle(): empty client pointer");
+void UploadFileClientHandler::handleBlockContent(Client *client) {
+	_state = UFH_READING_BLOCK_HEADERS;
+
+	// if previous block headers were file-related and created a stream
+	if (_contentStream) {
+		_contentStream->flush();
+		// success - redirect back to directory listing
+		HandlerUtils::setMessageHandler(*client,
+			Common::String::format(
+				"%s<br/><a href=\"files?path=%s\">%s</a>",
+				_("Uploaded successfully!"),
+				client->queryParameter("path").c_str(),
+				_("Back to parent directory")
+				),
+			"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
+			);
+		_state = UFH_STOP;
 		return;
 	}
-
-	while (true) {
-		switch (_state) {
-		case UFH_READING_CONTENT:
-			if (client->readContent(nullptr)) {
-				_state = UFH_READING_BLOCK_HEADERS;
-				continue;
-			}
-			break;
-
-		case UFH_READING_BLOCK_HEADERS:
-			if (_headersStream == nullptr)
-				_headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
-
-			if (client->readBlockHeaders(_headersStream)) {
-				handleBlockHeaders(client);
-				_state = UFH_READING_BLOCK_CONTENT;
-				continue;
-			}
-			break;
-
-		case UFH_READING_BLOCK_CONTENT:
-			if (client->readBlockContent(_contentStream)) {
-				if (_contentStream) {
-					_contentStream->flush();
-					// success - redirect back to directory listing
-					HandlerUtils::setMessageHandler(*client,
-						Common::String::format(
-							"%s<br/><a href=\"files?path=%s\">%s</a>",
-							_("Uploaded successfully!"),
-							client->queryParameter("path").c_str(),
-							_("Back to parent directory")
-						),
-						"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
-					);
-					return;
-				}
-				_state = UFH_READING_BLOCK_HEADERS;
-				continue;
-			}
-			break;
-
-		case UFH_ERROR:
-			return;
-		}
-
-		break;
+	
+	// if no file field was found, but no more content avaiable - failure
+	if (client->noMoreContent()) {
+		setErrorMessageHandler(*client, _("No file was passed!"));
+		return;
 	}
 }
 
-
 void UploadFileClientHandler::setErrorMessageHandler(Client &client, Common::String message) {
 	HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
 	_state = UFH_ERROR;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h
index 4f2ce62..de6941b 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.h
+++ b/backends/networking/sdl_net/uploadfileclienthandler.h
@@ -32,7 +32,8 @@ enum UploadFileHandlerState {
 	UFH_READING_CONTENT,
 	UFH_READING_BLOCK_HEADERS,
 	UFH_READING_BLOCK_CONTENT,
-	UFH_ERROR
+	UFH_ERROR,
+	UFH_STOP
 };
 
 class UploadFileClientHandler: public ClientHandler {
@@ -42,7 +43,7 @@ class UploadFileClientHandler: public ClientHandler {
 	Common::String _parentDirectoryPath;
 
 	void handleBlockHeaders(Client *client);
-	bool validFilename(Client &client, Common::String filename, Common::String &errorMessage);
+	void handleBlockContent(Client *client);
 	void setErrorMessageHandler(Client &client, Common::String message);
 
 public:


Commit: d1b5a640205af29af9785846d2e6153e67241f2c
    https://github.com/scummvm/scummvm/commit/d1b5a640205af29af9785846d2e6153e67241f2c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in Reader and Client

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index ef46e81..a486e3c 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -150,7 +150,7 @@ void Client::close() {
 
 ClientState Client::state() const { return _state; }
 
-//Common::String Client::headers() const { return _headers; }
+Common::String Client::headers() const { return _reader.headers(); }
 
 Common::String Client::method() const { return _reader.method(); }
 
@@ -160,8 +160,6 @@ Common::String Client::query() const { return _reader.query(); }
 
 Common::String Client::queryParameter(Common::String name) const { return _reader.queryParameter(name); }
 
-Common::String Client::attachedFile(Common::String name) const { return _reader.attachedFile(name); }
-
 Common::String Client::anchor() const { return _reader.anchor(); }
 
 bool Client::noMoreContent() const { return _reader.noMoreContent(); }
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index a60545e..eab3bcf 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -23,7 +23,6 @@
 #ifndef BACKENDS_NETWORKING_SDL_NET_CLIENT_H
 #define BACKENDS_NETWORKING_SDL_NET_CLIENT_H
 
-#include "common/scummsys.h"
 #include "common/str.h"
 #include "reader.h"
 
@@ -73,18 +72,17 @@ public:
 	bool readBlockHeaders(Common::WriteStream *stream);
 	bool readBlockContent(Common::WriteStream *stream);
 	void setHandler(ClientHandler *handler);
-	void dropHandler();
 	void handle();
 	void close();
 
 	ClientState state() const;
-	//Common::String headers() const;
+	Common::String headers() const;
 	Common::String method() const;
 	Common::String path() const;
 	Common::String query() const;
 	Common::String queryParameter(Common::String name) const;
-	Common::String attachedFile(Common::String name) const;
 	Common::String anchor() const;
+
 	bool noMoreContent() const;
 
 	/**
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 143ab93..1e5693c 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -20,27 +20,15 @@
 *
 */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/sdl_net/reader.h"
+#include "backends/fs/fs-factory.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "common/debug.h"
-#include "common/stream.h"
 #include "common/memstream.h"
-#include "backends/fs/fs-factory.h"
-
-// This define lets us use the system function remove() on Symbian, which
-// is disabled by default due to a macro conflict.
-// See backends/platform/symbian/src/portdefs.h .
-#define SYMBIAN_USE_SYSTEM_REMOVE
-
-#ifndef _WIN32_WCE
-#include <errno.h>	// for removeFile()
-#endif
+#include "common/stream.h"
 
 namespace Networking {
 
-Reader::Reader(): _randomSource("Networking::Reader") {
+Reader::Reader() {
 	_state = RS_NONE;
 	_content = nullptr;
 	_bytesLeft = 0;
@@ -50,36 +38,14 @@ Reader::Reader(): _randomSource("Networking::Reader") {
 	_windowSize = 0;
 
 	_headers = "";
-	_stream = nullptr;
 	_firstBlock = true;
 
 	_contentLength = 0;
 	_availableBytes = 0;
-	_isFileField = false;
 	_isBadRequest = false;
 	_allContentRead = false;
 }
 
-namespace {
-bool removeFile(const char *filename) {
-	// FIXME: remove does not exist on all systems. If your port fails to
-	// compile because of this, please let us know (scummvm-devel).
-	// There is a nicely portable workaround, too: Make this method overloadable.
-	if (remove(filename) != 0) {
-#ifndef _WIN32_WCE
-		if (errno == EACCES)
-			error("Reader: removeFile(): Search or write permission denied: %s", filename);
-
-		if (errno == ENOENT)
-			error("Reader: removeFile(): '%s' does not exist or path is invalid", filename);
-#endif
-		return false;
-	} else {
-		return true;
-	}
-}
-}
-
 Reader::~Reader() {
 	cleanup();
 }
@@ -99,25 +65,15 @@ Reader &Reader::operator=(Reader &r) {
 	r._window = nullptr;
 
 	_headers = r._headers;
-	_stream = r._stream;
-	_firstBlock = r._firstBlock;
-	r._stream = nullptr;
-
-	_headers = r._headers;
 	_method = r._method;
 	_path = r._path;
 	_query = r._query;
 	_anchor = r._anchor;
 	_queryParameters = r._queryParameters;
-	_attachedFiles = r._attachedFiles;
-	r._attachedFiles.clear();
 	_contentLength = r._contentLength;
 	_boundary = r._boundary;
 	_availableBytes = r._availableBytes;
-	_currentFieldName = r._currentFieldName;
-	_currentFileName = r._currentFileName;
-	_currentTempFileName = r._currentTempFileName;
-	_isFileField = r._isFileField;
+	_firstBlock = r._firstBlock;
 	_isBadRequest = r._isBadRequest;
 	_allContentRead = r._allContentRead;
 
@@ -128,112 +84,9 @@ void Reader::cleanup() {
 	//_content is not to be freed, it's not owned by Reader
 
 	if (_window != nullptr) freeWindow();
-	delete _stream;
-
-	//delete temp files (by the time Reader is destucted those must be renamed or read)
-	for (Common::HashMap<Common::String, Common::String>::iterator i = _attachedFiles.begin(); i != _attachedFiles.end(); ++i) {
-		AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(i->_value);
-		if (node->exists()) removeFile(node->getPath().c_str());
-	}
 }
 
-bool Reader::readWholeRequest() {
-	if (_state == RS_NONE) _state = RS_READING_HEADERS;
-
-	while (true) {
-		if (!bytesLeft()) return false;
-
-		if (_state == RS_READING_HEADERS)
-			if (!readWholeHeaders())
-				return false;
-
-		if (_boundary.empty()) return true; //not POST multipart
-
-		if (!bytesLeft()) return false;
-
-		if (_state == RS_READING_CONTENT)
-			if (!readWholeContent())
-				return false;
-
-		if (_availableBytes >= 2) {
-			Common::String bts;
-			bts += readOne();
-			bts += readOne();
-			if (bts == "--") break;
-			if (bts == "\r\n") continue;
-			warning("strange bytes: \"%s\"", bts.c_str());
-		} else {
-			warning("strange ending");
-			break;
-		}
-	}	
-	if (_availableBytes > 0) debug("STRANGE END: %llu bytes left", _availableBytes);
-	else debug("END");
-
-	return true;
-}
-
-bool Reader::readFirstHeaders() {
-	if (_state == RS_NONE) _state = RS_READING_HEADERS;
-
-	if (!bytesLeft()) return false;
-
-	if (_state == RS_READING_HEADERS)
-		return readWholeHeaders();
-
-	warning("Reader::readFirstHeaders(): bad state");
-	return false;
-}
-
-bool Reader::readFirstContent(Common::WriteStream *stream) {
-	if (_state != RS_READING_CONTENT) {
-		warning("Reader::readFirstContent(): bad state");
-		return false;
-	}
-
-	if (!bytesLeft()) return false;
-
-	return readWholeContentIntoStream(stream);
-}
-
-bool Reader::readBlockHeaders(Common::WriteStream *stream) {
-	if (_state != RS_READING_HEADERS) {
-		warning("Reader::readBlockHeaders(): bad state");
-		return false;
-	}
-
-	if (!bytesLeft()) return false;
-
-	return readWholeHeadersIntoStream(stream);
-}
-
-bool Reader::readBlockContent(Common::WriteStream *stream) {
-	if (_state != RS_READING_CONTENT) {
-		warning("Reader::readBlockContent(): bad state");
-		return false;
-	}
-
-	if (!bytesLeft()) return false;
-
-	if (!readWholeContentIntoStream(stream))
-		return false;
-
-	if (_availableBytes >= 2) {
-		Common::String bts;
-		bts += readOne();
-		bts += readOne();
-		if (bts == "--") _allContentRead = true;
-		else if (bts != "\r\n")
-			warning("strange bytes: \"%s\"", bts.c_str());
-	} else {
-		warning("strange ending");
-		_allContentRead = true;
-	}
-
-	return true;
-}
-
-bool Reader::readWholeHeaders() {
+bool Reader::readAndHandleFirstHeaders() {
 	Common::String boundary = "\r\n\r\n";
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
@@ -243,14 +96,14 @@ bool Reader::readWholeHeaders() {
 	while (readOneByteInString(_headers, boundary)) {
 		if (!bytesLeft()) return false;
 	}
-	handleHeaders(_headers);
+	handleFirstHeaders(_headers);
 
 	freeWindow();
 	_state = RS_READING_CONTENT;
 	return true;
 }
 
-bool Reader::readWholeHeadersIntoStream(Common::WriteStream *stream) {
+bool Reader::readBlockHeadersIntoStream(Common::WriteStream *stream) {
 	Common::String boundary = "\r\n\r\n";
 	if (_window == nullptr) makeWindow(boundary.size());
 
@@ -278,51 +131,24 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
 }
 }
 
-void Reader::handleHeaders(Common::String headers) {
-	debug("\nHANDLE HEADERS:\n>>%s<<", headers.c_str());
-	if (_boundary.empty()) {
-		//parse method, path, query, fragment
-		parseFirstLine(headers);
-
-		//find boundary
-		_boundary = "";
-		readFromThatUntilLineEnd(headers.c_str(), "boundary=", _boundary);
-
-		//find content length
-		Common::String contentLength = "";
-		readFromThatUntilLineEnd(headers.c_str(), "Content-Length: ", contentLength);
-		_contentLength = contentLength.asUint64();
-		_availableBytes = _contentLength;
-		debug("BOUNDARY: %s", _boundary.c_str());
-		debug("LENGTH: %llu", _contentLength);
-	} else {
-		//find field name
-		_currentFieldName = "";
-		readFromThatUntilLineEnd(headers.c_str(), "name=\"", _currentFieldName);
-		for (uint32 i = 0; i < _currentFieldName.size(); ++i)
-			if (_currentFieldName[i] == '\"') {
-				_currentFieldName.erase(i);
-				break;
-			}
-		debug("FIELD NAME: >>%s<<", _currentFieldName.c_str());
-
-		//find out field type
-		_currentFileName = "";
-		readFromThatUntilLineEnd(headers.c_str(), "filename=\"", _currentFileName);
-		for (uint32 i = 0; i < _currentFileName.size(); ++i)
-			if (_currentFileName[i] == '\"') {
-				_currentFileName.erase(i);
-				break;
-			}
-
-		if (!_currentFileName.empty()) {
-			_isFileField = true;
-			_queryParameters[_currentFieldName] = _currentFileName;
-			debug("FILE NAME: >>%s<<", _currentFileName.c_str());
-		} else {
-			_isFileField = false;
-		}
+void Reader::handleFirstHeaders(Common::String headers) {
+	if (!_boundary.empty()) {
+		warning("handleFirstHeaders() called when first headers were already handled");
+		return;
 	}
+
+	//parse method, path, query, fragment
+	parseFirstLine(headers);
+
+	//find boundary
+	_boundary = "";
+	readFromThatUntilLineEnd(headers.c_str(), "boundary=", _boundary);
+
+	//find content length
+	Common::String contentLength = "";
+	readFromThatUntilLineEnd(headers.c_str(), "Content-Length: ", contentLength);
+	_contentLength = contentLength.asUint64();
+	_availableBytes = _contentLength;
 }
 
 void Reader::parseFirstLine(const Common::String &headers) {
@@ -419,101 +245,10 @@ void Reader::parseQueryParameters() {
 	}
 }
 
-namespace {
-char generateRandomChar(Common::RandomSource &random) {
-	int r = random.getRandomNumber(36);
-	char c = '0' + r;
-	if (r > 9) c = 'a' + r - 10;
-	return c;
-}
-
-Common::String generateTempFileName(Common::String originalFilename, Common::RandomSource &random) {
-	//generates "./<originalFilename>-<uniqueSequence>.scummtmp"
-	//normalize <originalFilename>
-	Common::String prefix = "./";
-	for (uint32 i = 0; i < originalFilename.size(); ++i) {
-		char c = originalFilename[i];
-		if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '.' || c == '_' || c == '-') {
-			prefix += c;
-		} else {
-			prefix += '_';
-		}
-	}
-	prefix += '-';
-
-	//generate initial sequence
-	Common::String uniqueSequence;
-	for (uint32 i = 0; i < 5; ++i)
-		uniqueSequence += generateRandomChar(random);
-
-	//update sequence while generate path exists
-	AbstractFSNode *node;
-	Common::String path;
-	do {
-		uniqueSequence += generateRandomChar(random);
-		path = prefix + uniqueSequence + ".scummtmp";
-		node = g_system->getFilesystemFactory()->makeFileNodePath(path);
-	} while (node->exists());
-
-	return path;
-}
-}
-
-bool Reader::readWholeContent() {
-	Common::String boundary = "--" + _boundary;
-	if (!_firstBlock) boundary = "\r\n" + boundary;
-	if (_window == nullptr) {
-		makeWindow(boundary.size());
-
-		if (_stream) delete _stream;
-		if (_isFileField) {
-			//create temporary file
-			_currentTempFileName = generateTempFileName(_currentFileName, _randomSource);
-			AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(_currentTempFileName);
-			_stream = node->createWriteStream();
-			if (_stream == nullptr)
-				error("Unable to open temp file to write into!");
-		} else {
-			_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
-		}
-	}
-
-	while (readOneByteInStream(_stream, boundary)) {
-		if (!bytesLeft()) return false;
-	}
-
-	_firstBlock = false;
-	if (_isFileField) {
-		if (_stream != nullptr) {
-			_stream->flush();
-			delete _stream;
-			_stream = nullptr;
-		} else {
-			warning("No stream was created!");
-		}
-		handleFileContent(_currentTempFileName);
-	} else {
-		Common::MemoryReadWriteStream *dynamicStream = dynamic_cast<Common::MemoryReadWriteStream *>(_stream);
-		if (dynamicStream != nullptr)
-			if (dynamicStream->size() == 0)
-				handleValueContent("");
-			else
-				handleValueContent(Common::String((char *)dynamicStream->getData(), dynamicStream->size()));
-		else
-			if (_stream != nullptr)
-				warning("Stream somehow changed its type from MemoryReadWriteStream!");
-			else
-				warning("No stream was created!");
-	}
-
-	freeWindow();
-	_state = RS_READING_HEADERS;
-	return true;
-}
-
-bool Reader::readWholeContentIntoStream(Common::WriteStream *stream) {
+bool Reader::readContentIntoStream(Common::WriteStream *stream) {
 	Common::String boundary = "--" + _boundary;
 	if (!_firstBlock) boundary = "\r\n" + boundary;
+	if (_boundary.empty()) boundary = "\r\n";
 	if (_window == nullptr) makeWindow(boundary.size());
 
 	while (readOneByteInStream(stream, boundary)) {
@@ -528,16 +263,6 @@ bool Reader::readWholeContentIntoStream(Common::WriteStream *stream) {
 	return true;
 }
 
-void Reader::handleFileContent(Common::String filename) {
-	debug("\nHANDLE FILE CONTENT:\nFILE >>%s<< SAVED INTO >>%s<<", _currentFileName.c_str(), filename.c_str());
-	_attachedFiles[_currentFileName] = filename;
-}
-
-void Reader::handleValueContent(Common::String value) {
-	debug("\nHANDLE CONTENT:\n>>%s<<", value.c_str());
-	_queryParameters[_currentFieldName] = value;
-}
-
 void Reader::makeWindow(uint32 size) {
 	freeWindow();
 
@@ -594,6 +319,67 @@ byte Reader::readOne() {
 	return b;
 }
 
+/// public
+
+bool Reader::readFirstHeaders() {
+	if (_state == RS_NONE) _state = RS_READING_HEADERS;
+
+	if (!bytesLeft()) return false;
+
+	if (_state == RS_READING_HEADERS)
+		return readAndHandleFirstHeaders();
+
+	warning("Reader::readFirstHeaders(): bad state");
+	return false;
+}
+
+bool Reader::readFirstContent(Common::WriteStream *stream) {
+	if (_state != RS_READING_CONTENT) {
+		warning("Reader::readFirstContent(): bad state");
+		return false;
+	}
+
+	// no difference, actually
+	return readBlockContent(stream);
+}
+
+bool Reader::readBlockHeaders(Common::WriteStream *stream) {
+	if (_state != RS_READING_HEADERS) {
+		warning("Reader::readBlockHeaders(): bad state");
+		return false;
+	}
+
+	if (!bytesLeft()) return false;
+
+	return readBlockHeadersIntoStream(stream);
+}
+
+bool Reader::readBlockContent(Common::WriteStream *stream) {
+	if (_state != RS_READING_CONTENT) {
+		warning("Reader::readBlockContent(): bad state");
+		return false;
+	}
+
+	if (!bytesLeft()) return false;
+
+	if (!readContentIntoStream(stream))
+		return false;
+
+	if (_availableBytes >= 2) {
+		Common::String bts;
+		bts += readOne();
+		bts += readOne();
+		if (bts == "--") _allContentRead = true;
+		else if (bts != "\r\n")
+			warning("strange bytes: \"%s\"", bts.c_str());
+	} else {
+		warning("strange ending");
+		_allContentRead = true;
+	}
+
+	return true;
+}
+
 uint32 Reader::bytesLeft() { return _bytesLeft; }
 
 void Reader::setContent(Common::MemoryReadWriteStream *stream) {
@@ -605,6 +391,8 @@ bool Reader::badRequest() const { return _isBadRequest; }
 
 bool Reader::noMoreContent() const { return _allContentRead; }
 
+Common::String Reader::headers() const { return _headers; }
+
 Common::String Reader::method() const { return _method; }
 
 Common::String Reader::path() const { return _path; }
@@ -613,8 +401,6 @@ Common::String Reader::query() const { return _query; }
 
 Common::String Reader::queryParameter(Common::String name) const { return _queryParameters[name]; }
 
-Common::String Reader::attachedFile(Common::String name) const { return _attachedFiles[name]; }
-
 Common::String Reader::anchor() const { return _anchor; }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index 0ed0d8c..d234a82 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -23,11 +23,9 @@
 #ifndef BACKENDS_NETWORKING_SDL_NET_READER_H
 #define BACKENDS_NETWORKING_SDL_NET_READER_H
 
-#include "common/scummsys.h"
 #include "common/str.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
-#include "common/random.h"
 
 namespace Common {
 class MemoryReadWriteStream;
@@ -43,8 +41,6 @@ enum ReaderState {
 };
 
 class Reader {
-	Common::RandomSource _randomSource; //for temp file names
-
 	ReaderState _state;
 	Common::MemoryReadWriteStream *_content;
 	uint32 _bytesLeft;
@@ -53,31 +49,22 @@ class Reader {
 	uint32 _windowUsed, _windowSize;
 
 	Common::String _headers;
-	Common::WriteStream *_stream;
-	bool _firstBlock;
-
-	///Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
 	Common::HashMap<Common::String, Common::String> _queryParameters;
-	Common::HashMap<Common::String, Common::String> _attachedFiles;
 	uint32 _contentLength;
 	Common::String _boundary;
 	uint32 _availableBytes;
-	Common::String _currentFieldName, _currentFileName, _currentTempFileName;
-	bool _isFileField;
+	bool _firstBlock;
 	bool _isBadRequest;
 	bool _allContentRead;
 
 	void cleanup();
 
-	bool readWholeHeaders(); //true when ended reading
-	bool readWholeContent(); //true when ended reading
-	bool readWholeHeadersIntoStream(Common::WriteStream *stream); //true when ended reading
-	bool readWholeContentIntoStream(Common::WriteStream *stream); //true when ended reading
-	void handleHeaders(Common::String headers);
-	void handleFileContent(Common::String filename);
-	void handleValueContent(Common::String value);
+	bool readAndHandleFirstHeaders(); //true when ended reading
+	bool readBlockHeadersIntoStream(Common::WriteStream *stream); //true when ended reading
+	bool readContentIntoStream(Common::WriteStream *stream); //true when ended reading
 
+	void handleFirstHeaders(Common::String headers);
 	void parseFirstLine(const Common::String &headers);
 	void parsePathQueryAndAnchor(Common::String path);
 	void parseQueryParameters();
@@ -96,20 +83,21 @@ public:
 
 	Reader &operator=(Reader &r);
 
-	bool readWholeRequest(); //true when ended reading
 	bool readFirstHeaders(); //true when ended reading
 	bool readFirstContent(Common::WriteStream *stream); //true when ended reading
 	bool readBlockHeaders(Common::WriteStream *stream); //true when ended reading
 	bool readBlockContent(Common::WriteStream *stream); //true when ended reading
+
 	void setContent(Common::MemoryReadWriteStream *stream);
+
 	bool badRequest() const;
 	bool noMoreContent() const;
 
+	Common::String headers() const;
 	Common::String method() const;
 	Common::String path() const;
 	Common::String query() const;
 	Common::String queryParameter(Common::String name) const;
-	Common::String attachedFile(Common::String name) const;
 	Common::String anchor() const;
 };
 


Commit: 34dd84f42920bc717ad321e42194346c3c33ec90
    https://github.com/scummvm/scummvm/commit/34dd84f42920bc717ad321e42194346c3c33ec90
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: More cleanup in Client

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index a486e3c..3b3f45b 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -24,9 +24,8 @@
 
 #include "backends/networking/sdl_net/client.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "common/textconsole.h"
+#include "common/memstream.h"
 #include <SDL/SDL_net.h>
-#include <common/memstream.h>
 
 namespace Networking {
 
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index eab3bcf..22e450a 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -23,8 +23,8 @@
 #ifndef BACKENDS_NETWORKING_SDL_NET_CLIENT_H
 #define BACKENDS_NETWORKING_SDL_NET_CLIENT_H
 
+#include "backends/networking/sdl_net/reader.h"
 #include "common/str.h"
-#include "reader.h"
 
 namespace Common {
 class MemoryReadWriteStream;


Commit: f1830645d067379d9b0a96d71220ced59287e622
    https://github.com/scummvm/scummvm/commit/f1830645d067379d9b0a96d71220ced59287e622
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in LocalWebserver

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 86a1d1a..a99df8d 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -28,9 +28,7 @@
 #include "common/str.h"
 #include "common/system.h"
 #include "common/timer.h"
-#include "common/textconsole.h"
 #include <SDL/SDL_net.h>
-#include "reader.h"
 
 namespace Common {
 class MemoryReadWriteStream;


Commit: 36b0069e95a5eb6816e75bb9cf4aae56c7b5b258
    https://github.com/scummvm/scummvm/commit/36b0069e95a5eb6816e75bb9cf4aae56c7b5b258
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in Handlers

Simplified some stuff here and there by using HandlerUtils static
methods.

Changed paths:
  R backends/networking/sdl_net/handlers/basehandler.cpp
    backends/module.mk
    backends/networking/sdl_net/handlers/basehandler.h
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/createdirectoryhandler.h
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.h
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/sdl_net/handlers/indexpagehandler.cpp
    backends/networking/sdl_net/handlers/indexpagehandler.h
    backends/networking/sdl_net/handlers/resourcehandler.cpp



diff --git a/backends/module.mk b/backends/module.mk
index 8b51a28..a472a0e 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -58,7 +58,6 @@ ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/client.o \
 	networking/sdl_net/getclienthandler.o \
-	networking/sdl_net/handlers/basehandler.o \
 	networking/sdl_net/handlers/createdirectoryhandler.o \
 	networking/sdl_net/handlers/downloadfilehandler.o \
 	networking/sdl_net/handlers/filesbasehandler.o \
diff --git a/backends/networking/sdl_net/handlers/basehandler.cpp b/backends/networking/sdl_net/handlers/basehandler.cpp
deleted file mode 100644
index da90bb4..0000000
--- a/backends/networking/sdl_net/handlers/basehandler.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/* 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 "backends/networking/sdl_net/handlers/basehandler.h"
-#include "common/archive.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/unzip.h"
-
-namespace Networking {
-
-#define ARCHIVE_NAME "wwwroot.zip"
-
-BaseHandler::BaseHandler() {}
-
-BaseHandler::~BaseHandler() {}
-
-/// utils
-
-Common::Archive *BaseHandler::getZipArchive() const {
-	// first search in themepath
-	if (ConfMan.hasKey("themepath")) {
-		const Common::FSNode &node = Common::FSNode(ConfMan.get("themepath"));
-		if (!node.exists() || !node.isReadable() || !node.isDirectory())
-			return nullptr;
-
-		Common::FSNode fileNode = node.getChild(ARCHIVE_NAME);
-		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
-			Common::SeekableReadStream *const stream = fileNode.createReadStream();
-			Common::Archive *zipArchive = Common::makeZipArchive(stream);
-			if (zipArchive) return zipArchive;
-		}
-	}
-
-	// then use SearchMan to find it
-	Common::ArchiveMemberList fileList;
-	SearchMan.listMatchingMembers(fileList, ARCHIVE_NAME);
-	for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
-		Common::ArchiveMember       const &m = **it;
-		Common::SeekableReadStream *const stream = m.createReadStream();
-		Common::Archive *zipArchive = Common::makeZipArchive(stream);
-		if (zipArchive) return zipArchive;
-	}
-
-	return nullptr;
-}
-
-Common::ArchiveMemberList BaseHandler::listArchive() const {
-	Common::ArchiveMemberList resultList;
-	Common::Archive *zipArchive = getZipArchive();
-	if (zipArchive) {
-		zipArchive->listMembers(resultList);
-		delete zipArchive;
-	}
-	return resultList;
-}
-
-Common::SeekableReadStream *BaseHandler::getArchiveFile(Common::String name) const {
-	Common::SeekableReadStream *result = nullptr;
-	Common::Archive *zipArchive = getZipArchive();
-	if (zipArchive) {
-		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
-		if (ptr.get() == nullptr) return nullptr;
-		result = ptr->createReadStream();
-		delete zipArchive;
-	}
-	return result;
-}
-
-Common::String BaseHandler::readEverythingFromStream(Common::SeekableReadStream *const stream) {
-	Common::String result;
-	char buf[1024];
-	uint32 readBytes;
-	while (!stream->eos()) {
-		readBytes = stream->read(buf, 1024);
-		result += Common::String(buf, readBytes);
-	}
-	return result;
-}
-
-} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/basehandler.h b/backends/networking/sdl_net/handlers/basehandler.h
index feea83a..fd55b13 100644
--- a/backends/networking/sdl_net/handlers/basehandler.h
+++ b/backends/networking/sdl_net/handlers/basehandler.h
@@ -24,7 +24,6 @@
 #define BACKENDS_NETWORKING_SDL_NET_BASEHANDLER_H
 
 #include "backends/networking/sdl_net/client.h"
-#include "common/archive.h"
 #include "common/callback.h"
 
 namespace Networking {
@@ -32,15 +31,9 @@ namespace Networking {
 typedef Common::BaseCallback<Client &> *ClientHandlerCallback;
 
 class BaseHandler {
-protected:
-	Common::Archive *getZipArchive() const;
-	Common::ArchiveMemberList listArchive() const;
-	Common::SeekableReadStream *getArchiveFile(Common::String name) const;
-	static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
-
 public:
-	BaseHandler();
-	virtual ~BaseHandler();
+	BaseHandler() {}
+	virtual ~BaseHandler() {}
 
 	virtual ClientHandlerCallback getHandler() = 0;
 };
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index 7b7d9ed..97d75fc 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -21,15 +21,13 @@
 */
 
 #include "backends/networking/sdl_net/handlers/createdirectoryhandler.h"
-#include "backends/networking/sdl_net/localwebserver.h"
 #include "backends/fs/fs-factory.h"
-#include "common/file.h"
+#include "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
 #include "common/translation.h"
 
 namespace Networking {
 
-#define INDEX_PAGE_NAME ".index.html"
-
 CreateDirectoryHandler::CreateDirectoryHandler() {}
 
 CreateDirectoryHandler::~CreateDirectoryHandler() {}
@@ -37,94 +35,58 @@ CreateDirectoryHandler::~CreateDirectoryHandler() {}
 void CreateDirectoryHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
 	Common::String name = client.queryParameter("directory_name");
-	Common::String errorMessage = "";
-
-	// show an error message if failed to create directory	
-	if (!createDirectory(path, name, errorMessage)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=%s\">%s</a>",
-				errorMessage.c_str(),
-				"%2F", //that's encoded "/"
-				_("Back to the files manager")
-				)
-			);
-		return;
-	}
-
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", Common::String::format(
-			"%s<br/><a href=\"files?path=%s\">%s</a>",
-			_("Directory created successfully!"),
-			client.queryParameter("path").c_str(),
-			_("Back to parent directory")
-		)
-	);
-	LocalWebserver::setClientRedirectHandler(
-		client, response,
-		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
-	);
-}
 
-void CreateDirectoryHandler::handleErrorMessage(Client &client, Common::String message) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", message);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
-bool CreateDirectoryHandler::createDirectory(Common::String path, Common::String name, Common::String &errorMessage) {
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/") {
-		errorMessage = _("Can't create directory here!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't create directory here!"));
+		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
-		errorMessage = _("Invalid path!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
 	}
 
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
-		errorMessage = _("Parent directory doesn't exists!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Parent directory doesn't exists!"));
+		return;
 	}
 	if (!node->isDirectory()) {
-		errorMessage = _("Can't create a directory within a file!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't create a directory within a file!"));
+		return;
 	}
-	
+
 	// check that <directory_name> doesn't exist or is directory
 	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
 	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
 	if (node->exists()) {
 		if (!node->isDirectory()) {
-			errorMessage = _("There is a file with that name in the parent directory!");
-			return false;
-		} else return true;
-	}
-	
-	// create the <directory_name> in <path>
-	if (!node->create(true)) {
-		errorMessage = _("Failed to create the directory!");
-		return false;
+			HandlerUtils::setFilesManagerErrorMessageHandler(client, _("There is a file with that name in the parent directory!"));
+			return;
+		}
+	} else {
+		// create the <directory_name> in <path>
+		if (!node->create(true)) {
+			HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to create the directory!"));
+			return;
+		}
 	}
 
-	return true;
+	// set redirect on success
+	HandlerUtils::setMessageHandler(
+		client,
+		Common::String::format(
+			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			_("Directory created successfully!"),
+			client.queryParameter("path").c_str(),
+			_("Back to parent directory")
+		),
+		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+	);
 }
 
 /// public
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.h b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
index 023c3e7..2ffaf96 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.h
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
@@ -29,16 +29,6 @@ namespace Networking {
 
 class CreateDirectoryHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	void handleErrorMessage(Client &client, Common::String message);
-
-	/**
-	 * Creates the directory <name> in <path>.
-	 *
-	 * Fills <errorMessage> on failure.
-	 *
-	 * Returns true on success.
-	 */
-	bool createDirectory(Common::String path, Common::String name, Common::String &errorMessage);
 public:
 	CreateDirectoryHandler();
 	virtual ~CreateDirectoryHandler();
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 8974cb7..ff45517 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -21,77 +21,48 @@
 */
 
 #include "backends/networking/sdl_net/handlers/downloadfilehandler.h"
-#include "backends/networking/sdl_net/localwebserver.h"
 #include "backends/fs/fs-factory.h"
-#include "common/file.h"
+#include "backends/networking/sdl_net/getclienthandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
 #include "common/translation.h"
-#include "../getclienthandler.h"
 
 namespace Networking {
 
-#define INDEX_PAGE_NAME ".index.html"
-
 DownloadFileHandler::DownloadFileHandler() {}
 
 DownloadFileHandler::~DownloadFileHandler() {}
 
 void DownloadFileHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
-	Common::String errorMessage = "";
-
-	// show an error message if failed to download the file
-	if (!downloadFile(client, path, errorMessage)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=%s\">%s</a>",
-				errorMessage.c_str(),
-				"%2F", //that's encoded "/"
-				_("Back to the files manager")
-				)
-			);
-	}
-}
-
-void DownloadFileHandler::handleErrorMessage(Client &client, Common::String message) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", message);
-	LocalWebserver::setClientGetHandler(client, response);
-}
 
-bool DownloadFileHandler::downloadFile(Client &client, Common::String path, Common::String &errorMessage) {
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/") {
-		errorMessage = _("Invalid path!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
-		errorMessage = _("Invalid path!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
 	}
 
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
-		errorMessage = _("The file doesn't exist!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!"));
+		return;
 	}
 	if (node->isDirectory()) {
-		errorMessage = _("Can't download a directory!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't download a directory!"));
+		return;
 	}
 	Common::SeekableReadStream *stream = node->createReadStream();
 	if (stream == nullptr) {
-		errorMessage = _("Failed to read the file!");
-		return false;
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to read the file!"));
+		return;
 	}
 
 	GetClientHandler *handler = new GetClientHandler(stream);
@@ -100,7 +71,6 @@ bool DownloadFileHandler::downloadFile(Client &client, Common::String path, Comm
 	handler->setHeader("Content-Disposition", "attachment; filename=\"" + node->getDisplayName() + "\"");
 	handler->setHeader("Content-Transfer-Encoding", "binary");	
 	client.setHandler(handler);
-	return true;
 }
 
 /// public
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.h b/backends/networking/sdl_net/handlers/downloadfilehandler.h
index 6b69664..afcedec 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.h
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.h
@@ -29,16 +29,6 @@ namespace Networking {
 
 class DownloadFileHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	void handleErrorMessage(Client &client, Common::String message);
-
-	/**
-	 * Creates a client handler for downloading file <path>.
-	 *
-	 * Fills <errorMessage> on failure.
-	 *
-	 * Returns true on success.
-	 */
-	bool downloadFile(Client &client, Common::String path, Common::String &errorMessage);
 public:
 	DownloadFileHandler();
 	virtual ~DownloadFileHandler();
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 4b18ac3..2db80f7 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -21,8 +21,8 @@
 */
 
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "common/file.h"
 #include "common/translation.h"
 
 namespace Networking {
@@ -52,23 +52,15 @@ void FilesPageHandler::handle(Client &client) {
 	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
 	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(FILES_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
+	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
+	if (stream) response = HandlerUtils::readEverythingFromStream(stream);
 
 	Common::String path = client.queryParameter("path");
 	Common::String content = "";
 	
 	// show an error message if failed to list directory
 	if (!listDirectory(path, content, itemTemplate)) {
-		handleErrorMessage(
-			client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=%s\">%s</a>",
-				_("ScummVM couldn't list the directory you specified."),
-				"%2F", //that's encoded "/"
-				_("Back to the files manager")
-			)
-		);
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ScummVM couldn't list the directory you specified."));
 		return;
 	}
 
@@ -85,17 +77,6 @@ void FilesPageHandler::handle(Client &client) {
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
-void FilesPageHandler::handleErrorMessage(Client &client, Common::String message) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
-	replace(response, "{message}", message);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
 		addItem(content, itemTemplate, true, "/root/", _("File system root"));
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index 6205dcd..ba132fd 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -29,7 +29,6 @@ namespace Networking {
 
 class FilesPageHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	void handleErrorMessage(Client &client, Common::String message);
 
 	/**
 	 * Lists the directory <path>.
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 0d7256c..da5cd2d 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -21,47 +21,33 @@
 */
 
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "backends/saves/default/default-saves.h"
-#include "common/archive.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/savefile.h"
 #include "common/translation.h"
 #include "gui/storagewizarddialog.h"
 
 namespace Networking {
 
-#define INDEX_PAGE_NAME ".index.html"
-
 IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
 
 IndexPageHandler::~IndexPageHandler() {}
 
 void IndexPageHandler::handle(Client &client) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
-
 	Common::String code = client.queryParameter("code");
 
 	if (code == "") {		
-		replace(response, "{message}", _("This is a local webserver index page."));
-		LocalWebserver::setClientGetHandler(client, response);
+		HandlerUtils::setMessageHandler(client, _("This is a local webserver index page."));
 		return;
 	}
 
 	_code = code;
 	sendCommand(GUI::kStorageCodePassedCmd, 0);
-	replace(response, "{message}", _("ScummVM got the code and already connects to your cloud storage!"));
-	LocalWebserver::setClientGetHandler(client, response);
+	HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!"));
 }
 
 /// public
 
-Common::String IndexPageHandler::code() { return _code; }
+Common::String IndexPageHandler::code() const { return _code; }
 
 ClientHandlerCallback IndexPageHandler::getHandler() {
 	return new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle);
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.h b/backends/networking/sdl_net/handlers/indexpagehandler.h
index f10ab63..0e256b5 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.h
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.h
@@ -37,7 +37,7 @@ public:
 	IndexPageHandler();
 	virtual ~IndexPageHandler();
 
-	Common::String code();
+	Common::String code() const;
 	virtual ClientHandlerCallback getHandler();
 };
 
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
index 160f78f..1205c4a 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.cpp
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -21,6 +21,7 @@
 */
 
 #include "backends/networking/sdl_net/handlers/resourcehandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
 
 namespace Networking {
@@ -37,7 +38,7 @@ void ResourceHandler::handle(Client &client) {
 	if (filename.size() && filename[0] == '.') return;
 
 	// if file not found, don't set handler either
-	Common::SeekableReadStream *file = getArchiveFile(filename);
+	Common::SeekableReadStream *file = HandlerUtils::getArchiveFile(filename);
 	if (file == nullptr) return;
 
 	LocalWebserver::setClientGetHandler(client, file, 200, LocalWebserver::determineMimeType(filename));


Commit: 30430b379fa373c277c3a64cfa03aa108b560476
    https://github.com/scummvm/scummvm/commit/30430b379fa373c277c3a64cfa03aa108b560476
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Client's buffer

Changed paths:
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/client.h



diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 3b3f45b..9d49480 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -29,14 +29,19 @@
 
 namespace Networking {
 
-Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) {}
+Client::Client():
+	_state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr),
+	_previousHandler(nullptr), _stream(nullptr), _buffer(new byte[CLIENT_BUFFER_SIZE]) {}
 
-Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr), _previousHandler(nullptr), _stream(nullptr) {
+Client::Client(SDLNet_SocketSet set, TCPsocket socket):
+	_state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr),
+	_previousHandler(nullptr), _stream(nullptr), _buffer(new byte[CLIENT_BUFFER_SIZE]) {
 	open(set, socket);
 }
 
 Client::~Client() {
 	close();
+	delete[] _buffer;
 }
 
 void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
@@ -65,16 +70,14 @@ bool Client::readMoreIfNeeded() {
 	if (!_socket) return false;
 	if (!SDLNet_SocketReady(_socket)) return false;
 
-	const uint32 BUFFER_SIZE = 16 * 1024;
-	byte buffer[BUFFER_SIZE];
-	int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE);
+	int bytes = SDLNet_TCP_Recv(_socket, _buffer, CLIENT_BUFFER_SIZE);
 	if (bytes <= 0) {
 		warning("Client::readHeaders recv fail");
 		close();
 		return false;
 	}
 
-	if (_stream->write(buffer, bytes) != bytes) {
+	if (_stream->write(_buffer, bytes) != bytes) {
 		warning("failed to write() into MemoryReadWriteStream");
 		close();
 		return false;
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index 22e450a..f37d046 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -45,6 +45,8 @@ enum ClientState {
 
 class Client;
 
+#define CLIENT_BUFFER_SIZE 1 * 1024 * 1024
+
 class ClientHandler {
 public:
 	virtual ~ClientHandler() {};
@@ -58,6 +60,7 @@ class Client {
 	Reader _reader;
 	ClientHandler *_handler, *_previousHandler;
 	Common::MemoryReadWriteStream *_stream;
+	byte *_buffer;
 
 	bool readMoreIfNeeded();
 


Commit: 35b2471290d3c8dd5c1752bf826d69ee52fdaad0
    https://github.com/scummvm/scummvm/commit/35b2471290d3c8dd5c1752bf826d69ee52fdaad0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix '\' encoding back

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 2db80f7..4de76b9 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -40,8 +40,6 @@ Common::String encodeDoubleQuotes(Common::String s) {
 	for (uint32 i = 0; i < s.size(); ++i)
 		if (s[i] == '"') {
 			result += "\\\"";
-		} else if (s[i] == '\\') {
-			result += "\\\\";
 		} else result += s[i];
 	return result;
 }


Commit: 12518ed0bf60689e69c9f569bf90d5466cdc0551
    https://github.com/scummvm/scummvm/commit/12518ed0bf60689e69c9f569bf90d5466cdc0551
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix gradient on LocalWebserver's pages

It was starting over every 100vh (each screen).

Changed paths:
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index a57355e..4dcb9b0 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index 59d9667..e00a2bc 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -1,7 +1,7 @@
 html {
 	background: rgb(212, 117, 11);
 	background: linear-gradient(to bottom, rgb(212, 117, 11) 0%, rgb(212, 117, 11) 36%, rgb(239, 196, 24) 100%);
-	height: 100vh;
+	min-height: 100vh;
 }
 
 .container {


Commit: 3064b44b92e8d0aff3a7d5b4b721d635ead7c5e9
    https://github.com/scummvm/scummvm/commit/3064b44b92e8d0aff3a7d5b4b721d635ead7c5e9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Switch to "multiple" files uploading

Still doesn't support directories uploading.

Changed paths:
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html



diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 733a87b..0ca5e3f 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -114,7 +114,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	Common::String headers = readEverythingFromMemoryStream(_headersStream);
 	Common::String fieldName = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
-	if (fieldName != "upload_file") return;
+	if (!fieldName.hasPrefix("upload_file[")) return;
 
 	Common::String filename = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "filename=\"", filename);
@@ -148,18 +148,21 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 	// if previous block headers were file-related and created a stream
 	if (_contentStream) {
 		_contentStream->flush();
-		// success - redirect back to directory listing
-		HandlerUtils::setMessageHandler(*client,
-			Common::String::format(
-				"%s<br/><a href=\"files?path=%s\">%s</a>",
-				_("Uploaded successfully!"),
-				client->queryParameter("path").c_str(),
-				_("Back to parent directory")
-				),
-			"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
-			);
-		_state = UFH_STOP;
-		return;
+
+		if (client->noMoreContent()) {
+			// success - redirect back to directory listing
+			HandlerUtils::setMessageHandler(*client,
+				Common::String::format(
+					"%s<br/><a href=\"files?path=%s\">%s</a>",
+					_("Uploaded successfully!"),
+					client->queryParameter("path").c_str(),
+					_("Back to parent directory")
+					),
+				"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
+				);
+			_state = UFH_STOP;		
+			return;
+		}
 	}
 	
 	// if no file field was found, but no more content avaiable - failure
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 4dcb9b0..ae19ef7 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index d14acfc..c83a59f 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -26,7 +26,7 @@
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
 					<form action="upload?path={path}" method="post" enctype="multipart/form-data">
-						<input type="file" name="upload_file"/>
+						<input type="file" name="upload_file[]" multiple/>
 						<input type="submit" value="{upload_file_button}"/>
 					</form>
 				</div>


Commit: d5753a484719019af9b322a74c414a9d242f8f83
    https://github.com/scummvm/scummvm/commit/d5753a484719019af9b322a74c414a9d242f8f83
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add auto-detect for downloaded game

If that's the game, that is.

Method is copy-pasted from Launcher, but fixed not to ask the directory
and thus doesn't contain the loop.

Changed paths:
  A gui/editgamedialog.cpp
  A gui/editgamedialog.h
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/launcher.cpp
    gui/module.mk



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index d926030..001a058 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -21,15 +21,17 @@
  */
 
 #include "gui/downloaddialog.h"
-#include "gui/widgets/list.h"
-#include "gui/widget.h"
-#include "gui/gui-manager.h"
 #include "backends/cloud/cloudmanager.h"
+#include "common/config-manager.h"
 #include "common/translation.h"
-#include "widgets/edittext.h"
-#include "message.h"
-#include "browser.h"
-#include "remotebrowser.h"
+#include "engines/metaengine.h"
+#include "gui/browser.h"
+#include "gui/chooser.h"
+#include "gui/editgamedialog.h"
+#include "gui/message.h"
+#include "gui/remotebrowser.h"
+#include "gui/widgets/edittext.h"
+#include "gui/widgets/list.h"
 
 namespace GUI {
 
@@ -38,7 +40,7 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false), _reflow(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false), _redraw(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -93,7 +95,7 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	case kDownloadProgressCmd:		
 		_percentLabel->setLabel(Common::String::format("%u %%", data));
 		_progressBar->setValue(data);
-		_reflow = true;
+		_redraw = true;
 		break;
 	case kDownloadEndedCmd:
 		_close = true;
@@ -155,33 +157,105 @@ bool DownloadDialog::selectDirectories() {
 
 	CloudMan.startDownload(remoteDirectory.path(), localPath);
 	CloudMan.setDownloadTarget(this);
+	_localDirectory = localPath;
 	return true;
 }
 
 void DownloadDialog::handleTickle() {
 	if (_close) {
+		addGame();
 		close();
 		_close = false;
 		return;
 	}
 
-	if (_reflow) {
-		reflowLayout();
+	if (_redraw) {
+		refreshWidgets();
 		draw();
-		_reflow = false;
+		_redraw = false;
 	}
 
 	Dialog::handleTickle();
 }
 
+void DownloadDialog::addGame() {
+	// Allow user to add a new game to the list.
+	// 2) try to auto detect which game is in the directory, if we cannot
+	//    determine it uniquely present a list of candidates to the user
+	//    to pick from
+	// 3) Display the 'Edit' dialog for that item, letting the user specify
+	//    an alternate description (to distinguish multiple versions of the
+	//    game, e.g. 'Monkey German' and 'Monkey English') and set default
+	//    options for that game
+	// 4) If no game is found in the specified directory, return to the
+	//    dialog.
+
+	// User made his choice...
+	Common::FSNode dir(_localDirectory);
+	Common::FSList files;
+	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
+		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
+		alert.runModal();
+		return;
+	}
+
+	// ...so let's determine a list of candidates, games that
+	// could be contained in the specified directory.
+	GameList candidates(EngineMan.detectGames(files));
+
+	int idx;
+	if (candidates.empty()) {
+		// No game was found in the specified directory
+		MessageDialog alert(_("ScummVM could not find any game in the specified directory!"));
+		alert.runModal();
+		return;
+	}
+				
+	if (candidates.size() == 1) {
+		// Exact match
+		idx = 0;
+	} else {
+		// Display the candidates to the user and let her/him pick one
+		Common::StringArray list;
+		for (idx = 0; idx < (int)candidates.size(); idx++)
+			list.push_back(candidates[idx].description());
+
+		ChooserDialog dialog(_("Pick the game:"));
+		dialog.setList(list);
+		idx = dialog.runModal();
+	}
+	if (0 <= idx && idx < (int)candidates.size()) {
+		GameDescriptor result = candidates[idx];
+
+		// TODO: Change the detectors to set "path" !
+		result["path"] = dir.getPath();
+
+		Common::String domain = addGameToConf(result);
+
+		// Display edit dialog for the new entry
+		EditGameDialog editDialog(domain, result.description());
+		if (editDialog.runModal() > 0) {
+			// User pressed OK, so make changes permanent
+
+			// Write config to disk
+			ConfMan.flushToDisk();
+		} else {
+			// User aborted, remove the the new domain again
+			ConfMan.removeGameDomain(domain);
+		}
+
+	}
+}
+
 void DownloadDialog::reflowLayout() {
 	Dialog::reflowLayout();
 	refreshWidgets();
 }
 
 void DownloadDialog::refreshWidgets() {	
+	_localDirectory = CloudMan.getDownloadLocalDirectory();
 	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
-	_localDirectoryLabel->setLabel(_("To: ") + CloudMan.getDownloadLocalDirectory());
+	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
 	_percentLabel->setLabel(Common::String::format("%u %%", progress));
 	_progressBar->setValue(progress);
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 74dd1e2..baa3f15 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -53,10 +53,12 @@ class DownloadDialog : public Dialog {
 	ButtonWidget *_cancelButton;
 	ButtonWidget *_closeButton;
 
-	bool _close, _reflow;
+	Common::String _localDirectory;
+	bool _close, _redraw;
 
 	void refreshWidgets();
 	bool selectDirectories();
+	void addGame();
 
 public:
 	DownloadDialog(uint32 storageId);
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
new file mode 100644
index 0000000..28f71d7
--- /dev/null
+++ b/gui/editgamedialog.cpp
@@ -0,0 +1,549 @@
+/* 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/editgamedialog.h"
+
+#include "common/config-manager.h"
+#include "common/gui_options.h"
+#include "common/translation.h"
+
+#include "gui/browser.h"
+#include "gui/message.h"
+#ifdef ENABLE_EVENTRECORDER
+#include "gui/onscreendialog.h"
+#include "gui/recorderdialog.h"
+#include "gui/EventRecorder.h"
+#endif
+#include "gui/widgets/edittext.h"
+#include "gui/widgets/tab.h"
+#include "gui/widgets/popup.h"
+
+#ifdef USE_LIBCURL
+#include "backends/cloud/cloudmanager.h"
+#endif
+
+using Common::ConfigManager;
+
+namespace GUI {
+
+enum {
+	kStartCmd = 'STRT',
+	kAboutCmd = 'ABOU',
+	kOptionsCmd = 'OPTN',
+	kAddGameCmd = 'ADDG',
+	kEditGameCmd = 'EDTG',
+	kRemoveGameCmd = 'REMG',
+	kLoadGameCmd = 'LOAD',
+	kQuitCmd = 'QUIT',
+	kSearchCmd = 'SRCH',
+	kListSearchCmd = 'LSSR',
+	kSearchClearCmd = 'SRCL',
+
+	kCmdGlobalGraphicsOverride = 'OGFX',
+	kCmdGlobalAudioOverride = 'OSFX',
+	kCmdGlobalMIDIOverride = 'OMID',
+	kCmdGlobalMT32Override = 'OM32',
+	kCmdGlobalVolumeOverride = 'OVOL',
+
+	kCmdChooseSoundFontCmd = 'chsf',
+
+	kCmdExtraBrowser = 'PEXT',
+	kCmdExtraPathClear = 'PEXC',
+	kCmdGameBrowser = 'PGME',
+	kCmdSaveBrowser = 'PSAV',
+	kCmdSavePathClear = 'PSAC'
+};
+
+/*
+* TODO: Clean up this ugly design: we subclass EditTextWidget to perform
+* input validation. It would be much more elegant to use a decorator pattern,
+* or a validation callback, or something like that.
+*/
+class DomainEditTextWidget : public EditTextWidget {
+public:
+	DomainEditTextWidget(GuiObject *boss, const String &name, const String &text, const char *tooltip = 0)
+		: EditTextWidget(boss, name, text, tooltip) {}
+
+protected:
+	bool tryInsertChar(byte c, int pos) {
+		if (Common::isAlnum(c) || c == '-' || c == '_') {
+			_editString.insertChar(c, pos);
+			return true;
+		}
+		return false;
+	}
+};
+
+EditGameDialog::EditGameDialog(const String &domain, const String &desc)
+	: OptionsDialog(domain, "GameOptions") {
+	// Retrieve all game specific options.
+	const EnginePlugin *plugin = 0;
+	// To allow for game domains without a gameid.
+	// TODO: Is it intentional that this is still supported?
+	String gameId(ConfMan.get("gameid", domain));
+	if (gameId.empty())
+		gameId = domain;
+	// Retrieve the plugin, since we need to access the engine's MetaEngine
+	// implementation.
+	EngineMan.findGame(gameId, &plugin);
+	if (plugin) {
+		_engineOptions = (*plugin)->getExtraGuiOptions(domain);
+	} else {
+		warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str());
+	}
+
+	// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
+	String gamePath(ConfMan.get("path", _domain));
+	String extraPath(ConfMan.get("extrapath", _domain));
+	String savePath(ConfMan.get("savepath", _domain));
+
+	// GAME: Determine the description string
+	String description(ConfMan.get("description", domain));
+	if (description.empty() && !desc.empty()) {
+		description = desc;
+	}
+
+	// GUI:  Add tab widget
+	TabWidget *tab = new TabWidget(this, "GameOptions.TabWidget");
+
+	//
+	// 1) The game tab
+	//
+	tab->addTab(_("Game"));
+
+	// GUI:  Label & edit widget for the game ID
+	if (g_system->getOverlayWidth() > 320)
+		new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to saved games and running the game from the command line"));
+	else
+		new StaticTextWidget(tab, "GameOptions_Game.Id", _c("ID:", "lowres"), _("Short game identifier used for referring to saved games and running the game from the command line"));
+	_domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain, _("Short game identifier used for referring to saved games and running the game from the command line"));
+
+	// GUI:  Label & edit widget for the description
+	if (g_system->getOverlayWidth() > 320)
+		new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game"));
+	else
+		new StaticTextWidget(tab, "GameOptions_Game.Name", _c("Name:", "lowres"), _("Full title of the game"));
+	_descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description, _("Full title of the game"));
+
+	// Language popup
+	_langPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.LangPopupDesc", _("Language:"), _("Language of the game. This will not turn your Spanish game version into English"));
+	_langPopUp = new PopUpWidget(tab, "GameOptions_Game.LangPopup", _("Language of the game. This will not turn your Spanish game version into English"));
+	_langPopUp->appendEntry(_("<default>"), (uint32)Common::UNK_LANG);
+	_langPopUp->appendEntry("", (uint32)Common::UNK_LANG);
+	const Common::LanguageDescription *l = Common::g_languages;
+	for (; l->code; ++l) {
+		if (checkGameGUIOptionLanguage(l->id, _guioptionsString))
+			_langPopUp->appendEntry(l->description, l->id);
+	}
+
+	// Platform popup
+	if (g_system->getOverlayWidth() > 320)
+		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _("Platform:"), _("Platform the game was originally designed for"));
+	else
+		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _c("Platform:", "lowres"), _("Platform the game was originally designed for"));
+	_platformPopUp = new PopUpWidget(tab, "GameOptions_Game.PlatformPopup", _("Platform the game was originally designed for"));
+	_platformPopUp->appendEntry(_("<default>"));
+	_platformPopUp->appendEntry("");
+	const Common::PlatformDescription *p = Common::g_platforms;
+	for (; p->code; ++p) {
+		_platformPopUp->appendEntry(p->description, p->id);
+	}
+
+	//
+	// 2) The engine tab (shown only if there are custom engine options)
+	//
+	if (_engineOptions.size() > 0) {
+		tab->addTab(_("Engine"));
+
+		addEngineControls(tab, "GameOptions_Engine.", _engineOptions);
+	}
+
+	//
+	// 3) The graphics tab
+	//
+	_graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"));
+
+	if (g_system->getOverlayWidth() > 320)
+		_globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", _("Override global graphic settings"), 0, kCmdGlobalGraphicsOverride);
+	else
+		_globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", _c("Override global graphic settings", "lowres"), 0, kCmdGlobalGraphicsOverride);
+
+	addGraphicControls(tab, "GameOptions_Graphics.");
+
+	//
+	// 4) The audio tab
+	//
+	tab->addTab(_("Audio"));
+
+	if (g_system->getOverlayWidth() > 320)
+		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _("Override global audio settings"), 0, kCmdGlobalAudioOverride);
+	else
+		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _c("Override global audio settings", "lowres"), 0, kCmdGlobalAudioOverride);
+
+	addAudioControls(tab, "GameOptions_Audio.");
+	addSubtitleControls(tab, "GameOptions_Audio.");
+
+	//
+	// 5) The volume tab
+	//
+	if (g_system->getOverlayWidth() > 320)
+		tab->addTab(_("Volume"));
+	else
+		tab->addTab(_c("Volume", "lowres"));
+
+	if (g_system->getOverlayWidth() > 320)
+		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _("Override global volume settings"), 0, kCmdGlobalVolumeOverride);
+	else
+		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _c("Override global volume settings", "lowres"), 0, kCmdGlobalVolumeOverride);
+
+	addVolumeControls(tab, "GameOptions_Volume.");
+
+	//
+	// 6) The MIDI tab
+	//
+	_globalMIDIOverride = NULL;
+	if (!_guioptions.contains(GUIO_NOMIDI)) {
+		tab->addTab(_("MIDI"));
+
+		if (g_system->getOverlayWidth() > 320)
+			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _("Override global MIDI settings"), 0, kCmdGlobalMIDIOverride);
+		else
+			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _c("Override global MIDI settings", "lowres"), 0, kCmdGlobalMIDIOverride);
+
+		addMIDIControls(tab, "GameOptions_MIDI.");
+	}
+
+	//
+	// 7) The MT-32 tab
+	//
+	_globalMT32Override = NULL;
+	if (!_guioptions.contains(GUIO_NOMIDI)) {
+		tab->addTab(_("MT-32"));
+
+		if (g_system->getOverlayWidth() > 320)
+			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _("Override global MT-32 settings"), 0, kCmdGlobalMT32Override);
+		else
+			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _c("Override global MT-32 settings", "lowres"), 0, kCmdGlobalMT32Override);
+
+		addMT32Controls(tab, "GameOptions_MT32.");
+	}
+
+	//
+	// 8) The Paths tab
+	//
+	if (g_system->getOverlayWidth() > 320)
+		tab->addTab(_("Paths"));
+	else
+		tab->addTab(_c("Paths", "lowres"));
+
+	// These buttons have to be extra wide, or the text will be truncated
+	// in the small version of the GUI.
+
+	// GUI:  Button + Label for the game path
+	if (g_system->getOverlayWidth() > 320)
+		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), 0, kCmdGameBrowser);
+	else
+		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _c("Game Path:", "lowres"), 0, kCmdGameBrowser);
+	_gamePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.GamepathText", gamePath);
+
+	// GUI:  Button + Label for the additional path
+	if (g_system->getOverlayWidth() > 320)
+		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
+	else
+		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _c("Extra Path:", "lowres"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
+	_extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath, _("Specifies path to additional data used by the game"));
+
+	_extraPathClearButton = addClearButton(tab, "GameOptions_Paths.ExtraPathClearButton", kCmdExtraPathClear);
+
+	// GUI:  Button + Label for the save path
+	if (g_system->getOverlayWidth() > 320)
+		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _("Save Path:"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
+	else
+		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _c("Save Path:", "lowres"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
+	_savePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.SavepathText", savePath, _("Specifies where your saved games are put"));
+
+	_savePathClearButton = addClearButton(tab, "GameOptions_Paths.SavePathClearButton", kCmdSavePathClear);
+
+	// Activate the first tab
+	tab->setActiveTab(0);
+	_tabWidget = tab;
+
+	// Add OK & Cancel buttons
+	new ButtonWidget(this, "GameOptions.Cancel", _("Cancel"), 0, kCloseCmd);
+	new ButtonWidget(this, "GameOptions.Ok", _("OK"), 0, kOKCmd);
+}
+
+void EditGameDialog::open() {
+	OptionsDialog::open();
+
+	String extraPath(ConfMan.get("extrapath", _domain));
+	if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) {
+		_extraPathWidget->setLabel(_c("None", "path"));
+	}
+
+	String savePath(ConfMan.get("savepath", _domain));
+	if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) {
+		_savePathWidget->setLabel(_("Default"));
+	}
+
+	int sel, i;
+	bool e;
+
+	// En-/disable dialog items depending on whether overrides are active or not.
+
+	e = ConfMan.hasKey("gfx_mode", _domain) ||
+		ConfMan.hasKey("render_mode", _domain) ||
+		ConfMan.hasKey("fullscreen", _domain) ||
+		ConfMan.hasKey("aspect_ratio", _domain);
+	_globalGraphicsOverride->setState(e);
+
+	e = ConfMan.hasKey("music_driver", _domain) ||
+		ConfMan.hasKey("output_rate", _domain) ||
+		ConfMan.hasKey("opl_driver", _domain) ||
+		ConfMan.hasKey("subtitles", _domain) ||
+		ConfMan.hasKey("talkspeed", _domain);
+	_globalAudioOverride->setState(e);
+
+	e = ConfMan.hasKey("music_volume", _domain) ||
+		ConfMan.hasKey("sfx_volume", _domain) ||
+		ConfMan.hasKey("speech_volume", _domain);
+	_globalVolumeOverride->setState(e);
+
+	if (!_guioptions.contains(GUIO_NOMIDI)) {
+		e = ConfMan.hasKey("soundfont", _domain) ||
+			ConfMan.hasKey("multi_midi", _domain) ||
+			ConfMan.hasKey("midi_gain", _domain);
+		_globalMIDIOverride->setState(e);
+	}
+
+	if (!_guioptions.contains(GUIO_NOMIDI)) {
+		e = ConfMan.hasKey("native_mt32", _domain) ||
+			ConfMan.hasKey("enable_gs", _domain);
+		_globalMT32Override->setState(e);
+	}
+
+	// TODO: game path
+
+	const Common::Language lang = Common::parseLanguage(ConfMan.get("language", _domain));
+
+	if (ConfMan.hasKey("language", _domain)) {
+		_langPopUp->setSelectedTag(lang);
+	} else {
+		_langPopUp->setSelectedTag((uint32)Common::UNK_LANG);
+	}
+
+	if (_langPopUp->numEntries() <= 3) { // If only one language is avaliable
+		_langPopUpDesc->setEnabled(false);
+		_langPopUp->setEnabled(false);
+	}
+
+	// Set the state of engine-specific checkboxes
+	for (uint j = 0; j < _engineOptions.size(); ++j) {
+		// The default values for engine-specific checkboxes are not set when
+		// ScummVM starts, as this would require us to load and poll all of the
+		// engine plugins on startup. Thus, we set the state of each custom
+		// option checkbox to what is specified by the engine plugin, and
+		// update it only if a value has been set in the configuration of the
+		// currently selected game.
+		bool isChecked = _engineOptions[j].defaultState;
+		if (ConfMan.hasKey(_engineOptions[j].configOption, _domain))
+			isChecked = ConfMan.getBool(_engineOptions[j].configOption, _domain);
+		_engineCheckboxes[j]->setState(isChecked);
+	}
+
+	const Common::PlatformDescription *p = Common::g_platforms;
+	const Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", _domain));
+	sel = 0;
+	for (i = 0; p->code; ++p, ++i) {
+		if (platform == p->id)
+			sel = i + 2;
+	}
+	_platformPopUp->setSelected(sel);
+}
+
+
+void EditGameDialog::close() {
+	if (getResult()) {
+		ConfMan.set("description", _descriptionWidget->getEditString(), _domain);
+
+		Common::Language lang = (Common::Language)_langPopUp->getSelectedTag();
+		if (lang < 0)
+			ConfMan.removeKey("language", _domain);
+		else
+			ConfMan.set("language", Common::getLanguageCode(lang), _domain);
+
+		String gamePath(_gamePathWidget->getLabel());
+		if (!gamePath.empty())
+			ConfMan.set("path", gamePath, _domain);
+
+		String extraPath(_extraPathWidget->getLabel());
+		if (!extraPath.empty() && (extraPath != _c("None", "path")))
+			ConfMan.set("extrapath", extraPath, _domain);
+		else
+			ConfMan.removeKey("extrapath", _domain);
+
+		String savePath(_savePathWidget->getLabel());
+		if (!savePath.empty() && (savePath != _("Default")))
+			ConfMan.set("savepath", savePath, _domain);
+		else
+			ConfMan.removeKey("savepath", _domain);
+
+		Common::Platform platform = (Common::Platform)_platformPopUp->getSelectedTag();
+		if (platform < 0)
+			ConfMan.removeKey("platform", _domain);
+		else
+			ConfMan.set("platform", Common::getPlatformCode(platform), _domain);
+
+		// Set the state of engine-specific checkboxes
+		for (uint i = 0; i < _engineOptions.size(); i++) {
+			ConfMan.setBool(_engineOptions[i].configOption, _engineCheckboxes[i]->getState(), _domain);
+		}
+	}
+	OptionsDialog::close();
+}
+
+void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kCmdGlobalGraphicsOverride:
+		setGraphicSettingsState(data != 0);
+		draw();
+		break;
+	case kCmdGlobalAudioOverride:
+		setAudioSettingsState(data != 0);
+		setSubtitleSettingsState(data != 0);
+		if (_globalVolumeOverride == NULL)
+			setVolumeSettingsState(data != 0);
+		draw();
+		break;
+	case kCmdGlobalMIDIOverride:
+		setMIDISettingsState(data != 0);
+		draw();
+		break;
+	case kCmdGlobalMT32Override:
+		setMT32SettingsState(data != 0);
+		draw();
+		break;
+	case kCmdGlobalVolumeOverride:
+		setVolumeSettingsState(data != 0);
+		draw();
+		break;
+	case kCmdChooseSoundFontCmd:
+	{
+		BrowserDialog browser(_("Select SoundFont"), false);
+
+		if (browser.runModal() > 0) {
+			// User made this choice...
+			Common::FSNode file(browser.getResult());
+			_soundFont->setLabel(file.getPath());
+
+			if (!file.getPath().empty() && (file.getPath() != _c("None", "path")))
+				_soundFontClearButton->setEnabled(true);
+			else
+				_soundFontClearButton->setEnabled(false);
+
+			draw();
+		}
+		break;
+	}
+
+	// Change path for the game
+	case kCmdGameBrowser:
+	{
+		BrowserDialog browser(_("Select directory with game data"), true);
+		if (browser.runModal() > 0) {
+			// User made his choice...
+			Common::FSNode dir(browser.getResult());
+
+			// TODO: Verify the game can be found in the new directory... Best
+			// done with optional specific gameid to pluginmgr detectgames?
+			// FSList files = dir.listDir(FSNode::kListFilesOnly);
+
+			_gamePathWidget->setLabel(dir.getPath());
+			draw();
+		}
+		draw();
+		break;
+	}
+
+	// Change path for extra game data (eg, using sword cutscenes when playing via CD)
+	case kCmdExtraBrowser:
+	{
+		BrowserDialog browser(_("Select additional game directory"), true);
+		if (browser.runModal() > 0) {
+			// User made his choice...
+			Common::FSNode dir(browser.getResult());
+			_extraPathWidget->setLabel(dir.getPath());
+			draw();
+		}
+		draw();
+		break;
+	}
+	// Change path for stored save game (perm and temp) data
+	case kCmdSaveBrowser:
+	{
+		BrowserDialog browser(_("Select directory for saved games"), true);
+		if (browser.runModal() > 0) {
+			// User made his choice...
+			Common::FSNode dir(browser.getResult());
+			_savePathWidget->setLabel(dir.getPath());
+#ifdef USE_LIBCURL
+			MessageDialog warningMessage(_("Saves sync feature doesn't work with non-default directories. If you want your saves to sync, use default directory."));
+			warningMessage.runModal();
+#endif
+			draw();
+		}
+		draw();
+		break;
+	}
+
+	case kCmdExtraPathClear:
+		_extraPathWidget->setLabel(_c("None", "path"));
+		break;
+
+	case kCmdSavePathClear:
+		_savePathWidget->setLabel(_("Default"));
+		break;
+
+	case kOKCmd:
+	{
+		// Write back changes made to config object
+		String newDomain(_domainWidget->getEditString());
+		if (newDomain != _domain) {
+			if (newDomain.empty()
+				|| newDomain.hasPrefix("_")
+				|| newDomain == ConfigManager::kApplicationDomain
+				|| ConfMan.hasGameDomain(newDomain)) {
+				MessageDialog alert(_("This game ID is already taken. Please choose another one."));
+				alert.runModal();
+				return;
+			}
+			ConfMan.renameGameDomain(_domain, newDomain);
+			_domain = newDomain;
+		}
+	}
+	// FALL THROUGH to default case
+	default:
+		OptionsDialog::handleCommand(sender, cmd, data);
+	}
+}
+
+} // End of namespace GUI
diff --git a/gui/editgamedialog.h b/gui/editgamedialog.h
new file mode 100644
index 0000000..0be6c16
--- /dev/null
+++ b/gui/editgamedialog.h
@@ -0,0 +1,97 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_EDITGAMEDIALOG_H
+#define GUI_EDITGAMEDIALOG_H
+
+#include "engines/game.h"
+#include "gui/dialog.h"
+#include "gui/options.h"
+
+namespace GUI {
+
+class BrowserDialog;
+class CommandSender;
+class DomainEditTextWidget;
+class ListWidget;
+class ButtonWidget;
+class PicButtonWidget;
+class GraphicsWidget;
+class StaticTextWidget;
+class EditTextWidget;
+class SaveLoadChooser;
+
+Common::String addGameToConf(const GameDescriptor &result);
+
+/*
+* A dialog that allows the user to edit a config game entry.
+* TODO: add widgets for some/all of the following
+* - Maybe scaler/graphics mode. But there are two problems:
+*   1) Different backends can have different scalers with different names,
+*      so we first have to add a way to query those... no Ender, I don't
+*      think a bitmasked property() value is nice for this,  because we would
+*      have to add to the bitmask values whenever a backends adds a new scaler).
+*   2) At the time the launcher is running, the GFX backend is already setup.
+*      So when a game is run via the launcher, the custom scaler setting for it won't be
+*      used. So we'd also have to add an API to change the scaler during runtime
+*      (the SDL backend can already do that based on user input, but there is no API
+*      to achieve it)
+*   If the APIs for 1&2 are in place, we can think about adding this to the Edit&Option dialogs
+*/
+
+class EditGameDialog : public OptionsDialog {
+	typedef Common::String String;
+	typedef Common::Array<Common::String> StringArray;
+public:
+	EditGameDialog(const String &domain, const String &desc);
+
+	void open();
+	void close();
+	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+
+protected:
+	EditTextWidget *_descriptionWidget;
+	DomainEditTextWidget *_domainWidget;
+
+	StaticTextWidget *_gamePathWidget;
+	StaticTextWidget *_extraPathWidget;
+	StaticTextWidget *_savePathWidget;
+	ButtonWidget *_extraPathClearButton;
+	ButtonWidget *_savePathClearButton;
+
+	StaticTextWidget *_langPopUpDesc;
+	PopUpWidget *_langPopUp;
+	StaticTextWidget *_platformPopUpDesc;
+	PopUpWidget *_platformPopUp;
+
+	CheckboxWidget *_globalGraphicsOverride;
+	CheckboxWidget *_globalAudioOverride;
+	CheckboxWidget *_globalMIDIOverride;
+	CheckboxWidget *_globalMT32Override;
+	CheckboxWidget *_globalVolumeOverride;
+
+	ExtraGuiOptions _engineOptions;
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 309c747..f2474a5 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -33,6 +33,7 @@
 #include "gui/about.h"
 #include "gui/browser.h"
 #include "gui/chooser.h"
+#include "gui/editgamedialog.h"
 #include "gui/launcher.h"
 #include "gui/massadd.h"
 #include "gui/message.h"
@@ -87,525 +88,6 @@ enum {
 	kCmdSavePathClear = 'PSAC'
 };
 
-/*
- * TODO: Clean up this ugly design: we subclass EditTextWidget to perform
- * input validation. It would be much more elegant to use a decorator pattern,
- * or a validation callback, or something like that.
- */
-class DomainEditTextWidget : public EditTextWidget {
-public:
-	DomainEditTextWidget(GuiObject *boss, const String &name, const String &text, const char *tooltip = 0)
-		: EditTextWidget(boss, name, text, tooltip) {
-	}
-
-protected:
-	bool tryInsertChar(byte c, int pos) {
-		if (Common::isAlnum(c) || c == '-' || c == '_') {
-			_editString.insertChar(c, pos);
-			return true;
-		}
-		return false;
-	}
-};
-
-/*
- * A dialog that allows the user to edit a config game entry.
- * TODO: add widgets for some/all of the following
- * - Maybe scaler/graphics mode. But there are two problems:
- *   1) Different backends can have different scalers with different names,
- *      so we first have to add a way to query those... no Ender, I don't
- *      think a bitmasked property() value is nice for this,  because we would
- *      have to add to the bitmask values whenever a backends adds a new scaler).
- *   2) At the time the launcher is running, the GFX backend is already setup.
- *      So when a game is run via the launcher, the custom scaler setting for it won't be
- *      used. So we'd also have to add an API to change the scaler during runtime
- *      (the SDL backend can already do that based on user input, but there is no API
- *      to achieve it)
- *   If the APIs for 1&2 are in place, we can think about adding this to the Edit&Option dialogs
- */
-
-class EditGameDialog : public OptionsDialog {
-	typedef Common::String String;
-	typedef Common::Array<Common::String> StringArray;
-public:
-	EditGameDialog(const String &domain, const String &desc);
-
-	void open();
-	void close();
-	virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
-
-protected:
-	EditTextWidget *_descriptionWidget;
-	DomainEditTextWidget *_domainWidget;
-
-	StaticTextWidget *_gamePathWidget;
-	StaticTextWidget *_extraPathWidget;
-	StaticTextWidget *_savePathWidget;
-	ButtonWidget *_extraPathClearButton;
-	ButtonWidget *_savePathClearButton;
-
-	StaticTextWidget *_langPopUpDesc;
-	PopUpWidget *_langPopUp;
-	StaticTextWidget *_platformPopUpDesc;
-	PopUpWidget *_platformPopUp;
-
-	CheckboxWidget *_globalGraphicsOverride;
-	CheckboxWidget *_globalAudioOverride;
-	CheckboxWidget *_globalMIDIOverride;
-	CheckboxWidget *_globalMT32Override;
-	CheckboxWidget *_globalVolumeOverride;
-
-	ExtraGuiOptions _engineOptions;
-};
-
-EditGameDialog::EditGameDialog(const String &domain, const String &desc)
-	: OptionsDialog(domain, "GameOptions") {
-	// Retrieve all game specific options.
-	const EnginePlugin *plugin = 0;
-	// To allow for game domains without a gameid.
-	// TODO: Is it intentional that this is still supported?
-	String gameId(ConfMan.get("gameid", domain));
-	if (gameId.empty())
-		gameId = domain;
-	// Retrieve the plugin, since we need to access the engine's MetaEngine
-	// implementation.
-	EngineMan.findGame(gameId, &plugin);
-	if (plugin) {
-		_engineOptions = (*plugin)->getExtraGuiOptions(domain);
-	} else {
-		warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str());
-	}
-
-	// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
-	String gamePath(ConfMan.get("path", _domain));
-	String extraPath(ConfMan.get("extrapath", _domain));
-	String savePath(ConfMan.get("savepath", _domain));
-
-	// GAME: Determine the description string
-	String description(ConfMan.get("description", domain));
-	if (description.empty() && !desc.empty()) {
-		description = desc;
-	}
-
-	// GUI:  Add tab widget
-	TabWidget *tab = new TabWidget(this, "GameOptions.TabWidget");
-
-	//
-	// 1) The game tab
-	//
-	tab->addTab(_("Game"));
-
-	// GUI:  Label & edit widget for the game ID
-	if (g_system->getOverlayWidth() > 320)
-		new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to saved games and running the game from the command line"));
-	else
-		new StaticTextWidget(tab, "GameOptions_Game.Id", _c("ID:", "lowres"), _("Short game identifier used for referring to saved games and running the game from the command line"));
-	_domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain, _("Short game identifier used for referring to saved games and running the game from the command line"));
-
-	// GUI:  Label & edit widget for the description
-	if (g_system->getOverlayWidth() > 320)
-		new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game"));
-	else
-		new StaticTextWidget(tab, "GameOptions_Game.Name", _c("Name:", "lowres"), _("Full title of the game"));
-	_descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description, _("Full title of the game"));
-
-	// Language popup
-	_langPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.LangPopupDesc", _("Language:"), _("Language of the game. This will not turn your Spanish game version into English"));
-	_langPopUp = new PopUpWidget(tab, "GameOptions_Game.LangPopup", _("Language of the game. This will not turn your Spanish game version into English"));
-	_langPopUp->appendEntry(_("<default>"), (uint32)Common::UNK_LANG);
-	_langPopUp->appendEntry("", (uint32)Common::UNK_LANG);
-	const Common::LanguageDescription *l = Common::g_languages;
-	for (; l->code; ++l) {
-		if (checkGameGUIOptionLanguage(l->id, _guioptionsString))
-			_langPopUp->appendEntry(l->description, l->id);
-	}
-
-	// Platform popup
-	if (g_system->getOverlayWidth() > 320)
-		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _("Platform:"), _("Platform the game was originally designed for"));
-	else
-		_platformPopUpDesc = new StaticTextWidget(tab, "GameOptions_Game.PlatformPopupDesc", _c("Platform:", "lowres"), _("Platform the game was originally designed for"));
-	_platformPopUp = new PopUpWidget(tab, "GameOptions_Game.PlatformPopup", _("Platform the game was originally designed for"));
-	_platformPopUp->appendEntry(_("<default>"));
-	_platformPopUp->appendEntry("");
-	const Common::PlatformDescription *p = Common::g_platforms;
-	for (; p->code; ++p) {
-		_platformPopUp->appendEntry(p->description, p->id);
-	}
-
-	//
-	// 2) The engine tab (shown only if there are custom engine options)
-	//
-	if (_engineOptions.size() > 0) {
-		tab->addTab(_("Engine"));
-
-		addEngineControls(tab, "GameOptions_Engine.", _engineOptions);
-	}
-
-	//
-	// 3) The graphics tab
-	//
-	_graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"));
-
-	if (g_system->getOverlayWidth() > 320)
-		_globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", _("Override global graphic settings"), 0, kCmdGlobalGraphicsOverride);
-	else
-		_globalGraphicsOverride = new CheckboxWidget(tab, "GameOptions_Graphics.EnableTabCheckbox", _c("Override global graphic settings", "lowres"), 0, kCmdGlobalGraphicsOverride);
-
-	addGraphicControls(tab, "GameOptions_Graphics.");
-
-	//
-	// 4) The audio tab
-	//
-	tab->addTab(_("Audio"));
-
-	if (g_system->getOverlayWidth() > 320)
-		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _("Override global audio settings"), 0, kCmdGlobalAudioOverride);
-	else
-		_globalAudioOverride = new CheckboxWidget(tab, "GameOptions_Audio.EnableTabCheckbox", _c("Override global audio settings", "lowres"), 0, kCmdGlobalAudioOverride);
-
-	addAudioControls(tab, "GameOptions_Audio.");
-	addSubtitleControls(tab, "GameOptions_Audio.");
-
-	//
-	// 5) The volume tab
-	//
-	if (g_system->getOverlayWidth() > 320)
-		tab->addTab(_("Volume"));
-	else
-		tab->addTab(_c("Volume", "lowres"));
-
-	if (g_system->getOverlayWidth() > 320)
-		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _("Override global volume settings"), 0, kCmdGlobalVolumeOverride);
-	else
-		_globalVolumeOverride = new CheckboxWidget(tab, "GameOptions_Volume.EnableTabCheckbox", _c("Override global volume settings", "lowres"), 0, kCmdGlobalVolumeOverride);
-
-	addVolumeControls(tab, "GameOptions_Volume.");
-
-	//
-	// 6) The MIDI tab
-	//
-	_globalMIDIOverride = NULL;
-	if (!_guioptions.contains(GUIO_NOMIDI)) {
-		tab->addTab(_("MIDI"));
-
-		if (g_system->getOverlayWidth() > 320)
-			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _("Override global MIDI settings"), 0, kCmdGlobalMIDIOverride);
-		else
-			_globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", _c("Override global MIDI settings", "lowres"), 0, kCmdGlobalMIDIOverride);
-
-		addMIDIControls(tab, "GameOptions_MIDI.");
-	}
-
-	//
-	// 7) The MT-32 tab
-	//
-	_globalMT32Override = NULL;
-	if (!_guioptions.contains(GUIO_NOMIDI)) {
-		tab->addTab(_("MT-32"));
-
-		if (g_system->getOverlayWidth() > 320)
-			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _("Override global MT-32 settings"), 0, kCmdGlobalMT32Override);
-		else
-			_globalMT32Override = new CheckboxWidget(tab, "GameOptions_MT32.EnableTabCheckbox", _c("Override global MT-32 settings", "lowres"), 0, kCmdGlobalMT32Override);
-
-		addMT32Controls(tab, "GameOptions_MT32.");
-	}
-
-	//
-	// 8) The Paths tab
-	//
-	if (g_system->getOverlayWidth() > 320)
-		tab->addTab(_("Paths"));
-	else
-		tab->addTab(_c("Paths", "lowres"));
-
-	// These buttons have to be extra wide, or the text will be truncated
-	// in the small version of the GUI.
-
-	// GUI:  Button + Label for the game path
-	if (g_system->getOverlayWidth() > 320)
-		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), 0, kCmdGameBrowser);
-	else
-		new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _c("Game Path:", "lowres"), 0, kCmdGameBrowser);
-	_gamePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.GamepathText", gamePath);
-
-	// GUI:  Button + Label for the additional path
-	if (g_system->getOverlayWidth() > 320)
-		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
-	else
-		new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _c("Extra Path:", "lowres"), _("Specifies path to additional data used by the game"), kCmdExtraBrowser);
-	_extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath, _("Specifies path to additional data used by the game"));
-
-	_extraPathClearButton = addClearButton(tab, "GameOptions_Paths.ExtraPathClearButton", kCmdExtraPathClear);
-
-	// GUI:  Button + Label for the save path
-	if (g_system->getOverlayWidth() > 320)
-		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _("Save Path:"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
-	else
-		new ButtonWidget(tab, "GameOptions_Paths.Savepath", _c("Save Path:", "lowres"), _("Specifies where your saved games are put"), kCmdSaveBrowser);
-	_savePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.SavepathText", savePath, _("Specifies where your saved games are put"));
-
-	_savePathClearButton = addClearButton(tab, "GameOptions_Paths.SavePathClearButton", kCmdSavePathClear);
-
-	// Activate the first tab
-	tab->setActiveTab(0);
-	_tabWidget = tab;
-
-	// Add OK & Cancel buttons
-	new ButtonWidget(this, "GameOptions.Cancel", _("Cancel"), 0, kCloseCmd);
-	new ButtonWidget(this, "GameOptions.Ok", _("OK"), 0, kOKCmd);
-}
-
-void EditGameDialog::open() {
-	OptionsDialog::open();
-
-	String extraPath(ConfMan.get("extrapath", _domain));
-	if (extraPath.empty() || !ConfMan.hasKey("extrapath", _domain)) {
-		_extraPathWidget->setLabel(_c("None", "path"));
-	}
-
-	String savePath(ConfMan.get("savepath", _domain));
-	if (savePath.empty() || !ConfMan.hasKey("savepath", _domain)) {
-		_savePathWidget->setLabel(_("Default"));
-	}
-
-	int sel, i;
-	bool e;
-
-	// En-/disable dialog items depending on whether overrides are active or not.
-
-	e = ConfMan.hasKey("gfx_mode", _domain) ||
-		ConfMan.hasKey("render_mode", _domain) ||
-		ConfMan.hasKey("fullscreen", _domain) ||
-		ConfMan.hasKey("aspect_ratio", _domain);
-	_globalGraphicsOverride->setState(e);
-
-	e = ConfMan.hasKey("music_driver", _domain) ||
-		ConfMan.hasKey("output_rate", _domain) ||
-		ConfMan.hasKey("opl_driver", _domain) ||
-		ConfMan.hasKey("subtitles", _domain) ||
-		ConfMan.hasKey("talkspeed", _domain);
-	_globalAudioOverride->setState(e);
-
-	e = ConfMan.hasKey("music_volume", _domain) ||
-		ConfMan.hasKey("sfx_volume", _domain) ||
-		ConfMan.hasKey("speech_volume", _domain);
-	_globalVolumeOverride->setState(e);
-
-	if (!_guioptions.contains(GUIO_NOMIDI)) {
-		e = ConfMan.hasKey("soundfont", _domain) ||
-			ConfMan.hasKey("multi_midi", _domain) ||
-			ConfMan.hasKey("midi_gain", _domain);
-		_globalMIDIOverride->setState(e);
-	}
-
-	if (!_guioptions.contains(GUIO_NOMIDI)) {
-		e = ConfMan.hasKey("native_mt32", _domain) ||
-			ConfMan.hasKey("enable_gs", _domain);
-		_globalMT32Override->setState(e);
-	}
-
-	// TODO: game path
-
-	const Common::Language lang = Common::parseLanguage(ConfMan.get("language", _domain));
-
-	if (ConfMan.hasKey("language", _domain)) {
-		_langPopUp->setSelectedTag(lang);
-	} else {
-		_langPopUp->setSelectedTag((uint32)Common::UNK_LANG);
-	}
-
-	if (_langPopUp->numEntries() <= 3) { // If only one language is avaliable
-		_langPopUpDesc->setEnabled(false);
-		_langPopUp->setEnabled(false);
-	}
-
-	// Set the state of engine-specific checkboxes
-	for (uint j = 0; j < _engineOptions.size(); ++j) {
-		// The default values for engine-specific checkboxes are not set when
-		// ScummVM starts, as this would require us to load and poll all of the
-		// engine plugins on startup. Thus, we set the state of each custom
-		// option checkbox to what is specified by the engine plugin, and
-		// update it only if a value has been set in the configuration of the
-		// currently selected game.
-		bool isChecked = _engineOptions[j].defaultState;
-		if (ConfMan.hasKey(_engineOptions[j].configOption, _domain))
-			isChecked = ConfMan.getBool(_engineOptions[j].configOption, _domain);
-		_engineCheckboxes[j]->setState(isChecked);
-	}
-
-	const Common::PlatformDescription *p = Common::g_platforms;
-	const Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", _domain));
-	sel = 0;
-	for (i = 0; p->code; ++p, ++i) {
-		if (platform == p->id)
-			sel = i + 2;
-	}
-	_platformPopUp->setSelected(sel);
-}
-
-
-void EditGameDialog::close() {
-	if (getResult()) {
-		ConfMan.set("description", _descriptionWidget->getEditString(), _domain);
-
-		Common::Language lang = (Common::Language)_langPopUp->getSelectedTag();
-		if (lang < 0)
-			ConfMan.removeKey("language", _domain);
-		else
-			ConfMan.set("language", Common::getLanguageCode(lang), _domain);
-
-		String gamePath(_gamePathWidget->getLabel());
-		if (!gamePath.empty())
-			ConfMan.set("path", gamePath, _domain);
-
-		String extraPath(_extraPathWidget->getLabel());
-		if (!extraPath.empty() && (extraPath != _c("None", "path")))
-			ConfMan.set("extrapath", extraPath, _domain);
-		else
-			ConfMan.removeKey("extrapath", _domain);
-
-		String savePath(_savePathWidget->getLabel());
-		if (!savePath.empty() && (savePath != _("Default")))
-			ConfMan.set("savepath", savePath, _domain);
-		else
-			ConfMan.removeKey("savepath", _domain);
-
-		Common::Platform platform = (Common::Platform)_platformPopUp->getSelectedTag();
-		if (platform < 0)
-			ConfMan.removeKey("platform", _domain);
-		else
-			ConfMan.set("platform", Common::getPlatformCode(platform), _domain);
-
-		// Set the state of engine-specific checkboxes
-		for (uint i = 0; i < _engineOptions.size(); i++) {
-			ConfMan.setBool(_engineOptions[i].configOption, _engineCheckboxes[i]->getState(), _domain);
-		}
-	}
-	OptionsDialog::close();
-}
-
-void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
-	switch (cmd) {
-	case kCmdGlobalGraphicsOverride:
-		setGraphicSettingsState(data != 0);
-		draw();
-		break;
-	case kCmdGlobalAudioOverride:
-		setAudioSettingsState(data != 0);
-		setSubtitleSettingsState(data != 0);
-		if (_globalVolumeOverride == NULL)
-			setVolumeSettingsState(data != 0);
-		draw();
-		break;
-	case kCmdGlobalMIDIOverride:
-		setMIDISettingsState(data != 0);
-		draw();
-		break;
-	case kCmdGlobalMT32Override:
-		setMT32SettingsState(data != 0);
-		draw();
-		break;
-	case kCmdGlobalVolumeOverride:
-		setVolumeSettingsState(data != 0);
-		draw();
-		break;
-	case kCmdChooseSoundFontCmd: {
-		BrowserDialog browser(_("Select SoundFont"), false);
-
-		if (browser.runModal() > 0) {
-			// User made this choice...
-			Common::FSNode file(browser.getResult());
-			_soundFont->setLabel(file.getPath());
-
-			if (!file.getPath().empty() && (file.getPath() != _c("None", "path")))
-				_soundFontClearButton->setEnabled(true);
-			else
-				_soundFontClearButton->setEnabled(false);
-
-			draw();
-		}
-		break;
-	}
-
-	// Change path for the game
-	case kCmdGameBrowser: {
-		BrowserDialog browser(_("Select directory with game data"), true);
-		if (browser.runModal() > 0) {
-			// User made his choice...
-			Common::FSNode dir(browser.getResult());
-
-			// TODO: Verify the game can be found in the new directory... Best
-			// done with optional specific gameid to pluginmgr detectgames?
-			// FSList files = dir.listDir(FSNode::kListFilesOnly);
-
-			_gamePathWidget->setLabel(dir.getPath());
-			draw();
-		}
-		draw();
-		break;
-	}
-
-	// Change path for extra game data (eg, using sword cutscenes when playing via CD)
-	case kCmdExtraBrowser: {
-		BrowserDialog browser(_("Select additional game directory"), true);
-		if (browser.runModal() > 0) {
-			// User made his choice...
-			Common::FSNode dir(browser.getResult());
-			_extraPathWidget->setLabel(dir.getPath());
-			draw();
-		}
-		draw();
-		break;
-	}
-	// Change path for stored save game (perm and temp) data
-	case kCmdSaveBrowser: {
-		BrowserDialog browser(_("Select directory for saved games"), true);
-		if (browser.runModal() > 0) {
-			// User made his choice...
-			Common::FSNode dir(browser.getResult());
-			_savePathWidget->setLabel(dir.getPath());
-#ifdef USE_LIBCURL
-			MessageDialog warningMessage(_("Saves sync feature doesn't work with non-default directories. If you want your saves to sync, use default directory."));
-			warningMessage.runModal();
-#endif
-			draw();
-		}
-		draw();
-		break;
-	}
-
-	case kCmdExtraPathClear:
-		_extraPathWidget->setLabel(_c("None", "path"));
-		break;
-
-	case kCmdSavePathClear:
-		_savePathWidget->setLabel(_("Default"));
-		break;
-
-	case kOKCmd: {
-		// Write back changes made to config object
-		String newDomain(_domainWidget->getEditString());
-		if (newDomain != _domain) {
-			if (newDomain.empty()
-				|| newDomain.hasPrefix("_")
-				|| newDomain == ConfigManager::kApplicationDomain
-				|| ConfMan.hasGameDomain(newDomain)) {
-				MessageDialog alert(_("This game ID is already taken. Please choose another one."));
-				alert.runModal();
-				return;
-			}
-			ConfMan.renameGameDomain(_domain, newDomain);
-			_domain = newDomain;
-		}
-		}
-		// FALL THROUGH to default case
-	default:
-		OptionsDialog::handleCommand(sender, cmd, data);
-	}
-}
-
 #pragma mark -
 
 LauncherDialog::LauncherDialog()
diff --git a/gui/module.mk b/gui/module.mk
index 1fdd0d4..24becfd 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	debugger.o \
 	dialog.o \
 	downloaddialog.o \
+	editgamedialog.o \
 	error.o \
 	EventRecorder.o \
 	filebrowser-dialog.o \


Commit: ee3ce476068e487517022bdb9ee397ff2f070d86
    https://github.com/scummvm/scummvm/commit/ee3ce476068e487517022bdb9ee397ff2f070d86
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Use Container in the Cloud tab

It has a visibility issue, but we're already working on it.

Changed paths:
    gui/options.cpp
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/options.cpp b/gui/options.cpp
index bb9bf4d..a272b22 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -43,6 +43,7 @@
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
 #include "downloaddialog.h"
+#include "widgets/scrollcontainer.h"
 
 #ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
@@ -1279,14 +1280,16 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	else
 		tab->addTab(_c("Cloud", "lowres"));
 
+	ScrollContainerWidget *container = new ScrollContainerWidget(tab, "GlobalOptions_Cloud.Container");
+
 #ifdef USE_LIBCURL
 	_selectedStorageIndex = CloudMan.getStorageIndex();
 #else
 	_selectedStorageIndex = 0;
 #endif
 
-	_storagePopUpDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StoragePopupDesc", _("Storage:"), _("Active cloud storage"));
-	_storagePopUp = new PopUpWidget(tab, "GlobalOptions_Cloud.StoragePopup");
+	_storagePopUpDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StoragePopupDesc", _("Storage:"), _("Active cloud storage"));
+	_storagePopUp = new PopUpWidget(container, "GlobalOptions_Cloud_Container.StoragePopup");
 #ifdef USE_LIBCURL
 	Common::StringArray list = CloudMan.listStorages();
 	for (uint32 i = 0; i < list.size(); ++i)
@@ -1296,21 +1299,21 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 #endif
 	_storagePopUp->setSelected(_selectedStorageIndex);
 
-	_storageUsernameDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
-	_storageUsername = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsernameLabel", "<none>");
+	_storageUsernameDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageUsernameDesc", _("Username:"), _("Username used by this storage"));
+	_storageUsername = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageUsernameLabel", "<none>");
 
-	_storageUsedSpaceDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM's saves on this storage"));
-	_storageUsedSpace = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageUsedSpaceLabel", "0 bytes");
+	_storageUsedSpaceDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageUsedSpaceDesc", _("Used space:"), _("Space used by ScummVM's saves on this storage"));
+	_storageUsedSpace = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageUsedSpaceLabel", "0 bytes");
 
-	_storageLastSyncDesc = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did saves sync last time"));
-	_storageLastSync = new StaticTextWidget(tab, "GlobalOptions_Cloud.StorageLastSyncLabel", "<never>");
+	_storageLastSyncDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageLastSyncDesc", _("Last sync time:"), _("When this storage did saves sync last time"));
+	_storageLastSync = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.StorageLastSyncLabel", "<never>");
 
-	_storageConnectButton = new ButtonWidget(tab, "GlobalOptions_Cloud.ConnectButton", _("Connect"), _("Open wizard dialog to connect your cloud storage account"), kConfigureStorageCmd);
-	_storageRefreshButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RefreshButton", _("Refresh"), _("Refresh current cloud storage information (username and usage)"), kRefreshStorageCmd);
-	_storageDownloadButton = new ButtonWidget(tab, "GlobalOptions_Cloud.DownloadButton", _("Downloads"), _("Open downloads manager dialog"), kDownloadStorageCmd);
+	_storageConnectButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.ConnectButton", _("Connect"), _("Open wizard dialog to connect your cloud storage account"), kConfigureStorageCmd);
+	_storageRefreshButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RefreshButton", _("Refresh"), _("Refresh current cloud storage information (username and usage)"), kRefreshStorageCmd);
+	_storageDownloadButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.DownloadButton", _("Downloads"), _("Open downloads manager dialog"), kDownloadStorageCmd);
 
-	_runServerButton = new ButtonWidget(tab, "GlobalOptions_Cloud.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
-	_serverInfoLabel = new StaticTextWidget(tab, "GlobalOptions_Cloud.ServerInfoLabel", _("Not running"));
+	_runServerButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
+	_serverInfoLabel = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerInfoLabel", _("Not running"));
 
 	setupCloudTab();
 	_redrawCloudTab = false;
@@ -1768,11 +1771,11 @@ void GlobalOptionsDialog::setupCloudTab() {
 	int16 x, y;
 	uint16 w, h;
 	int serverButtonY, serverInfoY;
-	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud.RunServerButton", x, y, w, h))
-		warning("GlobalOptions_Cloud.RunServerButton's position is undefined");
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RunServerButton", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.RunServerButton's position is undefined");
 	serverButtonY = y;
-	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud.ServerInfoLabel", x, y, w, h))
-		warning("GlobalOptions_Cloud.ServerInfoLabel's position is undefined");
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerInfoLabel", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.ServerInfoLabel's position is undefined");
 	serverInfoY = y;
 
 	bool serverIsRunning = LocalServer.isRunning();
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 9e79ac2..753d061 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -539,6 +539,12 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<widget name = 'StoragePopupDesc'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 9f00d2e..f27c8b6 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -528,6 +528,12 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'StoragePopupDesc'						


Commit: 9975307caf5452b25ad706ca2594239909f2354f
    https://github.com/scummvm/scummvm/commit/9975307caf5452b25ad706ca2594239909f2354f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix Container's visibility issue

Now it respects outer code's decision to hide or move some widgets
around. Outer code must be CommandReceiver which is set as
ScrollContainer's target.

Changed paths:
    gui/options.cpp
    gui/widgets/scrollcontainer.cpp
    gui/widgets/scrollcontainer.h



diff --git a/gui/options.cpp b/gui/options.cpp
index a272b22..42d6a66 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -100,7 +100,8 @@ enum {
 	kConfigureStorageCmd = 'cfst',
 	kRefreshStorageCmd = 'rfst',
 	kDownloadStorageCmd = 'dlst',
-	kRunServerCmd = 'rnsv'
+	kRunServerCmd = 'rnsv',
+	kCloudTabContainerReflowCmd = 'ctcr'
 };
 #endif
 
@@ -1280,7 +1281,8 @@ GlobalOptionsDialog::GlobalOptionsDialog()
 	else
 		tab->addTab(_c("Cloud", "lowres"));
 
-	ScrollContainerWidget *container = new ScrollContainerWidget(tab, "GlobalOptions_Cloud.Container");
+	ScrollContainerWidget *container = new ScrollContainerWidget(tab, "GlobalOptions_Cloud.Container", kCloudTabContainerReflowCmd);
+	container->setTarget(this);
 
 #ifdef USE_LIBCURL
 	_selectedStorageIndex = CloudMan.getStorageIndex();
@@ -1600,19 +1602,24 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		}
 		break;
 	}
+#ifdef USE_CLOUD
+	case kCloudTabContainerReflowCmd:
+		setupCloudTab();
+		break;
+#endif
 #ifdef USE_LIBCURL
 	case kPopUpItemSelectedCmd:
 	{
-		setupCloudTab();
-		draw();
+		//update container's scrollbar
+		reflowLayout();
 		break;
 	}
 	case kConfigureStorageCmd:
 	{
 		StorageWizardDialog dialog(_selectedStorageIndex);
 		dialog.runModal();
-		setupCloudTab();
-		draw();
+		//update container's scrollbar
+		reflowLayout();
 		break;
 	}
 	case kRefreshStorageCmd:
diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 1b38478..9251047 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -28,13 +28,13 @@
 
 namespace GUI {
 
-ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h)
-	: Widget(boss, x, y, w, h) {
+ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h, uint32 reflowCmd)
+	: Widget(boss, x, y, w, h), CommandSender(nullptr), _reflowCmd(reflowCmd) {
 	init();
 }
 
-ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, const Common::String &name)
-	: Widget(boss, name) {
+ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, const Common::String &name, uint32 reflowCmd)
+	: Widget(boss, name), CommandSender(nullptr), _reflowCmd(reflowCmd) {
 	init();
 }
 
@@ -59,7 +59,7 @@ void ScrollContainerWidget::recalc() {
 	int min = spacing, max = 0;
 	Widget *ptr = _firstWidget;
 	while (ptr) {
-		if (ptr != _verticalScroll) {
+		if (ptr != _verticalScroll && ptr->isVisible()) {
 			int y = ptr->getAbsY() - getChildY();
 			min = MIN(min, y - spacing);
 			max = MAX(max, y + ptr->getHeight() + spacing);
@@ -115,6 +115,9 @@ void ScrollContainerWidget::reflowLayout() {
 		ptr->reflowLayout();
 		ptr = ptr->next();
 	}
+
+	//hide and move widgets, if needed
+	sendCommand(_reflowCmd, 0);
 	
 	//recalculate height
 	recalc();
@@ -124,7 +127,7 @@ void ScrollContainerWidget::reflowLayout() {
 	while (ptr) {
 		int y = ptr->getAbsY() - getChildY();
 		int h = ptr->getHeight();
-		bool visible = true;
+		bool visible = ptr->isVisible();
 		if (y + h - _scrolledY < 0) visible = false;
 		if (y - _scrolledY > _limitH) visible = false;
 		ptr->setVisible(visible);
diff --git a/gui/widgets/scrollcontainer.h b/gui/widgets/scrollcontainer.h
index 692c7e3..c2d4737 100644
--- a/gui/widgets/scrollcontainer.h
+++ b/gui/widgets/scrollcontainer.h
@@ -29,16 +29,17 @@
 
 namespace GUI {
 
-class ScrollContainerWidget: public Widget {
+class ScrollContainerWidget: public Widget, public CommandSender {
 	ScrollBarWidget *_verticalScroll;
 	int16 _scrolledX, _scrolledY;
 	uint16 _limitH;
+	uint32 _reflowCmd;
 
 	void recalc();
 
 public:
-	ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h);
-	ScrollContainerWidget(GuiObject *boss, const Common::String &name);
+	ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h, uint32 reflowCmd = 0);
+	ScrollContainerWidget(GuiObject *boss, const Common::String &name, uint32 reflowCmd = 0);
 	~ScrollContainerWidget();
 
 	void init();


Commit: f01402f4d8cfa5123677708622c4d15e30724b7c
    https://github.com/scummvm/scummvm/commit/f01402f4d8cfa5123677708622c4d15e30724b7c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Remove unnecessary DownloadDialog's flag

Changed paths:
    gui/downloaddialog.cpp
    gui/downloaddialog.h



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 001a058..1d838f1 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -40,7 +40,7 @@ enum {
 };
 
 DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false), _redraw(false) {
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -72,6 +72,7 @@ DownloadDialog::~DownloadDialog() {
 
 void DownloadDialog::open() {
 	Dialog::open();
+	CloudMan.setDownloadTarget(this);
 	if (!CloudMan.isDownloading())
 		if (!selectDirectories())
 			close();
@@ -92,10 +93,11 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		close();
 		break;
 	}
-	case kDownloadProgressCmd:		
-		_percentLabel->setLabel(Common::String::format("%u %%", data));
-		_progressBar->setValue(data);
-		_redraw = true;
+	case kDownloadProgressCmd:
+		if (!_close) {
+			refreshWidgets();
+			draw();
+		}
 		break;
 	case kDownloadEndedCmd:
 		_close = true;
@@ -169,12 +171,6 @@ void DownloadDialog::handleTickle() {
 		return;
 	}
 
-	if (_redraw) {
-		refreshWidgets();
-		draw();
-		_redraw = false;
-	}
-
 	Dialog::handleTickle();
 }
 
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index baa3f15..39c4cea 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -54,7 +54,7 @@ class DownloadDialog : public Dialog {
 	ButtonWidget *_closeButton;
 
 	Common::String _localDirectory;
-	bool _close, _redraw;
+	bool _close;
 
 	void refreshWidgets();
 	bool selectDirectories();


Commit: 2f5138f7959e74606f0b9493ce9523110d2f46dd
    https://github.com/scummvm/scummvm/commit/2f5138f7959e74606f0b9493ce9523110d2f46dd
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Minor Container fixes

I should've done these in PR, I guess.

Changed paths:
    gui/widgets/scrollcontainer.cpp



diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 9251047..820c00f 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -68,6 +68,8 @@ void ScrollContainerWidget::recalc() {
 	}
 	h = max - min;
 
+	if (h <= _limitH) _scrolledY = 0;
+
 	_verticalScroll->_numEntries = h;
 	_verticalScroll->_currentPos = _scrolledY;
 	_verticalScroll->_entriesPerPage = _limitH;
@@ -135,6 +137,7 @@ void ScrollContainerWidget::reflowLayout() {
 	}
 
 	_verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
+	_verticalScroll->recalc();
 }
 
 void ScrollContainerWidget::drawWidget() {


Commit: 29e6020574e1bed81e20cf5ab5df521285d28615
    https://github.com/scummvm/scummvm/commit/29e6020574e1bed81e20cf5ab5df521285d28615
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update "/files" hardcoded response template

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 4de76b9..9dba0c5 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -46,7 +46,26 @@ Common::String encodeDoubleQuotes(Common::String s) {
 }
 
 void FilesPageHandler::handle(Client &client) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body><table>{content}</table></body></html>"; //TODO: add controls
+	Common::String response = 
+		"<html>" \
+			"<head><title>ScummVM</title></head>" \
+			"<body>" \
+				"<p>{create_directory_desc}</p>" \
+				"<form action=\"create\">" \
+					"<input type=\"hidden\" name=\"path\" value=\"{path}\"/>" \
+					"<input type=\"text\" name=\"directory_name\" value=\"\"/>" \
+					"<input type=\"submit\" value=\"{create_directory_button}\"/>" \
+				"</form>" \
+				"<hr/>" \
+				"<p>{upload_file_desc}</p>" \
+				"<form action=\"upload?path={path}\" method=\"post\" enctype=\"multipart/form-data\">" \
+					"<input type=\"file\" name=\"upload_file[]\" multiple/>" \
+					"<input type=\"submit\" value=\"{upload_file_button}\"/>" \
+				"</form>"
+				"<hr/>" \
+				"<table>{content}</table>" \
+			"</body>" \
+		"</html>";
 	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
 	// load stylish response page from the archive


Commit: 389c669a4744629e19e48e8243dac1a8875e60c2
    https://github.com/scummvm/scummvm/commit/389c669a4744629e19e48e8243dac1a8875e60c2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "directory" form for webserver "/upload"

The attribute is Chrome-only.

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 9dba0c5..e519c5a 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -59,7 +59,9 @@ void FilesPageHandler::handle(Client &client) {
 				"<hr/>" \
 				"<p>{upload_file_desc}</p>" \
 				"<form action=\"upload?path={path}\" method=\"post\" enctype=\"multipart/form-data\">" \
-					"<input type=\"file\" name=\"upload_file[]\" multiple/>" \
+					"<input type=\"file\" name=\"upload_file-f\" allowdirs multiple/>" \
+					"<span>{or_upload_directory_desc}</span>" \
+					"<input type=\"file\" name=\"upload_file-d\" directory webkitdirectory multiple/>" \
 					"<input type=\"submit\" value=\"{upload_file_button}\"/>" \
 				"</form>"
 				"<hr/>" \
@@ -90,6 +92,7 @@ void FilesPageHandler::handle(Client &client) {
 	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
 	replace(response, "{create_directory_desc}", _("Type new directory name:"));
 	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
+	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
 	replace(response, "{content}", content);	
 	LocalWebserver::setClientGetHandler(client, response);
 }
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 0ca5e3f..7cbb118 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -24,6 +24,7 @@
 #include "backends/fs/fs-factory.h"
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "common/file.h"
 #include "common/memstream.h"
 #include "common/translation.h"
 
@@ -31,7 +32,7 @@ namespace Networking {
 
 UploadFileClientHandler::UploadFileClientHandler(Common::String parentDirectoryPath):
 	_state(UFH_READING_CONTENT), _headersStream(nullptr), _contentStream(nullptr),
-	_parentDirectoryPath(parentDirectoryPath) {}
+	_parentDirectoryPath(parentDirectoryPath), _uploadedFiles(0) {}
 
 UploadFileClientHandler::~UploadFileClientHandler() {
 	delete _headersStream;
@@ -114,16 +115,13 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	Common::String headers = readEverythingFromMemoryStream(_headersStream);
 	Common::String fieldName = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
-	if (!fieldName.hasPrefix("upload_file[")) return;
+	if (!fieldName.hasPrefix("upload_file")) return;
 
 	Common::String filename = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "filename=\"", filename);
 
-	// check that <filename> is not empty
-	if (filename.empty()) {
-		setErrorMessageHandler(*client, _("Invalid filename!"));
-		return;
-	}
+	// skip block if <filename> is empty
+	if (filename.empty()) return;
 
 	// check that <path>/<filename> doesn't exist
 	Common::String path = _parentDirectoryPath;
@@ -134,12 +132,15 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 		return;
 	}
 
-	// create file stream
-	_contentStream = originalNode->createWriteStream();
-	if (_contentStream == nullptr) {
+	// create file stream (and necessary subdirectories)
+	Common::DumpFile *f = new Common::DumpFile();
+	if (!f->open(originalNode->getPath(), true)) {
+		delete f;
 		setErrorMessageHandler(*client, _("Failed to upload the file!"));
 		return;
 	}
+
+	_contentStream = f;
 }
 
 void UploadFileClientHandler::handleBlockContent(Client *client) {
@@ -148,6 +149,7 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 	// if previous block headers were file-related and created a stream
 	if (_contentStream) {
 		_contentStream->flush();
+		++_uploadedFiles;
 
 		if (client->noMoreContent()) {
 			// success - redirect back to directory listing
@@ -165,9 +167,12 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 		}
 	}
 	
-	// if no file field was found, but no more content avaiable - failure
+	// no more content avaiable
 	if (client->noMoreContent()) {
-		setErrorMessageHandler(*client, _("No file was passed!"));
+		// if no file field was found - failure
+		if (_uploadedFiles == 0) {
+			setErrorMessageHandler(*client, _("No file was passed!"));
+		} else _state = UFH_STOP;
 		return;
 	}
 }
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h
index de6941b..f61a2fa 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.h
+++ b/backends/networking/sdl_net/uploadfileclienthandler.h
@@ -41,6 +41,7 @@ class UploadFileClientHandler: public ClientHandler {
 	Common::MemoryReadWriteStream *_headersStream;
 	Common::WriteStream *_contentStream;
 	Common::String _parentDirectoryPath;
+	uint32 _uploadedFiles;
 
 	void handleBlockHeaders(Client *client);
 	void handleBlockContent(Client *client);
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index ae19ef7..3aa9bdd 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index c83a59f..2c0bceb 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -26,7 +26,13 @@
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
 					<form action="upload?path={path}" method="post" enctype="multipart/form-data">
-						<input type="file" name="upload_file[]" multiple/>
+						<!-- we don't need "[]" in the name, as our webserver is not using PHP -->
+						<!-- "allowdirs" is a proposal, not implemented yet -->
+						<input type="file" name="upload_file-f" allowdirs multiple/>
+						<br/><br/>
+						<p>{or_upload_directory_desc}</p>
+						<!-- "directory"/"webkitdirectory" works in Chrome only yet, "multiple" is just in case here -->
+						<input type="file" name="upload_file-d" directory webkitdirectory multiple/>
 						<input type="submit" value="{upload_file_button}"/>
 					</form>
 				</div>


Commit: 5c60cd14c2cf27e4b0c03183cef1b1b2e596782a
    https://github.com/scummvm/scummvm/commit/5c60cd14c2cf27e4b0c03183cef1b1b2e596782a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add LocalWebserver::resolveAddress()

Works on Linux too. And, well, I'm bad in adding backends, so it's just
#ifdefed there.

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index a99df8d..9d53543 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -30,6 +30,13 @@
 #include "common/timer.h"
 #include <SDL/SDL_net.h>
 
+#ifdef POSIX
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
 namespace Common {
 class MemoryReadWriteStream;
 
@@ -87,23 +94,7 @@ void LocalWebserver::start() {
 		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
 
-	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", SERVER_PORT);
-
-	const char *name = SDLNet_ResolveIP(&ip);
-	if (name == NULL) {
-		warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
-	} else {
-		IPaddress localIp;
-		if (SDLNet_ResolveHost(&localIp, name, SERVER_PORT) == -1) {
-			warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
-		} else {
-			_address = Common::String::format(
-				"http://%u.%u.%u.%u:%u/",
-				localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
-				SERVER_PORT
-			);
-		}
-	}
+	resolveAddress(&ip);
 
 	_serverSocket = SDLNet_TCP_Open(&ip);
 	if (!_serverSocket) {
@@ -239,6 +230,89 @@ void LocalWebserver::acceptClient() {
 		}
 }
 
+void LocalWebserver::resolveAddress(void *ipAddress) {
+	IPaddress *ip = (IPaddress *)ipAddress;
+
+	// not resolved
+	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", SERVER_PORT);
+
+	// default way (might work everywhere, surely works on Windows)
+	const char *name = SDLNet_ResolveIP(ip);
+	if (name == NULL) {
+		warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+	} else {
+		IPaddress localIp;
+		if (SDLNet_ResolveHost(&localIp, name, SERVER_PORT) == -1) {
+			warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		} else {
+			_address = Common::String::format(
+				"http://%u.%u.%u.%u:%u/",
+				localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
+				SERVER_PORT
+			);
+		}
+	}
+
+	// check that our trick worked
+	if (_address.contains("/127.0.0.1:") || _address.contains("localhost") || _address.contains("/0.0.0.0:"))
+		warning("Failed to resolve IP with the default way");
+	else
+		return;
+
+	// if not - try platform-specific
+#ifdef POSIX
+		struct ifaddrs *ifAddrStruct = NULL;
+		void *tmpAddrPtr = NULL;
+
+		getifaddrs(&ifAddrStruct);
+
+		for (struct ifaddrs *i = ifAddrStruct; i != NULL; i = i->ifa_next) {
+			if (!i->ifa_addr) {
+				continue;
+			}
+			
+			Common::String addr;
+			
+			// IPv4
+			if (i->ifa_addr->sa_family == AF_INET) { 
+				tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
+				char addressBuffer[INET_ADDRSTRLEN];
+				inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
+				debug("%s IP Address %s", i->ifa_name, addressBuffer);
+				addr = addressBuffer;
+			}
+			
+			// IPv6
+			/*
+			if (i->ifa_addr->sa_family == AF_INET6) {
+				tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
+				char addressBuffer[INET6_ADDRSTRLEN];
+				inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
+				debug("%s IP Address %s", i->ifa_name, addressBuffer);
+				addr = addressBuffer;
+			}
+			*/
+			
+			if (addr.empty()) continue;
+			
+			// ignored IPv4 addresses
+			if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
+				continue;
+
+			// ignored IPv6 addresses
+			/*
+			if (addr.equals("::1"))
+				continue;
+			*/
+			
+			// use the address found
+			_address = "http://" + addr + Common::String::format(":%u/", SERVER_PORT);
+		}
+		
+		if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
+#endif
+}
+
 void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code, const char *mimeType) {
 	byte *data = new byte[response.size()];
 	memcpy(data, response.c_str(), response.size());
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 124b1b3..cdd991d 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -75,6 +75,8 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handle();
 	void handleClient(uint32 i);
 	void acceptClient();
+        
+        void resolveAddress(void *ipAddress);
 	
 public:
 	LocalWebserver();


Commit: d795c77ef53720fa423d9b827a66d1bea8b8e761
    https://github.com/scummvm/scummvm/commit/d795c77ef53720fa423d9b827a66d1bea8b8e761
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix DownloadDialog detection

Now it calls Launcher directly, so it updates games list on success.

Changed paths:
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/launcher.cpp
    gui/launcher.h
    gui/options.cpp
    gui/options.h



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 1d838f1..5ed2871 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -28,6 +28,7 @@
 #include "gui/browser.h"
 #include "gui/chooser.h"
 #include "gui/editgamedialog.h"
+#include "gui/launcher.h"
 #include "gui/message.h"
 #include "gui/remotebrowser.h"
 #include "gui/widgets/edittext.h"
@@ -39,8 +40,8 @@ enum {
 	kDownloadDialogButtonCmd = 'Dldb'	
 };
 
-DownloadDialog::DownloadDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_DownloadDialog"), _close(false) {
+DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher):
+	Dialog("GlobalOptions_Cloud_DownloadDialog"), _launcher(launcher), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
@@ -165,7 +166,8 @@ bool DownloadDialog::selectDirectories() {
 
 void DownloadDialog::handleTickle() {
 	if (_close) {
-		addGame();
+		if (_launcher)
+			_launcher->doGameDetection(_localDirectory);
 		close();
 		_close = false;
 		return;
@@ -174,75 +176,6 @@ void DownloadDialog::handleTickle() {
 	Dialog::handleTickle();
 }
 
-void DownloadDialog::addGame() {
-	// Allow user to add a new game to the list.
-	// 2) try to auto detect which game is in the directory, if we cannot
-	//    determine it uniquely present a list of candidates to the user
-	//    to pick from
-	// 3) Display the 'Edit' dialog for that item, letting the user specify
-	//    an alternate description (to distinguish multiple versions of the
-	//    game, e.g. 'Monkey German' and 'Monkey English') and set default
-	//    options for that game
-	// 4) If no game is found in the specified directory, return to the
-	//    dialog.
-
-	// User made his choice...
-	Common::FSNode dir(_localDirectory);
-	Common::FSList files;
-	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
-		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
-		alert.runModal();
-		return;
-	}
-
-	// ...so let's determine a list of candidates, games that
-	// could be contained in the specified directory.
-	GameList candidates(EngineMan.detectGames(files));
-
-	int idx;
-	if (candidates.empty()) {
-		// No game was found in the specified directory
-		MessageDialog alert(_("ScummVM could not find any game in the specified directory!"));
-		alert.runModal();
-		return;
-	}
-				
-	if (candidates.size() == 1) {
-		// Exact match
-		idx = 0;
-	} else {
-		// Display the candidates to the user and let her/him pick one
-		Common::StringArray list;
-		for (idx = 0; idx < (int)candidates.size(); idx++)
-			list.push_back(candidates[idx].description());
-
-		ChooserDialog dialog(_("Pick the game:"));
-		dialog.setList(list);
-		idx = dialog.runModal();
-	}
-	if (0 <= idx && idx < (int)candidates.size()) {
-		GameDescriptor result = candidates[idx];
-
-		// TODO: Change the detectors to set "path" !
-		result["path"] = dir.getPath();
-
-		Common::String domain = addGameToConf(result);
-
-		// Display edit dialog for the new entry
-		EditGameDialog editDialog(domain, result.description());
-		if (editDialog.runModal() > 0) {
-			// User pressed OK, so make changes permanent
-
-			// Write config to disk
-			ConfMan.flushToDisk();
-		} else {
-			// User aborted, remove the the new domain again
-			ConfMan.removeGameDomain(domain);
-		}
-
-	}
-}
-
 void DownloadDialog::reflowLayout() {
 	Dialog::reflowLayout();
 	refreshWidgets();
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 39c4cea..4b277c8 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -28,6 +28,7 @@
 #include <backends/cloud/storage.h>
 
 namespace GUI {
+class LauncherDialog;
 
 class CommandSender;
 class EditTextWidget;
@@ -43,6 +44,7 @@ enum DownloadProgress {
 };
 
 class DownloadDialog : public Dialog {
+	LauncherDialog *_launcher;
 	BrowserDialog *_browser;
 	RemoteBrowserDialog *_remoteBrowser;
 	
@@ -58,10 +60,9 @@ class DownloadDialog : public Dialog {
 
 	void refreshWidgets();
 	bool selectDirectories();
-	void addGame();
 
 public:
-	DownloadDialog(uint32 storageId);
+	DownloadDialog(uint32 storageId, LauncherDialog *launcher);
 	virtual ~DownloadDialog();
 
 	virtual void open();
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index f2474a5..d1a226f 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -328,9 +328,8 @@ void LauncherDialog::addGame() {
 
 		if (_browser->runModal() > 0) {
 			// User made his choice...
-			Common::FSNode dir(_browser->getResult());
 #ifdef USE_LIBCURL
-			String selectedDirectory = dir.getPath();
+			String selectedDirectory = _browser->getResult().getPath();
 			String bannedDirectory = CloudMan.getDownloadLocalDirectory();
 			if (selectedDirectory.size() && selectedDirectory.lastChar() != '/' && selectedDirectory.lastChar() != '\\')
 				selectedDirectory += '/';
@@ -343,64 +342,7 @@ void LauncherDialog::addGame() {
 				return;
 			}
 #endif
-			Common::FSList files;
-			if (!dir.getChildren(files, Common::FSNode::kListAll)) {
-				MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
-				alert.runModal();
-				return;
-			}
-
-			// ...so let's determine a list of candidates, games that
-			// could be contained in the specified directory.
-			GameList candidates(EngineMan.detectGames(files));
-
-			int idx;
-			if (candidates.empty()) {
-				// No game was found in the specified directory
-				MessageDialog alert(_("ScummVM could not find any game in the specified directory!"));
-				alert.runModal();
-				idx = -1;
-
-				looping = true;
-			} else if (candidates.size() == 1) {
-				// Exact match
-				idx = 0;
-			} else {
-				// Display the candidates to the user and let her/him pick one
-				StringArray list;
-				for (idx = 0; idx < (int)candidates.size(); idx++)
-					list.push_back(candidates[idx].description());
-
-				ChooserDialog dialog(_("Pick the game:"));
-				dialog.setList(list);
-				idx = dialog.runModal();
-			}
-			if (0 <= idx && idx < (int)candidates.size()) {
-				GameDescriptor result = candidates[idx];
-
-				// TODO: Change the detectors to set "path" !
-				result["path"] = dir.getPath();
-
-				Common::String domain = addGameToConf(result);
-
-				// Display edit dialog for the new entry
-				EditGameDialog editDialog(domain, result.description());
-				if (editDialog.runModal() > 0) {
-					// User pressed OK, so make changes permanent
-
-					// Write config to disk
-					ConfMan.flushToDisk();
-
-					// Update the ListWidget, select the new item, and force a redraw
-					updateListing();
-					selectTarget(editDialog.getDomain());
-					draw();
-				} else {
-					// User aborted, remove the the new domain again
-					ConfMan.removeGameDomain(domain);
-				}
-
-			}
+			looping = !doGameDetection(_browser->getResult().getPath());
 		}
 	} while (looping);
 }
@@ -579,6 +521,81 @@ void LauncherDialog::handleKeyUp(Common::KeyState state) {
 	updateButtons();
 }
 
+bool LauncherDialog::doGameDetection(const Common::String &path) {
+	// Allow user to add a new game to the list.
+	// 2) try to auto detect which game is in the directory, if we cannot
+	//    determine it uniquely present a list of candidates to the user
+	//    to pick from
+	// 3) Display the 'Edit' dialog for that item, letting the user specify
+	//    an alternate description (to distinguish multiple versions of the
+	//    game, e.g. 'Monkey German' and 'Monkey English') and set default
+	//    options for that game
+	// 4) If no game is found in the specified directory, return to the
+	//    dialog.
+
+	// User made his choice...
+	Common::FSNode dir(path);
+	Common::FSList files;
+	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
+		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
+		alert.runModal();
+		return true;
+	}
+
+	// ...so let's determine a list of candidates, games that
+	// could be contained in the specified directory.
+	GameList candidates(EngineMan.detectGames(files));
+
+	int idx;
+	if (candidates.empty()) {
+		// No game was found in the specified directory
+		MessageDialog alert(_("ScummVM could not find any game in the specified directory!"));
+		alert.runModal();
+		idx = -1;
+		return false;
+	} else if (candidates.size() == 1) {
+		// Exact match
+		idx = 0;
+	} else {
+		// Display the candidates to the user and let her/him pick one
+		StringArray list;
+		for (idx = 0; idx < (int)candidates.size(); idx++)
+			list.push_back(candidates[idx].description());
+
+		ChooserDialog dialog(_("Pick the game:"));
+		dialog.setList(list);
+		idx = dialog.runModal();
+	}
+	if (0 <= idx && idx < (int)candidates.size()) {
+		GameDescriptor result = candidates[idx];
+
+		// TODO: Change the detectors to set "path" !
+		result["path"] = dir.getPath();
+
+		Common::String domain = addGameToConf(result);
+
+		// Display edit dialog for the new entry
+		EditGameDialog editDialog(domain, result.description());
+		if (editDialog.runModal() > 0) {
+			// User pressed OK, so make changes permanent
+
+			// Write config to disk
+			ConfMan.flushToDisk();
+
+			// Update the ListWidget, select the new item, and force a redraw
+			updateListing();
+			selectTarget(editDialog.getDomain());
+			draw();
+		} else {
+			// User aborted, remove the the new domain again
+			ConfMan.removeGameDomain(domain);
+		}
+
+	}
+
+	return true;
+}
+
 void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	int item = _list->getSelected();
 
@@ -596,7 +613,7 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		loadGameButtonPressed(item);
 		break;
 	case kOptionsCmd: {
-		GlobalOptionsDialog options;
+		GlobalOptionsDialog options(this);
 		options.runModal();
 		}
 		break;
diff --git a/gui/launcher.h b/gui/launcher.h
index e9c76a5..58f1c93 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -51,7 +51,7 @@ public:
 
 	virtual void handleKeyDown(Common::KeyState state);
 	virtual void handleKeyUp(Common::KeyState state);
-
+	bool doGameDetection(const Common::String &path);
 protected:
 	EditTextWidget  *_searchWidget;
 	ListWidget		*_list;
diff --git a/gui/options.cpp b/gui/options.cpp
index 42d6a66..b5f7293 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1099,8 +1099,8 @@ void OptionsDialog::reflowLayout() {
 #pragma mark -
 
 
-GlobalOptionsDialog::GlobalOptionsDialog()
-	: OptionsDialog(Common::ConfigManager::kApplicationDomain, "GlobalOptions") {
+GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
+	: OptionsDialog(Common::ConfigManager::kApplicationDomain, "GlobalOptions"), _launcher(launcher) {
 
 	// The tab widget
 	TabWidget *tab = new TabWidget(this, "GlobalOptions.TabWidget");
@@ -1632,7 +1632,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 	case kDownloadStorageCmd:
 	{
-		DownloadDialog dialog(_selectedStorageIndex);
+		DownloadDialog dialog(_selectedStorageIndex, _launcher);
 		dialog.runModal();
 		break;
 	}
diff --git a/gui/options.h b/gui/options.h
index 1d7f9ff..e68778c 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -42,6 +42,7 @@
 #endif
 
 namespace GUI {
+class LauncherDialog;
 
 class CheckboxWidget;
 class PopUpWidget;
@@ -204,7 +205,7 @@ protected:
 
 class GlobalOptionsDialog : public OptionsDialog {
 public:
-	GlobalOptionsDialog();
+	GlobalOptionsDialog(LauncherDialog *launcher);
 	~GlobalOptionsDialog();
 
 	void open();
@@ -215,6 +216,7 @@ public:
 	virtual void reflowLayout();
 
 protected:
+	LauncherDialog *_launcher;
 #ifdef GUI_ENABLE_KEYSDIALOG
 	KeysDialog *_keysDialog;
 #endif


Commit: 03f33be54c58de59087ed2cfc8285455087df1c4
    https://github.com/scummvm/scummvm/commit/03f33be54c58de59087ed2cfc8285455087df1c4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix Options Cloud tab widgets visibility

As it's controlled by ScrollContainer also, we have to explicitly
setVisible(true) for "always" visible widgets.

Changed paths:
    gui/options.cpp



diff --git a/gui/options.cpp b/gui/options.cpp
index b5f7293..b8ff2d5 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1726,6 +1726,9 @@ void GlobalOptionsDialog::setupCloudTab() {
 	int serverLabelPosition = -1; //no override
 #ifdef USE_LIBCURL
 	_selectedStorageIndex = _storagePopUp->getSelectedTag();
+	
+	if (_storagePopUpDesc) _storagePopUpDesc->setVisible(true);
+	if (_storagePopUp) _storagePopUp->setVisible(true);
 
 	bool shown = (_selectedStorageIndex != Cloud::kStorageNoneId);
 	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(shown);
@@ -1789,10 +1792,12 @@ void GlobalOptionsDialog::setupCloudTab() {
 		
 	if (serverLabelPosition < 0) serverLabelPosition = serverInfoY;
 	if (_runServerButton) {
+		_runServerButton->setVisible(true);
 		_runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
 		_runServerButton->setLabel(_(serverIsRunning ? "Stop server" : "Run server"));
 	}
 	if (_serverInfoLabel) {
+		_serverInfoLabel->setVisible(true);
 		_serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition);
 		if (serverIsRunning) _serverInfoLabel->setLabel(LocalServer.getAddress());
 		else _serverInfoLabel->setLabel(_("Not running"));


Commit: b37b392fa07c5d24fbef2ffe4b4704e8f666f9a1
    https://github.com/scummvm/scummvm/commit/b37b392fa07c5d24fbef2ffe4b4704e8f666f9a1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add BoxStorage sketch

Changed paths:
  A backends/cloud/box/boxstorage.cpp
  A backends/cloud/box/boxstorage.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/module.mk
    gui/storagewizarddialog.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
new file mode 100644
index 0000000..8a2e004
--- /dev/null
+++ b/backends/cloud/box/boxstorage.cpp
@@ -0,0 +1,292 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/box/boxstorage.h"
+#include "backends/cloud/cloudmanager.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+#include "common/config-manager.h"
+
+namespace Cloud {
+namespace Box {
+
+char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
+char *BoxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
+
+void BoxStorage::loadKeyAndSecret() {
+	Common::String k = ConfMan.get("BOX_KEY", ConfMan.kCloudDomain);
+	KEY = new char[k.size() + 1];
+	memcpy(KEY, k.c_str(), k.size());
+	KEY[k.size()] = 0;
+
+	k = ConfMan.get("BOX_SECRET", ConfMan.kCloudDomain);
+	SECRET = new char[k.size() + 1];
+	memcpy(SECRET, k.c_str(), k.size());
+	SECRET[k.size()] = 0;
+}
+
+BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
+	_token(accessToken), _refreshToken(refreshToken) {}
+
+BoxStorage::BoxStorage(Common::String code) {
+	getAccessToken(new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete), code);
+}
+
+BoxStorage::~BoxStorage() {}
+
+void BoxStorage::getAccessToken(BoolCallback callback, Common::String code) {
+	if (!KEY || !SECRET) loadKeyAndSecret();
+	bool codeFlow = (code != "");
+
+	if (!codeFlow && _refreshToken == "") {
+		warning("BoxStorage: no refresh token available to get new access token.");
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+		return;
+	}
+
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://api.box.com/oauth2/token");
+	if (codeFlow) {
+		request->addPostField("grant_type=authorization_code");
+		request->addPostField("code=" + code);
+	} else {
+		request->addPostField("grant_type=refresh_token");
+		request->addPostField("refresh_token=" + _refreshToken);
+	}
+	request->addPostField("client_id=" + Common::String(KEY));
+	request->addPostField("client_secret=" + Common::String(SECRET));
+	/*
+#ifdef USE_SDL_NET
+	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
+#else
+	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+#endif
+	*/
+	addRequest(request);
+}
+
+void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("BoxStorage: got NULL instead of JSON");
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (!result.contains("access_token") || !result.contains("refresh_token")) {
+		warning("Bad response, no token passed");
+		debug("%s", json->stringify().c_str());
+		if (callback) (*callback)(BoolResponse(nullptr, false));
+	} else {
+		_token = result.getVal("access_token")->asString();
+		_refreshToken = result.getVal("refresh_token")->asString();
+		CloudMan.save(); //ask CloudManager to save our new refreshToken
+		if (callback) (*callback)(BoolResponse(nullptr, true));
+	}
+	delete json;
+}
+
+void BoxStorage::codeFlowComplete(BoolResponse response) {
+	if (!response.value) {
+		warning("BoxStorage: failed to get access token through code flow");
+		return;
+	}
+
+	CloudMan.replaceStorage(this, kStorageBoxId);
+	ConfMan.flushToDisk();
+}
+
+void BoxStorage::saveConfig(Common::String keyPrefix) {
+	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
+	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
+}
+
+Common::String BoxStorage::name() const {
+	return "Box";
+}
+
+void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+	
+	Common::JSONObject info = json->asObject();
+
+	Common::String uid, name, email;
+	uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
+
+	if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
+		Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
+		if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
+			Common::JSONObject user = createdBy.getVal("user")->asObject();
+			uid = user.getVal("id")->asString();
+			name = user.getVal("displayName")->asString();
+		}
+	}
+
+	if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
+		quotaUsed = info.getVal("size")->asIntegerNumber();
+	}
+
+	Common::String username = email;
+	if (username == "") username = name;
+	if (username == "") username = uid;
+	CloudMan.setStorageUsername(kStorageBoxId, username);
+
+	if (outerCallback) {
+		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+		delete outerCallback;
+	}
+
+	delete json;
+}
+
+void BoxStorage::printJson(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("printJson: NULL");
+		return;
+	}
+
+	debug("%s", json->stringify().c_str());
+	delete json;
+}
+
+void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
+	if (!response.value) {
+		warning("fileInfoCallback: NULL");
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+		return;
+	}
+
+	Common::JSONObject result = response.value->asObject();
+	if (result.contains("@content.downloadUrl")) {
+		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
+		if (outerCallback)
+			(*outerCallback)(Networking::NetworkReadStreamResponse(
+				response.request,
+				new Networking::NetworkReadStream(url, 0, "")
+			));
+	} else {
+		warning("downloadUrl not found in passed JSON");
+		debug("%s", response.value->stringify().c_str());
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+	}
+	delete response.value;
+}
+
+Networking::Request *BoxStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	//return addRequest(new BoxListDirectoryRequest(this, path, callback, errorCallback, recursive));
+	return nullptr; //TODO
+}
+
+Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	//return addRequest(new BoxUploadRequest(this, path, contents, callback, errorCallback));
+	return nullptr; //TODO
+}
+
+Networking::Request *BoxStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
+	/*
+	Common::String url = "https://api.Box.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(path);
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &BoxStorage::fileInfoCallback, outerCallback);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _token);
+	return addRequest(request);
+	*/
+	return nullptr; //TODO
+}
+
+void BoxStorage::fileDownloaded(BoolResponse response) {
+	if (response.value) debug("file downloaded!");
+	else debug("download failed!");
+}
+
+void BoxStorage::printFiles(FileArrayResponse response) {
+	debug("files:");
+	Common::Array<StorageFile> &files = response.value;
+	for (uint32 i = 0; i < files.size(); ++i)
+		debug("\t%s", files[i].path().c_str());
+}
+
+void BoxStorage::printBool(BoolResponse response) {
+	debug("bool: %s", response.value ? "true" : "false");
+}
+
+void BoxStorage::printFile(UploadResponse response) {
+	debug("\nuploaded file info:");
+	debug("\tpath: %s", response.value.path().c_str());
+	debug("\tsize: %u", response.value.size());
+	debug("\ttimestamp: %u", response.value.timestamp());
+}
+
+Networking::Request *BoxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	//return addRequest(new BoxCreateDirectoryRequest(this, path, callback, errorCallback));
+	return nullptr; //TODO
+}
+
+Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+	/*
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.Box.com/v1.0/drive/special/approot");
+	request->addHeader("Authorization: bearer " + _token);
+	return addRequest(request);
+	*/
+	return nullptr; //TODO
+}
+
+Common::String BoxStorage::savesDirectoryPath() { return "saves/"; }
+
+BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
+	loadKeyAndSecret();
+
+	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
+		warning("No access_token found");
+		return 0;
+	}
+
+	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
+		warning("No refresh_token found");
+		return 0;
+	}
+
+	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
+	Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
+	return new BoxStorage(accessToken, refreshToken);
+}
+
+Common::String BoxStorage::getAuthLink() {
+	// now we only specify short "scummvm.org/c/bx" with actual redirect to the auth page
+	return "";
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
new file mode 100644
index 0000000..b58608c
--- /dev/null
+++ b/backends/cloud/box/boxstorage.h
@@ -0,0 +1,127 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXSTORAGE_H
+#define BACKENDS_CLOUD_BOX_BOXSTORAGE_H
+
+#include "backends/cloud/storage.h"
+#include "common/callback.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace Box {
+
+class BoxStorage: public Cloud::Storage {
+	static char *KEY, *SECRET;
+
+	static void loadKeyAndSecret();
+
+	Common::String _token, _refreshToken;
+
+	/** This private constructor is called from loadFromConfig(). */
+	BoxStorage(Common::String token, Common::String refreshToken);
+
+	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
+	void codeFlowComplete(BoolResponse response);
+
+	/** Constructs StorageInfo based on JSON response from cloud. */
+	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
+
+	void printJson(Networking::JsonResponse response);
+	void fileDownloaded(BoolResponse response);
+	void printFiles(FileArrayResponse response);
+	void printBool(BoolResponse response);
+	void printFile(UploadResponse response);
+
+	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
+public:	
+	/** This constructor uses OAuth code flow to get tokens. */
+	BoxStorage(Common::String code);
+	virtual ~BoxStorage();
+
+	/**
+	 * Storage methods, which are used by CloudManager to save
+	 * storage in configuration file.
+	 */
+
+	/**
+	 * Save storage data using ConfMan.
+	 * @param keyPrefix all saved keys must start with this prefix.
+	 * @note every Storage must write keyPrefix + "type" key
+	 *       with common value (e.g. "Dropbox").
+	 */
+	virtual void saveConfig(Common::String keyPrefix);
+
+	/**
+	* Return unique storage name.
+	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	*/
+	virtual Common::String name() const;
+
+	/** Public Cloud API comes down there. */
+
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+
+	/** Returns UploadStatus struct with info about uploaded file. */
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns the StorageInfo struct. */
+	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns storage's saves directory path with the trailing slash. */
+	virtual Common::String savesDirectoryPath();
+
+	/**
+	 * Load token and user id from configs and return BoxStorage for those.	
+	 * @return pointer to the newly created BoxStorage or 0 if some problem occured.
+	 */
+	static BoxStorage *loadFromConfig(Common::String keyPrefix);
+
+	/**
+	 * Returns Box auth link.
+	 */
+	static Common::String getAuthLink();
+
+	/**
+	 * Gets new access_token. If <code> passed is "", refresh_token is used.
+	 * Use "" in order to refresh token and pass a callback, so you could
+	 * continue your work when new token is available.
+	 */
+	void getAccessToken(BoolCallback callback, Common::String code = "");
+
+	Common::String accessToken() { return _token; }
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index dfe65e7..e5b2c2a 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -21,6 +21,7 @@
 */
 
 #include "backends/cloud/cloudmanager.h"
+#include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
@@ -52,6 +53,7 @@ Common::String CloudManager::getStorageConfigName(uint32 index) const {
 	case kStorageDropboxId: return "Dropbox";
 	case kStorageOneDriveId: return "OneDrive";
 	case kStorageGoogleDriveId: return "GoogleDrive";
+	case kStorageBoxId: return "Box";
 	}
 	assert(false); // Unhandled StorageID value
 	return "";
@@ -68,6 +70,9 @@ void CloudManager::loadStorage() {
 	case kStorageGoogleDriveId:
 		_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
 		break;
+	case kStorageBoxId:
+		_activeStorage = Box::BoxStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
+		break;
 	default:
 		_activeStorage = nullptr;
 	}
@@ -207,6 +212,7 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 	case kStorageDropboxId: storage = new Dropbox::DropboxStorage(code); break;
 	case kStorageOneDriveId: storage = new OneDrive::OneDriveStorage(code); break;
 	case kStorageGoogleDriveId: storage = new GoogleDrive::GoogleDriveStorage(code); break;
+	case kStorageBoxId: storage = new Box::BoxStorage(code); break;
 	}
 	//these would automatically request replaceStorage() when they receive the token
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 1cdbbcc..1098bdd 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -42,6 +42,7 @@ enum StorageID {
 	kStorageDropboxId = 1,
 	kStorageOneDriveId = 2,
 	kStorageGoogleDriveId = 3,
+	kStorageBoxId = 4,
 
 	kStorageTotal
 };
diff --git a/backends/module.mk b/backends/module.mk
index a472a0e..d1737b1 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -28,6 +28,7 @@ MODULE_OBJS += \
 	cloud/downloadrequest.o \
 	cloud/folderdownloadrequest.o \
 	cloud/savessyncrequest.o \
+	cloud/box/boxstorage.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 7871017..8e3f177 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -52,6 +52,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 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";


Commit: 0a43dad629099505701b14723f1b22ba013a869e
    https://github.com/scummvm/scummvm/commit/0a43dad629099505701b14723f1b22ba013a869e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Redirect to "/files" from "/"

"/" is used to receive "?code", but when there is no such parameter
passed, it's safe to redirect user to the "/files".

Changed paths:
    backends/networking/sdl_net/handlers/indexpagehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index da5cd2d..9208cd0 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -35,8 +35,18 @@ IndexPageHandler::~IndexPageHandler() {}
 void IndexPageHandler::handle(Client &client) {
 	Common::String code = client.queryParameter("code");
 
-	if (code == "") {		
-		HandlerUtils::setMessageHandler(client, _("This is a local webserver index page."));
+	if (code == "") {
+		// redirect to "/files"
+		HandlerUtils::setMessageHandler(
+			client,
+			Common::String::format(
+				"%s<br/><a href=\"files\">%s</a>",
+				_("This is a local webserver index page."),
+				client.queryParameter("path").c_str(),
+				_("Open Files manager")
+			),
+			"/files"
+		);
 		return;
 	}
 


Commit: 85f1ce8ece7d4f4c6e54cfe29eaebe2c4ec13aec
    https://github.com/scummvm/scummvm/commit/85f1ce8ece7d4f4c6e54cfe29eaebe2c4ec13aec
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add BoxTokenRefresher and BoxStorage::info()

BoxTokenRefresher does refresh if HTTP 401 is returned by the server.

To test refresher, BoxStorage::info() was added.

Changed paths:
  A backends/cloud/box/boxtokenrefresher.cpp
  A backends/cloud/box/boxtokenrefresher.h
    backends/cloud/box/boxstorage.cpp
    backends/module.mk



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 8a2e004..85bbf34 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/box/boxstorage.h"
+#include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
@@ -141,20 +142,25 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 	Common::JSONObject info = json->asObject();
 
 	Common::String uid, name, email;
-	uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
-
-	if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
-		Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
-		if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
-			Common::JSONObject user = createdBy.getVal("user")->asObject();
-			uid = user.getVal("id")->asString();
-			name = user.getVal("displayName")->asString();
-		}
-	}
+	uint64 quotaUsed = 0, quotaAllocated = 0;
 
-	if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
-		quotaUsed = info.getVal("size")->asIntegerNumber();
-	}
+	// can check that "type": "user"
+	// there is also "max_upload_size", "phone" and "avatar_url"
+
+	if (info.contains("id") && info.getVal("id")->isString())
+		uid = info.getVal("id")->asString();
+
+	if (info.contains("name") && info.getVal("name")->isString())
+		name = info.getVal("name")->asString();
+
+	if (info.contains("login") && info.getVal("login")->isString())
+		email = info.getVal("login")->asString();
+
+	if (info.contains("space_amount") && info.getVal("space_amount")->isIntegerNumber())
+		quotaAllocated = info.getVal("space_amount")->asIntegerNumber();
+
+	if (info.contains("space_used") && info.getVal("space_used")->isIntegerNumber())
+		quotaUsed = info.getVal("space_used")->asIntegerNumber();
 
 	Common::String username = email;
 	if (username == "") username = name;
@@ -254,13 +260,10 @@ Networking::Request *BoxStorage::createDirectory(Common::String path, BoolCallba
 }
 
 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
-	/*
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.Box.com/v1.0/drive/special/approot");
-	request->addHeader("Authorization: bearer " + _token);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.box.com/2.0/users/me");
+	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
-	*/
-	return nullptr; //TODO
 }
 
 Common::String BoxStorage::savesDirectoryPath() { return "saves/"; }
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
new file mode 100644
index 0000000..9dfbef5
--- /dev/null
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -0,0 +1,134 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/box/boxtokenrefresher.h"
+#include "backends/cloud/box/boxstorage.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace Box {
+
+BoxTokenRefresher::BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
+	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+
+BoxTokenRefresher::~BoxTokenRefresher() {}
+
+void BoxTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
+	if (!response.value) {
+		//failed to refresh token, notify user with NULL in original callback
+		warning("BoxTokenRefresher: failed to refresh token");
+		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
+	}
+
+	//update headers: first change header with token, then pass those to request
+	for (uint32 i = 0; i < _headers.size(); ++i) {
+		if (_headers[i].contains("Authorization")) {
+			_headers[i] = "Authorization: Bearer " + _parentStorage->accessToken();
+		}
+	}
+	setHeaders(_headers);
+
+	//successfully received refreshed token, can restart the original request now	
+	retry(0);
+}
+
+void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
+	if (!json) {
+		//that's probably not an error (200 OK)
+		CurlJsonRequest::finishJson(nullptr);
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (result.contains("type") && result.getVal("type")->isString() && result.getVal("type")->asString() == "error") {
+		//new token needed => request token & then retry original request
+		long httpCode = -1;
+		if (_stream) {
+			httpCode = _stream->httpResponseCode();
+			debug("code %ld", httpCode);
+		}
+
+		bool irrecoverable = true;
+
+		Common::String code, message;
+		if (result.contains("code")) {
+			code = result.getVal("code")->asString();
+			debug("code = %s", code.c_str());			
+		}
+
+		if (result.contains("message")) {
+			message = result.getVal("message")->asString();
+			debug("message = %s", message.c_str());
+		}
+
+		//TODO: decide when token refreshment will help
+		//if (code == "unauthenticated") irrecoverable = false;
+
+		if (irrecoverable) {			
+			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
+			delete json;
+			return;
+		}
+
+		pause();		
+		delete json;
+		_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
+		return;
+	}
+
+	//notify user of success
+	CurlJsonRequest::finishJson(json);
+}
+
+void BoxTokenRefresher::finishError(Networking::ErrorResponse error) {
+	if (error.httpResponseCode == 401) { // invalid_token
+		pause();
+		_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
+		return;
+	}
+
+	// there are also 400 == invalid_request and 403 == insufficient_scope
+	// but TokenRefresher is there to refresh token when it's invalid only
+
+	Request::finishError(error);
+}
+
+void BoxTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+	_headers = headers;
+	curl_slist_free_all(_headersList);
+	_headersList = 0;
+	for (uint32 i = 0; i < headers.size(); ++i)
+		CurlJsonRequest::addHeader(headers[i]);
+}
+
+void BoxTokenRefresher::addHeader(Common::String header) {
+	_headers.push_back(header);
+	CurlJsonRequest::addHeader(header);
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxtokenrefresher.h b/backends/cloud/box/boxtokenrefresher.h
new file mode 100644
index 0000000..7dedefd
--- /dev/null
+++ b/backends/cloud/box/boxtokenrefresher.h
@@ -0,0 +1,53 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXTOKENREFRESHER_H
+#define BACKENDS_CLOUD_BOX_BOXTOKENREFRESHER_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace Box {
+
+class BoxStorage;
+
+class BoxTokenRefresher: public Networking::CurlJsonRequest {
+	BoxStorage *_parentStorage;
+	Common::Array<Common::String> _headers;	
+	
+	void tokenRefreshed(Storage::BoolResponse response);
+
+	virtual void finishJson(Common::JSONValue *json);
+	virtual void finishError(Networking::ErrorResponse error);
+public:	
+	BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
+	virtual ~BoxTokenRefresher();
+
+	virtual void setHeaders(Common::Array<Common::String> &headers);
+	virtual void addHeader(Common::String header);
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index d1737b1..2c2a4e2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS += \
 	cloud/folderdownloadrequest.o \
 	cloud/savessyncrequest.o \
 	cloud/box/boxstorage.o \
+	cloud/box/boxtokenrefresher.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \


Commit: eb269e137f8a494a670bd4f2ee3370fdfb81e113
    https://github.com/scummvm/scummvm/commit/eb269e137f8a494a670bd4f2ee3370fdfb81e113
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add BoxListDirectoryByIdRequest

Similarly to Google Drive, Box uses only ids of files. That means id
resolving would be slow.

Changed paths:
  A backends/cloud/box/boxlistdirectorybyidrequest.cpp
  A backends/cloud/box/boxlistdirectorybyidrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/iso8601.cpp
    backends/module.mk



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
new file mode 100644
index 0000000..d4606b1
--- /dev/null
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -0,0 +1,169 @@
+/* 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 "backends/cloud/box/boxlistdirectorybyidrequest.h"
+#include "backends/cloud/box/boxstorage.h"
+#include "backends/cloud/box/boxtokenrefresher.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace Box {
+
+#define BOX_LIST_DIRECTORY_LIMIT 1000
+
+BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
+	 _workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+BoxListDirectoryByIdRequest::~BoxListDirectoryByIdRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void BoxListDirectoryByIdRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_files.clear();
+	_ignoreCallback = false;
+
+	makeRequest(0);
+}
+
+void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
+	Common::String url = Common::String::format(
+		"https://api.box.com/2.0/folders/%s/items?offset=%u&limit=%u&fields=%s",
+		_requestedId.c_str(),
+		offset,
+		BOX_LIST_DIRECTORY_LIMIT,
+		"id,type,name,size,modified_at"
+	);
+
+	Networking::JsonCallback callback = new Common::Callback<BoxListDirectoryByIdRequest, Networking::JsonResponse>(this, &BoxListDirectoryByIdRequest::responseCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<BoxListDirectoryByIdRequest, Networking::ErrorResponse>(this, &BoxListDirectoryByIdRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
+	Common::JSONValue *json = response.value;
+	if (json) {
+		Common::JSONObject responseObject = json->asObject();
+
+		//debug("%s", json->stringify(true).c_str());
+		
+		//TODO: check that error is returned the right way
+		/*
+		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
+			warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
+			error.failed = true;
+			error.response = json->stringify();
+			finishError(error);
+			delete json;
+			return;
+		}
+		*/
+
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+
+		if (responseObject.contains("entries") && responseObject.getVal("entries")->isArray()) {
+			Common::JSONArray items = responseObject.getVal("entries")->asArray();
+			for (uint32 i = 0; i < items.size(); ++i) {
+				Common::JSONObject item = items[i]->asObject();
+				Common::String id = item.getVal("id")->asString();
+				Common::String name = item.getVal("name")->asString();
+				bool isDirectory = (item.getVal("type")->asString() == "folder");
+				uint32 size = 0, timestamp = 0;
+				if (item.contains("size")) {
+					if (item.getVal("size")->isString())
+						size = item.getVal("size")->asString().asUint64();
+					else if (item.getVal("size")->isIntegerNumber())
+						size = item.getVal("size")->asIntegerNumber();
+					else
+						warning("strange type for field 'size'");
+				}
+				if (item.contains("modified_at") && item.getVal("modified_at")->isString())
+					timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
+
+				//as we list directory by id, we can't determine full path for the file, so we leave it empty
+				_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
+			}
+		}
+
+		uint32 received = 0;
+		uint32 totalCount = 0;
+		if (responseObject.contains("total_count") && responseObject.getVal("total_count")->isIntegerNumber())
+			totalCount = responseObject.getVal("total_count")->asIntegerNumber();
+		if (responseObject.contains("offset") && responseObject.getVal("offset")->isIntegerNumber())
+			received = responseObject.getVal("offset")->asIntegerNumber();
+		if (responseObject.contains("limit") && responseObject.getVal("limit")->isIntegerNumber())
+			received += responseObject.getVal("limit")->asIntegerNumber();
+		bool hasMore = (received < totalCount);
+
+		if (hasMore) makeRequest(received);
+		else finishListing(_files);
+	} else {
+		warning("null, not json");
+		error.failed = true;
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void BoxListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void BoxListDirectoryByIdRequest::handle() {}
+
+void BoxListDirectoryByIdRequest::restart() { start(); }
+
+Common::String BoxListDirectoryByIdRequest::date() const { return _date; }
+
+void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {	
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.h b/backends/cloud/box/boxlistdirectorybyidrequest.h
new file mode 100644
index 0000000..ccf8d2e
--- /dev/null
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.h
@@ -0,0 +1,63 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYBYIDREQUEST_H
+#define BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYBYIDREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Box {
+
+class BoxStorage;
+
+class BoxListDirectoryByIdRequest: public Networking::Request {
+	Common::String _requestedId;	
+	BoxStorage *_storage;
+
+	Storage::ListDirectoryCallback _listDirectoryCallback;
+	Common::Array<StorageFile> _files;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _date;
+	
+	void start();
+	void makeRequest(uint32 offset);
+	void responseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishListing(Common::Array<StorageFile> &files);
+public:
+	BoxListDirectoryByIdRequest(BoxStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb);
+	virtual ~BoxListDirectoryByIdRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual Common::String date() const;
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 85bbf34..9842ed2 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/box/boxstorage.h"
+#include "backends/cloud/box/boxlistdirectorybyidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
@@ -210,10 +211,18 @@ void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCal
 }
 
 Networking::Request *BoxStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<BoxStorage, FileArrayResponse>(this, &BoxStorage::printFiles);
 	//return addRequest(new BoxListDirectoryRequest(this, path, callback, errorCallback, recursive));
 	return nullptr; //TODO
 }
 
+Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<BoxStorage, FileArrayResponse>(this, &BoxStorage::printFiles);
+	return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
+}
+
 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	//return addRequest(new BoxUploadRequest(this, path, contents, callback, errorCallback));
 	return nullptr; //TODO
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index b58608c..3ce7a37 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -81,6 +81,7 @@ public:
 
 	/** Returns ListDirectoryStatus struct with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
index 3bc169a..1675c7c 100644
--- a/backends/cloud/iso8601.cpp
+++ b/backends/cloud/iso8601.cpp
@@ -33,6 +33,12 @@ Common::String getSubstring(const Common::String &s, uint32 beginning, uint32 en
 	return result;
 }
 
+int find(const char *cstr, uint32 startPosition, char needle) {
+	const char *res = strchr(cstr + startPosition, needle);
+	if (res == nullptr) return -1;
+	return res - cstr;
+}
+
 }
 
 namespace Cloud {
@@ -41,12 +47,13 @@ namespace ISO8601 {
 uint32 convertToTimestamp(const Common::String &iso8601Date) {		
 	//2015-05-12T15:50:38Z
 	const char *cstr = iso8601Date.c_str();	
-	uint32 firstHyphen = strchr(cstr, '-') - cstr;
-	uint32 secondHyphen = strchr(cstr + firstHyphen + 1, '-') - cstr;
-	uint32 tSeparator = strchr(cstr + secondHyphen + 1, 'T') - cstr;
-	uint32 firstColon = strchr(cstr + tSeparator + 1, ':') - cstr;
-	uint32 secondColon = strchr(cstr + firstColon + 1, ':') - cstr;
-	uint32 zSeparator = strchr(cstr + secondColon + 1, 'Z') - cstr;
+	int firstHyphen = find(cstr, 0, '-');
+	int secondHyphen = find(cstr, firstHyphen + 1, '-');
+	int tSeparator = find(cstr, secondHyphen + 1, 'T');
+	int firstColon = find(cstr, tSeparator + 1, ':');
+	int secondColon = find(cstr, firstColon + 1, ':');
+	int zSeparator = find(cstr, secondColon + 1, 'Z');
+	if (zSeparator == -1) zSeparator = find(cstr, secondColon + 1, '-'); // Box's RFC 3339
 	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char	
 
 	Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
diff --git a/backends/module.mk b/backends/module.mk
index 2c2a4e2..07ba983 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS += \
 	cloud/folderdownloadrequest.o \
 	cloud/savessyncrequest.o \
 	cloud/box/boxstorage.o \
+	cloud/box/boxlistdirectorybyidrequest.o \
 	cloud/box/boxtokenrefresher.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \


Commit: e0a6b2135de6a83f2ed4177c96fabf216412e5fb
    https://github.com/scummvm/scummvm/commit/e0a6b2135de6a83f2ed4177c96fabf216412e5fb
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add BoxListDirectoryRequest

And used in it BoxResolveIdRequest.

TODO: make some generic ResolveIdRequest and ListDirectoryRequest for
id-based storages. It's really similar, I just had to change a few
details in GoogleDrive ListDirectory and ResolveId requests.

Changed paths:
  A backends/cloud/box/boxlistdirectoryrequest.cpp
  A backends/cloud/box/boxlistdirectoryrequest.h
  A backends/cloud/box/boxresolveidrequest.cpp
  A backends/cloud/box/boxresolveidrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/module.mk



diff --git a/backends/cloud/box/boxlistdirectoryrequest.cpp b/backends/cloud/box/boxlistdirectoryrequest.cpp
new file mode 100644
index 0000000..b35c8c8
--- /dev/null
+++ b/backends/cloud/box/boxlistdirectoryrequest.cpp
@@ -0,0 +1,129 @@
+/* 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 "backends/cloud/box/boxlistdirectoryrequest.h"
+#include "backends/cloud/box/boxstorage.h"
+
+namespace Cloud {
+namespace Box {
+
+BoxListDirectoryRequest::BoxListDirectoryRequest(BoxStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+BoxListDirectoryRequest::~BoxListDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void BoxListDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_files.clear();
+	_directoriesQueue.clear();
+	_currentDirectory = StorageFile();
+	_ignoreCallback = false;
+
+	//find out that directory's id
+	Storage::UploadCallback innerCallback = new Common::Callback<BoxListDirectoryRequest, Storage::UploadResponse>(this, &BoxListDirectoryRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<BoxListDirectoryRequest, Networking::ErrorResponse>(this, &BoxListDirectoryRequest::idResolveErrorCallback);
+	_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
+}
+
+void BoxListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	StorageFile directory = response.value;
+	directory.setPath(_requestedPath);
+	_directoriesQueue.push_back(directory);
+	listNextDirectory();
+}
+
+void BoxListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void BoxListDirectoryRequest::listNextDirectory() {
+	if (_directoriesQueue.empty()) {
+		finishListing(_files);
+		return;
+	}
+
+	_currentDirectory = _directoriesQueue.back();
+	_directoriesQueue.pop_back();
+
+	Storage::FileArrayCallback callback = new Common::Callback<BoxListDirectoryRequest, Storage::FileArrayResponse>(this, &BoxListDirectoryRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<BoxListDirectoryRequest, Networking::ErrorResponse>(this, &BoxListDirectoryRequest::listedDirectoryErrorCallback);	
+	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
+}
+
+void BoxListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	for (uint32 i = 0; i < response.value.size(); ++i) {
+		StorageFile &file = response.value[i];
+		Common::String path = _currentDirectory.path();
+		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+		path += file.name();
+		file.setPath(path);
+		_files.push_back(file);
+		if (_requestedRecursive && file.isDirectory()) {
+			_directoriesQueue.push_back(file);
+		}
+	}
+
+	listNextDirectory();
+}
+
+void BoxListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void BoxListDirectoryRequest::handle() {}
+
+void BoxListDirectoryRequest::restart() { start(); }
+
+Common::String BoxListDirectoryRequest::date() const { return _date; }
+
+void BoxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxlistdirectoryrequest.h b/backends/cloud/box/boxlistdirectoryrequest.h
new file mode 100644
index 0000000..7392cdd
--- /dev/null
+++ b/backends/cloud/box/boxlistdirectoryrequest.h
@@ -0,0 +1,66 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Box {
+
+class BoxStorage;
+
+class BoxListDirectoryRequest: public Networking::Request {
+	Common::String _requestedPath;
+	bool _requestedRecursive;
+	BoxStorage *_storage;
+	Storage::ListDirectoryCallback _listDirectoryCallback;
+	Common::Array<StorageFile> _files;
+	Common::Array<StorageFile> _directoriesQueue;
+	StorageFile _currentDirectory;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _date;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveErrorCallback(Networking::ErrorResponse error);
+	void listNextDirectory();
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishListing(Common::Array<StorageFile> &files);
+public:
+	BoxListDirectoryRequest(BoxStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
+	virtual ~BoxListDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual Common::String date() const;
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/box/boxresolveidrequest.cpp b/backends/cloud/box/boxresolveidrequest.cpp
new file mode 100644
index 0000000..f07a94b
--- /dev/null
+++ b/backends/cloud/box/boxresolveidrequest.cpp
@@ -0,0 +1,125 @@
+/* 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 "backends/cloud/box/boxresolveidrequest.h"
+#include "backends/cloud/box/boxstorage.h"
+
+namespace Cloud {
+namespace Box {
+
+BoxResolveIdRequest::BoxResolveIdRequest(BoxStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _storage(storage), _uploadCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+BoxResolveIdRequest::~BoxResolveIdRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _uploadCallback;
+}
+
+void BoxResolveIdRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_currentDirectory = "";
+	_currentDirectoryId = "0";
+	_ignoreCallback = false;
+	
+	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
+}
+
+void BoxResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
+	if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
+		finishFile(fileToReturn);
+		return;
+	}
+
+	Storage::FileArrayCallback callback = new Common::Callback<BoxResolveIdRequest, Storage::FileArrayResponse>(this, &BoxResolveIdRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<BoxResolveIdRequest, Networking::ErrorResponse>(this, &BoxResolveIdRequest::listedDirectoryErrorCallback);
+	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
+}
+
+void BoxResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	Common::String currentLevelName = _requestedPath;
+	///debug("'%s'", currentLevelName.c_str());
+	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
+	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
+	///debug("'%s'", currentLevelName.c_str());
+	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
+		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
+			currentLevelName.erase(i);
+			///debug("'%s'", currentLevelName.c_str());
+			break;
+		}
+	}
+
+	Common::String path = _currentDirectory;
+	if (path != "") path += "/";
+	path += currentLevelName;
+	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
+
+	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
+
+	Common::Array<StorageFile> &files = response.value;
+	bool found = false;
+	for (uint32 i = 0; i < files.size(); ++i) {
+		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
+			if (_currentDirectory != "") _currentDirectory += "/";
+			_currentDirectory += files[i].name();
+			_currentDirectoryId = files[i].id();
+			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
+			listNextDirectory(files[i]);
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
+		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
+	}
+}
+
+void BoxResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void BoxResolveIdRequest::handle() {}
+
+void BoxResolveIdRequest::restart() { start(); }
+
+void BoxResolveIdRequest::finishFile(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxresolveidrequest.h b/backends/cloud/box/boxresolveidrequest.h
new file mode 100644
index 0000000..3807549
--- /dev/null
+++ b/backends/cloud/box/boxresolveidrequest.h
@@ -0,0 +1,60 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXRESOLVEIDREQUEST_H
+#define BACKENDS_CLOUD_BOX_BOXRESOLVEIDREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Box {
+
+class BoxStorage;
+
+class BoxResolveIdRequest: public Networking::Request {
+	Common::String _requestedPath;	
+	BoxStorage *_storage;
+	Storage::UploadCallback _uploadCallback;
+	Common::String _currentDirectory;
+	Common::String _currentDirectoryId;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void listNextDirectory(StorageFile fileToReturn);
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishFile(StorageFile file);
+public:
+	BoxResolveIdRequest(BoxStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
+	virtual ~BoxResolveIdRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 9842ed2..65f90a5 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -23,6 +23,8 @@
 
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
+#include "backends/cloud/box/boxlistdirectoryrequest.h"
+#include "backends/cloud/box/boxresolveidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
@@ -210,11 +212,16 @@ void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCal
 	delete response.value;
 }
 
+Networking::Request *BoxStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<BoxStorage, UploadResponse>(this, &BoxStorage::printFile);
+	return addRequest(new BoxResolveIdRequest(this, path, callback, errorCallback));
+}
+
 Networking::Request *BoxStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	if (!callback) callback = new Common::Callback<BoxStorage, FileArrayResponse>(this, &BoxStorage::printFiles);
-	//return addRequest(new BoxListDirectoryRequest(this, path, callback, errorCallback, recursive));
-	return nullptr; //TODO
+	return addRequest(new BoxListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
 Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 3ce7a37..d0b4d1a 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -79,6 +79,9 @@ public:
 
 	/** Public Cloud API comes down there. */
 
+	/** Returns StorageFile with the resolved file's id. */
+	virtual Networking::Request *resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
 	/** Returns ListDirectoryStatus struct with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/module.mk b/backends/module.mk
index 07ba983..53eca87 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -30,6 +30,8 @@ MODULE_OBJS += \
 	cloud/savessyncrequest.o \
 	cloud/box/boxstorage.o \
 	cloud/box/boxlistdirectorybyidrequest.o \
+	cloud/box/boxlistdirectoryrequest.o \
+	cloud/box/boxresolveidrequest.o \
 	cloud/box/boxtokenrefresher.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \


Commit: 2b3caf1efadd2a68384978e77cfceab158c703f3
    https://github.com/scummvm/scummvm/commit/2b3caf1efadd2a68384978e77cfceab158c703f3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add IdStorage

This is a special base class for Storages which are using ids instead of
paths in their APIs, like Box or Google Drive.

This commit makes Box derived from IdStorage.

Changed paths:
  A backends/cloud/id/idlistdirectoryrequest.cpp
  A backends/cloud/id/idlistdirectoryrequest.h
  A backends/cloud/id/idresolveidrequest.cpp
  A backends/cloud/id/idresolveidrequest.h
  A backends/cloud/id/idstorage.cpp
  A backends/cloud/id/idstorage.h
  R backends/cloud/box/boxlistdirectoryrequest.cpp
  R backends/cloud/box/boxlistdirectoryrequest.h
  R backends/cloud/box/boxresolveidrequest.cpp
  R backends/cloud/box/boxresolveidrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/module.mk



diff --git a/backends/cloud/box/boxlistdirectoryrequest.cpp b/backends/cloud/box/boxlistdirectoryrequest.cpp
deleted file mode 100644
index b35c8c8..0000000
--- a/backends/cloud/box/boxlistdirectoryrequest.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/* 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 "backends/cloud/box/boxlistdirectoryrequest.h"
-#include "backends/cloud/box/boxstorage.h"
-
-namespace Cloud {
-namespace Box {
-
-BoxListDirectoryRequest::BoxListDirectoryRequest(BoxStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
-	Networking::Request(nullptr, ecb),
-	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-BoxListDirectoryRequest::~BoxListDirectoryRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _listDirectoryCallback;
-}
-
-void BoxListDirectoryRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_files.clear();
-	_directoriesQueue.clear();
-	_currentDirectory = StorageFile();
-	_ignoreCallback = false;
-
-	//find out that directory's id
-	Storage::UploadCallback innerCallback = new Common::Callback<BoxListDirectoryRequest, Storage::UploadResponse>(this, &BoxListDirectoryRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<BoxListDirectoryRequest, Networking::ErrorResponse>(this, &BoxListDirectoryRequest::idResolveErrorCallback);
-	_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
-}
-
-void BoxListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-
-	StorageFile directory = response.value;
-	directory.setPath(_requestedPath);
-	_directoriesQueue.push_back(directory);
-	listNextDirectory();
-}
-
-void BoxListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void BoxListDirectoryRequest::listNextDirectory() {
-	if (_directoriesQueue.empty()) {
-		finishListing(_files);
-		return;
-	}
-
-	_currentDirectory = _directoriesQueue.back();
-	_directoriesQueue.pop_back();
-
-	Storage::FileArrayCallback callback = new Common::Callback<BoxListDirectoryRequest, Storage::FileArrayResponse>(this, &BoxListDirectoryRequest::listedDirectoryCallback);
-	Networking::ErrorCallback failureCallback = new Common::Callback<BoxListDirectoryRequest, Networking::ErrorResponse>(this, &BoxListDirectoryRequest::listedDirectoryErrorCallback);	
-	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
-}
-
-void BoxListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-
-	for (uint32 i = 0; i < response.value.size(); ++i) {
-		StorageFile &file = response.value[i];
-		Common::String path = _currentDirectory.path();
-		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-		path += file.name();
-		file.setPath(path);
-		_files.push_back(file);
-		if (_requestedRecursive && file.isDirectory()) {
-			_directoriesQueue.push_back(file);
-		}
-	}
-
-	listNextDirectory();
-}
-
-void BoxListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void BoxListDirectoryRequest::handle() {}
-
-void BoxListDirectoryRequest::restart() { start(); }
-
-Common::String BoxListDirectoryRequest::date() const { return _date; }
-
-void BoxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
-	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
-}
-
-} // End of namespace Box
-} // End of namespace Cloud
diff --git a/backends/cloud/box/boxlistdirectoryrequest.h b/backends/cloud/box/boxlistdirectoryrequest.h
deleted file mode 100644
index 7392cdd..0000000
--- a/backends/cloud/box/boxlistdirectoryrequest.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYREQUEST_H
-#define BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace Box {
-
-class BoxStorage;
-
-class BoxListDirectoryRequest: public Networking::Request {
-	Common::String _requestedPath;
-	bool _requestedRecursive;
-	BoxStorage *_storage;
-	Storage::ListDirectoryCallback _listDirectoryCallback;
-	Common::Array<StorageFile> _files;
-	Common::Array<StorageFile> _directoriesQueue;
-	StorageFile _currentDirectory;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-	Common::String _date;
-
-	void start();
-	void idResolvedCallback(Storage::UploadResponse response);
-	void idResolveErrorCallback(Networking::ErrorResponse error);
-	void listNextDirectory();
-	void listedDirectoryCallback(Storage::FileArrayResponse response);
-	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishListing(Common::Array<StorageFile> &files);
-public:
-	BoxListDirectoryRequest(BoxStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
-	virtual ~BoxListDirectoryRequest();
-
-	virtual void handle();
-	virtual void restart();
-	virtual Common::String date() const;
-};
-
-} // End of namespace Box
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/box/boxresolveidrequest.cpp b/backends/cloud/box/boxresolveidrequest.cpp
deleted file mode 100644
index f07a94b..0000000
--- a/backends/cloud/box/boxresolveidrequest.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/* 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 "backends/cloud/box/boxresolveidrequest.h"
-#include "backends/cloud/box/boxstorage.h"
-
-namespace Cloud {
-namespace Box {
-
-BoxResolveIdRequest::BoxResolveIdRequest(BoxStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
-	Networking::Request(nullptr, ecb),
-	_requestedPath(path), _storage(storage), _uploadCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-BoxResolveIdRequest::~BoxResolveIdRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _uploadCallback;
-}
-
-void BoxResolveIdRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_currentDirectory = "";
-	_currentDirectoryId = "0";
-	_ignoreCallback = false;
-	
-	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
-}
-
-void BoxResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
-	if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
-		finishFile(fileToReturn);
-		return;
-	}
-
-	Storage::FileArrayCallback callback = new Common::Callback<BoxResolveIdRequest, Storage::FileArrayResponse>(this, &BoxResolveIdRequest::listedDirectoryCallback);
-	Networking::ErrorCallback failureCallback = new Common::Callback<BoxResolveIdRequest, Networking::ErrorResponse>(this, &BoxResolveIdRequest::listedDirectoryErrorCallback);
-	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
-}
-
-void BoxResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	
-	Common::String currentLevelName = _requestedPath;
-	///debug("'%s'", currentLevelName.c_str());
-	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
-	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
-	///debug("'%s'", currentLevelName.c_str());
-	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
-		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
-			currentLevelName.erase(i);
-			///debug("'%s'", currentLevelName.c_str());
-			break;
-		}
-	}
-
-	Common::String path = _currentDirectory;
-	if (path != "") path += "/";
-	path += currentLevelName;
-	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
-
-	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
-
-	Common::Array<StorageFile> &files = response.value;
-	bool found = false;
-	for (uint32 i = 0; i < files.size(); ++i) {
-		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
-			if (_currentDirectory != "") _currentDirectory += "/";
-			_currentDirectory += files[i].name();
-			_currentDirectoryId = files[i].id();
-			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
-			listNextDirectory(files[i]);
-			found = true;
-			break;
-		}
-	}
-
-	if (!found) {
-		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
-		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
-	}
-}
-
-void BoxResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void BoxResolveIdRequest::handle() {}
-
-void BoxResolveIdRequest::restart() { start(); }
-
-void BoxResolveIdRequest::finishFile(StorageFile file) {
-	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
-}
-
-} // End of namespace Box
-} // End of namespace Cloud
diff --git a/backends/cloud/box/boxresolveidrequest.h b/backends/cloud/box/boxresolveidrequest.h
deleted file mode 100644
index 3807549..0000000
--- a/backends/cloud/box/boxresolveidrequest.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_BOX_BOXRESOLVEIDREQUEST_H
-#define BACKENDS_CLOUD_BOX_BOXRESOLVEIDREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace Box {
-
-class BoxStorage;
-
-class BoxResolveIdRequest: public Networking::Request {
-	Common::String _requestedPath;	
-	BoxStorage *_storage;
-	Storage::UploadCallback _uploadCallback;
-	Common::String _currentDirectory;
-	Common::String _currentDirectoryId;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-
-	void start();
-	void listNextDirectory(StorageFile fileToReturn);
-	void listedDirectoryCallback(Storage::FileArrayResponse response);
-	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishFile(StorageFile file);
-public:
-	BoxResolveIdRequest(BoxStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
-	virtual ~BoxResolveIdRequest();
-
-	virtual void handle();
-	virtual void restart();
-};
-
-} // End of namespace Box
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 65f90a5..3681cbf 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -23,8 +23,6 @@
 
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
-#include "backends/cloud/box/boxlistdirectoryrequest.h"
-#include "backends/cloud/box/boxresolveidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
@@ -178,17 +176,6 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 	delete json;
 }
 
-void BoxStorage::printJson(Networking::JsonResponse response) {
-	Common::JSONValue *json = response.value;
-	if (!json) {
-		warning("printJson: NULL");
-		return;
-	}
-
-	debug("%s", json->stringify().c_str());
-	delete json;
-}
-
 void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
 	if (!response.value) {
 		warning("fileInfoCallback: NULL");
@@ -212,21 +199,9 @@ void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCal
 	delete response.value;
 }
 
-Networking::Request *BoxStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<BoxStorage, UploadResponse>(this, &BoxStorage::printFile);
-	return addRequest(new BoxResolveIdRequest(this, path, callback, errorCallback));
-}
-
-Networking::Request *BoxStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<BoxStorage, FileArrayResponse>(this, &BoxStorage::printFiles);
-	return addRequest(new BoxListDirectoryRequest(this, path, callback, errorCallback, recursive));
-}
-
 Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<BoxStorage, FileArrayResponse>(this, &BoxStorage::printFiles);
+	if (!callback) callback = getPrintFilesCallback();
 	return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
 }
 
@@ -251,24 +226,6 @@ void BoxStorage::fileDownloaded(BoolResponse response) {
 	else debug("download failed!");
 }
 
-void BoxStorage::printFiles(FileArrayResponse response) {
-	debug("files:");
-	Common::Array<StorageFile> &files = response.value;
-	for (uint32 i = 0; i < files.size(); ++i)
-		debug("\t%s", files[i].path().c_str());
-}
-
-void BoxStorage::printBool(BoolResponse response) {
-	debug("bool: %s", response.value ? "true" : "false");
-}
-
-void BoxStorage::printFile(UploadResponse response) {
-	debug("\nuploaded file info:");
-	debug("\tpath: %s", response.value.path().c_str());
-	debug("\tsize: %u", response.value.size());
-	debug("\ttimestamp: %u", response.value.timestamp());
-}
-
 Networking::Request *BoxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	//return addRequest(new BoxCreateDirectoryRequest(this, path, callback, errorCallback));
@@ -307,5 +264,9 @@ Common::String BoxStorage::getAuthLink() {
 	return "";
 }
 
+Common::String BoxStorage::getRootDirectoryId() {
+	return "0";
+}
+
 } // End of namespace Box
 } // End of namespace Cloud
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index d0b4d1a..865358c 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -23,14 +23,14 @@
 #ifndef BACKENDS_CLOUD_BOX_BOXSTORAGE_H
 #define BACKENDS_CLOUD_BOX_BOXSTORAGE_H
 
-#include "backends/cloud/storage.h"
+#include "backends/cloud/id/idstorage.h"
 #include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
 namespace Box {
 
-class BoxStorage: public Cloud::Storage {
+class BoxStorage: public Id::IdStorage {
 	static char *KEY, *SECRET;
 
 	static void loadKeyAndSecret();
@@ -46,11 +46,7 @@ class BoxStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void printJson(Networking::JsonResponse response);
 	void fileDownloaded(BoolResponse response);
-	void printFiles(FileArrayResponse response);
-	void printBool(BoolResponse response);
-	void printFile(UploadResponse response);
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 public:	
@@ -79,11 +75,6 @@ public:
 
 	/** Public Cloud API comes down there. */
 
-	/** Returns StorageFile with the resolved file's id. */
-	virtual Networking::Request *resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Returns ListDirectoryStatus struct with list of files. */
-	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
@@ -115,6 +106,8 @@ public:
 	 */
 	static Common::String getAuthLink();
 
+	virtual Common::String getRootDirectoryId();
+
 	/**
 	 * Gets new access_token. If <code> passed is "", refresh_token is used.
 	 * Use "" in order to refresh token and pass a callback, so you could
diff --git a/backends/cloud/id/idlistdirectoryrequest.cpp b/backends/cloud/id/idlistdirectoryrequest.cpp
new file mode 100644
index 0000000..012065d
--- /dev/null
+++ b/backends/cloud/id/idlistdirectoryrequest.cpp
@@ -0,0 +1,129 @@
+/* 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 "backends/cloud/id/idlistdirectoryrequest.h"
+#include "backends/cloud/id/idstorage.h"
+
+namespace Cloud {
+namespace Id {
+
+IdListDirectoryRequest::IdListDirectoryRequest(IdStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+IdListDirectoryRequest::~IdListDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _listDirectoryCallback;
+}
+
+void IdListDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_files.clear();
+	_directoriesQueue.clear();
+	_currentDirectory = StorageFile();
+	_ignoreCallback = false;
+
+	//find out that directory's id
+	Storage::UploadCallback innerCallback = new Common::Callback<IdListDirectoryRequest, Storage::UploadResponse>(this, &IdListDirectoryRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdListDirectoryRequest, Networking::ErrorResponse>(this, &IdListDirectoryRequest::idResolveErrorCallback);
+	_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
+}
+
+void IdListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	StorageFile directory = response.value;
+	directory.setPath(_requestedPath);
+	_directoriesQueue.push_back(directory);
+	listNextDirectory();
+}
+
+void IdListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void IdListDirectoryRequest::listNextDirectory() {
+	if (_directoriesQueue.empty()) {
+		finishListing(_files);
+		return;
+	}
+
+	_currentDirectory = _directoriesQueue.back();
+	_directoriesQueue.pop_back();
+
+	Storage::FileArrayCallback callback = new Common::Callback<IdListDirectoryRequest, Storage::FileArrayResponse>(this, &IdListDirectoryRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<IdListDirectoryRequest, Networking::ErrorResponse>(this, &IdListDirectoryRequest::listedDirectoryErrorCallback);	
+	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
+}
+
+void IdListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	for (uint32 i = 0; i < response.value.size(); ++i) {
+		StorageFile &file = response.value[i];
+		Common::String path = _currentDirectory.path();
+		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+		path += file.name();
+		file.setPath(path);
+		_files.push_back(file);
+		if (_requestedRecursive && file.isDirectory()) {
+			_directoriesQueue.push_back(file);
+		}
+	}
+
+	listNextDirectory();
+}
+
+void IdListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void IdListDirectoryRequest::handle() {}
+
+void IdListDirectoryRequest::restart() { start(); }
+
+Common::String IdListDirectoryRequest::date() const { return _date; }
+
+void IdListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
+	Request::finishSuccess();
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/idlistdirectoryrequest.h b/backends/cloud/id/idlistdirectoryrequest.h
new file mode 100644
index 0000000..58c5d2c
--- /dev/null
+++ b/backends/cloud/id/idlistdirectoryrequest.h
@@ -0,0 +1,66 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDLISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ID_IDLISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage;
+
+class IdListDirectoryRequest: public Networking::Request {
+	Common::String _requestedPath;
+	bool _requestedRecursive;
+	IdStorage *_storage;
+	Storage::ListDirectoryCallback _listDirectoryCallback;
+	Common::Array<StorageFile> _files;
+	Common::Array<StorageFile> _directoriesQueue;
+	StorageFile _currentDirectory;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _date;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveErrorCallback(Networking::ErrorResponse error);
+	void listNextDirectory();
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishListing(Common::Array<StorageFile> &files);
+public:
+	IdListDirectoryRequest(IdStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
+	virtual ~IdListDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual Common::String date() const;
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/id/idresolveidrequest.cpp b/backends/cloud/id/idresolveidrequest.cpp
new file mode 100644
index 0000000..fc61137
--- /dev/null
+++ b/backends/cloud/id/idresolveidrequest.cpp
@@ -0,0 +1,125 @@
+/* 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 "backends/cloud/id/idresolveidrequest.h"
+#include "backends/cloud/id/idstorage.h"
+
+namespace Cloud {
+namespace Id {
+
+IdResolveIdRequest::IdResolveIdRequest(IdStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
+	Networking::Request(nullptr, ecb),
+	_requestedPath(path), _storage(storage), _uploadCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+IdResolveIdRequest::~IdResolveIdRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _uploadCallback;
+}
+
+void IdResolveIdRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_currentDirectory = "";
+	_currentDirectoryId = _storage->getRootDirectoryId();
+	_ignoreCallback = false;
+	
+	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
+}
+
+void IdResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
+	if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
+		finishFile(fileToReturn);
+		return;
+	}
+
+	Storage::FileArrayCallback callback = new Common::Callback<IdResolveIdRequest, Storage::FileArrayResponse>(this, &IdResolveIdRequest::listedDirectoryCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<IdResolveIdRequest, Networking::ErrorResponse>(this, &IdResolveIdRequest::listedDirectoryErrorCallback);
+	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
+}
+
+void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	
+	Common::String currentLevelName = _requestedPath;
+	///debug("'%s'", currentLevelName.c_str());
+	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
+	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
+	///debug("'%s'", currentLevelName.c_str());
+	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
+		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
+			currentLevelName.erase(i);
+			///debug("'%s'", currentLevelName.c_str());
+			break;
+		}
+	}
+
+	Common::String path = _currentDirectory;
+	if (path != "") path += "/";
+	path += currentLevelName;
+	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
+
+	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
+
+	Common::Array<StorageFile> &files = response.value;
+	bool found = false;
+	for (uint32 i = 0; i < files.size(); ++i) {
+		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
+			if (_currentDirectory != "") _currentDirectory += "/";
+			_currentDirectory += files[i].name();
+			_currentDirectoryId = files[i].id();
+			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
+			listNextDirectory(files[i]);
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
+		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
+	}
+}
+
+void IdResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void IdResolveIdRequest::handle() {}
+
+void IdResolveIdRequest::restart() { start(); }
+
+void IdResolveIdRequest::finishFile(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/idresolveidrequest.h b/backends/cloud/id/idresolveidrequest.h
new file mode 100644
index 0000000..94d4af5
--- /dev/null
+++ b/backends/cloud/id/idresolveidrequest.h
@@ -0,0 +1,60 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDRESOLVEIDREQUEST_H
+#define BACKENDS_CLOUD_ID_IDRESOLVEIDREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage;
+
+class IdResolveIdRequest: public Networking::Request {
+	Common::String _requestedPath;	
+	IdStorage *_storage;
+	Storage::UploadCallback _uploadCallback;
+	Common::String _currentDirectory;
+	Common::String _currentDirectoryId;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void listNextDirectory(StorageFile fileToReturn);
+	void listedDirectoryCallback(Storage::FileArrayResponse response);
+	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishFile(StorageFile file);
+public:
+	IdResolveIdRequest(IdStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
+	virtual ~IdResolveIdRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
new file mode 100644
index 0000000..aed1738
--- /dev/null
+++ b/backends/cloud/id/idstorage.cpp
@@ -0,0 +1,86 @@
+/* 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.
+*
+*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/id/idstorage.h"
+#include "backends/cloud/id/idlistdirectoryrequest.h"
+#include "backends/cloud/id/idresolveidrequest.h"
+#include "common/debug.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace Id {
+
+IdStorage::~IdStorage() {}
+
+void IdStorage::printJson(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("printJson: NULL");
+		return;
+	}
+
+	debug("%s", json->stringify().c_str());
+	delete json;
+}
+
+void IdStorage::printFiles(FileArrayResponse response) {
+	debug("files:");
+	Common::Array<StorageFile> &files = response.value;
+	for (uint32 i = 0; i < files.size(); ++i) {
+		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
+		debug("\t%s", files[i].path().c_str());
+		debug("\t%s", files[i].id().c_str());
+		debug(" ");
+	}
+}
+
+void IdStorage::printBool(BoolResponse response) {
+	debug("bool: %s", response.value ? "true" : "false");
+}
+
+void IdStorage::printFile(UploadResponse response) {
+	debug("\nuploaded file info:");
+	debug("\tid: %s", response.value.path().c_str());
+	debug("\tname: %s", response.value.name().c_str());
+	debug("\tsize: %u", response.value.size());
+	debug("\ttimestamp: %u", response.value.timestamp());
+}
+
+Storage::ListDirectoryCallback IdStorage::getPrintFilesCallback() {
+	return new Common::Callback<IdStorage, FileArrayResponse>(this, &IdStorage::printFiles);
+}
+
+Networking::Request *IdStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<IdStorage, UploadResponse>(this, &IdStorage::printFile);
+	return addRequest(new IdResolveIdRequest(this, path, callback, errorCallback));
+}
+
+Networking::Request *IdStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<IdStorage, FileArrayResponse>(this, &IdStorage::printFiles);
+	return addRequest(new IdListDirectoryRequest(this, path, callback, errorCallback, recursive));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
new file mode 100644
index 0000000..a5e1c1e
--- /dev/null
+++ b/backends/cloud/id/idstorage.h
@@ -0,0 +1,73 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDSTORAGE_H
+#define BACKENDS_CLOUD_ID_IDSTORAGE_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+/*
+ * Id::IdStorage is a special base class, which is created
+ * to simplify adding new storages which use ids instead of
+ * paths in their API.
+ *
+ * Some Requests are already implemented, and Storage based
+ * on IdStorage needs to override/implement a few basic things.
+ *
+ * For example, ListDirectoryRequest and ResolveIdRequests are
+ * based on listDirectoryById() and getRootDirectoryId() methods.
+ * Implementing these you'll get id resolving and directory
+ * listing by path.
+ */
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage: public Cloud::Storage {
+protected:
+	void printJson(Networking::JsonResponse response);
+	void printFiles(FileArrayResponse response);
+	void printBool(BoolResponse response);
+	void printFile(UploadResponse response);
+
+	ListDirectoryCallback getPrintFilesCallback();
+	
+public:
+	virtual ~IdStorage();
+
+	/** Public Cloud API comes down there. */
+
+	/** Returns StorageFile with the resolved file's id. */
+	virtual Networking::Request *resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
+	/** Returns ListDirectoryStatus struct with list of files. */
+	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) = 0;
+
+	virtual Common::String getRootDirectoryId() = 0;
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 53eca87..3c3b343 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -30,8 +30,6 @@ MODULE_OBJS += \
 	cloud/savessyncrequest.o \
 	cloud/box/boxstorage.o \
 	cloud/box/boxlistdirectorybyidrequest.o \
-	cloud/box/boxlistdirectoryrequest.o \
-	cloud/box/boxresolveidrequest.o \
 	cloud/box/boxtokenrefresher.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
@@ -46,6 +44,9 @@ MODULE_OBJS += \
 	cloud/googledrive/googledrivestreamfilerequest.o \
 	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/googledrive/googledriveuploadrequest.o \
+	cloud/id/idstorage.o \
+	cloud/id/idlistdirectoryrequest.o \
+	cloud/id/idresolveidrequest.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \


Commit: d943d7c3a805afb14755cb95ea29bbf91358bbd6
    https://github.com/scummvm/scummvm/commit/d943d7c3a805afb14755cb95ea29bbf91358bbd6
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add IdCreateDirectoryRequest

Box gets createDirectoryWithParentId(), so now creating directories
works there.

Changed paths:
  A backends/cloud/id/idcreatedirectoryrequest.cpp
  A backends/cloud/id/idcreatedirectoryrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/id/idstorage.cpp
    backends/cloud/id/idstorage.h
    backends/module.mk



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 3681cbf..5bbe377 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -205,6 +205,45 @@ Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirect
 	return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
 }
 
+void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (outerCallback) {
+		Common::JSONObject info = json->asObject();
+		(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+		delete outerCallback;
+	}
+
+	delete json;
+}
+
+Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+
+	Common::String url = "https://api.box.com/2.0/folders";
+	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::createDirectoryInnerCallback, callback);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + accessToken());
+	request->addHeader("Content-Type: application/json");
+
+	Common::JSONObject parentObject;
+	parentObject.setVal("id", new Common::JSONValue(parentId));
+
+	Common::JSONObject jsonRequestParameters;	
+	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+	jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
+
+	Common::JSONValue value(jsonRequestParameters);
+	request->addPostField(Common::JSON::stringify(&value));
+
+	return addRequest(request);
+}
+
 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	//return addRequest(new BoxUploadRequest(this, path, contents, callback, errorCallback));
 	return nullptr; //TODO
@@ -226,12 +265,6 @@ void BoxStorage::fileDownloaded(BoolResponse response) {
 	else debug("download failed!");
 }
 
-Networking::Request *BoxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	//return addRequest(new BoxCreateDirectoryRequest(this, path, callback, errorCallback));
-	return nullptr; //TODO
-}
-
 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.box.com/2.0/users/me");
@@ -239,7 +272,7 @@ Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::
 	return addRequest(request);
 }
 
-Common::String BoxStorage::savesDirectoryPath() { return "saves/"; }
+Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; }
 
 BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 865358c..3b02f88 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -49,6 +49,7 @@ class BoxStorage: public Id::IdStorage {
 	void fileDownloaded(BoolResponse response);
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
+	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response);
 public:	
 	/** This constructor uses OAuth code flow to get tokens. */
 	BoxStorage(Common::String code);
@@ -76,6 +77,7 @@ public:
 	/** Public Cloud API comes down there. */
 
 	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
@@ -86,9 +88,6 @@ public:
 	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
-	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
-
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
diff --git a/backends/cloud/id/idcreatedirectoryrequest.cpp b/backends/cloud/id/idcreatedirectoryrequest.cpp
new file mode 100644
index 0000000..7968a4b
--- /dev/null
+++ b/backends/cloud/id/idcreatedirectoryrequest.cpp
@@ -0,0 +1,146 @@
+/* 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 "backends/cloud/id/idcreatedirectoryrequest.h"
+#include "backends/cloud/id/idstorage.h"
+#include "common/debug.h"
+
+namespace Cloud {
+namespace Id {
+
+IdCreateDirectoryRequest::IdCreateDirectoryRequest(IdStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb),
+	_requestedParentPath(parentPath), _requestedDirectoryName(directoryName), _storage(storage), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+IdCreateDirectoryRequest::~IdCreateDirectoryRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void IdCreateDirectoryRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_ignoreCallback = false;
+
+	//the only exception when we create parent folder - is when it's ScummVM/ base folder
+	Common::String prefix = _requestedParentPath;
+	if (prefix.size() > 7) prefix.erase(7);
+	if (prefix.equalsIgnoreCase("ScummVM")) {
+		Storage::BoolCallback callback = new Common::Callback<IdCreateDirectoryRequest, Storage::BoolResponse>(this, &IdCreateDirectoryRequest::createdBaseDirectoryCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<IdCreateDirectoryRequest, Networking::ErrorResponse>(this, &IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback);
+		_workingRequest = _storage->createDirectory("ScummVM", callback, failureCallback);
+		return;
+	}
+	
+	resolveId();
+}
+
+void IdCreateDirectoryRequest::createdBaseDirectoryCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+	resolveId();
+}
+
+void IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void IdCreateDirectoryRequest::resolveId() {
+	//check whether such folder already exists
+	Storage::UploadCallback innerCallback = new Common::Callback<IdCreateDirectoryRequest, Storage::UploadResponse>(this, &IdCreateDirectoryRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdCreateDirectoryRequest, Networking::ErrorResponse>(this, &IdCreateDirectoryRequest::idResolveFailedCallback);
+	Common::String path = _requestedParentPath;
+	if (_requestedParentPath != "") path += "/";
+	path += _requestedDirectoryName;
+	_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
+}
+
+void IdCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+
+	//resolved => folder already exists
+	finishCreation(false);
+}
+
+void IdCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	
+	//not resolved => folder not exists
+	if (error.response.contains("no such file found in its parent directory")) {		
+		//parent's id after the '\n'
+		Common::String parentId = error.response;
+		for (uint32 i = 0; i < parentId.size(); ++i)
+			if (parentId[i] == '\n') {
+				parentId.erase(0, i+1);
+				break;
+			}
+
+		Storage::BoolCallback callback = new Common::Callback<IdCreateDirectoryRequest, Storage::BoolResponse>(this, &IdCreateDirectoryRequest::createdDirectoryCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<IdCreateDirectoryRequest, Networking::ErrorResponse>(this, &IdCreateDirectoryRequest::createdDirectoryErrorCallback);
+		_workingRequest = _storage->createDirectoryWithParentId(parentId, _requestedDirectoryName, callback, failureCallback);
+		return;
+	}
+
+	finishError(error);
+}
+
+void IdCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (response.request) _date = response.request->date();
+	finishCreation(response.value);
+}
+
+void IdCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
+	finishError(error);
+}
+
+void IdCreateDirectoryRequest::handle() {}
+
+void IdCreateDirectoryRequest::restart() { start(); }
+
+Common::String IdCreateDirectoryRequest::date() const { return _date; }
+
+void IdCreateDirectoryRequest::finishCreation(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/idcreatedirectoryrequest.h b/backends/cloud/id/idcreatedirectoryrequest.h
new file mode 100644
index 0000000..241bcd3
--- /dev/null
+++ b/backends/cloud/id/idcreatedirectoryrequest.h
@@ -0,0 +1,65 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDCREATEDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ID_IDCREATEDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage;
+
+class IdCreateDirectoryRequest: public Networking::Request {
+	Common::String _requestedParentPath;
+	Common::String _requestedDirectoryName;
+	IdStorage *_storage;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _date;
+
+	void start();
+	void createdBaseDirectoryCallback(Storage::BoolResponse response);
+	void createdBaseDirectoryErrorCallback(Networking::ErrorResponse error);
+	void resolveId();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void createdDirectoryCallback(Storage::BoolResponse response);
+	void createdDirectoryErrorCallback(Networking::ErrorResponse error);
+	void finishCreation(bool success);
+public:
+	IdCreateDirectoryRequest(IdStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~IdCreateDirectoryRequest();
+
+	virtual void handle();
+	virtual void restart();
+	virtual Common::String date() const;
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index aed1738..f26dee4 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "backends/cloud/id/idstorage.h"
+#include "backends/cloud/id/idcreatedirectoryrequest.h"
 #include "backends/cloud/id/idlistdirectoryrequest.h"
 #include "backends/cloud/id/idresolveidrequest.h"
 #include "common/debug.h"
@@ -82,5 +83,23 @@ Networking::Request *IdStorage::listDirectory(Common::String path, ListDirectory
 	return addRequest(new IdListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
+Networking::Request *IdStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<IdStorage, BoolResponse>(this, &IdStorage::printBool);
+
+	//find out the parent path and directory name
+	Common::String parentPath = "", directoryName = path;
+	for (uint32 i = path.size(); i > 0; --i) {
+		if (path[i - 1] == '/' || path[i - 1] == '\\') {
+			parentPath = path;
+			parentPath.erase(i - 1);
+			directoryName.erase(0, i);
+			break;
+		}
+	}
+
+	return addRequest(new IdCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
+}
+
 } // End of namespace Id
 } // End of namespace Cloud
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
index a5e1c1e..a657f5c 100644
--- a/backends/cloud/id/idstorage.h
+++ b/backends/cloud/id/idstorage.h
@@ -64,6 +64,10 @@ public:
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
+	/** Calls the callback when finished. */
+	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
+
 	virtual Common::String getRootDirectoryId() = 0;
 };
 
diff --git a/backends/module.mk b/backends/module.mk
index 3c3b343..6176119 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -45,6 +45,7 @@ MODULE_OBJS += \
 	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/googledrive/googledriveuploadrequest.o \
 	cloud/id/idstorage.o \
+	cloud/id/idcreatedirectoryrequest.o \
 	cloud/id/idlistdirectoryrequest.o \
 	cloud/id/idresolveidrequest.o \
 	cloud/onedrive/onedrivestorage.o \


Commit: 34ee1d29d5597e4914f5e06c7770137f4dd856d7
    https://github.com/scummvm/scummvm/commit/34ee1d29d5597e4914f5e06c7770137f4dd856d7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Storage::streamFile()

Changed paths:
    backends/cloud/storage.cpp



diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 4e3dc43..2cf851f 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -87,7 +87,7 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 
 Networking::Request *Storage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	//most Storages use paths instead of ids, so this should work
-	return streamFile(path, callback, errorCallback);
+	return streamFileById(path, callback, errorCallback);
 }
 
 Networking::Request *Storage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {


Commit: 19ae61dffc2541e855c1376a52e461f199af2f99
    https://github.com/scummvm/scummvm/commit/19ae61dffc2541e855c1376a52e461f199af2f99
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add IdDownloadRequest and IdStreamFileRequest

Used for downloading files in Box.

Changed paths:
  A backends/cloud/id/iddownloadrequest.cpp
  A backends/cloud/id/iddownloadrequest.h
  A backends/cloud/id/idstreamfilerequest.cpp
  A backends/cloud/id/idstreamfilerequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/id/idstorage.cpp
    backends/cloud/id/idstorage.h
    backends/module.mk



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 5bbe377..1fbe2f1 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -249,15 +249,18 @@ Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableRea
 	return nullptr; //TODO
 }
 
-Networking::Request *BoxStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
-	/*
-	Common::String url = "https://api.Box.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(path);
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &BoxStorage::fileInfoCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
-	request->addHeader("Authorization: Bearer " + _token);
-	return addRequest(request);
-	*/
-	return nullptr; //TODO
+Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
+	if (callback) {
+		Common::String url = "https://api.box.com/2.0/files/" + id + "/content";
+		debug("%s", url.c_str());
+		Common::String header = "Authorization: Bearer " + _token;
+		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
+		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
+		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
+	}
+	delete callback;
+	delete errorCallback;
+	return nullptr;
 }
 
 void BoxStorage::fileDownloaded(BoolResponse response) {
diff --git a/backends/cloud/id/iddownloadrequest.cpp b/backends/cloud/id/iddownloadrequest.cpp
new file mode 100644
index 0000000..154bd16
--- /dev/null
+++ b/backends/cloud/id/iddownloadrequest.cpp
@@ -0,0 +1,91 @@
+/* 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 "backends/cloud/id/iddownloadrequest.h"
+#include "backends/cloud/id/idstorage.h"
+
+namespace Cloud {
+namespace Id {
+
+IdDownloadRequest::IdDownloadRequest(IdStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedFile(remotePath), _requestedLocalFile(localPath), _storage(storage), _boolCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+IdDownloadRequest::~IdDownloadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _boolCallback;
+}
+
+void IdDownloadRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_ignoreCallback = false;
+
+	//find file's id
+	Storage::UploadCallback innerCallback = new Common::Callback<IdDownloadRequest, Storage::UploadResponse>(this, &IdDownloadRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, Networking::ErrorResponse>(this, &IdDownloadRequest::idResolveFailedCallback);	
+	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
+}
+
+void IdDownloadRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Storage::BoolCallback innerCallback = new Common::Callback<IdDownloadRequest, Storage::BoolResponse>(this, &IdDownloadRequest::downloadCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, Networking::ErrorResponse>(this, &IdDownloadRequest::downloadErrorCallback);
+	_workingRequest = _storage->downloadById(response.value.id(), _requestedLocalFile, innerCallback, innerErrorCallback);
+}
+
+void IdDownloadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void IdDownloadRequest::downloadCallback(Storage::BoolResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishDownload(response.value);
+}
+
+void IdDownloadRequest::downloadErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void IdDownloadRequest::handle() {}
+
+void IdDownloadRequest::restart() { start(); }
+
+void IdDownloadRequest::finishDownload(bool success) {
+	Request::finishSuccess();
+	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/iddownloadrequest.h b/backends/cloud/id/iddownloadrequest.h
new file mode 100644
index 0000000..7039769
--- /dev/null
+++ b/backends/cloud/id/iddownloadrequest.h
@@ -0,0 +1,59 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDDOWNLOADREQUEST_H
+#define BACKENDS_CLOUD_ID_IDDOWNLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage;
+
+class IdDownloadRequest: public Networking::Request {
+	Common::String _requestedFile, _requestedLocalFile;
+	IdStorage *_storage;
+	Storage::BoolCallback _boolCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void downloadCallback(Storage::BoolResponse response);
+	void downloadErrorCallback(Networking::ErrorResponse error);
+	void finishDownload(bool success);
+public:
+	IdDownloadRequest(IdStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+	virtual ~IdDownloadRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index f26dee4..3aeb514 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -23,8 +23,10 @@
 
 #include "backends/cloud/id/idstorage.h"
 #include "backends/cloud/id/idcreatedirectoryrequest.h"
+#include "backends/cloud/id/iddownloadrequest.h"
 #include "backends/cloud/id/idlistdirectoryrequest.h"
 #include "backends/cloud/id/idresolveidrequest.h"
+#include "backends/cloud/id/idstreamfilerequest.h"
 #include "common/debug.h"
 #include "common/json.h"
 
@@ -101,5 +103,13 @@ Networking::Request *IdStorage::createDirectory(Common::String path, BoolCallbac
 	return addRequest(new IdCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
 }
 
+Networking::Request *IdStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
+	return addRequest(new IdStreamFileRequest(this, path, outerCallback, errorCallback));
+}
+
+Networking::Request *IdStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+	return addRequest(new IdDownloadRequest(this, remotePath, localPath, callback, errorCallback));
+}
+
 } // End of namespace Id
 } // End of namespace Cloud
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
index a657f5c..88853e4 100644
--- a/backends/cloud/id/idstorage.h
+++ b/backends/cloud/id/idstorage.h
@@ -68,6 +68,13 @@ public:
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
+	/** Returns pointer to Networking::NetworkReadStream. */
+	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
+
+	/** Calls the callback when finished. */
+	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
+
 	virtual Common::String getRootDirectoryId() = 0;
 };
 
diff --git a/backends/cloud/id/idstreamfilerequest.cpp b/backends/cloud/id/idstreamfilerequest.cpp
new file mode 100644
index 0000000..08060b9
--- /dev/null
+++ b/backends/cloud/id/idstreamfilerequest.cpp
@@ -0,0 +1,91 @@
+/* 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 "backends/cloud/id/idstreamfilerequest.h"
+#include "backends/cloud/id/idstorage.h"
+
+namespace Cloud {
+namespace Id {
+
+IdStreamFileRequest::IdStreamFileRequest(IdStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _requestedFile(path), _storage(storage), _streamCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+IdStreamFileRequest::~IdStreamFileRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _streamCallback;
+}
+
+void IdStreamFileRequest::start() {
+	//cleanup
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_workingRequest = nullptr;
+	_ignoreCallback = false;
+
+	//find file's id
+	Storage::UploadCallback innerCallback = new Common::Callback<IdStreamFileRequest, Storage::UploadResponse>(this, &IdStreamFileRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, Networking::ErrorResponse>(this, &IdStreamFileRequest::idResolveFailedCallback);	
+	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
+}
+
+void IdStreamFileRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	Networking::NetworkReadStreamCallback innerCallback = new Common::Callback<IdStreamFileRequest, Networking::NetworkReadStreamResponse>(this, &IdStreamFileRequest::streamFileCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, Networking::ErrorResponse>(this, &IdStreamFileRequest::streamFileErrorCallback);
+	_workingRequest = _storage->streamFileById(response.value.id(), innerCallback, innerErrorCallback);
+}
+
+void IdStreamFileRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void IdStreamFileRequest::streamFileCallback(Networking::NetworkReadStreamResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishStream(response.value);
+}
+
+void IdStreamFileRequest::streamFileErrorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void IdStreamFileRequest::handle() {}
+
+void IdStreamFileRequest::restart() { start(); }
+
+void IdStreamFileRequest::finishStream(Networking::NetworkReadStream *stream) {
+	Request::finishSuccess();
+	if (_streamCallback) (*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
+}
+
+} // End of namespace Id
+} // End of namespace Cloud
diff --git a/backends/cloud/id/idstreamfilerequest.h b/backends/cloud/id/idstreamfilerequest.h
new file mode 100644
index 0000000..20d7c0c
--- /dev/null
+++ b/backends/cloud/id/idstreamfilerequest.h
@@ -0,0 +1,59 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_ID_IDSTREAMFILEREQUEST_H
+#define BACKENDS_CLOUD_ID_IDSTREAMFILEREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Id {
+
+class IdStorage;
+
+class IdStreamFileRequest: public Networking::Request {
+	Common::String _requestedFile;
+	IdStorage *_storage;
+	Networking::NetworkReadStreamCallback _streamCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+
+	void start();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void streamFileCallback(Networking::NetworkReadStreamResponse response);
+	void streamFileErrorCallback(Networking::ErrorResponse error);
+	void finishStream(Networking::NetworkReadStream *stream);
+public:
+	IdStreamFileRequest(IdStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb);
+	virtual ~IdStreamFileRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Id
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 6176119..67246b8 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -46,8 +46,10 @@ MODULE_OBJS += \
 	cloud/googledrive/googledriveuploadrequest.o \
 	cloud/id/idstorage.o \
 	cloud/id/idcreatedirectoryrequest.o \
+	cloud/id/iddownloadrequest.o \
 	cloud/id/idlistdirectoryrequest.o \
 	cloud/id/idresolveidrequest.o \
+	cloud/id/idstreamfilerequest.o \
 	cloud/onedrive/onedrivestorage.o \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \


Commit: 16ed625dfefd7cc71e5d4088d0714fb09332a9c3
    https://github.com/scummvm/scummvm/commit/16ed625dfefd7cc71e5d4088d0714fb09332a9c3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove BoxStorage::streamFileById debug() call

Changed paths:
    backends/cloud/box/boxstorage.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 1fbe2f1..e14157c 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -252,7 +252,6 @@ Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableRea
 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
 		Common::String url = "https://api.box.com/2.0/files/" + id + "/content";
-		debug("%s", url.c_str());
 		Common::String header = "Authorization: Bearer " + _token;
 		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");


Commit: db72fa34d64f0ddcfb7b823e9060dde74fee63ed
    https://github.com/scummvm/scummvm/commit/db72fa34d64f0ddcfb7b823e9060dde74fee63ed
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update NetworkReadStream and CurlRequest

Now those support POST multipart/form upload.

Changed paths:
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/curlrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h



diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 3a143e5..05dfc4a 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -42,6 +42,8 @@ CurlRequest::~CurlRequest() {
 NetworkReadStream *CurlRequest::makeStream() {
 	if (_bytesBuffer)
 		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true);
+	if (!_formFields.empty() || !_formFiles.empty())
+		return new NetworkReadStream(_url.c_str(), _headersList, _formFields, _formFiles);
 	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch);
 }
 
@@ -100,12 +102,35 @@ void CurlRequest::addPostField(Common::String keyValuePair) {
 	if (_bytesBuffer)
 		warning("CurlRequest: added POST fields would be ignored, because there is buffer present");
 
+	if (!_formFields.empty() || !_formFiles.empty())
+		warning("CurlRequest: added POST fields would be ignored, because there are form fields/files present");
+
 	if (_postFields == "")
 		_postFields = keyValuePair;
 	else
 		_postFields += "&" + keyValuePair;
 }
 
+void CurlRequest::addFormField(Common::String name, Common::String value) {
+	if (_bytesBuffer)
+		warning("CurlRequest: added POST form fields would be ignored, because there is buffer present");
+	
+	if (_formFields.contains(name))
+		warning("CurlRequest: form field '%s' already had a value", name.c_str());
+
+	_formFields[name] = value;
+}
+
+void CurlRequest::addFormFile(Common::String name, Common::String filename) {
+	if (_bytesBuffer)
+		warning("CurlRequest: added POST form files would be ignored, because there is buffer present");
+
+	if (_formFields.contains(name))
+		warning("CurlRequest: form file field '%s' already had a value", name.c_str());
+
+	_formFiles[name] = filename;
+}
+
 void CurlRequest::setBuffer(byte *buffer, uint32 size) {
 	if (_postFields != "")
 		warning("CurlRequest: added POST fields would be ignored, because buffer added");
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
index 68ea3a5..6ce94f8 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/curl/curlrequest.h
@@ -26,6 +26,8 @@
 #include "backends/networking/curl/request.h"
 #include "common/str.h"
 #include "common/array.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
 
 struct curl_slist;
 
@@ -42,6 +44,8 @@ protected:
 	NetworkReadStream *_stream;
 	curl_slist *_headersList;
 	Common::String _postFields;
+	Common::HashMap<Common::String, Common::String> _formFields;
+	Common::HashMap<Common::String, Common::String> _formFiles;
 	byte *_bytesBuffer;
 	uint32 _bytesBufferSize;
 	bool _uploading; //using PUT method
@@ -66,6 +70,12 @@ public:
 	/** Adds a post field (key=value pair). */
 	virtual void addPostField(Common::String field);
 
+	/** Adds a form/multipart field (name, value). */
+	virtual void addFormField(Common::String name, Common::String value);
+
+	/** Adds a form/multipart file (field name, file name). */
+	virtual void addFormFile(Common::String name, Common::String filename);
+
 	/** Sets bytes buffer. */
 	virtual void setBuffer(byte *buffer, uint32 size);
 
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 5c37306..41839f7 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -80,10 +80,63 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
 	ConnMan.registerEasyHandle(_easy);
 }
 
+void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
+	_eos = _requestComplete = false;
+	_sendingContentsBuffer = nullptr;
+	_sendingContentsSize = _sendingContentsPos = 0;
+
+	_easy = curl_easy_init();
+	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
+	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
+	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+	curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
+	curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
+	curl_easy_setopt(_easy, CURLOPT_URL, url);
+	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	
+	// set POST multipart upload form fields/files
+	struct curl_httppost *formpost = NULL;
+	struct curl_httppost *lastptr = NULL;
+
+	for (Common::HashMap<Common::String, Common::String>::iterator i = formFields.begin(); i != formFields.end(); ++i) {
+		if (0 != curl_formadd(&formpost, &lastptr,
+			CURLFORM_COPYNAME, i->_key.c_str(),
+			CURLFORM_COPYCONTENTS, i->_value.c_str(),
+			CURLFORM_END)) debug("file failed formadd");
+	}
+
+	/*
+	curl_formadd(&formpost, &lastptr,
+		CURLFORM_COPYNAME, "fieldname",
+		CURLFORM_BUFFER, "filename",
+		CURLFORM_BUFFERPTR, buffer,
+		CURLFORM_BUFFERLENGTH, bufferSize,
+		CURLFORM_END);
+	*/
+
+	for (Common::HashMap<Common::String, Common::String>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
+		if (0 != curl_formadd(&formpost, &lastptr,
+			CURLFORM_COPYNAME, i->_key.c_str(),
+			CURLFORM_FILE, i->_value.c_str(),
+			CURLFORM_END)) debug("file failed formadd");
+	}
+
+	curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
+
+	ConnMan.registerEasyHandle(_easy);
+}
+
 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch) {
 	init(url, headersList, (const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
 }
 
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
+	init(url, headersList, formFields, formFiles);
+}
+
 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	init(url, headersList, buffer, bufferSize, uploading, usingPatch, post);
 }
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 6f521cb..acd8eee 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -26,6 +26,8 @@
 #include "common/memstream.h"
 #include "common/stream.h"
 #include "common/str.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
 
 typedef void CURL;
 struct curl_slist;
@@ -40,9 +42,17 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 	uint32 _sendingContentsPos;
 	Common::String _responseHeaders;
 	void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
+	void init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles);
 
-public:	
+public:
+	/** Send <postFields>, using POST by default. */
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);
+	/** Send <formFields>, <formFiles>, using POST multipart/form. */
+	NetworkReadStream(
+		const char *url, curl_slist *headersList,
+		Common::HashMap<Common::String, Common::String> formFields,
+		Common::HashMap<Common::String, Common::String> formFiles);
+	/** Send <buffer, using POST by default. */
 	NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
 	virtual ~NetworkReadStream();
 


Commit: d96cdacb38afd9394ab442e7b5a74cc87a495092
    https://github.com/scummvm/scummvm/commit/d96cdacb38afd9394ab442e7b5a74cc87a495092
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add BoxUploadRequest

Changed paths:
  A backends/cloud/box/boxuploadrequest.cpp
  A backends/cloud/box/boxuploadrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/module.mk



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index e14157c..628d18b 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -24,6 +24,7 @@
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
+#include "backends/cloud/box/boxuploadrequest.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
@@ -244,9 +245,14 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
 	return addRequest(request);
 }
 
+Networking::Request *BoxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return addRequest(new BoxUploadRequest(this, remotePath, localPath, callback, errorCallback));
+}
+
 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	//return addRequest(new BoxUploadRequest(this, path, contents, callback, errorCallback));
-	return nullptr; //TODO
+	warning("BoxStorage::upload(ReadStream) not implemented");
+	return nullptr;
 }
 
 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 3b02f88..93afe4f 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -80,6 +80,7 @@ public:
 	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns UploadStatus struct with info about uploaded file. */
+	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
new file mode 100644
index 0000000..c94494e
--- /dev/null
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -0,0 +1,213 @@
+/* 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 "backends/cloud/box/boxuploadrequest.h"
+#include "backends/cloud/box/boxstorage.h"
+#include "backends/cloud/box/boxtokenrefresher.h"
+#include "backends/cloud/iso8601.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+#include "common/debug.h"
+
+namespace Cloud {
+namespace Box {
+
+BoxUploadRequest::BoxUploadRequest(BoxStorage *storage, Common::String path, Common::String localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _localPath(localPath), _uploadCallback(callback),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+BoxUploadRequest::~BoxUploadRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _uploadCallback;
+}
+
+void BoxUploadRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	_resolvedId = ""; //used to update file contents
+	_parentId = ""; //used to create file within parent directory
+	_ignoreCallback = false;
+
+	resolveId();
+}
+
+void BoxUploadRequest::resolveId() {
+	//check whether such file already exists
+	Storage::UploadCallback innerCallback = new Common::Callback<BoxUploadRequest, Storage::UploadResponse>(this, &BoxUploadRequest::idResolvedCallback);
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<BoxUploadRequest, Networking::ErrorResponse>(this, &BoxUploadRequest::idResolveFailedCallback);
+	_workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback);
+}
+
+void BoxUploadRequest::idResolvedCallback(Storage::UploadResponse response) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	_resolvedId = response.value.id();
+	upload();
+}
+
+void BoxUploadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+
+	//not resolved => error or no such file
+	if (error.response.contains("no such file found in its parent directory")) {
+		//parent's id after the '\n'
+		Common::String parentId = error.response;
+		for (uint32 i = 0; i < parentId.size(); ++i)
+			if (parentId[i] == '\n') {
+				parentId.erase(0, i + 1);
+				break;
+			}
+
+		_parentId = parentId;
+		upload();
+		return;
+	}
+
+	finishError(error);
+}
+
+void BoxUploadRequest::upload() {
+	Common::String name = _savePath;
+	for (uint32 i = name.size(); i > 0; --i) {
+		if (name[i - 1] == '/' || name[i - 1] == '\\') {
+			name.erase(0, i);
+			break;
+		}
+	}
+
+	Common::String url = "https://upload.box.com/api/2.0/files";
+	if (_resolvedId != "") url += "/" + _resolvedId;
+	url += "/content";
+	Networking::JsonCallback callback = new Common::Callback<BoxUploadRequest, Networking::JsonResponse>(this, &BoxUploadRequest::uploadedCallback);
+	Networking::ErrorCallback failureCallback = new Common::Callback<BoxUploadRequest, Networking::ErrorResponse>(this, &BoxUploadRequest::notUploadedCallback);
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
+
+	Common::JSONObject jsonRequestParameters;
+	if (_resolvedId == "") {
+		Common::JSONObject parentObject;
+		parentObject.setVal("id", new Common::JSONValue(_parentId));
+		jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
+		jsonRequestParameters.setVal("name", new Common::JSONValue(name));
+	}
+
+	Common::JSONValue value(jsonRequestParameters);
+	request->addFormField("attributes", Common::JSON::stringify(&value));
+	request->addFormFile("file", _localPath);
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+		
+	Networking::ErrorResponse error(this, false, true, "", -1);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;	
+	if (rq) {
+		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
+		if (stream) {
+			long code = stream->httpResponseCode();
+			error.httpResponseCode = code;
+		}
+	}
+
+	if (error.httpResponseCode != 200 && error.httpResponseCode != 201)
+		warning("looks like an error");
+
+	Common::JSONValue *json = response.value;
+	if (json) {
+		if (json->isObject()) {
+			Common::JSONObject object = json->asObject();
+			if (object.contains("entries") && object.getVal("entries")->isArray()) {
+				Common::JSONArray entries = object.getVal("entries")->asArray();
+				if (entries.size() > 0) {
+					Common::JSONObject entry = entries[0]->asObject();
+
+					//finished
+					Common::String id = entry.getVal("id")->asString();
+					Common::String name = entry.getVal("name")->asString();
+					bool isDirectory = (entry.getVal("type")->asString() == "folder");
+					uint32 size = 0, timestamp = 0;
+					if (entry.contains("size")) {
+						if (entry.getVal("size")->isString())
+							size = entry.getVal("size")->asString().asUint64();
+						else if (entry.getVal("size")->isIntegerNumber())
+							size = entry.getVal("size")->asIntegerNumber();
+						else
+							warning("strange type for field 'size'");
+					}
+					if (entry.contains("modified_at") && entry.getVal("modified_at")->isString())
+						timestamp = ISO8601::convertToTimestamp(entry.getVal("modified_at")->asString());
+
+					//as we list directory by id, we can't determine full path for the file, so we leave it empty
+					finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
+					return;
+				}
+			}
+
+			//TODO: check errors
+			/*
+			if (object.contains("error")) {
+				warning("Box returned error: %s", json->stringify(true).c_str());
+				delete json;
+				error.response = json->stringify(true);
+				finishError(error);
+				return;
+			}
+			*/
+		}
+
+		warning("no file info to return");
+		finishUpload(StorageFile(_savePath, 0, 0, false));
+	} else {
+		warning("null, not json");
+		finishError(error);
+	}
+
+	delete json;
+}
+
+void BoxUploadRequest::notUploadedCallback(Networking::ErrorResponse error) {	
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void BoxUploadRequest::handle() {}
+
+void BoxUploadRequest::restart() { start(); }
+
+void BoxUploadRequest::finishUpload(StorageFile file) {
+	Request::finishSuccess();
+	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace Box
+} // End of namespace Cloud
diff --git a/backends/cloud/box/boxuploadrequest.h b/backends/cloud/box/boxuploadrequest.h
new file mode 100644
index 0000000..3d15aa7
--- /dev/null
+++ b/backends/cloud/box/boxuploadrequest.h
@@ -0,0 +1,63 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_H
+#define BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/request.h"
+#include "common/callback.h"
+
+namespace Cloud {
+namespace Box {
+class BoxStorage;
+
+class BoxUploadRequest: public Networking::Request {
+	BoxStorage *_storage;
+	Common::String _savePath, _localPath;
+	Storage::UploadCallback _uploadCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	Common::String _resolvedId, _parentId;
+	
+	void start();
+	void resolveId();
+	void idResolvedCallback(Storage::UploadResponse response);
+	void idResolveFailedCallback(Networking::ErrorResponse error);
+	void upload();
+	void uploadedCallback(Networking::JsonResponse response);
+	void notUploadedCallback(Networking::ErrorResponse error);
+	void finishUpload(StorageFile status);
+
+public:
+	BoxUploadRequest(BoxStorage *storage, Common::String path, Common::String localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
+	virtual ~BoxUploadRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Box
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/module.mk b/backends/module.mk
index 67246b8..2d0a61b 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -31,6 +31,7 @@ MODULE_OBJS += \
 	cloud/box/boxstorage.o \
 	cloud/box/boxlistdirectorybyidrequest.o \
 	cloud/box/boxtokenrefresher.o \
+	cloud/box/boxuploadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \


Commit: 5cbb3e8705f51337c6455ecb5dc7004abf82bd89
    https://github.com/scummvm/scummvm/commit/5cbb3e8705f51337c6455ecb5dc7004abf82bd89
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Storage::uploadStreamSupported()

Box uses POST multipart/form requests for uploading. Such requests could
be sent with libcurl if we either have a file available or a buffer with
this file's contents.

SavesSyncRequest was using Storage::upload(ReadStream *), which couldn't
be implemented in BoxStorage. Thus I've added a method to test whether
such upload is supported and, if it's not, SavesSyncRequest uses the
other.

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 628d18b..35e8640 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -252,9 +252,16 @@ Networking::Request *BoxStorage::upload(Common::String remotePath, Common::Strin
 
 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	warning("BoxStorage::upload(ReadStream) not implemented");
+	if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "BoxStorage::upload(ReadStream) not implemented", -1));
+	delete callback;
+	delete errorCallback;
 	return nullptr;
 }
 
+bool BoxStorage::uploadStreamSupported() {
+	return false;	
+}
+
 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
 		Common::String url = "https://api.box.com/2.0/files/" + id + "/content";
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 93afe4f..a737e9d 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -83,6 +83,9 @@ public:
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
+	/** Returns whether Storage supports upload(ReadStream). */
+	virtual bool uploadStreamSupported();
+
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index e12f5af..32b22ed 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -279,10 +279,17 @@ void SavesSyncRequest::uploadNextFile() {
 	///////
 	debug("uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));
 	///////
-	_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
-		new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
-		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
-	);
+	if (_storage->uploadStreamSupported()) {
+		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
+			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
+			new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
+		);
+	} else {
+		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, DefaultSaveFileManager::concatWithSavesPath(_currentUploadingFile),
+			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
+			new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
+		);
+	}
 	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
 }
 
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 2cf851f..110c97a 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -85,6 +85,10 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 	return upload(remotePath, f, callback, errorCallback);
 }
 
+bool Storage::uploadStreamSupported() {
+	return true;
+}
+
 Networking::Request *Storage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	//most Storages use paths instead of ids, so this should work
 	return streamFileById(path, callback, errorCallback);
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 62b4269..273b93c 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -126,6 +126,9 @@ public:
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
+	/** Returns whether Storage supports upload(ReadStream). */
+	virtual bool uploadStreamSupported();
+
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;


Commit: 0b5bd18d8525e16749ad422913800b2120021240
    https://github.com/scummvm/scummvm/commit/0b5bd18d8525e16749ad422913800b2120021240
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GoogleDriveStorage

It now derives from IdStorage, so lots of GoogleDrive*Request classes
are removed and replaced with generic IdStorage*Request ones.

Changed paths:
  R backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
  R backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
  R backends/cloud/googledrive/googledrivedownloadrequest.cpp
  R backends/cloud/googledrive/googledrivedownloadrequest.h
  R backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
  R backends/cloud/googledrive/googledrivelistdirectoryrequest.h
  R backends/cloud/googledrive/googledriveresolveidrequest.cpp
  R backends/cloud/googledrive/googledriveresolveidrequest.h
  R backends/cloud/googledrive/googledrivestreamfilerequest.cpp
  R backends/cloud/googledrive/googledrivestreamfilerequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/module.mk



diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
deleted file mode 100644
index 9e339fd..0000000
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/* 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 "backends/cloud/googledrive/googledrivecreatedirectoryrequest.h"
-#include "backends/cloud/googledrive/googledrivestorage.h"
-#include "common/debug.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-GoogleDriveCreateDirectoryRequest::GoogleDriveCreateDirectoryRequest(GoogleDriveStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
-	Networking::Request(nullptr, ecb),
-	_requestedParentPath(parentPath), _requestedDirectoryName(directoryName), _storage(storage), _boolCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-GoogleDriveCreateDirectoryRequest::~GoogleDriveCreateDirectoryRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _boolCallback;
-}
-
-void GoogleDriveCreateDirectoryRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_ignoreCallback = false;
-
-	//the only exception when we create parent folder - is when it's ScummVM/ base folder
-	Common::String prefix = _requestedParentPath;
-	if (prefix.size() > 7) prefix.erase(7);
-	if (prefix.equalsIgnoreCase("ScummVM")) {
-		Storage::BoolCallback callback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::BoolResponse>(this, &GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback);
-		Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::createdBaseDirectoryErrorCallback);
-		_workingRequest = _storage->createDirectory("ScummVM", callback, failureCallback);
-		return;
-	}
-	
-	resolveId();
-}
-
-void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback(Storage::BoolResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-	resolveId();
-}
-
-void GoogleDriveCreateDirectoryRequest::createdBaseDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void GoogleDriveCreateDirectoryRequest::resolveId() {
-	//check whether such folder already exists
-	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::UploadResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::idResolveFailedCallback);
-	Common::String path = _requestedParentPath;
-	if (_requestedParentPath != "") path += "/";
-	path += _requestedDirectoryName;
-	_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-
-	//resolved => folder already exists
-	finishCreation(false);
-}
-
-void GoogleDriveCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	
-	//not resolved => folder not exists
-	if (error.response.contains("no such file found in its parent directory")) {		
-		//parent's id after the '\n'
-		Common::String parentId = error.response;
-		for (uint32 i = 0; i < parentId.size(); ++i)
-			if (parentId[i] == '\n') {
-				parentId.erase(0, i+1);
-				break;
-			}
-
-		Storage::BoolCallback callback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Storage::BoolResponse>(this, &GoogleDriveCreateDirectoryRequest::createdDirectoryCallback);
-		Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback);
-		_workingRequest = _storage->createDirectoryWithParentId(parentId, _requestedDirectoryName, callback, failureCallback);
-		return;
-	}
-
-	finishError(error);
-}
-
-void GoogleDriveCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-	finishCreation(response.value);
-}
-
-void GoogleDriveCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void GoogleDriveCreateDirectoryRequest::handle() {}
-
-void GoogleDriveCreateDirectoryRequest::restart() { start(); }
-
-Common::String GoogleDriveCreateDirectoryRequest::date() const { return _date; }
-
-void GoogleDriveCreateDirectoryRequest::finishCreation(bool success) {
-	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
-}
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
deleted file mode 100644
index 7a6ffac..0000000
--- a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVECREATEDIRECTORYREQUEST_H
-#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVECREATEDIRECTORYREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-class GoogleDriveStorage;
-
-class GoogleDriveCreateDirectoryRequest: public Networking::Request {
-	Common::String _requestedParentPath;
-	Common::String _requestedDirectoryName;
-	GoogleDriveStorage *_storage;
-	Storage::BoolCallback _boolCallback;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-	Common::String _date;
-
-	void start();
-	void createdBaseDirectoryCallback(Storage::BoolResponse response);
-	void createdBaseDirectoryErrorCallback(Networking::ErrorResponse error);
-	void resolveId();
-	void idResolvedCallback(Storage::UploadResponse response);
-	void idResolveFailedCallback(Networking::ErrorResponse error);
-	void createdDirectoryCallback(Storage::BoolResponse response);
-	void createdDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishCreation(bool success);
-public:
-	GoogleDriveCreateDirectoryRequest(GoogleDriveStorage *storage, Common::String parentPath, Common::String directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
-	virtual ~GoogleDriveCreateDirectoryRequest();
-
-	virtual void handle();
-	virtual void restart();
-	virtual Common::String date() const;
-};
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.cpp b/backends/cloud/googledrive/googledrivedownloadrequest.cpp
deleted file mode 100644
index df28c8b..0000000
--- a/backends/cloud/googledrive/googledrivedownloadrequest.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/* 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 "backends/cloud/googledrive/googledrivedownloadrequest.h"
-#include "backends/cloud/googledrive/googledrivestorage.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-GoogleDriveDownloadRequest::GoogleDriveDownloadRequest(GoogleDriveStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
-	Networking::Request(nullptr, ecb), _requestedFile(remotePath), _requestedLocalFile(localPath), _storage(storage), _boolCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-GoogleDriveDownloadRequest::~GoogleDriveDownloadRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _boolCallback;
-}
-
-void GoogleDriveDownloadRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_ignoreCallback = false;
-
-	//find file's id
-	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveDownloadRequest, Storage::UploadResponse>(this, &GoogleDriveDownloadRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveDownloadRequest, Networking::ErrorResponse>(this, &GoogleDriveDownloadRequest::idResolveFailedCallback);	
-	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveDownloadRequest::idResolvedCallback(Storage::UploadResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-
-	Storage::BoolCallback innerCallback = new Common::Callback<GoogleDriveDownloadRequest, Storage::BoolResponse>(this, &GoogleDriveDownloadRequest::downloadCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveDownloadRequest, Networking::ErrorResponse>(this, &GoogleDriveDownloadRequest::downloadErrorCallback);
-	_workingRequest = _storage->downloadById(response.value.id(), _requestedLocalFile, innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveDownloadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void GoogleDriveDownloadRequest::downloadCallback(Storage::BoolResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishDownload(response.value);
-}
-
-void GoogleDriveDownloadRequest::downloadErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void GoogleDriveDownloadRequest::handle() {}
-
-void GoogleDriveDownloadRequest::restart() { start(); }
-
-void GoogleDriveDownloadRequest::finishDownload(bool success) {
-	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
-}
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.h b/backends/cloud/googledrive/googledrivedownloadrequest.h
deleted file mode 100644
index 202a393..0000000
--- a/backends/cloud/googledrive/googledrivedownloadrequest.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEDOWNLOADREQUEST_H
-#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEDOWNLOADREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-class GoogleDriveStorage;
-
-class GoogleDriveDownloadRequest: public Networking::Request {
-	Common::String _requestedFile, _requestedLocalFile;
-	GoogleDriveStorage *_storage;
-	Storage::BoolCallback _boolCallback;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-
-	void start();
-	void idResolvedCallback(Storage::UploadResponse response);
-	void idResolveFailedCallback(Networking::ErrorResponse error);
-	void downloadCallback(Storage::BoolResponse response);
-	void downloadErrorCallback(Networking::ErrorResponse error);
-	void finishDownload(bool success);
-public:
-	GoogleDriveDownloadRequest(GoogleDriveStorage *storage, Common::String remotePath, Common::String localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
-	virtual ~GoogleDriveDownloadRequest();
-
-	virtual void handle();
-	virtual void restart();
-};
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
deleted file mode 100644
index f645041..0000000
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/* 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 "backends/cloud/googledrive/googledrivelistdirectoryrequest.h"
-#include "backends/cloud/googledrive/googledrivestorage.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-GoogleDriveListDirectoryRequest::GoogleDriveListDirectoryRequest(GoogleDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
-	Networking::Request(nullptr, ecb),
-	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-GoogleDriveListDirectoryRequest::~GoogleDriveListDirectoryRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _listDirectoryCallback;
-}
-
-void GoogleDriveListDirectoryRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_files.clear();
-	_directoriesQueue.clear();
-	_currentDirectory = StorageFile();
-	_ignoreCallback = false;
-
-	//find out that directory's id
-	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Storage::UploadResponse>(this, &GoogleDriveListDirectoryRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryRequest::idResolveErrorCallback);
-	_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-
-	StorageFile directory = response.value;
-	directory.setPath(_requestedPath);
-	_directoriesQueue.push_back(directory);
-	listNextDirectory();
-}
-
-void GoogleDriveListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void GoogleDriveListDirectoryRequest::listNextDirectory() {
-	if (_directoriesQueue.empty()) {
-		finishListing(_files);
-		return;
-	}
-
-	_currentDirectory = _directoriesQueue.back();
-	_directoriesQueue.pop_back();
-
-	Storage::FileArrayCallback callback = new Common::Callback<GoogleDriveListDirectoryRequest, Storage::FileArrayResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryCallback);
-	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryRequest, Networking::ErrorResponse>(this, &GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback);	
-	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
-}
-
-void GoogleDriveListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
-
-	for (uint32 i = 0; i < response.value.size(); ++i) {
-		StorageFile &file = response.value[i];
-		Common::String path = _currentDirectory.path();
-		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
-		path += file.name();
-		file.setPath(path);
-		_files.push_back(file);
-		if (_requestedRecursive && file.isDirectory()) {
-			_directoriesQueue.push_back(file);
-		}
-	}
-
-	listNextDirectory();
-}
-
-void GoogleDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
-	finishError(error);
-}
-
-void GoogleDriveListDirectoryRequest::handle() {}
-
-void GoogleDriveListDirectoryRequest::restart() { start(); }
-
-Common::String GoogleDriveListDirectoryRequest::date() const { return _date; }
-
-void GoogleDriveListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
-	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
-}
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h b/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
deleted file mode 100644
index d76338b..0000000
--- a/backends/cloud/googledrive/googledrivelistdirectoryrequest.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYREQUEST_H
-#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-class GoogleDriveStorage;
-
-class GoogleDriveListDirectoryRequest: public Networking::Request {
-	Common::String _requestedPath;
-	bool _requestedRecursive;
-	GoogleDriveStorage *_storage;
-	Storage::ListDirectoryCallback _listDirectoryCallback;
-	Common::Array<StorageFile> _files;
-	Common::Array<StorageFile> _directoriesQueue;
-	StorageFile _currentDirectory;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-	Common::String _date;
-
-	void start();
-	void idResolvedCallback(Storage::UploadResponse response);
-	void idResolveErrorCallback(Networking::ErrorResponse error);
-	void listNextDirectory();
-	void listedDirectoryCallback(Storage::FileArrayResponse response);
-	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishListing(Common::Array<StorageFile> &files);
-public:
-	GoogleDriveListDirectoryRequest(GoogleDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
-	virtual ~GoogleDriveListDirectoryRequest();
-
-	virtual void handle();
-	virtual void restart();
-	virtual Common::String date() const;
-};
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp
deleted file mode 100644
index 6d8da83..0000000
--- a/backends/cloud/googledrive/googledriveresolveidrequest.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/* 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 "backends/cloud/googledrive/googledriveresolveidrequest.h"
-#include "backends/cloud/googledrive/googledrivestorage.h"
-#include "backends/cloud/googledrive/googledrivetokenrefresher.h"
-#include "backends/cloud/iso8601.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
-#include "common/json.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-GoogleDriveResolveIdRequest::GoogleDriveResolveIdRequest(GoogleDriveStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
-	Networking::Request(nullptr, ecb),
-	_requestedPath(path), _storage(storage), _uploadCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-GoogleDriveResolveIdRequest::~GoogleDriveResolveIdRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _uploadCallback;
-}
-
-void GoogleDriveResolveIdRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_currentDirectory = "";
-	_currentDirectoryId = "root";
-	_ignoreCallback = false;
-	
-	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
-}
-
-void GoogleDriveResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
-	if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
-		finishFile(fileToReturn);
-		return;
-	}
-
-	Storage::FileArrayCallback callback = new Common::Callback<GoogleDriveResolveIdRequest, Storage::FileArrayResponse>(this, &GoogleDriveResolveIdRequest::listedDirectoryCallback);
-	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveResolveIdRequest, Networking::ErrorResponse>(this, &GoogleDriveResolveIdRequest::listedDirectoryErrorCallback);
-	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
-}
-
-void GoogleDriveResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	
-	Common::String currentLevelName = _requestedPath;
-	///debug("'%s'", currentLevelName.c_str());
-	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
-	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
-	///debug("'%s'", currentLevelName.c_str());
-	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
-		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
-			currentLevelName.erase(i);
-			///debug("'%s'", currentLevelName.c_str());
-			break;
-		}
-	}
-
-	Common::String path = _currentDirectory;
-	if (path != "") path += "/";
-	path += currentLevelName;
-	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
-
-	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
-
-	Common::Array<StorageFile> &files = response.value;
-	bool found = false;
-	for (uint32 i = 0; i < files.size(); ++i) {
-		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
-			if (_currentDirectory != "") _currentDirectory += "/";
-			_currentDirectory += files[i].name();
-			_currentDirectoryId = files[i].id();
-			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
-			listNextDirectory(files[i]);
-			found = true;
-			break;
-		}
-	}
-
-	if (!found) {
-		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
-		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
-	}
-}
-
-void GoogleDriveResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void GoogleDriveResolveIdRequest::handle() {}
-
-void GoogleDriveResolveIdRequest::restart() { start(); }
-
-void GoogleDriveResolveIdRequest::finishFile(StorageFile file) {
-	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
-}
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.h b/backends/cloud/googledrive/googledriveresolveidrequest.h
deleted file mode 100644
index cd6f244..0000000
--- a/backends/cloud/googledrive/googledriveresolveidrequest.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVERESOLVEIDREQUEST_H
-#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVERESOLVEIDREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-class GoogleDriveStorage;
-
-class GoogleDriveResolveIdRequest: public Networking::Request {
-	Common::String _requestedPath;	
-	GoogleDriveStorage *_storage;
-	Storage::UploadCallback _uploadCallback;
-	Common::String _currentDirectory;
-	Common::String _currentDirectoryId;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-
-	void start();
-	void listNextDirectory(StorageFile fileToReturn);
-	void listedDirectoryCallback(Storage::FileArrayResponse response);
-	void listedDirectoryErrorCallback(Networking::ErrorResponse error);
-	void finishFile(StorageFile file);
-public:
-	GoogleDriveResolveIdRequest(GoogleDriveStorage *storage, Common::String path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
-	virtual ~GoogleDriveResolveIdRequest();
-
-	virtual void handle();
-	virtual void restart();
-};
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 327c9ee..eeae2f2 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -24,20 +24,15 @@
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
+#include "backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h"
+#include "backends/cloud/googledrive/googledriveuploadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "googledrivelistdirectorybyidrequest.h"
-#include "googledriveresolveidrequest.h"
-#include "googledrivecreatedirectoryrequest.h"
-#include "googledrivelistdirectoryrequest.h"
-#include "googledrivestreamfilerequest.h"
-#include "googledrivedownloadrequest.h"
-#include "googledriveuploadrequest.h"
-#include "common/config-manager.h"
 
 namespace Cloud {
 namespace GoogleDrive {
@@ -198,29 +193,6 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
 	delete json;
 }
 
-void GoogleDriveStorage::printJson(Networking::JsonResponse response) {
-	Common::JSONValue *json = response.value;
-	if (!json) {
-		warning("printJson: NULL");
-		return;
-	}
-
-	debug("%s", json->stringify().c_str());
-	delete json;
-}
-
-Networking::Request *GoogleDriveStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<GoogleDriveStorage, UploadResponse>(this, &GoogleDriveStorage::printFile);
-	return addRequest(new GoogleDriveResolveIdRequest(this, path, callback, errorCallback));
-}
-
-Networking::Request *GoogleDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
-	return addRequest(new GoogleDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));	
-}
-
 Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	if (!callback) callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
@@ -231,10 +203,6 @@ Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::See
 	return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));	
 }
 
-Networking::Request *GoogleDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {	
-	return addRequest(new GoogleDriveStreamFileRequest(this, path, outerCallback, errorCallback));
-}
-
 Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
 		Common::String url = "https://www.googleapis.com/drive/v3/files/" + ConnMan.urlEncode(id) + "?alt=media";
@@ -248,38 +216,11 @@ Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Netwo
 	return nullptr;
 }
 
-Networking::Request *GoogleDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	return addRequest(new GoogleDriveDownloadRequest(this, remotePath, localPath, callback, errorCallback));
-}
-
 void GoogleDriveStorage::fileDownloaded(BoolResponse response) {
 	if (response.value) debug("file downloaded!");
 	else debug("download failed!");
 }
 
-void GoogleDriveStorage::printFiles(FileArrayResponse response) {
-	debug("files:");
-	Common::Array<StorageFile> &files = response.value;
-	for (uint32 i = 0; i < files.size(); ++i) {
-		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
-		debug("\t%s", files[i].path().c_str());
-		debug("\t%s", files[i].id().c_str());
-		debug(" ");
-	}
-}
-
-void GoogleDriveStorage::printBool(BoolResponse response) {
-	debug("bool: %s", response.value ? "true" : "false");
-}
-
-void GoogleDriveStorage::printFile(UploadResponse response) {
-	debug("\nuploaded file info:");
-	debug("\tid: %s", response.value.path().c_str());
-	debug("\tname: %s", response.value.name().c_str());
-	debug("\tsize: %u", response.value.size());
-	debug("\ttimestamp: %u", response.value.timestamp());
-}
-
 void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 	debug("\nuser info:");
 	debug("\tname: %s", response.value.name().c_str());
@@ -287,24 +228,6 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 	debug("\tdisk usage: %llu/%llu", response.value.used(), response.value.available());
 }
 
-Networking::Request *GoogleDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::printBool);
-
-	//find out the parent path and directory name
-	Common::String parentPath = "", directoryName = path;
-	for (uint32 i = path.size(); i > 0; --i) {
-		if (path[i-1] == '/' || path[i-1] == '\\') {
-			parentPath = path;
-			parentPath.erase(i-1);
-			directoryName.erase(0, i);
-			break;
-		}
-	}
-
-	return addRequest(new GoogleDriveCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
-}
-
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	
@@ -366,6 +289,11 @@ Common::String GoogleDriveStorage::getAuthLink() {
 	return url;
 }
 
+Common::String GoogleDriveStorage::getRootDirectoryId() {
+	return "root";
+}
+
+
 void GoogleDriveStorage::authThroughConsole() {
 	if (!ConfMan.hasKey("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain) || !ConfMan.hasKey("GOOGLE_DRIVE_SECRET", ConfMan.kCloudDomain)) {
 		warning("No Google Drive keys available, cannot do auth");
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 8093ef1..435f999 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -23,14 +23,14 @@
 #ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
 
-#include "backends/cloud/storage.h"
+#include "backends/cloud/id/idstorage.h"
 #include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
 
-class GoogleDriveStorage: public Cloud::Storage {
+class GoogleDriveStorage: public Id::IdStorage {
 	static char *KEY, *SECRET;
 
 	static void loadKeyAndSecret();
@@ -49,11 +49,7 @@ class GoogleDriveStorage: public Cloud::Storage {
 	/** Returns bool based on JSON response from cloud. */
 	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json);
 
-	void printJson(Networking::JsonResponse response);
 	void fileDownloaded(BoolResponse response);
-	void printFiles(FileArrayResponse response);
-	void printBool(BoolResponse response);
-	void printFile(UploadResponse response);
 	void printInfo(StorageInfoResponse response);
 public:
 	/** This constructor uses OAuth code flow to get tokens. */
@@ -81,12 +77,6 @@ public:
 
 	/** Public Cloud API comes down there. */
 
-	/** Returns StorageFile with the resolved file's id. */
-	virtual Networking::Request *resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Returns Array<StorageFile> - the list of files. */
-	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
-
 	/** Returns Array<StorageFile> - the list of files. */
 	virtual Networking::Request *listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback);
 
@@ -94,19 +84,12 @@ public:
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
-	virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns the StorageInfo struct. */
@@ -126,6 +109,8 @@ public:
 	 */
 	static Common::String getAuthLink();
 
+	virtual Common::String getRootDirectoryId();
+
 	/**
 	 * Show message with GoogleDrive auth instructions. (Temporary)
 	 */
diff --git a/backends/cloud/googledrive/googledrivestreamfilerequest.cpp b/backends/cloud/googledrive/googledrivestreamfilerequest.cpp
deleted file mode 100644
index 424e52c..0000000
--- a/backends/cloud/googledrive/googledrivestreamfilerequest.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/* 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 "backends/cloud/googledrive/googledrivestreamfilerequest.h"
-#include "backends/cloud/googledrive/googledrivestorage.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-GoogleDriveStreamFileRequest::GoogleDriveStreamFileRequest(GoogleDriveStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb):
-	Networking::Request(nullptr, ecb), _requestedFile(path), _storage(storage), _streamCallback(cb),
-	_workingRequest(nullptr), _ignoreCallback(false) {
-	start();
-}
-
-GoogleDriveStreamFileRequest::~GoogleDriveStreamFileRequest() {
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	delete _streamCallback;
-}
-
-void GoogleDriveStreamFileRequest::start() {
-	//cleanup
-	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
-	_workingRequest = nullptr;
-	_ignoreCallback = false;
-
-	//find file's id
-	Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveStreamFileRequest, Storage::UploadResponse>(this, &GoogleDriveStreamFileRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::ErrorResponse>(this, &GoogleDriveStreamFileRequest::idResolveFailedCallback);	
-	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveStreamFileRequest::idResolvedCallback(Storage::UploadResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-
-	Networking::NetworkReadStreamCallback innerCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::NetworkReadStreamResponse>(this, &GoogleDriveStreamFileRequest::streamFileCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveStreamFileRequest, Networking::ErrorResponse>(this, &GoogleDriveStreamFileRequest::streamFileErrorCallback);
-	_workingRequest = _storage->streamFileById(response.value.id(), innerCallback, innerErrorCallback);
-}
-
-void GoogleDriveStreamFileRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void GoogleDriveStreamFileRequest::streamFileCallback(Networking::NetworkReadStreamResponse response) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishStream(response.value);
-}
-
-void GoogleDriveStreamFileRequest::streamFileErrorCallback(Networking::ErrorResponse error) {
-	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	finishError(error);
-}
-
-void GoogleDriveStreamFileRequest::handle() {}
-
-void GoogleDriveStreamFileRequest::restart() { start(); }
-
-void GoogleDriveStreamFileRequest::finishStream(Networking::NetworkReadStream *stream) {
-	Request::finishSuccess();
-	if (_streamCallback) (*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
-}
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivestreamfilerequest.h b/backends/cloud/googledrive/googledrivestreamfilerequest.h
deleted file mode 100644
index aa55961..0000000
--- a/backends/cloud/googledrive/googledrivestreamfilerequest.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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.
-*
-*/
-
-#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTREAMFILEREQUEST_H
-#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTREAMFILEREQUEST_H
-
-#include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "common/callback.h"
-
-namespace Cloud {
-namespace GoogleDrive {
-
-class GoogleDriveStorage;
-
-class GoogleDriveStreamFileRequest: public Networking::Request {
-	Common::String _requestedFile;
-	GoogleDriveStorage *_storage;
-	Networking::NetworkReadStreamCallback _streamCallback;
-	Request *_workingRequest;
-	bool _ignoreCallback;
-
-	void start();
-	void idResolvedCallback(Storage::UploadResponse response);
-	void idResolveFailedCallback(Networking::ErrorResponse error);
-	void streamFileCallback(Networking::NetworkReadStreamResponse response);
-	void streamFileErrorCallback(Networking::ErrorResponse error);
-	void finishStream(Networking::NetworkReadStream *stream);
-public:
-	GoogleDriveStreamFileRequest(GoogleDriveStorage *storage, Common::String path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb);
-	virtual ~GoogleDriveStreamFileRequest();
-
-	virtual void handle();
-	virtual void restart();
-};
-
-} // End of namespace GoogleDrive
-} // End of namespace Cloud
-
-#endif
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index d9ba281..ce7d59a 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -198,11 +198,15 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 
 	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
-	request->setBuffer(buffer, size);
+	if (size != 0) request->setBuffer(buffer, size);
 
 	//request->addHeader(Common::String::format("Content-Length: %u", size));
-	if (_uploadUrl != "")
-		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));	;
+	if (_uploadUrl != "") {
+		if (_contentsStream->pos() == 0)
+			request->addHeader(Common::String::format("Content-Length: 0"));
+		else
+			request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos() - 1, _contentsStream->size()));
+	}
 	
 	_workingRequest = ConnMan.addRequest(request);
 }
diff --git a/backends/module.mk b/backends/module.mk
index 2d0a61b..1881cc2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -36,13 +36,8 @@ MODULE_OBJS += \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
-	cloud/googledrive/googledrivecreatedirectoryrequest.o \
-	cloud/googledrive/googledrivedownloadrequest.o \
 	cloud/googledrive/googledrivelistdirectorybyidrequest.o \
-	cloud/googledrive/googledrivelistdirectoryrequest.o \
-	cloud/googledrive/googledriveresolveidrequest.o \
 	cloud/googledrive/googledrivestorage.o \
-	cloud/googledrive/googledrivestreamfilerequest.o \
 	cloud/googledrive/googledrivetokenrefresher.o \
 	cloud/googledrive/googledriveuploadrequest.o \
 	cloud/id/idstorage.o \


Commit: b4e9e35e07538a118588742aff6fd4a7a2b4d600
    https://github.com/scummvm/scummvm/commit/b4e9e35e07538a118588742aff6fd4a7a2b4d600
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Cleanup in Storages

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/id/idstorage.cpp
    backends/cloud/id/idstorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/storage.h



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 35e8640..9e036b1 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -177,29 +177,6 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 	delete json;
 }
 
-void BoxStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
-	if (!response.value) {
-		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
-		return;
-	}
-
-	Common::JSONObject result = response.value->asObject();
-	if (result.contains("@content.downloadUrl")) {
-		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
-		if (outerCallback)
-			(*outerCallback)(Networking::NetworkReadStreamResponse(
-				response.request,
-				new Networking::NetworkReadStream(url, 0, "")
-			));
-	} else {
-		warning("downloadUrl not found in passed JSON");
-		debug("%s", response.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
-	}
-	delete response.value;
-}
-
 Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	if (!callback) callback = getPrintFilesCallback();
@@ -275,11 +252,6 @@ Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::N
 	return nullptr;
 }
 
-void BoxStorage::fileDownloaded(BoolResponse response) {
-	if (response.value) debug("file downloaded!");
-	else debug("download failed!");
-}
-
 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.box.com/2.0/users/me");
@@ -307,11 +279,6 @@ BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
 	return new BoxStorage(accessToken, refreshToken);
 }
 
-Common::String BoxStorage::getAuthLink() {
-	// now we only specify short "scummvm.org/c/bx" with actual redirect to the auth page
-	return "";
-}
-
 Common::String BoxStorage::getRootDirectoryId() {
 	return "0";
 }
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index a737e9d..51f2a95 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -46,9 +46,6 @@ class BoxStorage: public Id::IdStorage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void fileDownloaded(BoolResponse response);
-
-	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response);
 public:	
 	/** This constructor uses OAuth code flow to get tokens. */
@@ -104,11 +101,6 @@ public:
 	 */
 	static BoxStorage *loadFromConfig(Common::String keyPrefix);
 
-	/**
-	 * Returns Box auth link.
-	 */
-	static Common::String getAuthLink();
-
 	virtual Common::String getRootDirectoryId();
 
 	/**
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 6d73e52..8343b74 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -105,24 +105,6 @@ Common::String DropboxStorage::name() const {
 	return "Dropbox";
 }
 
-void DropboxStorage::printFiles(FileArrayResponse response) {
-	debug("files:");
-	Common::Array<StorageFile> &files = response.value;
-	for (uint32 i = 0; i < files.size(); ++i)
-		debug("\t%s", files[i].name().c_str());
-}
-
-void DropboxStorage::printBool(BoolResponse response) {
-	debug("bool: %s", (response.value?"true":"false"));
-}
-
-void DropboxStorage::printStorageFile(UploadResponse response) {	
-	debug("\nuploaded file info:");
-	debug("\tpath: %s", response.value.path().c_str());
-	debug("\tsize: %u", response.value.size());
-	debug("\ttimestamp: %u", response.value.timestamp());
-}
-
 Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) {
 	return addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive));
 }
@@ -218,14 +200,5 @@ DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	return new DropboxStorage(accessToken, userId);
 }
 
-Common::String DropboxStorage::getAuthLink() {
-	Common::String url = "https://www.dropbox.com/1/oauth2/authorize";
-	url += "?response_type=code";
-	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
-	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
-	url += "&client_id="; url += KEY;
-	return url;
-}
-
 } // End of namespace Dropbox
 } // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index d256e05..b3dc641 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -46,10 +46,6 @@ class DropboxStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void printFiles(FileArrayResponse response);
-	void printBool(BoolResponse response);
-	void printStorageFile(UploadResponse response);
-
 public:
 	/** This constructor uses OAuth code flow to get tokens. */
 	DropboxStorage(Common::String code);
@@ -105,11 +101,6 @@ public:
 	 * @return pointer to the newly created DropboxStorage or 0 if some problem occured.
 	 */
 	static DropboxStorage *loadFromConfig(Common::String keyPrefix);
-
-	/**
-	 * Returns Dropbox auth link.
-	 */
-	static Common::String getAuthLink();
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index eeae2f2..2816301 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -216,11 +216,6 @@ Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Netwo
 	return nullptr;
 }
 
-void GoogleDriveStorage::fileDownloaded(BoolResponse response) {
-	if (response.value) debug("file downloaded!");
-	else debug("download failed!");
-}
-
 void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 	debug("\nuser info:");
 	debug("\tname: %s", response.value.name().c_str());
@@ -279,41 +274,9 @@ GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix)
 	return new GoogleDriveStorage(accessToken, refreshToken);
 }
 
-Common::String GoogleDriveStorage::getAuthLink() {
-	Common::String url = "https://accounts.google.com/o/oauth2/auth";
-	url += "?response_type=code";
-	url += "&redirect_uri=http://localhost"; //that's for copy-pasting
-	//url += "&redirect_uri=http%3A%2F%2Flocalhost"; //that's "http://localhost" for automatic opening
-	url += "&client_id="; url += KEY;	
-	url += "&scope=https://www.googleapis.com/auth/drive"; //for copy-pasting
-	return url;
-}
-
 Common::String GoogleDriveStorage::getRootDirectoryId() {
 	return "root";
 }
 
-
-void GoogleDriveStorage::authThroughConsole() {
-	if (!ConfMan.hasKey("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain) || !ConfMan.hasKey("GOOGLE_DRIVE_SECRET", ConfMan.kCloudDomain)) {
-		warning("No Google Drive keys available, cannot do auth");
-		return;
-	}
-
-	loadKeyAndSecret();
-
-	if (ConfMan.hasKey("googledrive_code", ConfMan.kCloudDomain)) {
-		//phase 2: get access_token using specified code
-		new GoogleDriveStorage(ConfMan.get("googledrive_code", ConfMan.kCloudDomain));
-		return;
-	}
-
-	debug("Navigate to this URL and press \"Allow\":");
-	debug("%s\n", getAuthLink().c_str());
-	debug("Then, add googledrive_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
-	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
-	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
-}
-
 } // End of namespace GoogleDrive
 } // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 435f999..4a7dbab 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -49,7 +49,6 @@ class GoogleDriveStorage: public Id::IdStorage {
 	/** Returns bool based on JSON response from cloud. */
 	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json);
 
-	void fileDownloaded(BoolResponse response);
 	void printInfo(StorageInfoResponse response);
 public:
 	/** This constructor uses OAuth code flow to get tokens. */
@@ -104,19 +103,9 @@ public:
 	 */
 	static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix);
 
-	/**
-	 * Returns GoogleDrive auth link.
-	 */
-	static Common::String getAuthLink();
-
 	virtual Common::String getRootDirectoryId();
 
 	/**
-	 * Show message with GoogleDrive auth instructions. (Temporary)
-	 */
-	static void authThroughConsole();
-
-	/**
 	 * Gets new access_token. If <code> passed is "", refresh_token is used.
 	 * Use "" in order to refresh token and pass a callback, so you could
 	 * continue your work when new token is available.
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index 3aeb514..28f8805 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -35,17 +35,6 @@ namespace Id {
 
 IdStorage::~IdStorage() {}
 
-void IdStorage::printJson(Networking::JsonResponse response) {
-	Common::JSONValue *json = response.value;
-	if (!json) {
-		warning("printJson: NULL");
-		return;
-	}
-
-	debug("%s", json->stringify().c_str());
-	delete json;
-}
-
 void IdStorage::printFiles(FileArrayResponse response) {
 	debug("files:");
 	Common::Array<StorageFile> &files = response.value;
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
index 88853e4..ccadc0e 100644
--- a/backends/cloud/id/idstorage.h
+++ b/backends/cloud/id/idstorage.h
@@ -45,7 +45,6 @@ namespace Id {
 
 class IdStorage: public Cloud::Storage {
 protected:
-	void printJson(Networking::JsonResponse response);
 	void printFiles(FileArrayResponse response);
 	void printBool(BoolResponse response);
 	void printFile(UploadResponse response);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 0d2f91c..3c8ea5f 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -174,17 +174,6 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 	delete json;
 }
 
-void OneDriveStorage::printJson(Networking::JsonResponse response) {
-	Common::JSONValue *json = response.value;
-	if (!json) {
-		warning("printJson: NULL");
-		return;
-	}
-
-	debug("%s", json->stringify().c_str());
-	delete json;
-}
-
 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
 	if (!response.value) {
 		warning("fileInfoCallback: NULL");
@@ -224,29 +213,6 @@ Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networ
 	return addRequest(request);
 }
 
-void OneDriveStorage::fileDownloaded(BoolResponse response) {
-	if (response.value) debug("file downloaded!");
-	else debug("download failed!");
-}
-
-void OneDriveStorage::printFiles(FileArrayResponse response) {
-	debug("files:");
-	Common::Array<StorageFile> &files = response.value;
-	for (uint32 i = 0; i < files.size(); ++i)
-		debug("\t%s", files[i].path().c_str());
-}
-
-void OneDriveStorage::printBool(BoolResponse response) {
-	debug("bool: %s", response.value ? "true" : "false");
-}
-
-void OneDriveStorage::printFile(UploadResponse response) {
-	debug("\nuploaded file info:");
-	debug("\tpath: %s", response.value.path().c_str());
-	debug("\tsize: %u", response.value.size());
-	debug("\ttimestamp: %u", response.value.timestamp());
-}
-
 Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
 	return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
@@ -285,36 +251,5 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	return new OneDriveStorage(accessToken, userId, refreshToken);
 }
 
-Common::String OneDriveStorage::getAuthLink() {
-	Common::String url = "https://login.live.com/oauth20_authorize.srf";
-	url += "?response_type=code";
-	url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting
-	//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
-	url += "&client_id="; url += KEY;
-	url += "&scope=onedrive.appfolder%20offline_access"; //TODO
-	return url;
-}
-
-void OneDriveStorage::authThroughConsole() {
-	if (!ConfMan.hasKey("ONEDRIVE_KEY", ConfMan.kCloudDomain) || !ConfMan.hasKey("ONEDRIVE_SECRET", ConfMan.kCloudDomain)) {
-		warning("No OneDrive keys available, cannot do auth");
-		return;
-	}
-
-	loadKeyAndSecret();
-
-	if (ConfMan.hasKey("onedrive_code", ConfMan.kCloudDomain)) {
-		//phase 2: get access_token using specified code
-		new OneDriveStorage(ConfMan.get("onedrive_code", ConfMan.kCloudDomain));
-		return;
-	}
-
-	debug("Navigate to this URL and press \"Allow\":");
-	debug("%s\n", getAuthLink().c_str());
-	debug("Then, add onedrive_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n");
-	debug("Navigate to this URL to get more information on ScummVM's configuration files:");
-	debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n");	
-}
-
 } // End of namespace OneDrive
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 061d0fa..650c240 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
 
 #include "backends/cloud/storage.h"
-#include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
@@ -46,12 +45,6 @@ class OneDriveStorage: public Cloud::Storage {
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
-	void printJson(Networking::JsonResponse response);
-	void fileDownloaded(BoolResponse response);
-	void printFiles(FileArrayResponse response);
-	void printBool(BoolResponse response);
-	void printFile(UploadResponse response);
-
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
 public:	
 	/** This constructor uses OAuth code flow to get tokens. */
@@ -89,9 +82,6 @@ public:
 	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns the StorageInfo struct. */
@@ -107,16 +97,6 @@ public:
 	static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
 
 	/**
-	 * Returns OneDrive auth link.
-	 */
-	static Common::String getAuthLink();
-
-	/**
-	 * Show message with OneDrive auth instructions. (Temporary)
-	 */
-	static void authThroughConsole();
-
-	/**
 	 * Gets new access_token. If <code> passed is "", refresh_token is used.
 	 * Use "" in order to refresh token and pass a callback, so you could
 	 * continue your work when new token is available.
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 273b93c..414a722 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -141,9 +141,6 @@ public:
 	virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
-
-	/** Calls the callback when finished. */
 	virtual SavesSyncRequest *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */


Commit: 1a53dccf51aaf02ca4d0d7a176cca20d385d1ac4
    https://github.com/scummvm/scummvm/commit/1a53dccf51aaf02ca4d0d7a176cca20d385d1ac4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update DownloadRequest

It now uses a dynamically allocated 1 MB buffer.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index c95b8b8..43bb02a 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -29,7 +29,7 @@ namespace Cloud {
 
 DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFileId, Common::DumpFile *dumpFile):
 	Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileId(remoteFileId), _storage(storage),
-	_remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false) {
+	_remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false), _buffer(new byte[DOWNLOAD_REQUEST_BUFFER_SIZE]) {
 	start();
 }
 
@@ -38,6 +38,7 @@ DownloadRequest::~DownloadRequest() {
 	if (_workingRequest) _workingRequest->finish();
 	delete _boolCallback;
 	delete _localFile;
+	delete[] _buffer;
 }
 
 void DownloadRequest::start() {
@@ -84,12 +85,10 @@ void DownloadRequest::handle() {
 		return;
 	}
 
-	const int kBufSize = 640 * 1024; //640 KB is enough to everyone?..
-	char buf[kBufSize];
-	uint32 readBytes = _remoteFileStream->read(buf, kBufSize);
+	uint32 readBytes = _remoteFileStream->read(_buffer, DOWNLOAD_REQUEST_BUFFER_SIZE);
 
 	if (readBytes != 0)
-		if (_localFile->write(buf, readBytes) != readBytes) {
+		if (_localFile->write(_buffer, readBytes) != readBytes) {
 			warning("DownloadRequest: unable to write all received bytes into output file");
 			finishError(Networking::ErrorResponse(this, false, true, "", -1));
 			return;
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index d8e18f9..99b7de3 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -30,6 +30,8 @@
 
 namespace Cloud {
 
+#define DOWNLOAD_REQUEST_BUFFER_SIZE 1 * 1024 * 1024
+
 class DownloadRequest: public Networking::Request {	
 	Storage::BoolCallback _boolCallback;	
 	Common::DumpFile *_localFile;
@@ -38,6 +40,7 @@ class DownloadRequest: public Networking::Request {
 	Networking::NetworkReadStream *_remoteFileStream;
 	Request *_workingRequest;
 	bool _ignoreCallback;
+	byte *_buffer;
 
 	void start();
 	void streamCallback(Networking::NetworkReadStreamResponse response);


Commit: e25338ec2494c6ae2ff97f231108627635040f76
    https://github.com/scummvm/scummvm/commit/e25338ec2494c6ae2ff97f231108627635040f76
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update CurlJsonRequest

Uses dynamically allocated buffer now.

Changed paths:
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 3bfc823..875f5e7 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -32,9 +32,13 @@
 namespace Networking {
 
 CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url):
-	CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {}
+	CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES),
+	_buffer(new byte[CURL_JSON_REQUEST_BUFFER_SIZE]) {}
 
-CurlJsonRequest::~CurlJsonRequest() { delete _jsonCallback; }
+CurlJsonRequest::~CurlJsonRequest() {
+	delete _jsonCallback;
+	delete[] _buffer;
+}
 
 char *CurlJsonRequest::getPreparedContents() {
 	//write one more byte in the end
@@ -60,11 +64,9 @@ void CurlJsonRequest::handle() {
 	if (!_stream) _stream = makeStream();
 
 	if (_stream) {
-		const int kBufSize = 16*1024;
-		char buf[kBufSize+1];
-		uint32 readBytes = _stream->read(buf, kBufSize);
+		uint32 readBytes = _stream->read(_buffer, CURL_JSON_REQUEST_BUFFER_SIZE);
 		if (readBytes != 0)
-			if (_contentsStream.write(buf, readBytes) != readBytes)
+			if (_contentsStream.write(_buffer, readBytes) != readBytes)
 				warning("MemoryWriteStreamDynamic was unable to write all the bytes");
 
 		if (_stream->eos()) {
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index bd6f135..5a51065 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -32,10 +32,13 @@ namespace Networking {
 typedef Response<Common::JSONValue *> JsonResponse;
 typedef Common::BaseCallback<JsonResponse> *JsonCallback;
 
+#define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024
+
 class CurlJsonRequest: public CurlRequest {
 protected:
 	JsonCallback _jsonCallback;
 	Common::MemoryWriteStreamDynamic _contentsStream;
+	byte *_buffer;
 
 	/** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
 	char *getPreparedContents();


Commit: dfd68306de6f655a7bd2c68cea0b9299956ce8fc
    https://github.com/scummvm/scummvm/commit/dfd68306de6f655a7bd2c68cea0b9299956ce8fc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Upgrade FolderDownloadRequest::getProgress()

Now NetworkReadStream, which is used in DownloadRequest, which is used
in FolderDownloadRequest, returns progress information provided by
libcurl.

Changed paths:
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/id/iddownloadrequest.cpp
    backends/cloud/id/iddownloadrequest.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h
    gui/downloaddialog.cpp



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 43bb02a..5efb87e 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -122,4 +122,10 @@ void DownloadRequest::finishError(Networking::ErrorResponse error) {
 	Request::finishError(error);
 }
 
+double DownloadRequest::getProgress() const {
+	if (_remoteFileStream)
+		return _remoteFileStream->getProgress();
+	return 0;
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 99b7de3..138616a 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -54,6 +54,9 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getProgress() const;
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 6cf55b2..bbb3c64 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -21,6 +21,8 @@
 */
 
 #include "backends/cloud/folderdownloadrequest.h"
+#include "backends/cloud/downloadrequest.h"
+#include "backends/cloud/id/iddownloadrequest.h"
 #include "common/debug.h"
 #include "gui/downloaddialog.h"
 
@@ -133,9 +135,18 @@ void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
-double FolderDownloadRequest::getProgress() {
-	if (_totalFiles == 0) return 0;	
-	return (double)(_totalFiles - _files.size()) / (double)(_totalFiles);
+double FolderDownloadRequest::getProgress() const {
+	if (_totalFiles == 0) return 0;
+
+	double currentFileProgress = 0;
+	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
+	if (downloadRequest != nullptr) currentFileProgress = downloadRequest->getProgress();
+	else {
+		Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest);
+		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
+	}
+
+	return (double)(_totalFiles - _files.size() + currentFileProgress) / (double)(_totalFiles);
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 41eacc2..a5f13b7 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -56,7 +56,7 @@ public:
 	virtual void restart();
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
-	double getProgress();
+	double getProgress() const;
 
 	/** Returns remote directory path. */
 	Common::String getRemotePath() { return _remoteDirectoryPath; }
diff --git a/backends/cloud/id/iddownloadrequest.cpp b/backends/cloud/id/iddownloadrequest.cpp
index 154bd16..ac62284 100644
--- a/backends/cloud/id/iddownloadrequest.cpp
+++ b/backends/cloud/id/iddownloadrequest.cpp
@@ -22,6 +22,7 @@
 
 #include "backends/cloud/id/iddownloadrequest.h"
 #include "backends/cloud/id/idstorage.h"
+#include "backends/cloud/downloadrequest.h"
 
 namespace Cloud {
 namespace Id {
@@ -87,5 +88,11 @@ void IdDownloadRequest::finishDownload(bool success) {
 	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
+double IdDownloadRequest::getProgress() const {
+	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
+	if (downloadRequest == nullptr) return 0.02; // resolving id still
+	return 0.1 + 0.9 * downloadRequest->getProgress(); // downloading
+}
+
 } // End of namespace Id
 } // End of namespace Cloud
diff --git a/backends/cloud/id/iddownloadrequest.h b/backends/cloud/id/iddownloadrequest.h
index 7039769..65e05c0 100644
--- a/backends/cloud/id/iddownloadrequest.h
+++ b/backends/cloud/id/iddownloadrequest.h
@@ -51,6 +51,9 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getProgress() const;
 };
 
 } // End of namespace Id
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 41839f7..9b34aaa 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -47,10 +47,17 @@ static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
 	return 0;
 }
 
+static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
+	NetworkReadStream *stream = (NetworkReadStream *)p;
+	if (stream) stream->setProgress(dlnow, dltotal);
+	return 0;
+}
+
 void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	_eos = _requestComplete = false;
 	_sendingContentsBuffer = nullptr;
 	_sendingContentsSize = _sendingContentsPos = 0;
+	_progressDownloaded = _progressTotal = 0;
 
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@@ -62,7 +69,10 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
-	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);	
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+	curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
+	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
 	if (uploading) {
 		curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
 		curl_easy_setopt(_easy, CURLOPT_READDATA, this);
@@ -84,6 +94,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	_eos = _requestComplete = false;
 	_sendingContentsBuffer = nullptr;
 	_sendingContentsSize = _sendingContentsPos = 0;
+	_progressDownloaded = _progressTotal = 0;
 
 	_easy = curl_easy_init();
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
@@ -96,6 +107,9 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+	curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
+	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
 	
 	// set POST multipart upload form fields/files
 	struct curl_httppost *formpost = NULL;
@@ -201,4 +215,14 @@ uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) {
 	return size;
 }
 
+double NetworkReadStream::getProgress() const {
+	if (_progressTotal < 1) return 0;
+	return (double)_progressDownloaded / (double)_progressTotal;
+}
+
+void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
+	_progressDownloaded = downloaded;
+	_progressTotal = total;
+}
+
 } // End of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index acd8eee..a322101 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -41,6 +41,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream {
 	uint32 _sendingContentsSize;
 	uint32 _sendingContentsPos;
 	Common::String _responseHeaders;
+	uint64 _progressDownloaded, _progressTotal;
 	void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
 	void init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles);
 
@@ -128,6 +129,12 @@ public:
 	* @returns how many bytes were actually read
 	*/
 	uint32 addResponseHeaders(char *buffer, uint32 size);
+
+	/** Returns a number in range [0, 1], where 1 is "complete". */
+	double getProgress() const;
+
+	/** Used in curl progress callback to pass current downloaded/total values. */
+	void setProgress(uint64 downloaded, uint64 total);
 };
 
 } // End of namespace Networking
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 5ed2871..bfdd3d8 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -173,6 +173,12 @@ void DownloadDialog::handleTickle() {
 		return;
 	}
 
+	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
+	if (_progressBar->getValue() != progress) {
+		refreshWidgets();
+		draw();
+	}
+
 	Dialog::handleTickle();
 }
 


Commit: 990dee3c4fafdf58a3c8b2ecc08b4c6d44887ad8
    https://github.com/scummvm/scummvm/commit/990dee3c4fafdf58a3c8b2ecc08b4c6d44887ad8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Networking::Browser::openUrl() sketch

Only Windows' shellExecute() now.

Changed paths:
  A backends/networking/browser/openurl-default.cpp
  A backends/networking/browser/openurl-windows.cpp
  A backends/networking/browser/openurl.h
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index 1881cc2..c792a94 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -76,6 +76,14 @@ MODULE_OBJS += \
 	networking/sdl_net/uploadfileclienthandler.o
 endif
 
+ifdef WIN32
+MODULE_OBJS += \
+	networking/browser/openurl-windows.o
+else
+MODULE_OBJS += \
+	networking/browser/openurl-default.o
+endif
+
 ifdef USE_ELF_LOADER
 MODULE_OBJS += \
 	plugins/elf/arm-loader.o \
diff --git a/backends/networking/browser/openurl-default.cpp b/backends/networking/browser/openurl-default.cpp
new file mode 100644
index 0000000..c430953
--- /dev/null
+++ b/backends/networking/browser/openurl-default.cpp
@@ -0,0 +1,36 @@
+/* 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 "backends/networking/browser/openurl.h"
+#include "common/textconsole.h"
+
+namespace Networking {
+namespace Browser {
+
+bool openUrl(const Common::String &url) {
+	warning("Networking::Browser::openUrl(): not implemented");
+	return false;
+}
+
+} // End of namespace Browser
+} // End of namespace Networking
+
diff --git a/backends/networking/browser/openurl-windows.cpp b/backends/networking/browser/openurl-windows.cpp
new file mode 100644
index 0000000..53d76f0
--- /dev/null
+++ b/backends/networking/browser/openurl-windows.cpp
@@ -0,0 +1,43 @@
+/* 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 "backends/networking/browser/openurl.h"
+#include "common/textconsole.h"
+#include <windows.h>
+#include <Shellapi.h>
+
+namespace Networking {
+namespace Browser {
+
+bool openUrl(const Common::String &url) {
+	const uint64 result = (uint64)ShellExecute(0, 0, /*(wchar_t*)nativeFilePath.utf16()*/url.c_str(), 0, 0, SW_SHOWNORMAL);
+	// ShellExecute returns a value greater than 32 if successful
+	if (result <= 32) {
+		warning("ShellExecute failed: error = %u", result);
+		return false;
+	}
+	return true;
+}
+
+} // End of namespace Browser
+} // End of namespace Networking
+
diff --git a/backends/networking/browser/openurl.h b/backends/networking/browser/openurl.h
new file mode 100644
index 0000000..fe2f452
--- /dev/null
+++ b/backends/networking/browser/openurl.h
@@ -0,0 +1,36 @@
+/* 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.
+ *
+ */
+
+#ifndef NETWORKING_BROWSER_OPENURL_H
+#define NETWORKING_BROWSER_OPENURL_H
+
+#include "common/str.h"
+
+namespace Networking {
+namespace Browser {
+
+bool openUrl(const Common::String &url);
+
+} // End of namespace Browser
+} // End of namespace Networking
+
+#endif /*NETWORKING_BROWSER_OPENURL_H*/


Commit: 2ae438327b0c5cbe3fbc3a45ac1069fed7709326
    https://github.com/scummvm/scummvm/commit/2ae438327b0c5cbe3fbc3a45ac1069fed7709326
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add "Open URL" button in StorageWizardDialog

It uses Networking::Browser::openUrl().

Changed paths:
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 8e3f177..86513e2 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -28,6 +28,7 @@
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif
+#include "backends/networking/browser/openurl.h"
 #include "common/translation.h"
 #include "widgets/edittext.h"
 
@@ -35,7 +36,8 @@ namespace GUI {
 
 enum {
 	kConnectCmd = 'Cnnt',
-	kCodeBoxCmd = 'CdBx'
+	kCodeBoxCmd = 'CdBx',
+	kOpenUrlCmd = 'OpUr'
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
@@ -46,19 +48,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Headline", headline);
 	
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.NavigateLine", _s("Navigate to the following URL:"));
-
-	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
-
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", url);
+	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", getUrl());
 
 	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':"));
@@ -68,6 +58,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 
 	// Buttons
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
+	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 
 #ifdef USE_SDL_NET
@@ -147,6 +138,12 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		_messageWidget->setLabel(message);
 		break;
 	}
+	case kOpenUrlCmd: {
+		if (!Networking::Browser::openUrl(getUrl())) {
+			_messageWidget->setLabel(_("Failed to open URL!"));
+		}
+		break;
+	}
 	case kConnectCmd: {
 		Common::String code;
 		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
@@ -181,6 +178,21 @@ void StorageWizardDialog::handleTickle() {
 	Dialog::handleTickle();
 }
 
+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;
+}
+
+
 int StorageWizardDialog::decodeHashchar(char c) {
 	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
 	for (uint32 i = 0; i < 64; ++i)
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index ade3c57..6b00d60 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -48,6 +48,9 @@ class StorageWizardDialog : public Dialog {
 	bool _close;
 	bool _stopServerOnClose;
 
+	/** Return short scummvm.org URL for user to navigate to. */
+	Common::String getUrl() const;
+
 	/**
 	 * Return the value corresponding to the given character.
 	 *
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 753d061..6edfebc 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -699,6 +699,9 @@
 				<widget name = 'CancelButton'
 						type = 'Button'
 				/>
+				<widget name = 'OpenUrlButton'
+						type = 'Button'
+				/>
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index f27c8b6..e183cee 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -691,6 +691,9 @@
 				<widget name = 'CancelButton'
 						type = 'Button'
 				/>
+				<widget name = 'OpenUrlButton'
+						type = 'Button'
+				/>
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>


Commit: 33ca8d485c0973d30290163a198e81bd511cf0ec
    https://github.com/scummvm/scummvm/commit/33ca8d485c0973d30290163a198e81bd511cf0ec
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix StorageWizardDialog

It now shows a MessageDialog (its message label is hidden in some
cases).

Changed paths:
    gui/storagewizarddialog.cpp



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 86513e2..393b826 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -21,9 +21,9 @@
  */
 
 #include "gui/storagewizarddialog.h"
-#include "gui/widgets/list.h"
-#include "gui/widget.h"
 #include "gui/gui-manager.h"
+#include "gui/message.h"
+#include "gui/widget.h"
 #include "backends/cloud/cloudmanager.h"
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
@@ -140,7 +140,8 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 	case kOpenUrlCmd: {
 		if (!Networking::Browser::openUrl(getUrl())) {
-			_messageWidget->setLabel(_("Failed to open URL!"));
+			MessageDialog alert(_("Failed to open URL!\nYou should navigate there manually then."));
+			alert.runModal();
 		}
 		break;
 	}


Commit: d8a43cf290af683456dd6736c0f50b114349a3d9
    https://github.com/scummvm/scummvm/commit/d8a43cf290af683456dd6736c0f50b114349a3d9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add openUrl() for POSIX

Changed paths:
  A backends/networking/browser/openurl-posix.cpp
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index c792a94..72b00be 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -80,8 +80,13 @@ ifdef WIN32
 MODULE_OBJS += \
 	networking/browser/openurl-windows.o
 else
-MODULE_OBJS += \
-	networking/browser/openurl-default.o
+	ifdef POSIX
+	MODULE_OBJS += \
+		networking/browser/openurl-posix.o
+	else
+	MODULE_OBJS += \
+		networking/browser/openurl-default.o
+	endif
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/browser/openurl-posix.cpp b/backends/networking/browser/openurl-posix.cpp
new file mode 100644
index 0000000..562c1ad
--- /dev/null
+++ b/backends/networking/browser/openurl-posix.cpp
@@ -0,0 +1,77 @@
+/* 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.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/browser/openurl.h"
+#include "common/textconsole.h"
+#include <stdlib.h>
+
+namespace Networking {
+namespace Browser {
+
+namespace {    
+bool launch(const Common::String client, const Common::String &url) {
+    // FIXME: system's input must be heavily escaped
+    // well, when url's specified by user
+    // it's OK now (urls are hardcoded somewhere in GUI)
+    Common::String cmd = client + " " + url;
+    return (system(cmd.c_str()) != -1);
+}
+}
+
+bool openUrl(const Common::String &url) {
+    // inspired by Qt's "qdesktopservices_x11.cpp"
+    
+    // try "standards"
+    if (launch("xdg-open", url))
+        return true;    
+    if (launch(getenv("DEFAULT_BROWSER"), url))
+        return true;
+    if (launch(getenv("BROWSER"), url))
+        return true;
+
+    // try desktop environment specific tools
+    if (launch("gnome-open", url)) // gnome
+        return true;
+    if (launch("kfmclient openURL", url)) // kde
+        return true;
+    if (launch("exo-open", url)) // xfce
+        return true;
+
+    // try browser names
+    if (launch("firefox", url))
+        return true;
+    if (launch("mozilla", url))
+        return true;
+    if (launch("netscape", url))
+        return true;
+    if (launch("opera", url))
+        return true;
+    
+    warning("Networking::Browser::openUrl() (POSIX) failed to open URL");
+    return false;
+}
+
+} // End of namespace Browser
+} // End of namespace Networking
+


Commit: 7951a2ea167dee8c380ade40fefa95bb4d9baec1
    https://github.com/scummvm/scummvm/commit/7951a2ea167dee8c380ade40fefa95bb4d9baec1
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Rename _files to _pendingFiles in FolderDownloadRequest

Changed paths:
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index bbb3c64..1d42f27 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -47,7 +47,7 @@ void FolderDownloadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();	
 	_currentFile = StorageFile();
-	_files.clear();
+	_pendingFiles.clear();
 	_failedFiles.clear();
 	_ignoreCallback = false;
 	_totalFiles = 0;
@@ -64,8 +64,8 @@ void FolderDownloadRequest::start() {
 void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	_files = response.value;
-	_totalFiles = _files.size();
+	_pendingFiles = response.value;
+	_totalFiles = _pendingFiles.size();
 	downloadNextFile();
 }
 
@@ -90,13 +90,13 @@ void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorRespons
 
 void FolderDownloadRequest::downloadNextFile() {
 	do {
-		if (_files.empty()) {
+		if (_pendingFiles.empty()) {
 			finishDownload(_failedFiles);
 			return;
 		}
 	
-		_currentFile = _files.back();
-		_files.pop_back();
+		_currentFile = _pendingFiles.back();
+		_pendingFiles.pop_back();
 	} while (_currentFile.isDirectory()); //TODO: may be create these directories (in case those are empty)
 
 	sendCommand(GUI::kDownloadProgressCmd, (int)(getProgress() * 100));
@@ -146,7 +146,7 @@ double FolderDownloadRequest::getProgress() const {
 		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
 	}
 
-	return (double)(_totalFiles - _files.size() + currentFileProgress) / (double)(_totalFiles);
+	return (double)(_totalFiles - _pendingFiles.size() + currentFileProgress) / (double)(_totalFiles);
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index a5f13b7..ee17de0 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -35,7 +35,7 @@ class FolderDownloadRequest: public Networking::Request, public GUI::CommandSend
 	Storage::FileArrayCallback _fileArrayCallback;
 	Common::String _remoteDirectoryPath, _localDirectoryPath;
 	bool _recursive;
-	Common::Array<StorageFile> _files, _failedFiles;
+	Common::Array<StorageFile> _pendingFiles, _failedFiles;
 	StorageFile _currentFile;
 	Request *_workingRequest;
 	bool _ignoreCallback;


Commit: cdf8ab7949b93f30522ea2742eb4cea3cfcec778
    https://github.com/scummvm/scummvm/commit/cdf8ab7949b93f30522ea2742eb4cea3cfcec778
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Change 'OK' to 'Hide' on close button of DownloadDialog

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index bfdd3d8..1ae4942 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -61,7 +61,7 @@ DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher):
 	else
 		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _c("Cancel download", "lowres"), 0, kDownloadDialogButtonCmd);
 	
-	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("OK"), 0, kCloseCmd);
+	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("Hide"), 0, kCloseCmd);
 	refreshWidgets();
 	
 	CloudMan.setDownloadTarget(this);


Commit: d863dad055acbc066d86f7fbfa054e7f4c2dd7b2
    https://github.com/scummvm/scummvm/commit/d863dad055acbc066d86f7fbfa054e7f4c2dd7b2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix FolderDownloadRequest::getProgress()

Now it doesn't stop on 100 % on last file and it ignores the
directories, so it doesn't "jump" suddenly as there are no directories
to skip.

Changed paths:
    backends/cloud/folderdownloadrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 1d42f27..c77fc27 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -65,6 +65,14 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	_pendingFiles = response.value;
+
+	// remove all directories
+	for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end(); )
+		if (i->isDirectory())
+			_pendingFiles.erase(i);
+		else
+			++i;
+
 	_totalFiles = _pendingFiles.size();
 	downloadNextFile();
 }
@@ -146,7 +154,8 @@ double FolderDownloadRequest::getProgress() const {
 		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
 	}
 
-	return (double)(_totalFiles - _pendingFiles.size() + currentFileProgress) / (double)(_totalFiles);
+	uint32 uploadedFiles = _totalFiles - _pendingFiles.size() - 1; // -1 because currently downloaded file is already removed from _pendingFiles
+	return (double)(uploadedFiles + currentFileProgress) / (double)(_totalFiles);
 }
 
 } // End of namespace Cloud


Commit: ca33c0a0a8ce31b13137f74ba15e226b3855b75a
    https://github.com/scummvm/scummvm/commit/ca33c0a0a8ce31b13137f74ba15e226b3855b75a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix FolderDownloadRequest

It now sends kDownloadEndedCmd on success without waiting to be
destructed.

Changed paths:
    backends/cloud/folderdownloadrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index c77fc27..f5c872b 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -99,6 +99,7 @@ void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorRespons
 void FolderDownloadRequest::downloadNextFile() {
 	do {
 		if (_pendingFiles.empty()) {
+			sendCommand(GUI::kDownloadEndedCmd, 0);
 			finishDownload(_failedFiles);
 			return;
 		}


Commit: 0ca791709329c3e7f24f68fa2da7c86c87dac557
    https://github.com/scummvm/scummvm/commit/0ca791709329c3e7f24f68fa2da7c86c87dac557
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update FolderDownloadRequest

It now keeps track of downloaded bytes.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    gui/downloaddialog.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index e5b2c2a..80ee248 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -346,6 +346,18 @@ double CloudManager::getDownloadingProgress() {
 	return 1;
 }
 
+uint64 CloudManager::getDownloadBytesNumber() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadBytesNumber();
+	return 1;
+}
+
+uint64 CloudManager::getDownloadTotalBytesNumber() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadTotalBytesNumber();
+	return 1;
+}
+
 Common::String CloudManager::getDownloadRemoteDirectory() {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadRemoteDirectory();
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 1098bdd..0baede7 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -244,6 +244,12 @@ public:
 	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
 	double getDownloadingProgress();
 
+	/** Returns a number of bytes that is downloaded in current download progress. */
+	uint64 getDownloadBytesNumber();
+
+	/** Returns a total number of bytes to be downloaded in current download progress. */
+	uint64 getDownloadTotalBytesNumber();
+
 	/** Returns remote directory path. */
 	virtual Common::String getDownloadRemoteDirectory();
 
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index f5c872b..ca3f9a1 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -51,6 +51,7 @@ void FolderDownloadRequest::start() {
 	_failedFiles.clear();
 	_ignoreCallback = false;
 	_totalFiles = 0;
+	_downloadedBytes = _totalBytes = 0;
 
 	//list directory first
 	_workingRequest = _storage->listDirectory(
@@ -70,8 +71,10 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 	for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end(); )
 		if (i->isDirectory())
 			_pendingFiles.erase(i);
-		else
+		else {
+			_totalBytes += i->size();
 			++i;
+		}
 
 	_totalFiles = _pendingFiles.size();
 	downloadNextFile();
@@ -87,6 +90,7 @@ void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse respons
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	if (!response.value) _failedFiles.push_back(_currentFile);
+	_downloadedBytes += _currentFile.size();
 	downloadNextFile();
 }
 
@@ -159,4 +163,22 @@ double FolderDownloadRequest::getProgress() const {
 	return (double)(uploadedFiles + currentFileProgress) / (double)(_totalFiles);
 }
 
+uint64 FolderDownloadRequest::getDownloadedBytes() const {
+	if (_totalFiles == 0) return 0;
+
+	double currentFileProgress = 0;
+	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
+	if (downloadRequest != nullptr) currentFileProgress = downloadRequest->getProgress();
+	else {
+		Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest);
+		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
+	}
+
+	return _downloadedBytes + (uint64)(currentFileProgress * _currentFile.size());
+}
+
+uint64 FolderDownloadRequest::getTotalBytesToDownload() const {
+	return _totalBytes;
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index ee17de0..1a67f32 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -40,6 +40,7 @@ class FolderDownloadRequest: public Networking::Request, public GUI::CommandSend
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	uint32 _totalFiles;
+	uint64 _downloadedBytes, _totalBytes;
 
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
@@ -58,6 +59,12 @@ public:
 	/** Returns a number in range [0, 1], where 1 is "complete". */
 	double getProgress() const;
 
+	/** Returns a number of downloaded bytes. */
+	uint64 getDownloadedBytes() const;
+
+	/** Returns a total number of bytes to download. */
+	uint64 getTotalBytesToDownload() const;
+
 	/** Returns remote directory path. */
 	Common::String getRemotePath() { return _remoteDirectoryPath; }
 
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 110c97a..c9843d6 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -241,6 +241,24 @@ double Storage::getDownloadingProgress() {
 	return result;
 }
 
+uint64 Storage::getDownloadBytesNumber() {
+	uint64 result = 0;
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getDownloadedBytes();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
+uint64 Storage::getDownloadTotalBytesNumber() {
+	uint64 result = 0;
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getTotalBytesToDownload();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
 Common::String Storage::getDownloadRemoteDirectory() {
 	Common::String result = "";
 	_runningRequestsMutex.lock();
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 414a722..6d9db33 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -198,6 +198,12 @@ public:
 	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
 	virtual double getDownloadingProgress();
 
+	/** Returns a number of bytes that is downloaded in current download progress. */
+	virtual uint64 getDownloadBytesNumber();
+
+	/** Returns a total number of bytes to be downloaded in current download progress. */
+	virtual uint64 getDownloadTotalBytesNumber();
+
 	/** Returns remote directory path. */
 	virtual Common::String getDownloadRemoteDirectory();
 
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 1ae4942..fde560f 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -192,7 +192,7 @@ void DownloadDialog::refreshWidgets() {
 	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
 	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
-	_percentLabel->setLabel(Common::String::format("%u %%", progress));
+	_percentLabel->setLabel(Common::String::format("%u %% (%u bytes out of %u)", progress, CloudMan.getDownloadBytesNumber(), CloudMan.getDownloadTotalBytesNumber()));
 	_progressBar->setValue(progress);
 }
 


Commit: 479c76bbd2752f0bf2d1bab2a00cb7dd1544d1a8
    https://github.com/scummvm/scummvm/commit/479c76bbd2752f0bf2d1bab2a00cb7dd1544d1a8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix IdDownloadRequest

Wrong value was returned in getProgress() on nullptr there.

Changed paths:
    backends/cloud/id/iddownloadrequest.cpp



diff --git a/backends/cloud/id/iddownloadrequest.cpp b/backends/cloud/id/iddownloadrequest.cpp
index ac62284..7166a1e 100644
--- a/backends/cloud/id/iddownloadrequest.cpp
+++ b/backends/cloud/id/iddownloadrequest.cpp
@@ -90,7 +90,9 @@ void IdDownloadRequest::finishDownload(bool success) {
 
 double IdDownloadRequest::getProgress() const {
 	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
-	if (downloadRequest == nullptr) return 0.02; // resolving id still
+	if (downloadRequest == nullptr) return 0; // resolving id still
+
+	// id resolve is 10 % and download is the other 90 %
 	return 0.1 + 0.9 * downloadRequest->getProgress(); // downloading
 }
 


Commit: 85adefdb86e914a789a0db18c767c8ef5902f846
    https://github.com/scummvm/scummvm/commit/85adefdb86e914a789a0db18c767c8ef5902f846
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update FolderDownloadRequest::getProgress()

It now is based on downloaded size, not number of files.

Changed paths:
    backends/cloud/folderdownloadrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index ca3f9a1..6a7b5d3 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -149,18 +149,8 @@ void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 }
 
 double FolderDownloadRequest::getProgress() const {
-	if (_totalFiles == 0) return 0;
-
-	double currentFileProgress = 0;
-	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
-	if (downloadRequest != nullptr) currentFileProgress = downloadRequest->getProgress();
-	else {
-		Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest);
-		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
-	}
-
-	uint32 uploadedFiles = _totalFiles - _pendingFiles.size() - 1; // -1 because currently downloaded file is already removed from _pendingFiles
-	return (double)(uploadedFiles + currentFileProgress) / (double)(_totalFiles);
+	if (_totalFiles == 0 || _totalBytes == 0) return 0;
+	return (double)getDownloadedBytes() / (double)getTotalBytesToDownload();
 }
 
 uint64 FolderDownloadRequest::getDownloadedBytes() const {


Commit: c431ae6d84be1ef73c44b84c58ee3d9edff3d5e3
    https://github.com/scummvm/scummvm/commit/c431ae6d84be1ef73c44b84c58ee3d9edff3d5e3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Calculate FolderDownload download speed

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    gui/downloaddialog.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 80ee248..7156197 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -349,13 +349,19 @@ double CloudManager::getDownloadingProgress() {
 uint64 CloudManager::getDownloadBytesNumber() {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadBytesNumber();
-	return 1;
+	return 0;
 }
 
 uint64 CloudManager::getDownloadTotalBytesNumber() {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadTotalBytesNumber();
-	return 1;
+	return 0;
+}
+
+uint64 CloudManager::getDownloadSpeed() {
+	Storage *storage = getCurrentStorage();
+	if (storage) return storage->getDownloadSpeed();
+	return 0;
 }
 
 Common::String CloudManager::getDownloadRemoteDirectory() {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 0baede7..15409ee 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -250,6 +250,9 @@ public:
 	/** Returns a total number of bytes to be downloaded in current download progress. */
 	uint64 getDownloadTotalBytesNumber();
 
+	/** Returns download speed of current download progress. */
+	uint64 getDownloadSpeed();
+
 	/** Returns remote directory path. */
 	virtual Common::String getDownloadRemoteDirectory();
 
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 6a7b5d3..d506d80 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -25,6 +25,7 @@
 #include "backends/cloud/id/iddownloadrequest.h"
 #include "common/debug.h"
 #include "gui/downloaddialog.h"
+#include <backends/networking/curl/connectionmanager.h>
 
 namespace Cloud {
 
@@ -51,7 +52,7 @@ void FolderDownloadRequest::start() {
 	_failedFiles.clear();
 	_ignoreCallback = false;
 	_totalFiles = 0;
-	_downloadedBytes = _totalBytes = 0;
+	_downloadedBytes = _totalBytes = _wasDownloadedBytes = _currentDownloadSpeed = 0;
 
 	//list directory first
 	_workingRequest = _storage->listDirectory(
@@ -139,7 +140,13 @@ void FolderDownloadRequest::downloadNextFile() {
 	);
 }
 
-void FolderDownloadRequest::handle() {}
+void FolderDownloadRequest::handle() {
+	uint32 microsecondsPassed = Networking::ConnectionManager::getCloudRequestsPeriodInMicroseconds();
+	uint64 currentDownloadedBytes = getDownloadedBytes();
+	uint64 downloadedThisPeriod = currentDownloadedBytes - _wasDownloadedBytes;
+	_currentDownloadSpeed = downloadedThisPeriod * (1000000L / microsecondsPassed);
+	_wasDownloadedBytes = currentDownloadedBytes;
+}
 
 void FolderDownloadRequest::restart() { start(); }
 
@@ -171,4 +178,8 @@ uint64 FolderDownloadRequest::getTotalBytesToDownload() const {
 	return _totalBytes;
 }
 
+uint64 FolderDownloadRequest::getDownloadSpeed() const {
+	return _currentDownloadSpeed;
+}
+
 } // End of namespace Cloud
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 1a67f32..9d8dea4 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -40,7 +40,7 @@ class FolderDownloadRequest: public Networking::Request, public GUI::CommandSend
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	uint32 _totalFiles;
-	uint64 _downloadedBytes, _totalBytes;
+	uint64 _downloadedBytes, _totalBytes, _wasDownloadedBytes, _currentDownloadSpeed;
 
 	void start();
 	void directoryListedCallback(Storage::ListDirectoryResponse response);
@@ -65,6 +65,9 @@ public:
 	/** Returns a total number of bytes to download. */
 	uint64 getTotalBytesToDownload() const;
 
+	/** Returns average download speed for the last second. */
+	uint64 getDownloadSpeed() const;
+
 	/** Returns remote directory path. */
 	Common::String getRemotePath() { return _remoteDirectoryPath; }
 
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index c9843d6..c20ad86 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -259,6 +259,15 @@ uint64 Storage::getDownloadTotalBytesNumber() {
 	return result;
 }
 
+uint64 Storage::getDownloadSpeed() {
+	uint64 result = 0;
+	_runningRequestsMutex.lock();
+	if (_downloadFolderRequest)
+		result = _downloadFolderRequest->getDownloadSpeed();
+	_runningRequestsMutex.unlock();
+	return result;
+}
+
 Common::String Storage::getDownloadRemoteDirectory() {
 	Common::String result = "";
 	_runningRequestsMutex.lock();
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 6d9db33..a577b3c 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -204,6 +204,9 @@ public:
 	/** Returns a total number of bytes to be downloaded in current download progress. */
 	virtual uint64 getDownloadTotalBytesNumber();
 
+	/** Returns download speed of current download progress. */
+	virtual uint64 getDownloadSpeed();
+
 	/** Returns remote directory path. */
 	virtual Common::String getDownloadRemoteDirectory();
 
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index f7c8e2c..415151d 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -91,6 +91,10 @@ Common::String ConnectionManager::urlEncode(Common::String s) {
 	return "";
 }
 
+uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
+	return TIMER_INTERVAL * FRAMES_PER_SECOND / CLOUD_PERIOD;
+}
+
 //private goes here:
 
 void connectionsThread(void *ignored) {
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 602f893..0da5d5a 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -120,6 +120,8 @@ public:
 
 	/** Return URL-encoded version of given string. */
 	Common::String urlEncode(Common::String s);
+
+	static uint32 getCloudRequestsPeriodInMicroseconds();
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index fde560f..d734839 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -192,7 +192,7 @@ void DownloadDialog::refreshWidgets() {
 	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
 	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
-	_percentLabel->setLabel(Common::String::format("%u %% (%u bytes out of %u)", progress, CloudMan.getDownloadBytesNumber(), CloudMan.getDownloadTotalBytesNumber()));
+	_percentLabel->setLabel(Common::String::format("%u %% (%u bytes out of %u - %u bytes per second)", progress, CloudMan.getDownloadBytesNumber(), CloudMan.getDownloadTotalBytesNumber(), CloudMan.getDownloadSpeed()));
 	_progressBar->setValue(progress);
 }
 


Commit: 1b56f59add45af1894957a58fb4cf662f534d54d
    https://github.com/scummvm/scummvm/commit/1b56f59add45af1894957a58fb4cf662f534d54d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Update DownloadDialog

It now has download size and speed labels.

Commit also fixes minor mistake in ConnMan.

Changed paths:
    backends/networking/curl/connectionmanager.cpp
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 415151d..b553e8f 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -92,7 +92,7 @@ Common::String ConnectionManager::urlEncode(Common::String s) {
 }
 
 uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
-	return TIMER_INTERVAL * FRAMES_PER_SECOND / CLOUD_PERIOD;
+	return TIMER_INTERVAL * CLOUD_PERIOD;
 }
 
 //private goes here:
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index d734839..42c9628 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -37,16 +37,16 @@
 namespace GUI {
 
 enum {
-	kDownloadDialogButtonCmd = 'Dldb'	
+	kDownloadDialogButtonCmd = 'Dldb'
 };
 
-DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher):
+DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher) :
 	Dialog("GlobalOptions_Cloud_DownloadDialog"), _launcher(launcher), _close(false) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
 	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"));
-		
+
 	_remoteDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.RemoteDirectory", _("From: "));
 	_localDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.LocalDirectory", _("To: "));
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
@@ -56,14 +56,16 @@ DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher):
 	_progressBar->setValue(progress);
 	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.PercentText", Common::String::format("%u %%", progress));
-	if (g_system->getOverlayWidth() > 320)		
+	_downloadSizeLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DownloadSize", "");
+	_downloadSpeedLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DownloadSpeed", "");
+	if (g_system->getOverlayWidth() > 320)
 		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Cancel download"), 0, kDownloadDialogButtonCmd);
 	else
 		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _c("Cancel download", "lowres"), 0, kDownloadDialogButtonCmd);
-	
+
 	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("Hide"), 0, kCloseCmd);
 	refreshWidgets();
-	
+
 	CloudMan.setDownloadTarget(this);
 }
 
@@ -88,7 +90,8 @@ void DownloadDialog::close() {
 
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
-	case kDownloadDialogButtonCmd: {		
+	case kDownloadDialogButtonCmd:
+	{
 		CloudMan.setDownloadTarget(nullptr);
 		CloudMan.cancelDownload();
 		close();
@@ -138,7 +141,7 @@ bool DownloadDialog::selectDirectories() {
 				Common::String::format(_("The \"%s\" already exists in the specified directory.\nDo you really want to download files into that directory?"), remoteDirectory.name().c_str()),
 				_("Yes"),
 				_("No")
-			);
+				);
 			if (alert.runModal() != GUI::kMessageOK) return false;
 			break;
 		}
@@ -187,12 +190,65 @@ void DownloadDialog::reflowLayout() {
 	refreshWidgets();
 }
 
+namespace {
+Common::String getHumanReadableBytes(uint64 bytes, Common::String &unitsOut) {
+	Common::String result = Common::String::format("%u", bytes);
+	unitsOut = "B";
+
+	if (bytes >= 1024) {
+		bytes /= 1024;
+		result = Common::String::format("%u", bytes);
+		unitsOut = "KB";
+	}
+
+	double floating = bytes;
+
+	if (bytes >= 1024) {
+		bytes /= 1024;
+		floating /= 1024.0;
+		unitsOut = "MB";
+	}
+
+	if (bytes >= 1024) {
+		bytes /= 1024;
+		floating /= 1024.0;
+		unitsOut = "GB";
+	}
+
+	if (bytes >= 1024) { // woah
+		bytes /= 1024;
+		floating /= 1024.0;
+		unitsOut = "TB";
+	}
+
+	// print one digit after floating point
+	result = Common::String::format("%.1lf", floating);
+	return result;
+}
+}
+
+Common::String DownloadDialog::getSizeLabelText() {
+	Common::String downloaded, downloadedUnits, total, totalUnits;
+	downloaded = getHumanReadableBytes(CloudMan.getDownloadBytesNumber(), downloadedUnits);
+	total = getHumanReadableBytes(CloudMan.getDownloadTotalBytesNumber(), totalUnits);
+	return Common::String::format(_("Downloaded %s %s / %s %s"), downloaded.c_str(), _(downloadedUnits.c_str()), total.c_str(), _(totalUnits.c_str()));
+}
+
+Common::String DownloadDialog::getSpeedLabelText() {
+	Common::String speed, speedUnits;
+	speed = getHumanReadableBytes(CloudMan.getDownloadSpeed(), speedUnits);
+	speedUnits += "/s";
+	return Common::String::format("Download speed: %s %s", speed.c_str(), _(speedUnits.c_str()));
+}
+
 void DownloadDialog::refreshWidgets() {	
 	_localDirectory = CloudMan.getDownloadLocalDirectory();
 	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
 	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
 	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
-	_percentLabel->setLabel(Common::String::format("%u %% (%u bytes out of %u - %u bytes per second)", progress, CloudMan.getDownloadBytesNumber(), CloudMan.getDownloadTotalBytesNumber(), CloudMan.getDownloadSpeed()));
+	_percentLabel->setLabel(Common::String::format("%u %%", progress));
+	_downloadSizeLabel->setLabel(getSizeLabelText());
+	_downloadSpeedLabel->setLabel(getSpeedLabelText());
 	_progressBar->setValue(progress);
 }
 
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index 4b277c8..e91318b 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -51,6 +51,8 @@ class DownloadDialog : public Dialog {
 	StaticTextWidget *_remoteDirectoryLabel;
 	StaticTextWidget *_localDirectoryLabel;
 	StaticTextWidget *_percentLabel;
+	StaticTextWidget *_downloadSizeLabel;
+	StaticTextWidget *_downloadSpeedLabel;
 	SliderWidget *_progressBar;
 	ButtonWidget *_cancelButton;
 	ButtonWidget *_closeButton;
@@ -58,6 +60,9 @@ class DownloadDialog : public Dialog {
 	Common::String _localDirectory;
 	bool _close;
 
+	Common::String getSizeLabelText();
+	Common::String getSpeedLabelText();
+
 	void refreshWidgets();
 	bool selectDirectories();
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 6edfebc..5f4ef6f 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -616,6 +616,12 @@
 					height = 'Globals.Line.Height'
 					textalign = 'center'
 			/>
+			<widget name = 'DownloadSize'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'DownloadSpeed'
+					height = 'Globals.Line.Height'
+			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10'>
 				<widget name = 'MainButton'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index e183cee..24476e3 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -613,6 +613,12 @@
 					height = 'Globals.Line.Height'
 					textalign = 'center'
 			/>
+			<widget name = 'DownloadSize'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'DownloadSpeed'
+					height = 'Globals.Line.Height'
+			/>
 			<space/>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6'>
 				<widget name = 'MainButton'


Commit: 626d85ea49801a40196292569848268dfb93621e
    https://github.com/scummvm/scummvm/commit/626d85ea49801a40196292569848268dfb93621e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix module.mk for openurl-default.o

It's now added to all the backends manually.

Changed paths:
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index 72b00be..26fdca4 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -76,19 +76,6 @@ MODULE_OBJS += \
 	networking/sdl_net/uploadfileclienthandler.o
 endif
 
-ifdef WIN32
-MODULE_OBJS += \
-	networking/browser/openurl-windows.o
-else
-	ifdef POSIX
-	MODULE_OBJS += \
-		networking/browser/openurl-posix.o
-	else
-	MODULE_OBJS += \
-		networking/browser/openurl-default.o
-	endif
-endif
-
 ifdef USE_ELF_LOADER
 MODULE_OBJS += \
 	plugins/elf/arm-loader.o \
@@ -168,7 +155,8 @@ MODULE_OBJS += \
 	fs/chroot/chroot-fs.o \
 	plugins/posix/posix-provider.o \
 	saves/posix/posix-saves.o \
-	taskbar/unity/unity-taskbar.o
+	taskbar/unity/unity-taskbar.o \
+	networking/browser/openurl-posix.o
 endif
 
 ifdef MACOSX
@@ -177,7 +165,8 @@ MODULE_OBJS += \
 	midi/coreaudio.o \
 	midi/coremidi.o \
 	updates/macosx/macosx-updates.o \
-	taskbar/macosx/macosx-taskbar.o
+	taskbar/macosx/macosx-taskbar.o \
+	networking/browser/openurl-default.o
 endif
 
 ifdef WIN32
@@ -189,20 +178,23 @@ MODULE_OBJS += \
 	plugins/win32/win32-provider.o \
 	saves/windows/windows-saves.o \
 	updates/win32/win32-updates.o \
-	taskbar/win32/win32-taskbar.o
+	taskbar/win32/win32-taskbar.o \
+	networking/browser/openurl-windows.o
 endif
 
 ifeq ($(BACKEND),androidsdl)
 MODULE_OBJS += \
 	events/androidsdl/androidsdl-events.o \
-	graphics/androidsdl/androidsdl-graphics.o
+	graphics/androidsdl/androidsdl-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifdef AMIGAOS
 MODULE_OBJS += \
 	fs/amigaos4/amigaos4-fs.o \
 	fs/amigaos4/amigaos4-fs-factory.o \
-	midi/camd.o
+	midi/camd.o \
+	networking/browser/openurl-default.o
 endif
 
 ifdef PLAYSTATION3
@@ -210,7 +202,8 @@ MODULE_OBJS += \
 	fs/posix/posix-fs.o \
 	fs/posix/posix-fs-factory.o \
 	fs/ps3/ps3-fs-factory.o \
-	events/ps3sdl/ps3sdl-events.o
+	events/ps3sdl/ps3sdl-events.o \
+	networking/browser/openurl-default.o
 endif
 
 ifdef USE_LINUXCD
@@ -220,58 +213,67 @@ endif
 
 ifeq ($(BACKEND),tizen)
 MODULE_OBJS += \
-	timer/tizen/timer.o
+	timer/tizen/timer.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),ds)
 MODULE_OBJS += \
 	fs/ds/ds-fs.o \
 	fs/ds/ds-fs-factory.o \
-	plugins/ds/ds-provider.o
+	plugins/ds/ds-provider.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),dingux)
 MODULE_OBJS += \
 	events/dinguxsdl/dinguxsdl-events.o \
-	graphics/dinguxsdl/dinguxsdl-graphics.o
+	graphics/dinguxsdl/dinguxsdl-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),gph)
 MODULE_OBJS += \
 	events/gph/gph-events.o \
-	graphics/gph/gph-graphics.o
+	graphics/gph/gph-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),linuxmoto)
 MODULE_OBJS += \
 	events/linuxmotosdl/linuxmotosdl-events.o \
-	graphics/linuxmotosdl/linuxmotosdl-graphics.o
+	graphics/linuxmotosdl/linuxmotosdl-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),maemo)
 MODULE_OBJS += \
 	events/maemosdl/maemosdl-events.o \
-	graphics/maemosdl/maemosdl-graphics.o
+	graphics/maemosdl/maemosdl-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),n64)
 MODULE_OBJS += \
 	fs/n64/n64-fs.o \
 	fs/n64/n64-fs-factory.o \
-	fs/n64/romfsstream.o
+	fs/n64/romfsstream.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),openpandora)
 MODULE_OBJS += \
 	events/openpandora/op-events.o \
-	graphics/openpandora/op-graphics.o
+	graphics/openpandora/op-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),ps2)
 MODULE_OBJS += \
 	fs/ps2/ps2-fs.o \
 	fs/ps2/ps2-fs-factory.o \
-	plugins/ps2/ps2-provider.o
+	plugins/ps2/ps2-provider.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),psp)
@@ -281,18 +283,21 @@ MODULE_OBJS += \
 	fs/psp/psp-stream.o \
 	plugins/psp/psp-provider.o \
 	saves/psp/psp-saves.o \
-	timer/psp/timer.o
+	timer/psp/timer.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),samsungtv)
 MODULE_OBJS += \
 	events/samsungtvsdl/samsungtvsdl-events.o \
-	graphics/samsungtvsdl/samsungtvsdl-graphics.o
+	graphics/samsungtvsdl/samsungtvsdl-graphics.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),webos)
 MODULE_OBJS += \
-	events/webossdl/webossdl-events.o
+	events/webossdl/webossdl-events.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),wince)
@@ -302,14 +307,16 @@ MODULE_OBJS += \
 	fs/windows/windows-fs-factory.o \
 	graphics/wincesdl/wincesdl-graphics.o \
 	mixer/wincesdl/wincesdl-mixer.o \
-	plugins/win32/win32-provider.o
+	plugins/win32/win32-provider.o \
+	networking/browser/openurl-default.o
 endif
 
 ifeq ($(BACKEND),wii)
 MODULE_OBJS += \
 	fs/wii/wii-fs.o \
 	fs/wii/wii-fs-factory.o \
-	plugins/wii/wii-provider.o
+	plugins/wii/wii-provider.o \
+	networking/browser/openurl-default.o
 endif
 
 ifdef ENABLE_EVENTRECORDER


Commit: 06ccfd4b9af518707f51550a5b3fe6af313c3bd0
    https://github.com/scummvm/scummvm/commit/06ccfd4b9af518707f51550a5b3fe6af313c3bd0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add icons in "/files" list

Changed paths:
  A backends/networking/wwwroot/icons/7z.png
  A backends/networking/wwwroot/icons/dir.png
  A backends/networking/wwwroot/icons/txt.png
  A backends/networking/wwwroot/icons/unk.png
  A backends/networking/wwwroot/icons/up.png
  A backends/networking/wwwroot/icons/zip.png
    backends/networking/make_archive.py
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/make_archive.py b/backends/networking/make_archive.py
index a55a4bd..64d314b 100644
--- a/backends/networking/make_archive.py
+++ b/backends/networking/make_archive.py
@@ -17,12 +17,14 @@ def buildArchive(archiveName):
 	print ("Building '" + archiveName + "' archive:")
 	os.chdir(archiveName)
 
-	filenames = os.listdir('.')
-	filenames.sort()
-	for filename in filenames:
-		if os.path.isfile(filename) and filename.endswith(ARCHIVE_FILE_EXTENSIONS):
-			zf.write(filename, './' + filename)
-			print ("    Adding file: " + filename)
+	directories = ['.', './icons']
+	for d in directories:
+		filenames = os.listdir(d)
+		filenames.sort()
+		for filename in filenames:
+			if os.path.isfile(d + '/' + filename) and filename.endswith(ARCHIVE_FILE_EXTENSIONS):
+				zf.write(d + '/' + filename, d + '/' + filename)
+				print ("    Adding file: " + d + '/' + filename)
 
 	os.chdir('../')
 
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index e519c5a..1b8ab46 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -68,7 +68,7 @@ void FilesPageHandler::handle(Client &client) {
 				"<table>{content}</table>" \
 			"</body>" \
 		"</html>";
-	Common::String itemTemplate = "<tr><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
+	Common::String itemTemplate = "<tr><td><img src=\"icons/{icon}\"/></td><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
 
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
@@ -99,8 +99,8 @@ void FilesPageHandler::handle(Client &client) {
 
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
-		addItem(content, itemTemplate, true, "/root/", _("File system root"));
-		addItem(content, itemTemplate, true, "/saves/", _("Saved games"));
+		addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
+		addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games"));
 		return true;
 	}
 
@@ -127,7 +127,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 			filePath = "/";
 		else
 			filePath = parentPath(prefixToAdd + filePath);
-		addItem(content, itemTemplate, true, filePath, _("Parent directory"));
+		addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
 	}
 
 	// fill the content
@@ -140,14 +140,32 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 			filePath.erase(0, prefixToRemove.size());
 		filePath = prefixToAdd + filePath;
 
-		addItem(content, itemTemplate, i->isDirectory(), filePath, name);
+		addItem(content, itemTemplate, detectType(i->isDirectory(), name), filePath, name);
 	}
 
 	return true;
 }
 
-void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size) {
-	Common::String item = itemTemplate;
+FilesPageHandler::ItemType FilesPageHandler::detectType(bool isDirectory, const Common::String &name) const {
+	if (isDirectory) return IT_DIRECTORY;
+	if (name.hasSuffix(".txt")) return IT_TXT;
+	if (name.hasSuffix(".zip")) return IT_ZIP;
+	if (name.hasSuffix(".7z")) return IT_7Z;
+	return IT_UNKNOWN;
+}
+
+void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size) {
+	Common::String item = itemTemplate, icon;
+	bool isDirectory = (itemType == IT_DIRECTORY || itemType == IT_PARENT_DIRECTORY);
+	switch (itemType) {
+	case IT_DIRECTORY: icon = "dir.png"; break;
+	case IT_PARENT_DIRECTORY: icon = "up.png"; break;
+	case IT_TXT: icon = "txt.png"; break;
+	case IT_ZIP: icon = "zip.png"; break;
+	case IT_7Z: icon = "7z.png"; break;
+	default: icon = "unk.png";
+	}
+	replace(item, "{icon}", icon);
 	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + LocalWebserver::urlEncodeQueryParameterValue(path));
 	replace(item, "{name}", name);
 	replace(item, "{size}", size);
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index ba132fd..f102acd 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -28,6 +28,15 @@
 namespace Networking {
 
 class FilesPageHandler: public FilesBaseHandler {
+	enum ItemType {
+		IT_DIRECTORY,
+		IT_PARENT_DIRECTORY,
+		IT_TXT,
+		IT_ZIP,
+		IT_7Z,
+		IT_UNKNOWN
+	};
+
 	void handle(Client &client);
 
 	/**
@@ -37,8 +46,11 @@ class FilesPageHandler: public FilesBaseHandler {
 	 */
 	bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate);
 
+	/** Helper method for detecting items' type. */
+	ItemType detectType(bool isDirectory, const Common::String &name) const;
+
 	/** Helper method for adding items into the files list. */
-	void addItem(Common::String &content, const Common::String &itemTemplate, bool isDirectory, Common::String path, Common::String name, Common::String size = "");
+	void addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size = "");
 
 public:
 	FilesPageHandler();
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 3aa9bdd..6f695e0 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/icons/7z.png b/backends/networking/wwwroot/icons/7z.png
new file mode 100644
index 0000000..656e7e7
Binary files /dev/null and b/backends/networking/wwwroot/icons/7z.png differ
diff --git a/backends/networking/wwwroot/icons/dir.png b/backends/networking/wwwroot/icons/dir.png
new file mode 100644
index 0000000..bcdec04
Binary files /dev/null and b/backends/networking/wwwroot/icons/dir.png differ
diff --git a/backends/networking/wwwroot/icons/txt.png b/backends/networking/wwwroot/icons/txt.png
new file mode 100644
index 0000000..023d2ee
Binary files /dev/null and b/backends/networking/wwwroot/icons/txt.png differ
diff --git a/backends/networking/wwwroot/icons/unk.png b/backends/networking/wwwroot/icons/unk.png
new file mode 100644
index 0000000..346eebe
Binary files /dev/null and b/backends/networking/wwwroot/icons/unk.png differ
diff --git a/backends/networking/wwwroot/icons/up.png b/backends/networking/wwwroot/icons/up.png
new file mode 100644
index 0000000..2dc3df0
Binary files /dev/null and b/backends/networking/wwwroot/icons/up.png differ
diff --git a/backends/networking/wwwroot/icons/zip.png b/backends/networking/wwwroot/icons/zip.png
new file mode 100644
index 0000000..cdfc576
Binary files /dev/null and b/backends/networking/wwwroot/icons/zip.png differ
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index e00a2bc..d8fcfd3 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -85,4 +85,6 @@ html {
 .modal input[type="submit"]:hover {
 	background: #F3F3F3;
 	cursor: pointer;
-}
\ No newline at end of file
+}
+
+td img { vertical-align: middle; height: 20px; }
\ No newline at end of file


Commit: 40dfb0b4f17590100b4dcfe212ee3f34369d788e
    https://github.com/scummvm/scummvm/commit/40dfb0b4f17590100b4dcfe212ee3f34369d788e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix Cloud-related dialogs a little

Minor mistakes which lead to build failure in some cases.

Changed paths:
    gui/downloaddialog.h
    gui/editgamedialog.cpp
    gui/module.mk
    gui/options.cpp



diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index e91318b..cbeb78a 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -25,7 +25,6 @@
 
 #include "gui/dialog.h"
 #include "common/str.h"
-#include <backends/cloud/storage.h>
 
 namespace GUI {
 class LauncherDialog;
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
index 28f71d7..fbe76d2 100644
--- a/gui/editgamedialog.cpp
+++ b/gui/editgamedialog.cpp
@@ -25,6 +25,7 @@
 #include "common/config-manager.h"
 #include "common/gui_options.h"
 #include "common/translation.h"
+#include "common/system.h"
 
 #include "gui/browser.h"
 #include "gui/message.h"
diff --git a/gui/module.mk b/gui/module.mk
index 24becfd..54818c2 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -6,7 +6,6 @@ MODULE_OBJS := \
 	console.o \
 	debugger.o \
 	dialog.o \
-	downloaddialog.o \
 	editgamedialog.o \
 	error.o \
 	EventRecorder.o \
@@ -18,10 +17,8 @@ MODULE_OBJS := \
 	object.o \
 	options.o \
 	predictivedialog.o \
-	remotebrowser.o \
 	saveload.o \
 	saveload-dialog.o \
-	storagewizarddialog.o \
 	themebrowser.o \
 	ThemeEngine.o \
 	ThemeEval.o \
@@ -61,6 +58,13 @@ MODULE_OBJS += \
 endif
 endif
 
+ifdef USE_LIBCURL
+MODULE_OBJS += \
+	downloaddialog.o \
+	remotebrowser.o \
+	storagewizarddialog.o
+endif
+
 ifdef ENABLE_EVENTRECORDER
 MODULE_OBJS += \
 	editrecorddialog.o \
diff --git a/gui/options.cpp b/gui/options.cpp
index b8ff2d5..f3afd58 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -42,11 +42,11 @@
 #include "audio/musicplugin.h"
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
-#include "downloaddialog.h"
 #include "widgets/scrollcontainer.h"
 
 #ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
+#include "gui/downloaddialog.h"
 #include "gui/storagewizarddialog.h"
 #endif
 


Commit: ded8cdf0a092894781bb4d93f2d1e9d061878ea1
    https://github.com/scummvm/scummvm/commit/ded8cdf0a092894781bb4d93f2d1e9d061878ea1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add openurl-android.cpp

Changed paths:
  A backends/networking/browser/openurl-android.cpp
    backends/module.mk
    backends/platform/android/jni.cpp
    backends/platform/android/jni.h
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java



diff --git a/backends/module.mk b/backends/module.mk
index 26fdca4..b8feffb 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -155,9 +155,16 @@ MODULE_OBJS += \
 	fs/chroot/chroot-fs.o \
 	plugins/posix/posix-provider.o \
 	saves/posix/posix-saves.o \
-	taskbar/unity/unity-taskbar.o \
+	taskbar/unity/unity-taskbar.o
+
+ifeq ($(BACKEND),android)
+MODULE_OBJS += \
+	networking/browser/openurl-android.o
+else
+MODULE_OBJS += \
 	networking/browser/openurl-posix.o
 endif
+endif
 
 ifdef MACOSX
 MODULE_OBJS += \
diff --git a/backends/networking/browser/openurl-android.cpp b/backends/networking/browser/openurl-android.cpp
new file mode 100644
index 0000000..98fc803
--- /dev/null
+++ b/backends/networking/browser/openurl-android.cpp
@@ -0,0 +1,35 @@
+/* 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 "backends/networking/browser/openurl.h"
+#include "backends/platform/android/jni.h"
+
+namespace Networking {
+namespace Browser {
+
+bool openUrl(const Common::String &url) {	
+	return JNI::openUrl(url.c_str());
+}
+
+} // End of namespace Browser
+} // End of namespace Networking
+
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 22e6a74..6360bb1 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -76,6 +76,7 @@ bool JNI::_ready_for_events = 0;
 
 jmethodID JNI::_MID_getDPI = 0;
 jmethodID JNI::_MID_displayMessageOnOSD = 0;
+jmethodID JNI::_MID_openUrl = 0;
 jmethodID JNI::_MID_setWindowCaption = 0;
 jmethodID JNI::_MID_showVirtualKeyboard = 0;
 jmethodID JNI::_MID_getSysArchives = 0;
@@ -232,6 +233,25 @@ void JNI::displayMessageOnOSD(const char *msg) {
 	env->DeleteLocalRef(java_msg);
 }
 
+bool JNI::openUrl(const char *url) {
+	bool success = true;
+	JNIEnv *env = JNI::getEnv();
+	jstring javaUrl = env->NewStringUTF(url);
+
+	env->CallVoidMethod(_jobj, _MID_openUrl, javaUrl);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to open URL");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		success = false;
+	}
+
+	env->DeleteLocalRef(javaUrl);
+	return success;
+}
+
 void JNI::setWindowCaption(const char *caption) {
 	JNIEnv *env = JNI::getEnv();
 	jstring java_caption = env->NewStringUTF(caption);
@@ -411,6 +431,7 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
 	FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
 	FIND_METHOD(, getDPI, "([F)V");
 	FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
+	FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
 	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
 	FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
 	FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
index 70feaaf..de4c95b 100644
--- a/backends/platform/android/jni.h
+++ b/backends/platform/android/jni.h
@@ -58,6 +58,7 @@ public:
 	static void setWindowCaption(const char *caption);
 	static void getDPI(float *values);
 	static void displayMessageOnOSD(const char *msg);
+	static bool openUrl(const char *url);
 	static void showVirtualKeyboard(bool enable);
 	static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
 
@@ -89,6 +90,7 @@ private:
 
 	static jmethodID _MID_getDPI;
 	static jmethodID _MID_displayMessageOnOSD;
+	static jmethodID _MID_openUrl;
 	static jmethodID _MID_setWindowCaption;
 	static jmethodID _MID_showVirtualKeyboard;
 	static jmethodID _MID_getSysArchives;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index 3b370a5..5064280 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -53,6 +53,7 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
 	// Callbacks from C++ peer instance
 	abstract protected void getDPI(float[] values);
 	abstract protected void displayMessageOnOSD(String msg);
+	abstract protected void openUrl(String url);
 	abstract protected void setWindowCaption(String caption);
 	abstract protected void showVirtualKeyboard(boolean enable);
 	abstract protected String[] getSysArchives();
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 5b2dcae..2f3701a 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -5,6 +5,7 @@ import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
@@ -75,6 +76,12 @@ public class ScummVMActivity extends Activity {
 		}
 
 		@Override
+		protected void openUrl(String url) {
+			startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+		}
+
+
+		@Override
 		protected void setWindowCaption(final String caption) {
 			runOnUiThread(new Runnable() {
 					public void run() {


Commit: 6c0f491c4f8e809d5d2e5fd070461db1c9777958
    https://github.com/scummvm/scummvm/commit/6c0f491c4f8e809d5d2e5fd070461db1c9777958
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "Index of" label in server's "/files"

Changed paths:
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.files.html
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 1b8ab46..5ead4f3 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -43,6 +43,26 @@ Common::String encodeDoubleQuotes(Common::String s) {
 		} else result += s[i];
 	return result;
 }
+
+Common::String encodeHtmlEntities(Common::String s) {
+	Common::String result = "";
+	for (uint32 i = 0; i < s.size(); ++i)
+		if (s[i] == '<') result += "<";
+		else if (s[i] == '>') result += ">";
+		else if (s[i] == '&') result += "&";
+		else if (s[i] > 0x7F) result += Common::String::format("&#%d;", (int)s[i]);
+		else result += s[i];
+	return result;
+}
+
+Common::String getDisplayPath(Common::String s) {
+	Common::String result = "";
+	for (uint32 i = 0; i < s.size(); ++i)
+		if (s[i] == '\\') result += '/';
+		else result += s[i];
+	if (result == "") return "/";
+	return result;
+}
 }
 
 void FilesPageHandler::handle(Client &client) {
@@ -65,6 +85,7 @@ void FilesPageHandler::handle(Client &client) {
 					"<input type=\"submit\" value=\"{upload_file_button}\"/>" \
 				"</form>"
 				"<hr/>" \
+				"<h1>{index_of_directory}</h1>" \
 				"<table>{content}</table>" \
 			"</body>" \
 		"</html>";
@@ -93,7 +114,8 @@ void FilesPageHandler::handle(Client &client) {
 	replace(response, "{create_directory_desc}", _("Type new directory name:"));
 	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
 	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
-	replace(response, "{content}", content);	
+	replace(response, "{index_of_directory}", Common::String::format(_("Index of %s"), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
+	replace(response, "{content}", content);
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
@@ -167,7 +189,7 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
 	}
 	replace(item, "{icon}", icon);
 	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + LocalWebserver::urlEncodeQueryParameterValue(path));
-	replace(item, "{name}", name);
+	replace(item, "{name}", encodeHtmlEntities(name));
 	replace(item, "{size}", size);
 	content += item;
 }
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 9d53543..1999fe9 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -426,5 +426,4 @@ Common::String LocalWebserver::urlEncodeQueryParameterValue(Common::String value
 	return result;
 }
 
-
 } // End of namespace Networking
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 6f695e0..48e8cb6 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.files.html b/backends/networking/wwwroot/.files.html
index 2c0bceb..f05c011 100644
--- a/backends/networking/wwwroot/.files.html
+++ b/backends/networking/wwwroot/.files.html
@@ -39,6 +39,7 @@
 			</div>
 			<div class="content">
 				<table class="files_list">
+					<td></td><td><b class="directory_name">{index_of_directory}</b></td><td></td>
 					{content}
 				</table>
 			</div>
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index d8fcfd3..cc3a859 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -87,4 +87,9 @@ html {
 	cursor: pointer;
 }
 
-td img { vertical-align: middle; height: 20px; }
\ No newline at end of file
+td img { vertical-align: middle; height: 20px; }
+
+.directory_name {
+	display: block;
+	padding-bottom: 6px;
+}


Commit: 75fbecf616a9b3e2592cffa8000f92237db05640
    https://github.com/scummvm/scummvm/commit/75fbecf616a9b3e2592cffa8000f92237db05640
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Ctrl+V handling in EditableWidget

In SDL2 there is SDL_GetClipboardText(), so EditableWidget could support
pasting into it.

No copying yet, as there is no selecting.

Changed paths:
    gui/widgets/editable.cpp



diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp
index 4f7e584..6c63074 100644
--- a/gui/widgets/editable.cpp
+++ b/gui/widgets/editable.cpp
@@ -20,6 +20,13 @@
  *
  */
 
+#ifdef USE_SDL2
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_clipboard.h>
+#endif
+
 #include "common/rect.h"
 #include "common/system.h"
 #include "gui/widgets/editable.h"
@@ -185,6 +192,25 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 		forcecaret = true;
 		break;
 
+#ifdef USE_SDL2
+		case Common::KEYCODE_v:
+			if (state.flags & Common::KBD_CTRL) {
+				if (SDL_HasClipboardText() == SDL_TRUE) {
+					char *text = SDL_GetClipboardText();
+					if (text != nullptr) {
+						for (char *ptr = text; *ptr; ++ptr) {
+							if (tryInsertChar(*ptr, _caretPos))
+								++_caretPos;
+						}
+						dirty = true;
+					}
+				}
+			} else {
+				defaultKeyDownHandler(state, dirty, forcecaret, handled);
+			}
+			break;
+#endif
+
 #ifdef MACOSX
 	// Let ctrl-a / ctrl-e move the caret to the start / end of the line.
 	//


Commit: 4a0a5af52ef4a9c184f8aa49b191e78ed24b5b92
    https://github.com/scummvm/scummvm/commit/4a0a5af52ef4a9c184f8aa49b191e78ed24b5b92
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add first Cloud tests

Adding tests for:
* Storage::info();
* Storage::listDirectory();
* Storage::createDirectory().

Changed paths:
  A engines/testbed/cloud.cpp
  A engines/testbed/cloud.h
    engines/testbed/config-params.h
    engines/testbed/module.mk
    engines/testbed/testbed.cpp



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
new file mode 100644
index 0000000..655e2f7
--- /dev/null
+++ b/engines/testbed/cloud.cpp
@@ -0,0 +1,284 @@
+/* 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 "common/config-manager.h"
+#include "common/stream.h"
+#include "common/util.h"
+
+#include "testbed/cloud.h"
+#include <backends/cloud/cloudmanager.h>
+
+namespace Testbed {
+
+CloudTestSuite::CloudTestSuite() {
+	// Cloud tests depend on CloudMan.
+	// If there is no Storage connected to it, disable this test suite.
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		logPrintf("WARNING! : No Storage connected to CloudMan found. Skipping Cloud tests\n");
+		Testsuite::enable(false);
+	}
+
+	addTest("UserInfo", &CloudTests::testInfo, true);
+	addTest("ListDirectory", &CloudTests::testDirectoryListing, true);
+	addTest("CreateDirectory", &CloudTests::testDirectoryCreating, true);
+}
+
+/*
+void CloudTestSuite::enable(bool flag) {
+	Testsuite::enable(ConfParams.isGameDataFound() ? flag : false);
+}
+*/
+
+///// TESTS GO HERE /////
+
+bool CloudTests::waitForCallback() {
+	const int TIMEOUT = 30;
+
+	Common::Point pt;
+	pt.x = 10; pt.y = 10;
+	Testsuite::writeOnScreen("Waiting for callback...", pt);
+
+	int left = TIMEOUT;
+	while (--left) {
+		if (ConfParams.isCloudTestCallbackCalled()) return true;
+		if (ConfParams.isCloudTestErrorCallbackCalled()) return true;
+		g_system->delayMillis(1000);
+	}
+	return false;
+}
+
+void CloudTests::infoCallback(Cloud::Storage::StorageInfoResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	Testsuite::logPrintf("Info! User's ID: %s\n", response.value.uid().c_str());
+	Testsuite::logPrintf("Info! User's email: %s\n", response.value.email().c_str());
+	Testsuite::logPrintf("Info! User's name: %s\n", response.value.name().c_str());
+	Testsuite::logPrintf("Info! User's quota: %lu bytes used / %lu bytes available\n", response.value.used(), response.value.available());
+}
+
+void CloudTests::directoryListedCallback(Cloud::Storage::FileArrayResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	if (response.value.size() == 0) {
+		Testsuite::logPrintf("Warning! Directory is empty!\n");
+		return;
+	}
+
+	Common::String directory, file;
+	uint32 directories = 0, files = 0;
+	for (uint32 i = 0; i < response.value.size(); ++i) {
+		if (response.value[i].isDirectory()) {
+			if (++directories == 1) directory = response.value[i].path();
+		} else {
+			if (++files == 1) file = response.value[i].name();
+		}
+	}
+
+	if (directories == 0) {
+		Testsuite::logPrintf("Info! %u files listed, first one is '%s'\n", files, file.c_str());
+	} else if (files == 0) {
+		Testsuite::logPrintf("Info! %u directories listed, first one is '%s'\n", directories, directory.c_str());
+	} else {
+		Testsuite::logPrintf("Info! %u directories and %u files listed\n", directories, files);
+		Testsuite::logPrintf("Info! First directory is '%u' and first file is '%s'\n", directory.c_str(), file.c_str());
+	}
+}
+
+void CloudTests::directoryCreatedCallback(Cloud::Storage::BoolResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	if (response.value) {
+		Testsuite::logPrintf("Info! Directory created!\n");
+	} else {
+		Testsuite::logPrintf("Info! Such directory already exists!\n");
+	}
+}
+
+void CloudTests::errorCallback(Networking::ErrorResponse response) {
+	ConfParams.setCloudTestErrorCallbackCalled(true);
+	Testsuite::logPrintf("Info! Error Callback was called\n");
+	Testsuite::logPrintf("Info! code = %ld, message = %s\n", response.httpResponseCode, response.response.c_str());
+}
+
+/** This test calls Storage::info(). */
+
+TestExitStatus CloudTests::testInfo() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = Common::String::format(
+		"Welcome to the Cloud test suite!\n"
+		"We're going to use the %s cloud storage which is connected right now.\n\n"
+		"Testing Cloud Storage API info() method.\n"
+		"In this test we'll try to list user infomation.",
+		CloudMan.getCurrentStorage()->name().c_str()
+	);
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : info()\n");
+		return kTestSkipped;
+	}
+
+	if (CloudMan.info(
+			new Common::GlobalFunctionCallback<Cloud::Storage::StorageInfoResponse>(&infoCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	waitForCallback();
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Info was displayed\n");
+	return kTestPassed;
+}
+
+TestExitStatus CloudTests::testDirectoryListing() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API listDirectory() method.\n"
+		"In this test we'll try to list root directory.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : listDirectory()\n");
+		return kTestSkipped;
+	}
+
+	if (CloudMan.listDirectory(
+			"",
+			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	waitForCallback();
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Directory was listed\n");
+	return kTestPassed;
+}
+
+TestExitStatus CloudTests::testDirectoryCreating() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API createDirectory() method.\n"
+		"In this test we'll try to create a 'testbed' directory.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : createDirectory()\n");
+		return kTestSkipped;
+	}
+
+	Common::String info2 = "We'd list the root directory, create the directory and the list it again.\n"
+		"If all goes smoothly, you'd notice that there are more directories now.";
+	Testsuite::displayMessage(info2);
+
+	// list root directory
+	if (CloudMan.listDirectory(
+			"",
+			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	waitForCallback();
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	ConfParams.setCloudTestCallbackCalled(false);
+
+	// create 'testbed'
+	if (CloudMan.getCurrentStorage()->createDirectory(
+			"/testbed",
+			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&directoryCreatedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	waitForCallback();
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	ConfParams.setCloudTestCallbackCalled(false);
+
+	// list it again
+	if (CloudMan.listDirectory(
+		"",
+		new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+		new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	waitForCallback();
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	if (Testsuite::handleInteractiveInput("Was the CloudMan able to create a 'testbed' directory?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Directory was not created!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Directory was created\n");
+	return kTestPassed;
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
new file mode 100644
index 0000000..827b231
--- /dev/null
+++ b/engines/testbed/cloud.h
@@ -0,0 +1,73 @@
+/* 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.
+ *
+ */
+
+#ifndef TESTBED_CLOUD_H
+#define TESTBED_CLOUD_H
+
+#include "testbed/testsuite.h"
+#include <backends/cloud/storage.h>
+
+// This file can be used as template for header files of other newer testsuites.
+
+namespace Testbed {
+
+namespace CloudTests {
+
+// Helper functions for Cloud tests
+
+bool waitForCallback();
+void infoCallback(Cloud::Storage::StorageInfoResponse response);
+void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
+void directoryCreatedCallback(Cloud::Storage::BoolResponse response);
+void errorCallback(Networking::ErrorResponse response);
+
+TestExitStatus testInfo();
+TestExitStatus testDirectoryListing();
+TestExitStatus testDirectoryCreating();
+
+} // End of namespace CloudTests
+
+class CloudTestSuite : public Testsuite {
+public:
+	/**
+	 * The constructor for the CloudTestSuite
+	 * For every test to be executed one must:
+	 * 1) Create a function that would invoke the test
+	 * 2) Add that test to list by executing addTest()
+	 *
+	 * @see addTest()
+	 */
+	CloudTestSuite();
+	~CloudTestSuite() {}
+	const char *getName() const {
+		return "Cloud";
+	}
+
+	const char *getDescription() const {
+		return "CloudMan, Storage API tests";
+	}
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_TEMPLATE_H
diff --git a/engines/testbed/config-params.h b/engines/testbed/config-params.h
index 89aae19..57fd099 100644
--- a/engines/testbed/config-params.h
+++ b/engines/testbed/config-params.h
@@ -54,6 +54,10 @@ private:
 	 */
 	bool _isInteractive;
 	bool _isGameDataFound;
+#ifdef USE_LIBCURL
+	bool _isCloudTestCallbackCalled;
+	bool _isCloudTestErrorCallbackCalled;
+#endif
 	bool _rerunTests;
 	TestbedConfigManager *_testbedConfMan;
 
@@ -68,6 +72,14 @@ public:
 	bool isGameDataFound() { return _isGameDataFound; }
 	void setGameDataFound(bool status) { _isGameDataFound = status; }
 
+#ifdef USE_LIBCURL
+	bool isCloudTestCallbackCalled() const { return _isCloudTestCallbackCalled; }
+	void setCloudTestCallbackCalled(bool status) { _isCloudTestCallbackCalled = status; }
+
+	bool isCloudTestErrorCallbackCalled() const { return _isCloudTestErrorCallbackCalled; }
+	void setCloudTestErrorCallbackCalled(bool status) { _isCloudTestErrorCallbackCalled = status; }
+#endif
+
 	TestbedConfigManager *getTestbedConfigManager() { return _testbedConfMan; }
 	void setTestbedConfigManager(TestbedConfigManager* confMan) { _testbedConfMan = confMan; }
 
diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk
index ce78a48..3a7eb2e 100644
--- a/engines/testbed/module.mk
+++ b/engines/testbed/module.mk
@@ -14,6 +14,11 @@ MODULE_OBJS := \
 	testbed.o \
 	testsuite.o
 
+ifdef USE_LIBCURL
+MODULE_OBJS += \
+	cloud.o
+endif
+
 MODULE_DIRS += \
 	engines/testbed
 
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
index 885429c..a2f9aad 100644
--- a/engines/testbed/testbed.cpp
+++ b/engines/testbed/testbed.cpp
@@ -39,6 +39,9 @@
 #include "testbed/savegame.h"
 #include "testbed/sound.h"
 #include "testbed/testbed.h"
+#ifdef USE_LIBCURL
+#include "testbed/cloud.h"
+#endif
 
 namespace Testbed {
 
@@ -134,6 +137,11 @@ TestbedEngine::TestbedEngine(OSystem *syst)
 	// Midi
 	ts = new MidiTestSuite();
 	_testsuiteList.push_back(ts);
+#ifdef USE_LIBCURL
+	// Cloud
+	ts = new CloudTestSuite();
+	_testsuiteList.push_back(ts);
+#endif
 }
 
 TestbedEngine::~TestbedEngine() {


Commit: 721ee9527e9ff8d028aa0ba70fb4e4c80f7e6060
    https://github.com/scummvm/scummvm/commit/721ee9527e9ff8d028aa0ba70fb4e4c80f7e6060
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Fix CloudTests to ask users whether to wait

Callbacks might be slow (like in SyncSaves), but they also could hang
forever, so users now are being asked whether they want to wait or to
skip the test.

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 655e2f7..73b0b8b 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -66,6 +66,17 @@ bool CloudTests::waitForCallback() {
 	return false;
 }
 
+bool CloudTests::waitForCallbackMore() {
+	while (!waitForCallback()) {
+		Common::String info = "It takes more time than expected. Do you want to skip the test or wait more?";
+		if (Testsuite::handleInteractiveInput(info, "Wait", "Skip", kOptionRight)) {
+			Testsuite::logPrintf("Info! Skipping test : info()\n");
+			return false;
+		}
+	}
+	return true;
+}
+
 void CloudTests::infoCallback(Cloud::Storage::StorageInfoResponse response) {
 	ConfParams.setCloudTestCallbackCalled(true);
 	Testsuite::logPrintf("Info! User's ID: %s\n", response.value.uid().c_str());
@@ -147,7 +158,7 @@ TestExitStatus CloudTests::testInfo() {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
 
-	waitForCallback();
+	if (!waitForCallbackMore()) return kTestSkipped;
 	Testsuite::clearScreen();
 
 	if (ConfParams.isCloudTestErrorCallbackCalled()) {
@@ -184,7 +195,7 @@ TestExitStatus CloudTests::testDirectoryListing() {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
 
-	waitForCallback();
+	if (!waitForCallbackMore()) return kTestSkipped;
 	Testsuite::clearScreen();
 
 	if (ConfParams.isCloudTestErrorCallbackCalled()) {
@@ -226,7 +237,7 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
 
-	waitForCallback();
+	if (!waitForCallbackMore()) return kTestSkipped;
 	Testsuite::clearScreen();
 
 	if (ConfParams.isCloudTestErrorCallbackCalled()) {
@@ -245,7 +256,7 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
 
-	waitForCallback();
+	if (!waitForCallbackMore()) return kTestSkipped;
 	Testsuite::clearScreen();
 
 	if (ConfParams.isCloudTestErrorCallbackCalled()) {
@@ -264,7 +275,7 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
 
-	waitForCallback();
+	if (!waitForCallbackMore()) return kTestSkipped;
 	Testsuite::clearScreen();
 
 	if (ConfParams.isCloudTestErrorCallbackCalled()) {
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index 827b231..17bc093 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -35,6 +35,7 @@ namespace CloudTests {
 // Helper functions for Cloud tests
 
 bool waitForCallback();
+bool waitForCallbackMore();
 void infoCallback(Cloud::Storage::StorageInfoResponse response);
 void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
 void directoryCreatedCallback(Cloud::Storage::BoolResponse response);


Commit: 6d227a437a6b52f6bc0e75f459d79e22e3d8fb2d
    https://github.com/scummvm/scummvm/commit/6d227a437a6b52f6bc0e75f459d79e22e3d8fb2d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add CloudTests::testUploading()

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 73b0b8b..5138454 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -23,7 +23,7 @@
 #include "common/config-manager.h"
 #include "common/stream.h"
 #include "common/util.h"
-
+#include "testbed/fs.h"
 #include "testbed/cloud.h"
 #include <backends/cloud/cloudmanager.h>
 
@@ -40,6 +40,7 @@ CloudTestSuite::CloudTestSuite() {
 	addTest("UserInfo", &CloudTests::testInfo, true);
 	addTest("ListDirectory", &CloudTests::testDirectoryListing, true);
 	addTest("CreateDirectory", &CloudTests::testDirectoryCreating, true);
+	addTest("FileUpload", &CloudTests::testUploading, true);
 }
 
 /*
@@ -121,6 +122,12 @@ void CloudTests::directoryCreatedCallback(Cloud::Storage::BoolResponse response)
 	}
 }
 
+void CloudTests::fileUploadedCallback(Cloud::Storage::UploadResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	Testsuite::logPrintf("Info! Uploaded file into '%s'\n", response.value.path().c_str());
+	Testsuite::logPrintf("Info! It's id = '%s' and size = '%lu'\n", response.value.id().c_str(), response.value.size());
+}
+
 void CloudTests::errorCallback(Networking::ErrorResponse response) {
 	ConfParams.setCloudTestErrorCallbackCalled(true);
 	Testsuite::logPrintf("Info! Error Callback was called\n");
@@ -268,9 +275,9 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 
 	// list it again
 	if (CloudMan.listDirectory(
-		"",
-		new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
-		new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+			"",
+			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
 		) == nullptr) {
 		Testsuite::logPrintf("Warning! No Request is returned!\n");
 	}
@@ -292,4 +299,92 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 	return kTestPassed;
 }
 
+TestExitStatus CloudTests::testUploading() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API upload() method.\n"
+		"In this test we'll try to upload a 'test1/file.txt' file.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : upload()\n");
+		return kTestSkipped;
+	}
+
+	if (!ConfParams.isGameDataFound()) {
+		Testsuite::logPrintf("Info! Couldn't find the game data, so skipping test : upload()\n");
+		return kTestSkipped;
+	}
+
+	const Common::String &path = ConfMan.get("path");
+	Common::FSDirectory gameRoot(path);
+	Common::FSDirectory *directory = gameRoot.getSubDirectory("test1");
+	Common::FSNode node = directory->getFSNode().getChild("file.txt");
+	delete directory;
+
+	if (CloudMan.getCurrentStorage()->uploadStreamSupported()) {
+		if (CloudMan.getCurrentStorage()->upload(
+				"/testbed/testfile.txt",
+				node.createReadStream(),
+				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
+				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+			) == nullptr) {
+			Testsuite::logPrintf("Warning! No Request is returned!\n");
+		}
+	} else {
+		Common::String filepath = node.getPath();
+		if (CloudMan.getCurrentStorage()->upload(
+			"/testbed/testfile.txt",
+			filepath.c_str(),
+			new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+			) == nullptr) {
+			Testsuite::logPrintf("Warning! No Request is returned!\n");
+		}
+	}
+
+	if (!waitForCallbackMore()) return kTestSkipped;
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	Common::String info2 = "upload() is finished. Do you want to list '/testbed' directory?";
+
+	if (!Testsuite::handleInteractiveInput(info2, "Yes", "No", kOptionRight)) {
+		ConfParams.setCloudTestCallbackCalled(false);
+
+		if (CloudMan.listDirectory(
+			"/testbed/",
+			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+			) == nullptr) {
+			Testsuite::logPrintf("Warning! No Request is returned!\n");
+		}
+
+		if (!waitForCallbackMore()) return kTestSkipped;
+		Testsuite::clearScreen();
+
+		if (ConfParams.isCloudTestErrorCallbackCalled()) {
+			Testsuite::logPrintf("Error callback was called\n");
+			return kTestFailed;
+		}
+	}
+
+	if (Testsuite::handleInteractiveInput("Was the CloudMan able to upload into 'testbed/testfile.txt' file?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! File was not uploaded!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("File was uploaded\n");
+	return kTestPassed;
+}
+
 } // End of namespace Testbed
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index 17bc093..177ac74 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -39,11 +39,13 @@ bool waitForCallbackMore();
 void infoCallback(Cloud::Storage::StorageInfoResponse response);
 void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
 void directoryCreatedCallback(Cloud::Storage::BoolResponse response);
+void fileUploadedCallback(Cloud::Storage::UploadResponse response);
 void errorCallback(Networking::ErrorResponse response);
 
 TestExitStatus testInfo();
 TestExitStatus testDirectoryListing();
 TestExitStatus testDirectoryCreating();
+TestExitStatus testUploading();
 
 } // End of namespace CloudTests
 


Commit: 7a34abe39e7fc5dc7ece81db5be84701599d4be2
    https://github.com/scummvm/scummvm/commit/7a34abe39e7fc5dc7ece81db5be84701599d4be2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add CloudTests::testDownloading()

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 5138454..293fb79 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -41,6 +41,7 @@ CloudTestSuite::CloudTestSuite() {
 	addTest("ListDirectory", &CloudTests::testDirectoryListing, true);
 	addTest("CreateDirectory", &CloudTests::testDirectoryCreating, true);
 	addTest("FileUpload", &CloudTests::testUploading, true);
+	addTest("FileDownload", &CloudTests::testDownloading, true);
 }
 
 /*
@@ -128,6 +129,15 @@ void CloudTests::fileUploadedCallback(Cloud::Storage::UploadResponse response) {
 	Testsuite::logPrintf("Info! It's id = '%s' and size = '%lu'\n", response.value.id().c_str(), response.value.size());
 }
 
+void CloudTests::fileDownloadedCallback(Cloud::Storage::BoolResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	if (response.value) {
+		Testsuite::logPrintf("Info! File downloaded!\n");
+	} else {
+		Testsuite::logPrintf("Info! Failed to download the file!\n");
+	}
+}
+
 void CloudTests::errorCallback(Networking::ErrorResponse response) {
 	ConfParams.setCloudTestErrorCallbackCalled(true);
 	Testsuite::logPrintf("Info! Error Callback was called\n");
@@ -387,4 +397,51 @@ TestExitStatus CloudTests::testUploading() {
 	return kTestPassed;
 }
 
+TestExitStatus CloudTests::testDownloading() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API download() method.\n"
+		"In this test we'll try to download that 'testbed/testfile.txt' file.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : download()\n");
+		return kTestSkipped;
+	}
+
+	const Common::String &path = ConfMan.get("path");
+	Common::FSDirectory gameRoot(path);
+	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_file.txt");
+	Common::String filepath = node.getPath();
+	if (CloudMan.getCurrentStorage()->download(
+			"/testbed/testfile.txt",
+			filepath.c_str(),
+			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&fileDownloadedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	if (!waitForCallbackMore()) return kTestSkipped;
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	if (Testsuite::handleInteractiveInput("Was the CloudMan able to download into 'testbed/downloaded_file.txt' file?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! File was not downloaded!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("File was downloaded\n");
+	return kTestPassed;
+}
+
 } // End of namespace Testbed
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index 177ac74..266bd32 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -40,12 +40,14 @@ void infoCallback(Cloud::Storage::StorageInfoResponse response);
 void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
 void directoryCreatedCallback(Cloud::Storage::BoolResponse response);
 void fileUploadedCallback(Cloud::Storage::UploadResponse response);
+void fileDownloadedCallback(Cloud::Storage::BoolResponse response);
 void errorCallback(Networking::ErrorResponse response);
 
 TestExitStatus testInfo();
 TestExitStatus testDirectoryListing();
 TestExitStatus testDirectoryCreating();
 TestExitStatus testUploading();
+TestExitStatus testDownloading();
 
 } // End of namespace CloudTests
 


Commit: 4e2725135674515821247307ab8358069d1db48c
    https://github.com/scummvm/scummvm/commit/4e2725135674515821247307ab8358069d1db48c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add CloudTests::testFolderDownloading()

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 293fb79..9c6563c 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -42,6 +42,7 @@ CloudTestSuite::CloudTestSuite() {
 	addTest("CreateDirectory", &CloudTests::testDirectoryCreating, true);
 	addTest("FileUpload", &CloudTests::testUploading, true);
 	addTest("FileDownload", &CloudTests::testDownloading, true);
+	addTest("FolderDownload", &CloudTests::testFolderDownloading, true);
 }
 
 /*
@@ -138,6 +139,15 @@ void CloudTests::fileDownloadedCallback(Cloud::Storage::BoolResponse response) {
 	}
 }
 
+void CloudTests::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	if (response.value.size() == 0) {
+		Testsuite::logPrintf("Info! Directory is downloaded successfully!\n");
+	} else {
+		Testsuite::logPrintf("Warning! %lu files were not downloaded during folder downloading!\n", response.value.size());
+	}
+}
+
 void CloudTests::errorCallback(Networking::ErrorResponse response) {
 	ConfParams.setCloudTestErrorCallbackCalled(true);
 	Testsuite::logPrintf("Info! Error Callback was called\n");
@@ -444,4 +454,51 @@ TestExitStatus CloudTests::testDownloading() {
 	return kTestPassed;
 }
 
+TestExitStatus CloudTests::testFolderDownloading() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API downloadFolder() method.\n"
+		"In this test we'll try to download remote 'testbed/' directory.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : downloadFolder()\n");
+		return kTestSkipped;
+	}
+
+	const Common::String &path = ConfMan.get("path");
+	Common::FSDirectory gameRoot(path);
+	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
+	Common::String filepath = node.getPath();
+	if (CloudMan.downloadFolder(
+			"/testbed/",
+			filepath.c_str(),
+			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryDownloadedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	if (!waitForCallbackMore()) return kTestSkipped;
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	if (Testsuite::handleInteractiveInput("Was the CloudMan able to download into 'testbed/downloaded_directory'?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Directory was not downloaded!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Directory was downloaded\n");
+	return kTestPassed;
+}
+
 } // End of namespace Testbed
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index 266bd32..e9df51c 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -41,6 +41,7 @@ void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
 void directoryCreatedCallback(Cloud::Storage::BoolResponse response);
 void fileUploadedCallback(Cloud::Storage::UploadResponse response);
 void fileDownloadedCallback(Cloud::Storage::BoolResponse response);
+void directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response);
 void errorCallback(Networking::ErrorResponse response);
 
 TestExitStatus testInfo();
@@ -48,6 +49,7 @@ TestExitStatus testDirectoryListing();
 TestExitStatus testDirectoryCreating();
 TestExitStatus testUploading();
 TestExitStatus testDownloading();
+TestExitStatus testFolderDownloading();
 
 } // End of namespace CloudTests
 


Commit: 04888cf454fb3243e82d424d46af1e2ca74d0b8c
    https://github.com/scummvm/scummvm/commit/04888cf454fb3243e82d424d46af1e2ca74d0b8c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add CloudTests::testSavesSync()

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 9c6563c..9d10d7d 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -43,6 +43,7 @@ CloudTestSuite::CloudTestSuite() {
 	addTest("FileUpload", &CloudTests::testUploading, true);
 	addTest("FileDownload", &CloudTests::testDownloading, true);
 	addTest("FolderDownload", &CloudTests::testFolderDownloading, true);
+	addTest("SyncSaves", &CloudTests::testSavesSync, true);
 }
 
 /*
@@ -148,6 +149,15 @@ void CloudTests::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse r
 	}
 }
 
+void CloudTests::savesSyncedCallback(Cloud::Storage::BoolResponse response) {
+	ConfParams.setCloudTestCallbackCalled(true);
+	if (response.value) {
+		Testsuite::logPrintf("Info! Saves are synced successfully!\n");
+	} else {
+		Testsuite::logPrintf("Warning! Saves were not synced!\n");
+	}
+}
+
 void CloudTests::errorCallback(Networking::ErrorResponse response) {
 	ConfParams.setCloudTestErrorCallbackCalled(true);
 	Testsuite::logPrintf("Info! Error Callback was called\n");
@@ -501,4 +511,49 @@ TestExitStatus CloudTests::testFolderDownloading() {
 	return kTestPassed;
 }
 
+TestExitStatus CloudTests::testSavesSync() {
+	ConfParams.setCloudTestCallbackCalled(false);
+	ConfParams.setCloudTestErrorCallbackCalled(false);
+
+	if (CloudMan.getCurrentStorage() == nullptr) {
+		Testsuite::logPrintf("Couldn't find connected Storage\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Cloud Storage API syncSaves() method.\n"
+		"In this test we'll try to sync your saves.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : syncSaves()\n");
+		return kTestSkipped;
+	}
+
+	const Common::String &path = ConfMan.get("path");
+	Common::FSDirectory gameRoot(path);
+	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
+	Common::String filepath = node.getPath();
+	if (CloudMan.syncSaves(
+			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&savesSyncedCallback),
+			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+		) == nullptr) {
+		Testsuite::logPrintf("Warning! No Request is returned!\n");
+	}
+
+	if (!waitForCallbackMore()) return kTestSkipped;
+	Testsuite::clearScreen();
+
+	if (ConfParams.isCloudTestErrorCallbackCalled()) {
+		Testsuite::logPrintf("Error callback was called\n");
+		return kTestFailed;
+	}
+
+	if (Testsuite::handleInteractiveInput("Was the CloudMan able to sync saves?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Saves were not synced!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Saves were synced successfully\n");
+	return kTestPassed;
+}
+
 } // End of namespace Testbed
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index e9df51c..aa6ed87 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -42,6 +42,7 @@ void directoryCreatedCallback(Cloud::Storage::BoolResponse response);
 void fileUploadedCallback(Cloud::Storage::UploadResponse response);
 void fileDownloadedCallback(Cloud::Storage::BoolResponse response);
 void directoryDownloadedCallback(Cloud::Storage::FileArrayResponse response);
+void savesSyncedCallback(Cloud::Storage::BoolResponse response);
 void errorCallback(Networking::ErrorResponse response);
 
 TestExitStatus testInfo();
@@ -50,6 +51,7 @@ TestExitStatus testDirectoryCreating();
 TestExitStatus testUploading();
 TestExitStatus testDownloading();
 TestExitStatus testFolderDownloading();
+TestExitStatus testSavesSync();
 
 } // End of namespace CloudTests
 


Commit: 18fc113aa91d0563940b3de807576f7879e9ebea
    https://github.com/scummvm/scummvm/commit/18fc113aa91d0563940b3de807576f7879e9ebea
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add URL opening for OS X

Changed paths:
  A backends/networking/browser/openurl-osx.cpp
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index b8feffb..f04397d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -161,10 +161,15 @@ ifeq ($(BACKEND),android)
 MODULE_OBJS += \
 	networking/browser/openurl-android.o
 else
+ifdef MACOSX
+MODULE_OBJS += \
+	networking/browser/openurl-osx.o
+else
 MODULE_OBJS += \
 	networking/browser/openurl-posix.o
 endif
 endif
+endif
 
 ifdef MACOSX
 MODULE_OBJS += \
@@ -172,8 +177,7 @@ MODULE_OBJS += \
 	midi/coreaudio.o \
 	midi/coremidi.o \
 	updates/macosx/macosx-updates.o \
-	taskbar/macosx/macosx-taskbar.o \
-	networking/browser/openurl-default.o
+	taskbar/macosx/macosx-taskbar.o
 endif
 
 ifdef WIN32
diff --git a/backends/networking/browser/openurl-osx.cpp b/backends/networking/browser/openurl-osx.cpp
new file mode 100644
index 0000000..66d96b4
--- /dev/null
+++ b/backends/networking/browser/openurl-osx.cpp
@@ -0,0 +1,49 @@
+/* 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.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/browser/openurl.h"
+#include <CoreFoundation/CFBundle.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace Networking {
+namespace Browser {
+
+using namespace std;
+
+bool openUrl(const Common::String &url) {
+	CFURLRef urlRef = CFURLCreateWithBytes (
+      NULL,
+      (UInt8*)url.c_str(),
+      url.size(),
+      kCFStringEncodingASCII,
+      NULL
+    );
+	int result = LSOpenCFURLRef(urlRef, 0);
+  CFRelease(urlRef);
+	return result == 0;
+}
+
+} // End of namespace Browser
+} // End of namespace Networking
+


Commit: dcf904192630d1c180d60ff7989e3629ca5fc1b7
    https://github.com/scummvm/scummvm/commit/dcf904192630d1c180d60ff7989e3629ca5fc1b7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Fix CloudTests

Now work with all available Storages.

Changed paths:
    engines/testbed/cloud.cpp
    engines/testbed/cloud.h



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 9d10d7d..775ae10 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -81,6 +81,12 @@ bool CloudTests::waitForCallbackMore() {
 	return true;
 }
 
+const char *CloudTests::getRemoteTestPath() {
+	if (CloudMan.getStorageIndex() == Cloud::kStorageDropboxId)
+		return "/testbed";
+	return "testbed";
+}
+
 void CloudTests::infoCallback(Cloud::Storage::StorageInfoResponse response) {
 	ConfParams.setCloudTestCallbackCalled(true);
 	Testsuite::logPrintf("Info! User's ID: %s\n", response.value.uid().c_str());
@@ -286,7 +292,7 @@ TestExitStatus CloudTests::testDirectoryCreating() {
 
 	// create 'testbed'
 	if (CloudMan.getCurrentStorage()->createDirectory(
-			"/testbed",
+			getRemoteTestPath(),
 			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&directoryCreatedCallback),
 			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
 		) == nullptr) {
@@ -359,7 +365,7 @@ TestExitStatus CloudTests::testUploading() {
 
 	if (CloudMan.getCurrentStorage()->uploadStreamSupported()) {
 		if (CloudMan.getCurrentStorage()->upload(
-				"/testbed/testfile.txt",
+				Common::String(getRemoteTestPath()) + "/testfile.txt",
 				node.createReadStream(),
 				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
 				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
@@ -369,10 +375,10 @@ TestExitStatus CloudTests::testUploading() {
 	} else {
 		Common::String filepath = node.getPath();
 		if (CloudMan.getCurrentStorage()->upload(
-			"/testbed/testfile.txt",
-			filepath.c_str(),
-			new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
-			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+				Common::String(getRemoteTestPath()) + "/testfile.txt",
+				filepath.c_str(),
+				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
+				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
 			) == nullptr) {
 			Testsuite::logPrintf("Warning! No Request is returned!\n");
 		}
@@ -392,9 +398,9 @@ TestExitStatus CloudTests::testUploading() {
 		ConfParams.setCloudTestCallbackCalled(false);
 
 		if (CloudMan.listDirectory(
-			"/testbed/",
-			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
-			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
+				getRemoteTestPath(),
+				new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
+				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
 			) == nullptr) {
 			Testsuite::logPrintf("Warning! No Request is returned!\n");
 		}
@@ -439,7 +445,7 @@ TestExitStatus CloudTests::testDownloading() {
 	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_file.txt");
 	Common::String filepath = node.getPath();
 	if (CloudMan.getCurrentStorage()->download(
-			"/testbed/testfile.txt",
+			Common::String(getRemoteTestPath()) + "/testfile.txt",
 			filepath.c_str(),
 			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&fileDownloadedCallback),
 			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
@@ -486,7 +492,7 @@ TestExitStatus CloudTests::testFolderDownloading() {
 	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
 	Common::String filepath = node.getPath();
 	if (CloudMan.downloadFolder(
-			"/testbed/",
+			getRemoteTestPath(),
 			filepath.c_str(),
 			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryDownloadedCallback),
 			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
diff --git a/engines/testbed/cloud.h b/engines/testbed/cloud.h
index aa6ed87..ed27d7d 100644
--- a/engines/testbed/cloud.h
+++ b/engines/testbed/cloud.h
@@ -36,6 +36,7 @@ namespace CloudTests {
 
 bool waitForCallback();
 bool waitForCallbackMore();
+const char *getRemoteTestPath();
 void infoCallback(Cloud::Storage::StorageInfoResponse response);
 void directoryListedCallback(Cloud::Storage::FileArrayResponse response);
 void directoryCreatedCallback(Cloud::Storage::BoolResponse response);


Commit: b616cc3d574a4120440044b386f690f6d7f51a5f
    https://github.com/scummvm/scummvm/commit/b616cc3d574a4120440044b386f690f6d7f51a5f
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix indentation in openurl-osx.cpp

Changed paths:
    backends/networking/browser/openurl-osx.cpp



diff --git a/backends/networking/browser/openurl-osx.cpp b/backends/networking/browser/openurl-osx.cpp
index 66d96b4..8d786d7 100644
--- a/backends/networking/browser/openurl-osx.cpp
+++ b/backends/networking/browser/openurl-osx.cpp
@@ -33,14 +33,14 @@ using namespace std;
 
 bool openUrl(const Common::String &url) {
 	CFURLRef urlRef = CFURLCreateWithBytes (
-      NULL,
-      (UInt8*)url.c_str(),
-      url.size(),
-      kCFStringEncodingASCII,
-      NULL
-    );
+		NULL,
+		(UInt8*)url.c_str(),
+		url.size(),
+		kCFStringEncodingASCII,
+		NULL
+	);
 	int result = LSOpenCFURLRef(urlRef, 0);
-  CFRelease(urlRef);
+	CFRelease(urlRef);
 	return result == 0;
 }
 


Commit: 830c7b578caaec95594e190d6727892b1973df62
    https://github.com/scummvm/scummvm/commit/830c7b578caaec95594e190d6727892b1973df62
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add Webserver test suite

Two tests now: IP resolving and index page check.

Changed paths:
  A engines/testbed/webserver.cpp
  A engines/testbed/webserver.h
    engines/testbed/module.mk
    engines/testbed/testbed.cpp



diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk
index 3a7eb2e..99e6157 100644
--- a/engines/testbed/module.mk
+++ b/engines/testbed/module.mk
@@ -19,6 +19,11 @@ MODULE_OBJS += \
 	cloud.o
 endif
 
+ifdef USE_SDL_NET
+MODULE_OBJS += \
+	webserver.o
+endif
+
 MODULE_DIRS += \
 	engines/testbed
 
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
index a2f9aad..5943d47 100644
--- a/engines/testbed/testbed.cpp
+++ b/engines/testbed/testbed.cpp
@@ -42,6 +42,9 @@
 #ifdef USE_LIBCURL
 #include "testbed/cloud.h"
 #endif
+#ifdef USE_SDL_NET
+#include "testbed/webserver.h"
+#endif
 
 namespace Testbed {
 
@@ -142,6 +145,11 @@ TestbedEngine::TestbedEngine(OSystem *syst)
 	ts = new CloudTestSuite();
 	_testsuiteList.push_back(ts);
 #endif
+#ifdef USE_SDL_NET
+	// Webserver
+	ts = new WebserverTestSuite();
+	_testsuiteList.push_back(ts);
+#endif
 }
 
 TestbedEngine::~TestbedEngine() {
diff --git a/engines/testbed/webserver.cpp b/engines/testbed/webserver.cpp
new file mode 100644
index 0000000..f17b5a6
--- /dev/null
+++ b/engines/testbed/webserver.cpp
@@ -0,0 +1,102 @@
+/* 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 "testbed/webserver.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/config-manager.h"
+#include <backends/networking/browser/openurl.h>
+
+namespace Testbed {
+
+WebserverTestSuite::WebserverTestSuite() {
+	addTest("ResolveIP", &WebserverTests::testIP, true);
+	addTest("IndexPage", &WebserverTests::testIndexPage, true);
+}
+
+///// TESTS GO HERE /////
+
+/** This test calls Storage::info(). */
+
+bool WebserverTests::startServer() {
+	Common::Point pt;
+	pt.x = 10; pt.y = 10;
+	Testsuite::writeOnScreen("Starting webserver...", pt);
+	LocalServer.start();
+	g_system->delayMillis(500);
+	Testsuite::clearScreen();
+	return LocalServer.isRunning();
+}
+
+TestExitStatus WebserverTests::testIP() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Welcome to the Webserver test suite!\n"
+		"You would be visiting different server's pages and saying whether they work like they should.\n\n"
+		"Testing Webserver's IP resolving.\n"
+		"In this test we'll try to resolve server's IP.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : IP resolving\n");
+		return kTestSkipped;
+	}
+
+	if (Testsuite::handleInteractiveInput(
+			Common::String::format("Is this your machine's IP?\n%s", LocalServer.getAddress().c_str()),
+			"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! IP was not resolved!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("IP was resolved\n");
+	return kTestPassed;
+}
+
+TestExitStatus WebserverTests::testIndexPage() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's index page.\n"
+		"In this test we'll try to open server's index page.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : index page\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress());
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("The %s page opens well?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Couldn't open server's index page!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Server's index page is OK\n");
+	return kTestPassed;
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/webserver.h b/engines/testbed/webserver.h
new file mode 100644
index 0000000..bdb3740
--- /dev/null
+++ b/engines/testbed/webserver.h
@@ -0,0 +1,64 @@
+/* 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.
+ *
+ */
+
+#ifndef TESTBED_WEBSERVER_H
+#define TESTBED_WEBSERVER_H
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+namespace WebserverTests {
+
+// Helper functions for Webserver tests
+
+bool startServer();
+TestExitStatus testIP();
+TestExitStatus testIndexPage();
+
+} // End of namespace WebserverTests
+
+class WebserverTestSuite : public Testsuite {
+public:
+	/**
+	 * The constructor for the WebserverTestSuite
+	 * For every test to be executed one must:
+	 * 1) Create a function that would invoke the test
+	 * 2) Add that test to list by executing addTest()
+	 *
+	 * @see addTest()
+	 */
+	WebserverTestSuite();
+	~WebserverTestSuite() {}
+	const char *getName() const {
+		return "Webserver";
+	}
+
+	const char *getDescription() const {
+		return "Webserver tests";
+	}
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_TEMPLATE_H


Commit: 817d831255ad37f82c21ba7f00c4795a536d33fb
    https://github.com/scummvm/scummvm/commit/817d831255ad37f82c21ba7f00c4795a536d33fb
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add more Webserver tests

Changed paths:
    engines/testbed/webserver.cpp
    engines/testbed/webserver.h



diff --git a/engines/testbed/webserver.cpp b/engines/testbed/webserver.cpp
index f17b5a6..feb2911 100644
--- a/engines/testbed/webserver.cpp
+++ b/engines/testbed/webserver.cpp
@@ -30,6 +30,11 @@ namespace Testbed {
 WebserverTestSuite::WebserverTestSuite() {
 	addTest("ResolveIP", &WebserverTests::testIP, true);
 	addTest("IndexPage", &WebserverTests::testIndexPage, true);
+	addTest("FilesPage", &WebserverTests::testFilesPageInvalidParameterValue, true);
+	addTest("CreateDirectory", &WebserverTests::testFilesPageCreateDirectory, true);
+	addTest("UploadFile", &WebserverTests::testFilesPageUploadFile, true);
+	addTest("UploadDirectory", &WebserverTests::testFilesPageUploadDirectory, true);
+	addTest("DownloadFile", &WebserverTests::testFilesPageDownloadFile, true);
 }
 
 ///// TESTS GO HERE /////
@@ -99,4 +104,134 @@ TestExitStatus WebserverTests::testIndexPage() {
 	return kTestPassed;
 }
 
+TestExitStatus WebserverTests::testFilesPageInvalidParameterValue() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's files page.\n"
+		"In this test we'll try to pass invalid parameter to files page.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : files page (invalid parameter)\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress()+"files?path=error");
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("The %sfiles?path=error page displays error message?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! No error message on invalid parameter in '/files'!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Server's files page detects invalid paramters fine\n");
+	return kTestPassed;
+}
+
+TestExitStatus WebserverTests::testFilesPageCreateDirectory() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's files page Create Directory feature.\n"
+		"In this test you'll try to create directory.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : files page create directory\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress() + "files?path=/root/");
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("You could go to %sfiles page, navigate to some directory with write access and create a directory there?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Create Directory is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Create Directory is OK\n");
+	return kTestPassed;
+}
+
+TestExitStatus WebserverTests::testFilesPageUploadFile() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's files page Upload Files feature.\n"
+		"In this test you'll try to upload a file.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : files page file upload\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress() + "files?path=/root/");
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("You're able to upload a file in some directory with write access through %sfiles page?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Upload Files is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Upload Files is OK\n");
+	return kTestPassed;
+}
+
+TestExitStatus WebserverTests::testFilesPageUploadDirectory() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's files page Upload Directory feature.\n"
+		"In this test you'll try to upload a directory (works in Chrome only).";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : files page directory upload\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress() + "files?path=/root/");
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("You're able to upload a directory into some directory with write access through %sfiles page using Chrome?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Upload Directory is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Upload Directory is OK\n");
+	return kTestPassed;
+}
+
+TestExitStatus WebserverTests::testFilesPageDownloadFile() {
+	if (!startServer()) {
+		Testsuite::logPrintf("Error! Can't start local webserver!\n");
+		return kTestFailed;
+	}
+
+	Common::String info = "Testing Webserver's files downloading feature.\n"
+		"In this test you'll try to download a file.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : files page download\n");
+		return kTestSkipped;
+	}
+
+	Networking::Browser::openUrl(LocalServer.getAddress() + "files?path=/root/");
+	if (Testsuite::handleInteractiveInput(
+		Common::String::format("You're able to download a file through %sfiles page?", LocalServer.getAddress().c_str()),
+		"Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! Files downloading is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("Files downloading is OK\n");
+	return kTestPassed;
+}
+
 } // End of namespace Testbed
diff --git a/engines/testbed/webserver.h b/engines/testbed/webserver.h
index bdb3740..3f30834 100644
--- a/engines/testbed/webserver.h
+++ b/engines/testbed/webserver.h
@@ -34,6 +34,11 @@ namespace WebserverTests {
 bool startServer();
 TestExitStatus testIP();
 TestExitStatus testIndexPage();
+TestExitStatus testFilesPageInvalidParameterValue();
+TestExitStatus testFilesPageCreateDirectory();
+TestExitStatus testFilesPageUploadFile();
+TestExitStatus testFilesPageUploadDirectory();
+TestExitStatus testFilesPageDownloadFile();
 
 } // End of namespace WebserverTests
 


Commit: 737dc91e64c75e7b282e21d278f29705d74ce449
    https://github.com/scummvm/scummvm/commit/737dc91e64c75e7b282e21d278f29705d74ce449
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Add openUrl test in MiscTests

Changed paths:
    engines/testbed/misc.cpp
    engines/testbed/misc.h



diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp
index 5847a8d..3dde71e 100644
--- a/engines/testbed/misc.cpp
+++ b/engines/testbed/misc.cpp
@@ -22,6 +22,7 @@
 
 #include "testbed/misc.h"
 #include "common/timer.h"
+#include <backends/networking/browser/openurl.h>
 
 namespace Testbed {
 
@@ -160,10 +161,34 @@ TestExitStatus MiscTests::testMutexes() {
 	return kTestFailed;
 }
 
+TestExitStatus MiscTests::testOpenUrl() {
+	Common::String info = "Testing openUrl() method.\n"
+		"In this test we'll try to open scummvm.org in your default browser.";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : openUrl()\n");
+		return kTestSkipped;
+	}
+
+	if (!Networking::Browser::openUrl("http://scummvm.org/")) {
+		Testsuite::logPrintf("Info! openUrl() says it couldn't open the url (probably not supported on this platform)\n");
+		return kTestFailed;
+	}
+	
+	if (Testsuite::handleInteractiveInput("Was ScummVM able to open 'http://scummvm.org/' in your default browser?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! openUrl() is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("openUrl() is OK\n");
+	return kTestPassed;
+}
+
 MiscTestSuite::MiscTestSuite() {
 	addTest("Datetime", &MiscTests::testDateTime, false);
 	addTest("Timers", &MiscTests::testTimers, false);
 	addTest("Mutexes", &MiscTests::testMutexes, false);
+	addTest("openUrl", &MiscTests::testOpenUrl, true);
 }
 
 } // End of namespace Testbed
diff --git a/engines/testbed/misc.h b/engines/testbed/misc.h
index 23e303d..adfc322 100644
--- a/engines/testbed/misc.h
+++ b/engines/testbed/misc.h
@@ -49,6 +49,7 @@ void criticalSection(void *arg);
 TestExitStatus testDateTime();
 TestExitStatus testTimers();
 TestExitStatus testMutexes();
+TestExitStatus testOpenUrl();
 // add more here
 
 } // End of namespace MiscTests
@@ -69,7 +70,7 @@ public:
 		return "Misc";
 	}
 	const char *getDescription() const {
-		return "Miscellaneous: Timers/Mutexes/Datetime";
+		return "Miscellaneous: Timers/Mutexes/Datetime/openUrl";
 	}
 };
 


Commit: 8de753b68ad92913b73e1f7f5552b209f7a72876
    https://github.com/scummvm/scummvm/commit/8de753b68ad92913b73e1f7f5552b209f7a72876
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Cloud-related dialogs in classic theme's stx

Looks fine.

Changed paths:
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx



diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 5172326..545b3fe 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -524,6 +524,183 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StoragePopupDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StoragePopup'
+						type = 'PopUp'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageUsernameDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageUsernameLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageUsedSpaceDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageUsedSpaceLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'StorageLastSyncDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'StorageLastSyncLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+				<widget name = 'RefreshButton'
+						type = 'Button'
+				/>
+				<widget name = 'DownloadButton'
+						type = 'Button'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'RunServerButton'
+						type = 'Button'
+				/>
+				<widget name = 'ServerInfoLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 8' spacing = '8'>
+			<widget name = 'RemoteDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'LocalDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'ProgressBar'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<widget name = 'DownloadSize'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'DownloadSpeed'
+					height = 'Globals.Line.Height'
+			/>
+			<space/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10'>
+				<widget name = 'MainButton'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'CloseButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'Picture'
+						type = 'OptionsLabel'
+				/>
+				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
+					<widget name = 'Headline'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '4' />
+					<widget name = 'NavigateLine'
+							height = 'Globals.Line.Height'
+					/>
+					<widget name = 'URLLine'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '4' />
+					<widget name = 'ReturnLine1'
+							height = 'Globals.Line.Height'
+					/>
+					<widget name = 'ReturnLine2'
+							height = 'Globals.Line.Height'
+					/>					
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox1'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox2'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox3'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox4'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+					</layout>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox5'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox6'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox7'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+						<widget name = 'CodeBox8'
+							width = '70'
+							height = 'Globals.Line.Height'
+						/>
+					</layout>
+					<widget name = 'MessageLine'
+							height = 'Globals.Line.Height'
+					/>
+					<space size = '6' />
+				</layout>
+			</layout>			
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'CancelButton'
+						type = 'Button'
+				/>
+				<widget name = 'OpenUrlButton'
+						type = 'Button'
+				/>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>
 		<layout type='vertical' padding='8,8,8,8' center='true'>
 			<widget name='Action'
@@ -1042,6 +1219,38 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SaveLoadCloudSyncProgress' overlays = 'screen_center' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'TitleText'
+					width = '496'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<widget name = 'ProgressBar'
+					width = '496'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					width = '496'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' center = 'true' spacing = '10'>
+				<widget name = 'Cancel'
+						width = '150'
+						height = 'Globals.Button.Height'
+				/>				
+				<widget name = 'Background'
+						width = '150'
+						height = 'Globals.Button.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name = 'SavenameDialog' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'DescriptionText'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 0013b91..27a3ecd 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -529,6 +529,186 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'GlobalOptions_Cloud' overlays = 'Dialog.GlobalOptions.TabWidget'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StoragePopupDesc'						
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StoragePopup'
+						type = 'PopUp'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageUsernameDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StorageUsernameLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageUsedSpaceDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StorageUsedSpaceLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'StorageLastSyncDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'StorageLastSyncLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+				<widget name = 'RefreshButton'
+						type = 'Button'
+				/>
+				<widget name = 'DownloadButton'
+						type = 'Button'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'RunServerButton'
+						type = 'Button'
+				/>
+				<widget name = 'ServerInfoLabel'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_DownloadDialog' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '8, 8, 8, 4' spacing = '8'>
+			<widget name = 'RemoteDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'LocalDirectory'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'ProgressBar'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<widget name = 'DownloadSize'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'DownloadSpeed'
+					height = 'Globals.Line.Height'
+			/>
+			<space/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6'>
+				<widget name = 'MainButton'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'CloseButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+			<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '4'>
+				<widget name = 'Headline'
+						height = 'Globals.Line.Height'
+				/>
+				<space size = '2' />
+				<widget name = 'NavigateLine'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'URLLine'
+						height = 'Globals.Line.Height'
+				/>
+				<space size = '2' />
+				<widget name = 'ReturnLine1'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'ReturnLine2'
+						height = 'Globals.Line.Height'
+				/>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox1'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox2'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox3'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox4'
+							width = '60'
+							height = '16'
+						/>
+					</layout>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+						<widget name = 'CodeBox5'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox6'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox7'
+							width = '60'
+							height = '16'
+						/>
+						<widget name = 'CodeBox8'
+							width = '60'
+							height = '16'
+						/>
+					</layout>
+					<widget name = 'MessageLine'
+							height = 'Globals.Line.Height'
+					/>
+				<space size = '4' />
+			</layout>		
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'CancelButton'
+						type = 'Button'
+				/>
+				<widget name = 'OpenUrlButton'
+						type = 'Button'
+				/>
+				<widget name = 'ConnectButton'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>
 		<layout type='vertical' padding='8,8,8,8' center='true'>
 			<widget name='Action'
@@ -1040,6 +1220,38 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SaveLoadCloudSyncProgress' overlays = 'screen_center' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'TitleText'
+					width = '240'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<widget name = 'ProgressBar'
+					width = '240'
+					height = 'Globals.Button.Height'
+			/>
+			<space size = '1'/>	
+			<widget name = 'PercentText'
+					width = '240'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+			/>
+			<space size = '1'/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' center = 'true' spacing = '10'>
+				<widget name = 'Cancel'
+						width = '100'
+						height = 'Globals.Button.Height'
+				/>				
+				<widget name = 'Background'
+						width = '100'
+						height = 'Globals.Button.Height'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name = 'SavenameDialog' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'DescriptionText'


Commit: 5163fb4e02926bcc9fe12de3f3b031b1c8349c8b
    https://github.com/scummvm/scummvm/commit/5163fb4e02926bcc9fe12de3f3b031b1c8349c8b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add ListAjaxHandler

"/list" now returns JSON with directory information. Would be used in
AJAX-based Files Manager.

Changed paths:
  A backends/networking/sdl_net/handlers/listajaxhandler.cpp
  A backends/networking/sdl_net/handlers/listajaxhandler.h
    backends/module.mk
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/module.mk b/backends/module.mk
index f04397d..a753b4d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -68,6 +68,7 @@ MODULE_OBJS += \
 	networking/sdl_net/handlers/filesbasehandler.o \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
+	networking/sdl_net/handlers/listajaxhandler.o \
 	networking/sdl_net/handlers/resourcehandler.o \
 	networking/sdl_net/handlers/uploadfilehandler.o \
 	networking/sdl_net/handlerutils.o \
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
new file mode 100644
index 0000000..8657ab5
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -0,0 +1,134 @@
+/* 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 "backends/networking/sdl_net/handlers/listajaxhandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/translation.h"
+#include "common/json.h"
+
+namespace Networking {
+
+ListAjaxHandler::ListAjaxHandler() {}
+
+ListAjaxHandler::~ListAjaxHandler() {}
+
+void ListAjaxHandler::handle(Client &client) {
+	Common::String path = client.queryParameter("path");
+	Common::JSONValue jsonResponse = listDirectory(path);
+	Common::String response = jsonResponse.stringify(true);
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
+	Common::JSONArray itemsList;
+	Common::JSONObject errorResult;
+	Common::JSONObject successResult;
+	successResult.setVal("type", new Common::JSONValue("success"));
+	errorResult.setVal("type", new Common::JSONValue("error"));
+
+	if (path == "" || path == "/") {
+		addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root"));
+		addItem(itemsList, IT_DIRECTORY, "/saves/", _("Saved games"));
+		successResult.setVal("items", new Common::JSONValue(itemsList));
+		return successResult;
+	}
+
+	Common::String prefixToRemove = "", prefixToAdd = "";
+	if (!transformPath(path, prefixToRemove, prefixToAdd)) return errorResult;
+
+	Common::FSNode node = Common::FSNode(path);
+	if (path == "/") node = node.getParent(); // absolute root
+	if (!node.isDirectory()) return errorResult;
+
+	// list directory
+	Common::FSList _nodeContent;
+	if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files
+		_nodeContent.clear();
+	else
+		Common::sort(_nodeContent.begin(), _nodeContent.end());
+
+	// add parent directory link
+	{
+		Common::String filePath = path;
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		if (filePath == "" || filePath == "/" || filePath == "\\")
+			filePath = "/";
+		else
+			filePath = parentPath(prefixToAdd + filePath);
+		addItem(itemsList, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
+	}
+
+	// fill the content
+	for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
+		Common::String name = i->getDisplayName();
+		if (i->isDirectory()) name += "/";
+
+		Common::String filePath = i->getPath();
+		if (filePath.hasPrefix(prefixToRemove))
+			filePath.erase(0, prefixToRemove.size());
+		filePath = prefixToAdd + filePath;
+
+		addItem(itemsList, detectType(i->isDirectory(), name), filePath, name);
+	}
+
+	successResult.setVal("items", new Common::JSONValue(itemsList));
+	return successResult;
+}
+
+ListAjaxHandler::ItemType ListAjaxHandler::detectType(bool isDirectory, const Common::String &name) const {
+	if (isDirectory) return IT_DIRECTORY;
+	if (name.hasSuffix(".txt")) return IT_TXT;
+	if (name.hasSuffix(".zip")) return IT_ZIP;
+	if (name.hasSuffix(".7z")) return IT_7Z;
+	return IT_UNKNOWN;
+}
+
+void ListAjaxHandler::addItem(Common::JSONArray &responseItemsList, ItemType itemType, Common::String path, Common::String name, Common::String size) {
+	Common::String icon;
+	bool isDirectory = (itemType == IT_DIRECTORY || itemType == IT_PARENT_DIRECTORY);
+	switch (itemType) {
+	case IT_DIRECTORY: icon = "dir.png"; break;
+	case IT_PARENT_DIRECTORY: icon = "up.png"; break;
+	case IT_TXT: icon = "txt.png"; break;
+	case IT_ZIP: icon = "zip.png"; break;
+	case IT_7Z: icon = "7z.png"; break;
+	default: icon = "unk.png";
+	}
+
+	Common::JSONObject item;	
+	item.setVal("name", new Common::JSONValue(name));
+	item.setVal("path", new Common::JSONValue(path));
+	item.setVal("isDirectory", new Common::JSONValue(isDirectory));
+	item.setVal("size", new Common::JSONValue(size));
+	item.setVal("icon", new Common::JSONValue(icon));
+	responseItemsList.push_back(new Common::JSONValue(item));
+}
+
+/// public
+
+ClientHandlerCallback ListAjaxHandler::getHandler() {
+	return new Common::Callback<ListAjaxHandler, Client &>(this, &ListAjaxHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.h b/backends/networking/sdl_net/handlers/listajaxhandler.h
new file mode 100644
index 0000000..cef6a07
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.h
@@ -0,0 +1,65 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_LISTAJAXHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_LISTAJAXHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+#include "common/json.h"
+
+namespace Networking {
+
+class ListAjaxHandler: public FilesBaseHandler {
+	enum ItemType {
+		IT_DIRECTORY,
+		IT_PARENT_DIRECTORY,
+		IT_TXT,
+		IT_ZIP,
+		IT_7Z,
+		IT_UNKNOWN
+	};
+
+	void handle(Client &client);
+
+	/**
+	 * Lists the directory <path>.
+	 *
+	 * Returns JSON with either listed directory or error response.
+	 */
+	Common::JSONObject listDirectory(Common::String path);
+
+	/** Helper method for detecting items' type. */
+	ItemType detectType(bool isDirectory, const Common::String &name) const;
+
+	/** Helper method for adding items into the files list. */
+	void addItem(Common::JSONArray &responseItemsList, ItemType itemType, Common::String path, Common::String name, Common::String size = "");
+
+public:
+	ListAjaxHandler();
+	virtual ~ListAjaxHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 1999fe9..2ac4834 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -53,6 +53,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
 	addPathHandler("/download", _downloadFileHandler.getHandler());
 	addPathHandler("/upload", _uploadFileHandler.getHandler());
+	addPathHandler("/list", _listAjaxHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index cdd991d..b465511 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -29,6 +29,7 @@
 #include "backends/networking/sdl_net/handlers/downloadfilehandler.h"
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
+#include "backends/networking/sdl_net/handlers/listajaxhandler.h"
 #include "backends/networking/sdl_net/handlers/resourcehandler.h"
 #include "backends/networking/sdl_net/handlers/uploadfilehandler.h"
 #include "common/hash-str.h"
@@ -65,6 +66,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	CreateDirectoryHandler _createDirectoryHandler;
 	DownloadFileHandler _downloadFileHandler;
 	UploadFileHandler _uploadFileHandler;
+	ListAjaxHandler _listAjaxHandler;
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;


Commit: 09ae2f7593a66817f86fdb4f65076b24d9b2ff40
    https://github.com/scummvm/scummvm/commit/09ae2f7593a66817f86fdb4f65076b24d9b2ff40
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "/filesAJAX" sketch

It works already, but still requires some polishing.

Changed paths:
  A backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
  A backends/networking/sdl_net/handlers/filesajaxpagehandler.h
  A backends/networking/wwwroot/.filesAJAX.html
  A backends/networking/wwwroot/ajax.js
    backends/module.mk
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/wwwroot.zip



diff --git a/backends/module.mk b/backends/module.mk
index a753b4d..e74cf67 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -65,6 +65,7 @@ MODULE_OBJS += \
 	networking/sdl_net/getclienthandler.o \
 	networking/sdl_net/handlers/createdirectoryhandler.o \
 	networking/sdl_net/handlers/downloadfilehandler.o \
+	networking/sdl_net/handlers/filesajaxpagehandler.o \
 	networking/sdl_net/handlers/filesbasehandler.o \
 	networking/sdl_net/handlers/filespagehandler.o \
 	networking/sdl_net/handlers/indexpagehandler.o \
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
new file mode 100644
index 0000000..7fa7a60
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -0,0 +1,65 @@
+/* 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 "backends/networking/sdl_net/handlers/filesajaxpagehandler.h"
+#include "backends/networking/sdl_net/handlerutils.h"
+#include "backends/networking/sdl_net/localwebserver.h"
+#include "common/translation.h"
+
+namespace Networking {
+
+#define FILES_PAGE_NAME ".filesAJAX.html"
+
+FilesAjaxPageHandler::FilesAjaxPageHandler() {}
+
+FilesAjaxPageHandler::~FilesAjaxPageHandler() {}
+
+void FilesAjaxPageHandler::handle(Client &client) {
+	// load stylish response page from the archive
+	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
+	if (stream == nullptr) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The page is not available without the resources."));
+		return;
+	}
+		
+	Common::String response = HandlerUtils::readEverythingFromStream(stream);
+	Common::String path = client.queryParameter("path");
+
+	//these occur twice:
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{upload_files_button}", _("Upload files")); //tab
+	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
+	replace(response, "{create_directory_desc}", _("Type new directory name:"));
+	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
+	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
+	replace(response, "{index_of}", _("Index of "));
+	LocalWebserver::setClientGetHandler(client, response);
+}
+
+/// public
+
+ClientHandlerCallback FilesAjaxPageHandler::getHandler() {
+	return new Common::Callback<FilesAjaxPageHandler, Client &>(this, &FilesAjaxPageHandler::handle);
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.h b/backends/networking/sdl_net/handlers/filesajaxpagehandler.h
new file mode 100644
index 0000000..8e39ac0
--- /dev/null
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.h
@@ -0,0 +1,42 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_SDL_NET_FILESAJAXPAGEHANDLER_H
+#define BACKENDS_NETWORKING_SDL_NET_FILESAJAXPAGEHANDLER_H
+
+#include "backends/networking/sdl_net/handlers/filesbasehandler.h"
+
+namespace Networking {
+
+class FilesAjaxPageHandler: public FilesBaseHandler {
+	void handle(Client &client);
+
+public:
+	FilesAjaxPageHandler();
+	virtual ~FilesAjaxPageHandler();
+
+	virtual ClientHandlerCallback getHandler();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 2ac4834..29db0b7 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -54,6 +54,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/download", _downloadFileHandler.getHandler());
 	addPathHandler("/upload", _uploadFileHandler.getHandler());
 	addPathHandler("/list", _listAjaxHandler.getHandler());
+	addPathHandler("/filesAJAX", _filesAjaxPageHandler.getHandler());
 	_defaultHandler = _resourceHandler.getHandler();
 }
 
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index b465511..1185cda 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -27,6 +27,7 @@
 #include "backends/networking/sdl_net/handlers/basehandler.h"
 #include "backends/networking/sdl_net/handlers/createdirectoryhandler.h"
 #include "backends/networking/sdl_net/handlers/downloadfilehandler.h"
+#include "backends/networking/sdl_net/handlers/filesajaxpagehandler.h"
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlers/indexpagehandler.h"
 #include "backends/networking/sdl_net/handlers/listajaxhandler.h"
@@ -67,6 +68,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	DownloadFileHandler _downloadFileHandler;
 	UploadFileHandler _uploadFileHandler;
 	ListAjaxHandler _listAjaxHandler;
+	FilesAjaxPageHandler _filesAjaxPageHandler;
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 48e8cb6..7aa7df3 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
new file mode 100644
index 0000000..d1d077f
--- /dev/null
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<html>
+	<head>
+		<title>ScummVM</title>
+		<meta charset="utf-8"/>
+		<link rel="stylesheet" type="text/css" href="style.css"/>
+	</head>
+	<body>
+		<div class="container">
+			<div class='header'>
+				<center><img src="logo.png"/></center>
+			</div>
+			<div class="controls">
+				<table class="buttons"><tr>
+					<td><a href="javascript:show('create_directory');">{create_directory_button}</a></td>
+					<td><a href="javascript:show('upload_file');">{upload_files_button}</a></td>
+				</tr></table>
+				<div id="create_directory" class="modal">
+					<p>{create_directory_desc}</p>
+					<form action="create" id="create_directory_form">
+						<input type="hidden" name="path" value="{path}"/>
+						<input type="text" name="directory_name" value=""/>
+						<input type="submit" value="{create_directory_button}"/>
+					</form>
+				</div>
+				<div id="upload_file" class="modal">
+					<p>{upload_file_desc}</p>
+					<form action="upload?path={path}" method="post" enctype="multipart/form-data" id="files_upload_form">
+						<!-- we don't need "[]" in the name, as our webserver is not using PHP -->
+						<!-- "allowdirs" is a proposal, not implemented yet -->
+						<input type="file" name="upload_file-f" allowdirs multiple/>
+						<br/><br/>
+						<p>{or_upload_directory_desc}</p>
+						<!-- "directory"/"webkitdirectory" works in Chrome only yet, "multiple" is just in case here -->
+						<input type="file" name="upload_file-d" directory webkitdirectory multiple/>
+						<input type="submit" value="{upload_file_button}"/>
+					</form>
+				</div>
+			</div>
+			<div class="content">
+				<table class="files_list" id="files_list">
+				</table>
+			</div>
+		</div>
+		<script>
+			function show(id) {
+				var e = document.getElementById(id);
+				var visible = (e.style.display == "block");
+				if (visible) id = ""; //hide
+
+				e = document.getElementById("create_directory");
+				e.style.display = (e.id == id ? "block" : "none");
+				e = document.getElementById("upload_file");
+				e.style.display = (e.id == id ? "block" : "none");
+			}
+		</script>
+		<script src="ajax.js"></script>
+		<script>
+			window.onload = function () {
+				showDirectory("/");
+			}
+
+			function showDirectory(path) {
+				ajax.getAndParseJson("./list", {"path": path}, getCallback(path));
+			}
+
+			function getCallback(path) {
+				return function (jsonResponse) {
+					console.log(jsonResponse);
+
+					if (jsonResponse.type == "error") {
+						console.log("error");
+						return;
+					}
+
+					openDirectory(path, jsonResponse.items);
+				};
+			}
+
+			function openDirectory(path, items) {
+				// update path
+				document.getElementById("create_directory_form").elements["path"].value = path;
+				document.getElementById("files_upload_form").action = "upload?path=" + path;
+
+				// update table contents
+				listDirectory(path, items);
+			}
+
+			function listDirectory(path, items) {
+				// cleanup the list
+				var files_list = document.getElementById("files_list");
+				while (files_list.hasChildNodes())
+					files_list.removeChild(files_list.firstChild);
+				var tbody = document.createElement("tbody");
+
+				// add header item
+				var tr = document.createElement("tr");
+				tr.appendChild(createElementWithContents("td", ""));
+				var td = document.createElement("td");
+				var b = createElementWithContents("b", "{index_of}" + path.replace(new RegExp("\\\\", 'g'), '/'));
+				b.className = "directory_name";
+				td.appendChild(b);
+				tr.appendChild(td);
+				tr.appendChild(createElementWithContents("td", ""));
+				tbody.appendChild(tr);
+
+				// add items
+				for (var i in items)
+					addItem(tbody, items[i]);
+
+				files_list.appendChild(tbody);
+			}
+
+			function addItem(tbody, item) {
+				var tr = document.createElement("tr");
+				var td = document.createElement("td");
+				var img = document.createElement("img");
+				img.src = "http://localhost:12345/icons/" + item.icon;
+				td.appendChild(img);
+				tr.appendChild(td);
+
+				td = document.createElement("td");
+				var a = createElementWithContents("a", item.name);
+				if (item.isDirectory) {
+					a.onclick = function () { showDirectory(item.path); };
+					a.href = "javascript:onclick();";
+				} else
+					a.href = "http://localhost:12345/download?path=" + encodeURIComponent(item.path);
+				td.appendChild(a);
+				tr.appendChild(td);
+
+				tr.appendChild(createElementWithContents("td", ""));
+				tbody.appendChild(tr);
+			}
+
+			function createElementWithContents(type, innerHTML) {
+				var e = document.createElement(type);
+				e.innerHTML = innerHTML;
+				return e;
+			}
+		</script>
+	</body>
+</html>
\ No newline at end of file
diff --git a/backends/networking/wwwroot/ajax.js b/backends/networking/wwwroot/ajax.js
new file mode 100644
index 0000000..12c50f1
--- /dev/null
+++ b/backends/networking/wwwroot/ajax.js
@@ -0,0 +1,48 @@
+// the following is snippet from http://stackoverflow.com/a/18078705
+// I changed a few things though
+
+var ajax = {};
+ajax.x = function () { return new XMLHttpRequest(); }; // "no one uses IE6"
+
+ajax.send = function (url, callback, errorCallback, method, data, async) {
+    if (async === undefined) async = true;
+    
+    var x = ajax.x();
+    x.open(method, url, async);
+    x.onreadystatechange = function () {
+        if (x.readyState == XMLHttpRequest.DONE) {
+            if (x.status == 200)
+                callback(x.responseText);
+            else
+                errorCallback(x);
+        }
+    };
+    if (method == 'POST') {
+        x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+    }
+    x.send(data)
+};
+
+ajax.get = function (url, data, callback, errorCallback, async) {
+    var query = [];
+    for (var key in data) {
+        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
+    }
+    ajax.send(url + (query.length ? '?' + query.join('&') : ''), callback, errorCallback, 'GET', null, async)
+};
+
+ajax.post = function (url, data, callback, errorCallback, async) {
+    var query = [];
+    for (var key in data) {
+        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
+    }
+    ajax.send(url, callback, errorCallback, 'POST', query.join('&'), async)
+};
+
+ajax.getAndParseJson = function (url, data, callback) {
+    ajax.get(
+        url, data,
+        function (responseText) { callback(JSON.parse(responseText)); },
+        function (x) { console.log("error: " + x.status); }
+    );
+};
\ No newline at end of file


Commit: 6442dad71030737b218758838378d1ea26214c4d
    https://github.com/scummvm/scummvm/commit/6442dad71030737b218758838378d1ea26214c4d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "breadcrumbs" in "/filesAJAX"

Changed paths:
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.filesAJAX.html
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 7aa7df3..fc627d5 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index d1d077f..aab2a04 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -86,6 +86,46 @@
 				listDirectory(path, items);
 			}
 
+			function makeBreadcrumb(name, path) {
+				var a = createElementWithContents("a", name);
+				a.onclick = function () { showDirectory(path); };
+				a.href = "javascript:onclick();";
+				return a;
+			}
+
+			function makeBreadcrumbs(path) {
+				var b = document.createElement("b");
+				b.className = "directory_name";
+
+				b.appendChild(createElementWithContents("span", "{index_of}"));
+				var slashes = true;
+				var crumb = "";
+				var currentPath = "";
+				path += ' '; //so the last slash is added
+				for (var i=0; i<path.length; ++i) {
+					if (path[i] == '/' || path[i] == '\\') {
+						if (!slashes) {
+							currentPath += crumb;
+							b.appendChild(makeBreadcrumb(crumb, currentPath+'/'));
+							slashes = true;					
+						}
+					} else {
+						if (slashes) {
+							currentPath += "/";
+							if (currentPath == "/") { //make special '/' crumb here
+								b.appendChild(makeBreadcrumb('/', '/'));
+							} else {
+								b.appendChild(createElementWithContents("span", "/"));
+							}
+							slashes = false;
+							crumb = "";
+						}
+						crumb += path[i];
+					}
+				}
+				return b;
+			}
+
 			function listDirectory(path, items) {
 				// cleanup the list
 				var files_list = document.getElementById("files_list");
@@ -97,9 +137,7 @@
 				var tr = document.createElement("tr");
 				tr.appendChild(createElementWithContents("td", ""));
 				var td = document.createElement("td");
-				var b = createElementWithContents("b", "{index_of}" + path.replace(new RegExp("\\\\", 'g'), '/'));
-				b.className = "directory_name";
-				td.appendChild(b);
+				td.appendChild(makeBreadcrumbs(path));
 				tr.appendChild(td);
 				tr.appendChild(createElementWithContents("td", ""));
 				tbody.appendChild(tr);
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index cc3a859..c1bc14d 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -93,3 +93,6 @@ td img { vertical-align: middle; height: 20px; }
 	display: block;
 	padding-bottom: 6px;
 }
+
+.directory_name a { color: black; }
+.directory_name a:hover { color: blue; }


Commit: cd6d45ecf8f24a40d1a0829b93030a5c22829f23
    https://github.com/scummvm/scummvm/commit/cd6d45ecf8f24a40d1a0829b93030a5c22829f23
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor "/filesAJAX" fix

Changed paths:
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.filesAJAX.html



diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index fc627d5..8d6d630 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index aab2a04..a1c379d 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -153,7 +153,7 @@
 				var tr = document.createElement("tr");
 				var td = document.createElement("td");
 				var img = document.createElement("img");
-				img.src = "http://localhost:12345/icons/" + item.icon;
+				img.src = "./icons/" + item.icon;
 				td.appendChild(img);
 				tr.appendChild(td);
 
@@ -163,7 +163,7 @@
 					a.onclick = function () { showDirectory(item.path); };
 					a.href = "javascript:onclick();";
 				} else
-					a.href = "http://localhost:12345/download?path=" + encodeURIComponent(item.path);
+					a.href = "./download?path=" + encodeURIComponent(item.path);
 				td.appendChild(a);
 				tr.appendChild(td);
 


Commit: e6caa482e1e7c0e6d9f8201331332b9f5425b79d
    https://github.com/scummvm/scummvm/commit/e6caa482e1e7c0e6d9f8201331332b9f5425b79d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add messages in "/filesAJAX"

Changed paths:
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.filesAJAX.html
    backends/networking/wwwroot/style.css



diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index 7fa7a60..17be7fa 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -53,6 +53,8 @@ void FilesAjaxPageHandler::handle(Client &client) {
 	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
 	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
 	replace(response, "{index_of}", _("Index of "));
+	replace(response, "{loading}", _("Loading..."));
+	replace(response, "{error}", _("Error occurred"));
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 8d6d630..7e53969 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index a1c379d..16a4b54 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -38,6 +38,8 @@
 				</div>
 			</div>
 			<div class="content">
+				<div id="loading_message">{loading}</div>
+				<div id="error_message">{error}</div>
 				<table class="files_list" id="files_list">
 				</table>
 			</div>
@@ -61,6 +63,8 @@
 			}
 
 			function showDirectory(path) {
+				if (isLoading) return;
+				showLoading();
 				ajax.getAndParseJson("./list", {"path": path}, getCallback(path));
 			}
 
@@ -69,14 +73,42 @@
 					console.log(jsonResponse);
 
 					if (jsonResponse.type == "error") {
-						console.log("error");
+						showError();
 						return;
 					}
 
 					openDirectory(path, jsonResponse.items);
+					hideLoading();
 				};
 			}
 
+			var isLoading = false;
+
+			function showLoading() {
+				isLoading = true;
+				var e = document.getElementById("loading_message");
+				e.style.display = "block";
+				e = document.getElementById("error_message");
+				e.style.display = "none";
+			}
+
+			function showError() {
+				isLoading = false;
+				var e = document.getElementById("loading_message");
+				e.style.display = "none";
+				e = document.getElementById("error_message");
+				e.style.display = "block";
+				//TODO: pass the actual message there?
+			}
+
+			function hideLoading() {
+				isLoading = false;
+				var e = document.getElementById("loading_message");
+				e.style.display = "none";
+				e = document.getElementById("error_message");
+				e.style.display = "none";
+			}
+
 			function openDirectory(path, items) {
 				// update path
 				document.getElementById("create_directory_form").elements["path"].value = path;
diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css
index c1bc14d..ba31587 100644
--- a/backends/networking/wwwroot/style.css
+++ b/backends/networking/wwwroot/style.css
@@ -96,3 +96,18 @@ td img { vertical-align: middle; height: 20px; }
 
 .directory_name a { color: black; }
 .directory_name a:hover { color: blue; }
+
+#loading_message, #error_message {
+	margin: -8pt;
+	margin-bottom: 5pt;
+	padding: 4pt;
+	text-align: center;
+}
+
+#loading_message {
+	background: #99FF99;
+}
+
+#error_message {
+	background: #FF9999;
+}


Commit: da229dd84c5761f80b16c25edba7bfb738fc835f
    https://github.com/scummvm/scummvm/commit/da229dd84c5761f80b16c25edba7bfb738fc835f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "ajax" parameter for "/create" and "/upload"

If it's set, these redirect to "/filesAJAX" instead of "/files".

Changed paths:
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.filesAJAX.html



diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index 97d75fc..fc02b64 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -85,7 +85,9 @@ void CreateDirectoryHandler::handle(Client &client) {
 			client.queryParameter("path").c_str(),
 			_("Back to parent directory")
 		),
-		"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+
+		(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
+			LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
 	);
 }
 
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index 17be7fa..df44821 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -33,6 +33,19 @@ FilesAjaxPageHandler::FilesAjaxPageHandler() {}
 
 FilesAjaxPageHandler::~FilesAjaxPageHandler() {}
 
+namespace {
+Common::String encodeDoubleQuotesAndSlashes(Common::String s) {
+	Common::String result = "";
+	for (uint32 i = 0; i < s.size(); ++i)
+		if (s[i] == '"') {
+			result += "\\\"";
+		} else if (s[i] == '\\') {
+			result += "\\\\";
+		} else result += s[i];
+		return result;
+}
+}
+
 void FilesAjaxPageHandler::handle(Client &client) {
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
@@ -55,6 +68,7 @@ void FilesAjaxPageHandler::handle(Client &client) {
 	replace(response, "{index_of}", _("Index of "));
 	replace(response, "{loading}", _("Loading..."));
 	replace(response, "{error}", _("Error occurred"));
+	replace(response, "{start_path}", encodeDoubleQuotesAndSlashes(path));
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 7cbb118..7e93573 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -159,9 +159,11 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 					_("Uploaded successfully!"),
 					client->queryParameter("path").c_str(),
 					_("Back to parent directory")
-					),
-				"/files?path=" + LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
-				);
+				),
+
+				(client->queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
+					LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
+			);
 			_state = UFH_STOP;		
 			return;
 		}
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 7e53969..6f821d9 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index 16a4b54..d648466 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -19,13 +19,14 @@
 					<p>{create_directory_desc}</p>
 					<form action="create" id="create_directory_form">
 						<input type="hidden" name="path" value="{path}"/>
+						<input type="hidden" name="ajax" value="true"/>
 						<input type="text" name="directory_name" value=""/>
 						<input type="submit" value="{create_directory_button}"/>
 					</form>
 				</div>
 				<div id="upload_file" class="modal">
 					<p>{upload_file_desc}</p>
-					<form action="upload?path={path}" method="post" enctype="multipart/form-data" id="files_upload_form">
+					<form action="upload?path={path}&ajax=true" method="post" enctype="multipart/form-data" id="files_upload_form">
 						<!-- we don't need "[]" in the name, as our webserver is not using PHP -->
 						<!-- "allowdirs" is a proposal, not implemented yet -->
 						<input type="file" name="upload_file-f" allowdirs multiple/>
@@ -59,7 +60,7 @@
 		<script src="ajax.js"></script>
 		<script>
 			window.onload = function () {
-				showDirectory("/");
+				showDirectory("{start_path}");
 			}
 
 			function showDirectory(path) {
@@ -112,7 +113,7 @@
 			function openDirectory(path, items) {
 				// update path
 				document.getElementById("create_directory_form").elements["path"].value = path;
-				document.getElementById("files_upload_form").action = "upload?path=" + path;
+				document.getElementById("files_upload_form").action = "upload?path=" + path + "&ajax=true";
 
 				// update table contents
 				listDirectory(path, items);


Commit: 36b381e411f4e55ba9590a4102f5b84ce6b724ce
    https://github.com/scummvm/scummvm/commit/36b381e411f4e55ba9590a4102f5b84ce6b724ce
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Make "/create" support AJAX

Now creating directories doesn't refresh the "/filesAJAX" page.

Changed paths:
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/createdirectoryhandler.h
    backends/networking/wwwroot.zip
    backends/networking/wwwroot/.filesAJAX.html



diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index fc02b64..2ded978 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -38,25 +38,25 @@ void CreateDirectoryHandler::handle(Client &client) {
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/") {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't create directory here!"));
+		handleError(client, _("Can't create directory here!"));
 		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		handleError(client, _("Invalid path!"));
 		return;
 	}
 
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Parent directory doesn't exists!"));
+		handleError(client, _("Parent directory doesn't exists!"));
 		return;
 	}
 	if (!node->isDirectory()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't create a directory within a file!"));
+		handleError(client, _("Can't create a directory within a file!"));
 		return;
 	}
 
@@ -65,17 +65,23 @@ void CreateDirectoryHandler::handle(Client &client) {
 	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
 	if (node->exists()) {
 		if (!node->isDirectory()) {
-			HandlerUtils::setFilesManagerErrorMessageHandler(client, _("There is a file with that name in the parent directory!"));
+			handleError(client, _("There is a file with that name in the parent directory!"));
 			return;
 		}
 	} else {
 		// create the <directory_name> in <path>
 		if (!node->create(true)) {
-			HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to create the directory!"));
+			handleError(client, _("Failed to create the directory!"));
 			return;
 		}
 	}
 
+	// if json requested, respond with it
+	if (client.queryParameter("answer_json") == "true") {
+		setJsonResponseHandler(client, "success", _("Directory created successfully!"));
+		return;
+	}
+
 	// set redirect on success
 	HandlerUtils::setMessageHandler(
 		client,
@@ -91,6 +97,20 @@ void CreateDirectoryHandler::handle(Client &client) {
 	);
 }
 
+void CreateDirectoryHandler::handleError(Client &client, Common::String message) const {
+	if (client.queryParameter("answer_json") == "true") setJsonResponseHandler(client, "error", message);
+	else HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
+}
+
+void CreateDirectoryHandler::setJsonResponseHandler(Client &client, Common::String type, Common::String message) const {
+	Common::JSONObject response;
+	response.setVal("type", new Common::JSONValue(type));
+	response.setVal("message", new Common::JSONValue(message));
+
+	Common::JSONValue json = response;
+	LocalWebserver::setClientGetHandler(client, json.stringify(true));
+}
+
 /// public
 
 ClientHandlerCallback CreateDirectoryHandler::getHandler() {
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.h b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
index 2ffaf96..bd0e887 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.h
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
@@ -29,6 +29,8 @@ namespace Networking {
 
 class CreateDirectoryHandler: public FilesBaseHandler {
 	void handle(Client &client);
+	void handleError(Client &client, Common::String message) const;
+	void setJsonResponseHandler(Client &client, Common::String type, Common::String message) const;
 public:
 	CreateDirectoryHandler();
 	virtual ~CreateDirectoryHandler();
diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip
index 6f821d9..b767d7c 100644
Binary files a/backends/networking/wwwroot.zip and b/backends/networking/wwwroot.zip differ
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index d648466..f736b74 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -17,7 +17,7 @@
 				</tr></table>
 				<div id="create_directory" class="modal">
 					<p>{create_directory_desc}</p>
-					<form action="create" id="create_directory_form">
+					<form action="create" id="create_directory_form" onsubmit="return createDirectory();">
 						<input type="hidden" name="path" value="{path}"/>
 						<input type="hidden" name="ajax" value="true"/>
 						<input type="text" name="directory_name" value=""/>
@@ -71,6 +71,32 @@
 
 			function getCallback(path) {
 				return function (jsonResponse) {
+					if (jsonResponse.type == "error") {
+						showError();
+						return;
+					}
+
+					openDirectory(path, jsonResponse.items);
+					hideLoading();
+				};
+			}
+
+			function createDirectory() {
+				if (isLoading) return;
+				showLoading();
+
+				var data = {"answer_json": "true"};
+				var elements = document.getElementById("create_directory_form").elements;
+				for (var el in elements)
+					data[elements[el].name] = elements[el].value;
+
+				ajax.getAndParseJson("./create", data, getCreateDirectoryCallback(data["path"]));
+				show("create_directory");
+				return false; // invalidate form, so it won't submit
+			}
+
+			function getCreateDirectoryCallback(path) {
+				return function (jsonResponse) {
 					console.log(jsonResponse);
 
 					if (jsonResponse.type == "error") {
@@ -78,8 +104,8 @@
 						return;
 					}
 
-					openDirectory(path, jsonResponse.items);
 					hideLoading();
+					showDirectory(path);
 				};
 			}
 
@@ -122,7 +148,7 @@
 			function makeBreadcrumb(name, path) {
 				var a = createElementWithContents("a", name);
 				a.onclick = function () { showDirectory(path); };
-				a.href = "javascript:onclick();";
+				a.href = "javascript:void(0);";
 				return a;
 			}
 
@@ -194,7 +220,7 @@
 				var a = createElementWithContents("a", item.name);
 				if (item.isDirectory) {
 					a.onclick = function () { showDirectory(item.path); };
-					a.href = "javascript:onclick();";
+					a.href = "javascript:void(0);";
 				} else
 					a.href = "./download?path=" + encodeURIComponent(item.path);
 				td.appendChild(a);


Commit: 55568d757ce2990a285a6193a60242f9932d0797
    https://github.com/scummvm/scummvm/commit/55568d757ce2990a285a6193a60242f9932d0797
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Move Dropbox to API v2

We had a few places where their deprecated API v1 was used.

Changed paths:
  A backends/cloud/dropbox/dropboxinforequest.cpp
  A backends/cloud/dropbox/dropboxinforequest.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/module.mk



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 7156197..b6a721b 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -245,7 +245,7 @@ Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Com
 
 Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->info(callback, errorCallback);
+	if (storage) return storage->info(callback, errorCallback);
 	else {
 		delete callback;
 		delete errorCallback;
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
new file mode 100644
index 0000000..f5a14ab
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -0,0 +1,143 @@
+/* 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 "backends/cloud/dropbox/dropboxinforequest.h"
+#include "backends/cloud/cloudmanager.h"
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+DropboxInfoRequest::DropboxInfoRequest(Common::String token, Storage::StorageInfoCallback cb, Networking::ErrorCallback ecb):
+	Networking::Request(nullptr, ecb), _token(token), _infoCallback(cb),
+	_workingRequest(nullptr), _ignoreCallback(false) {
+	start();
+}
+
+DropboxInfoRequest::~DropboxInfoRequest() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();
+	delete _infoCallback;
+}
+
+void DropboxInfoRequest::start() {
+	_ignoreCallback = true;
+	if (_workingRequest) _workingRequest->finish();	
+	_ignoreCallback = false;
+
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback);
+	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/users/get_current_account");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/json");
+	request->addPostField("null"); //use POST
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	_workingRequest = nullptr;
+	if (_ignoreCallback) {
+		delete json;
+		return;
+	}
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+	
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		finishError(error);
+		return;
+	}
+
+	//Dropbox documentation states there are no errors for this API method
+	Common::JSONObject info = json->asObject();
+	Common::JSONObject nameInfo = info.getVal("name")->asObject();
+	_uid = info.getVal("account_id")->asString();
+	_name = nameInfo.getVal("display_name")->asString();
+	_email = info.getVal("email")->asString();
+	CloudMan.setStorageUsername(kStorageDropboxId, _email);
+	delete json;
+
+	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::quotaResponseCallback);
+	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/users/get_space_usage");
+	request->addHeader("Authorization: Bearer " + _token);
+	request->addHeader("Content-Type: application/json");
+	request->addPostField("null"); //use POST
+
+	_workingRequest = ConnMan.addRequest(request);
+}
+
+void DropboxInfoRequest::quotaResponseCallback(Networking::JsonResponse response) {
+	Common::JSONValue *json = response.value;
+	_workingRequest = nullptr;
+	if (_ignoreCallback) {
+		delete json;
+		return;
+	}
+
+	Networking::ErrorResponse error(this);
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+	if (rq && rq->getNetworkReadStream())
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
+	if (!json) {
+		warning("NULL passed instead of JSON");
+		finishError(error);
+		return;
+	}
+
+	//Dropbox documentation states there are no errors for this API method
+	Common::JSONObject info = json->asObject();
+	Common::JSONObject allocation = info.getVal("allocation")->asObject();
+	uint64 used = info.getVal("used")->asIntegerNumber();
+	uint64 allocated = allocation.getVal("allocated")->asIntegerNumber();
+	finishInfo(StorageInfo(_uid, _name, _email, used, allocated));
+	delete json;
+}
+
+void DropboxInfoRequest::errorCallback(Networking::ErrorResponse error) {
+	_workingRequest = nullptr;
+	if (_ignoreCallback) return;
+	finishError(error);
+}
+
+void DropboxInfoRequest::handle() {}
+
+void DropboxInfoRequest::restart() { start(); }
+
+void DropboxInfoRequest::finishInfo(StorageInfo info) {
+	Request::finishSuccess();
+	if (_infoCallback) (*_infoCallback)(Storage::StorageInfoResponse(this, info));
+}
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxinforequest.h b/backends/cloud/dropbox/dropboxinforequest.h
new file mode 100644
index 0000000..a91d016
--- /dev/null
+++ b/backends/cloud/dropbox/dropboxinforequest.h
@@ -0,0 +1,56 @@
+/* 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.
+*
+*/
+
+#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXINFOREQUEST_H
+#define BACKENDS_CLOUD_DROPBOX_DROPBOXINFOREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace Dropbox {
+
+class DropboxInfoRequest: public Networking::Request {
+	Common::String _token;
+	Common::String _uid, _name, _email;
+	Storage::StorageInfoCallback _infoCallback;
+	Request *_workingRequest;
+	bool _ignoreCallback;
+	
+	void start();
+	void userResponseCallback(Networking::JsonResponse response);
+	void quotaResponseCallback(Networking::JsonResponse response);
+	void errorCallback(Networking::ErrorResponse error);
+	void finishInfo(StorageInfo info);
+public:
+	DropboxInfoRequest(Common::String token, Storage::StorageInfoCallback cb, Networking::ErrorCallback ecb);
+	virtual ~DropboxInfoRequest();
+
+	virtual void handle();
+	virtual void restart();
+};
+
+} // End of namespace Dropbox
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 8343b74..e34912e 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -23,6 +23,7 @@
 
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
+#include "backends/cloud/dropbox/dropboxinforequest.h"
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxuploadrequest.h"
 #include "backends/cloud/cloudmanager.h"
@@ -62,7 +63,7 @@ DropboxStorage::~DropboxStorage() {}
 void DropboxStorage::getAccessToken(Common::String code) {
 	if (!KEY || !SECRET) loadKeyAndSecret();
 	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);		
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
@@ -133,54 +134,13 @@ Networking::Request *DropboxStorage::createDirectory(Common::String path, BoolCa
 	return addRequest(new DropboxCreateDirectoryRequest(_token, path, callback, errorCallback));
 }
 
-Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) {
-	Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info");
-	request->addHeader("Authorization: Bearer " + _token);
-	return addRequest(request);
-	//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
-	//so, when CurlJsonRequest is finished, it calls the innerCallback
-	//innerCallback (which is DropboxStorage::infoInnerCallback in this case) processes the void *ptr
-	//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
+Networking::Request *DropboxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	return addRequest(new DropboxInfoRequest(_token, callback, errorCallback));
 }
 
 Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; }
 
-void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
-	Common::JSONValue *json = response.value;
-	if (!json) {
-		warning("NULL passed instead of JSON");
-		delete outerCallback;
-		return;
-	}
-
-	//Dropbox documentation states there is no errors for this API method
-	Common::JSONObject info = json->asObject();
-	Common::String uid = Common::String::format("%d", (int)info.getVal("uid")->asIntegerNumber());
-	Common::String name = info.getVal("display_name")->asString();
-	Common::String email = info.getVal("email")->asString();
-	Common::JSONObject quota = info.getVal("quota_info")->asObject();
-	uint64 quotaNormal = quota.getVal("normal")->asIntegerNumber();
-	uint64 quotaShared = quota.getVal("shared")->asIntegerNumber();
-	uint64 quotaAllocated = quota.getVal("quota")->asIntegerNumber();
-		
-	CloudMan.setStorageUsername(kStorageDropboxId, email);
-
-	if (outerCallback) {
-		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaNormal+quotaShared, quotaAllocated)));
-		delete outerCallback;
-	}
-	
-	delete json;
-}
-
-void DropboxStorage::infoMethodCallback(StorageInfoResponse response) {
-	debug("\nStorage info:");
-	debug("User name: %s", response.value.name().c_str());
-	debug("Email: %s", response.value.email().c_str());
-	debug("Disk usage: %u/%u", (uint32)response.value.used(), (uint32)response.value.available());
-}
-
 DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index b3dc641..2c9cf16 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -43,9 +43,6 @@ class DropboxStorage: public Cloud::Storage {
 	void getAccessToken(Common::String code);
 	void codeFlowComplete(Networking::JsonResponse response);
 
-	/** Constructs StorageInfo based on JSON response from cloud. */
-	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
-
 public:
 	/** This constructor uses OAuth code flow to get tokens. */
 	DropboxStorage(Common::String code);
@@ -90,9 +87,6 @@ public:
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 
-	/** This method is passed into info(). (Temporary) */
-	void infoMethodCallback(StorageInfoResponse response);
-
 	/** Returns storage's saves directory path with the trailing slash. */
 	virtual Common::String savesDirectoryPath();
 
diff --git a/backends/module.mk b/backends/module.mk
index e74cf67..aea0f77 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -34,6 +34,7 @@ MODULE_OBJS += \
 	cloud/box/boxuploadrequest.o \
 	cloud/dropbox/dropboxstorage.o \
 	cloud/dropbox/dropboxcreatedirectoryrequest.o \
+	cloud/dropbox/dropboxinforequest.o \
 	cloud/dropbox/dropboxlistdirectoryrequest.o \
 	cloud/dropbox/dropboxuploadrequest.o \
 	cloud/googledrive/googledrivelistdirectorybyidrequest.o \


Commit: d0c54cdd64a174f648321608d29f3c0860837256
    https://github.com/scummvm/scummvm/commit/d0c54cdd64a174f648321608d29f3c0860837256
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix DropboxCreateDirectoryRequest

It now calls success callback with `false` on Dropbox's
"path/conflict/folder", indicating that the directory already exists.

Changed paths:
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index a52d41e..81ace75 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -84,6 +84,14 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 	Common::JSONObject info = json->asObject();
 	if (info.contains("id")) finishCreation(true);
 	else {
+		if (info.contains("error_summary") && info.getVal("error_summary")->isString()) {
+			Common::String summary = info.getVal("error_summary")->asString();
+			if (summary.contains("path") && summary.contains("conflict") && summary.contains("folder")) {
+				finishCreation(false);
+				delete json;
+				return;
+			}
+		}
 		error.response = json->stringify(true);
 		finishError(error);
 	}


Commit: 0c1c274abdbdb4183d9a08669082227aa912b1ea
    https://github.com/scummvm/scummvm/commit/0c1c274abdbdb4183d9a08669082227aa912b1ea
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix OneDriveUploadRequest

OneDrive doesn't accept empty files, so UploadRequest just skips such.

Changed paths:
    backends/cloud/onedrive/onedriveuploadrequest.cpp



diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index fb32443..55359f8 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -92,9 +92,15 @@ void OneDriveUploadRequest::uploadNextPart() {
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
 	request->setBuffer(buffer, size);
 
-	//request->addHeader(Common::String::format("Content-Length: %u", size));
 	if (_uploadUrl != "")
-		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));	;
+		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));
+	else
+		if (_contentsStream->size() == 0) {
+			warning("\"Sorry, OneDrive can't upload empty files\"");
+			finishUpload(StorageFile(_savePath, 0, 0, false));
+			delete request;
+			return;
+		}
 	
 	_workingRequest = ConnMan.addRequest(request);
 }


Commit: a13e03e988f85a67366f2ffe39bae67576e425bc
    https://github.com/scummvm/scummvm/commit/a13e03e988f85a67366f2ffe39bae67576e425bc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add Networking::Connection::isLimited()

`false` everywhere by default, but works on Android (`true` if not
Wi-Fi).

Changed paths:
  A backends/networking/connection/islimited-android.cpp
  A backends/networking/connection/islimited-default.cpp
  A backends/networking/connection/islimited.h
    backends/module.mk
    backends/platform/android/jni.cpp
    backends/platform/android/jni.h
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
    dists/android/AndroidManifest.xml
    dists/android/AndroidManifest.xml.in



diff --git a/backends/module.mk b/backends/module.mk
index aea0f77..d227f41 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -162,14 +162,17 @@ MODULE_OBJS += \
 
 ifeq ($(BACKEND),android)
 MODULE_OBJS += \
-	networking/browser/openurl-android.o
+	networking/browser/openurl-android.o \
+	networking/connection/islimited-android.o
 else
 ifdef MACOSX
 MODULE_OBJS += \
-	networking/browser/openurl-osx.o
+	networking/browser/openurl-osx.o \
+	networking/connection/islimited-default.o
 else
 MODULE_OBJS += \
-	networking/browser/openurl-posix.o
+	networking/browser/openurl-posix.o \
+	networking/connection/islimited-default.o
 endif
 endif
 endif
@@ -193,14 +196,16 @@ MODULE_OBJS += \
 	saves/windows/windows-saves.o \
 	updates/win32/win32-updates.o \
 	taskbar/win32/win32-taskbar.o \
-	networking/browser/openurl-windows.o
+	networking/browser/openurl-windows.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),androidsdl)
 MODULE_OBJS += \
 	events/androidsdl/androidsdl-events.o \
 	graphics/androidsdl/androidsdl-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifdef AMIGAOS
@@ -208,7 +213,8 @@ MODULE_OBJS += \
 	fs/amigaos4/amigaos4-fs.o \
 	fs/amigaos4/amigaos4-fs-factory.o \
 	midi/camd.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifdef PLAYSTATION3
@@ -217,7 +223,8 @@ MODULE_OBJS += \
 	fs/posix/posix-fs-factory.o \
 	fs/ps3/ps3-fs-factory.o \
 	events/ps3sdl/ps3sdl-events.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifdef USE_LINUXCD
@@ -228,7 +235,8 @@ endif
 ifeq ($(BACKEND),tizen)
 MODULE_OBJS += \
 	timer/tizen/timer.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),ds)
@@ -236,35 +244,40 @@ MODULE_OBJS += \
 	fs/ds/ds-fs.o \
 	fs/ds/ds-fs-factory.o \
 	plugins/ds/ds-provider.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),dingux)
 MODULE_OBJS += \
 	events/dinguxsdl/dinguxsdl-events.o \
 	graphics/dinguxsdl/dinguxsdl-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),gph)
 MODULE_OBJS += \
 	events/gph/gph-events.o \
 	graphics/gph/gph-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),linuxmoto)
 MODULE_OBJS += \
 	events/linuxmotosdl/linuxmotosdl-events.o \
 	graphics/linuxmotosdl/linuxmotosdl-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),maemo)
 MODULE_OBJS += \
 	events/maemosdl/maemosdl-events.o \
 	graphics/maemosdl/maemosdl-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),n64)
@@ -272,14 +285,16 @@ MODULE_OBJS += \
 	fs/n64/n64-fs.o \
 	fs/n64/n64-fs-factory.o \
 	fs/n64/romfsstream.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),openpandora)
 MODULE_OBJS += \
 	events/openpandora/op-events.o \
 	graphics/openpandora/op-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),ps2)
@@ -287,7 +302,8 @@ MODULE_OBJS += \
 	fs/ps2/ps2-fs.o \
 	fs/ps2/ps2-fs-factory.o \
 	plugins/ps2/ps2-provider.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),psp)
@@ -298,20 +314,23 @@ MODULE_OBJS += \
 	plugins/psp/psp-provider.o \
 	saves/psp/psp-saves.o \
 	timer/psp/timer.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),samsungtv)
 MODULE_OBJS += \
 	events/samsungtvsdl/samsungtvsdl-events.o \
 	graphics/samsungtvsdl/samsungtvsdl-graphics.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),webos)
 MODULE_OBJS += \
 	events/webossdl/webossdl-events.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),wince)
@@ -322,7 +341,8 @@ MODULE_OBJS += \
 	graphics/wincesdl/wincesdl-graphics.o \
 	mixer/wincesdl/wincesdl-mixer.o \
 	plugins/win32/win32-provider.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifeq ($(BACKEND),wii)
@@ -330,7 +350,8 @@ MODULE_OBJS += \
 	fs/wii/wii-fs.o \
 	fs/wii/wii-fs-factory.o \
 	plugins/wii/wii-provider.o \
-	networking/browser/openurl-default.o
+	networking/browser/openurl-default.o \
+	networking/connection/islimited-default.o
 endif
 
 ifdef ENABLE_EVENTRECORDER
diff --git a/backends/networking/connection/islimited-android.cpp b/backends/networking/connection/islimited-android.cpp
new file mode 100644
index 0000000..8989f21
--- /dev/null
+++ b/backends/networking/connection/islimited-android.cpp
@@ -0,0 +1,35 @@
+/* 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 "backends/networking/connection/islimited.h"
+#include "backends/platform/android/jni.h"
+
+namespace Networking {
+namespace Connection {
+
+bool isLimited() {
+	return JNI::isConnectionLimited();
+}
+
+} // End of namespace Connection
+} // End of namespace Networking
+
diff --git a/backends/networking/connection/islimited-default.cpp b/backends/networking/connection/islimited-default.cpp
new file mode 100644
index 0000000..a993077
--- /dev/null
+++ b/backends/networking/connection/islimited-default.cpp
@@ -0,0 +1,36 @@
+/* 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 "backends/networking/connection/islimited.h"
+#include "common/textconsole.h"
+
+namespace Networking {
+namespace Connection {
+
+bool isLimited() {
+	warning("Networking::Connection::isLimited(): not limited by default");
+	return false;
+}
+
+} // End of namespace Connection
+} // End of namespace Networking
+
diff --git a/backends/networking/connection/islimited.h b/backends/networking/connection/islimited.h
new file mode 100644
index 0000000..69be267
--- /dev/null
+++ b/backends/networking/connection/islimited.h
@@ -0,0 +1,34 @@
+/* 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.
+ *
+ */
+
+#ifndef NETWORKING_CONNECTION_ISLIMITED_H
+#define NETWORKING_CONNECTION_ISLIMITED_H
+
+namespace Networking {
+namespace Connection {
+
+bool isLimited();
+
+} // End of namespace Connection
+} // End of namespace Networking
+
+#endif /*NETWORKING_CONNECTION_ISLIMITED_H*/
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 6360bb1..91d9a57 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -77,6 +77,7 @@ bool JNI::_ready_for_events = 0;
 jmethodID JNI::_MID_getDPI = 0;
 jmethodID JNI::_MID_displayMessageOnOSD = 0;
 jmethodID JNI::_MID_openUrl = 0;
+jmethodID JNI::_MID_isConnectionLimited = 0;
 jmethodID JNI::_MID_setWindowCaption = 0;
 jmethodID JNI::_MID_showVirtualKeyboard = 0;
 jmethodID JNI::_MID_getSysArchives = 0;
@@ -252,6 +253,22 @@ bool JNI::openUrl(const char *url) {
 	return success;
 }
 
+bool JNI::isConnectionLimited() {
+	bool limited = false;
+	JNIEnv *env = JNI::getEnv();
+	limited = env->CallBooleanMethod(_jobj, _MID_isConnectionLimited);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to check whether connection's limited");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		limited = true;
+	}
+	
+	return limited;
+}
+
 void JNI::setWindowCaption(const char *caption) {
 	JNIEnv *env = JNI::getEnv();
 	jstring java_caption = env->NewStringUTF(caption);
@@ -432,6 +449,7 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
 	FIND_METHOD(, getDPI, "([F)V");
 	FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
 	FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
+	FIND_METHOD(, isConnectionLimited, "()Z");
 	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
 	FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
 	FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
index de4c95b..0798db4 100644
--- a/backends/platform/android/jni.h
+++ b/backends/platform/android/jni.h
@@ -59,6 +59,7 @@ public:
 	static void getDPI(float *values);
 	static void displayMessageOnOSD(const char *msg);
 	static bool openUrl(const char *url);
+	static bool isConnectionLimited();
 	static void showVirtualKeyboard(bool enable);
 	static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
 
@@ -91,6 +92,7 @@ private:
 	static jmethodID _MID_getDPI;
 	static jmethodID _MID_displayMessageOnOSD;
 	static jmethodID _MID_openUrl;
+	static jmethodID _MID_isConnectionLimited;
 	static jmethodID _MID_setWindowCaption;
 	static jmethodID _MID_showVirtualKeyboard;
 	static jmethodID _MID_getSysArchives;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index 5064280..47dcb32 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -54,6 +54,7 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
 	abstract protected void getDPI(float[] values);
 	abstract protected void displayMessageOnOSD(String msg);
 	abstract protected void openUrl(String url);
+	abstract protected boolean isConnectionLimited();
 	abstract protected void setWindowCaption(String caption);
 	abstract protected void showVirtualKeyboard(boolean enable);
 	abstract protected String[] getSysArchives();
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 2f3701a..225496c 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -2,10 +2,13 @@ package org.scummvm.scummvm;
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
@@ -80,6 +83,15 @@ public class ScummVMActivity extends Activity {
 			startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
 		}
 
+		@Override
+		protected boolean isConnectionLimited() {
+			WifiManager wifiMgr = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+			if (wifiMgr != null && wifiMgr.isWifiEnabled()) {
+				WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
+				return (wifiInfo == null || wifiInfo.getNetworkId() == -1); //WiFi is on, but it's not connected to any network
+			}
+			return true;
+		}
 
 		@Override
 		protected void setWindowCaption(final String caption) {
diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml
index c091039..64870a4 100644
--- a/dists/android/AndroidManifest.xml
+++ b/dists/android/AndroidManifest.xml
@@ -40,6 +40,8 @@
 
 	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
+	<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" android:required="true"/>
+
 	<uses-feature android:name="android.hardware.screen.landscape"
 		        android:required="false" />
 
diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in
index 7eaece9..9601425 100644
--- a/dists/android/AndroidManifest.xml.in
+++ b/dists/android/AndroidManifest.xml.in
@@ -40,6 +40,8 @@
 
 	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
+	<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" android:required="true"/>
+
 	<uses-feature android:name="android.hardware.screen.landscape"
 		        android:required="false" />
 


Commit: 8e9d106658467181370d12e97f178032f083390c
    https://github.com/scummvm/scummvm/commit/8e9d106658467181370d12e97f178032f083390c
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix makefile

Changed paths:
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index d227f41..d7728dc 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -170,12 +170,18 @@ MODULE_OBJS += \
 	networking/browser/openurl-osx.o \
 	networking/connection/islimited-default.o
 else
+ifdef WIN32
+MODULE_OBJS += \
+	networking/browser/openurl-windows.o \
+	networking/connection/islimited-default.o
+else
 MODULE_OBJS += \
 	networking/browser/openurl-posix.o \
 	networking/connection/islimited-default.o
 endif
 endif
 endif
+endif
 
 ifdef MACOSX
 MODULE_OBJS += \
@@ -195,9 +201,7 @@ MODULE_OBJS += \
 	plugins/win32/win32-provider.o \
 	saves/windows/windows-saves.o \
 	updates/win32/win32-updates.o \
-	taskbar/win32/win32-taskbar.o \
-	networking/browser/openurl-windows.o \
-	networking/connection/islimited-default.o
+	taskbar/win32/win32-taskbar.o
 endif
 
 ifeq ($(BACKEND),androidsdl)


Commit: a11b004b6bc18a23b5ab257f4da4ff9e5429c9d9
    https://github.com/scummvm/scummvm/commit/a11b004b6bc18a23b5ab257f4da4ff9e5429c9d9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Show warning in DownloadDialog

If user's connection seems limited, ScummVM shows a warning message to
prevent using that by accident.

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 42c9628..df87837 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -22,6 +22,7 @@
 
 #include "gui/downloaddialog.h"
 #include "backends/cloud/cloudmanager.h"
+#include "backends/networking/connection/islimited.h"
 #include "common/config-manager.h"
 #include "common/translation.h"
 #include "engines/metaengine.h"
@@ -112,6 +113,12 @@ void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 }
 
 bool DownloadDialog::selectDirectories() {
+	if (Networking::Connection::isLimited()) {
+		MessageDialog alert(_("It looks like your connection is limited. "
+			"Do you really want to download files with it?"), _("Yes"), _("No"));
+		if (alert.runModal() != GUI::kMessageOK) return false;
+	}
+
 	//first user should select remote directory to download	
 	if (_remoteBrowser->runModal() <= 0) return false;
 


Commit: 6753c18d6f182f98db0fe9403991392482d7a9fa
    https://github.com/scummvm/scummvm/commit/6753c18d6f182f98db0fe9403991392482d7a9fa
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add default SavesSync callbacks

With OSD messages indicating whether saves sync is complete, cancelled
or failed.

Changed paths:
    backends/cloud/storage.cpp
    backends/cloud/storage.h



diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index c20ad86..d4548b8 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -128,7 +128,8 @@ SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCal
 		_runningRequestsMutex.unlock();
 		return _savesSyncRequest;
 	}
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!callback) callback = new Common::Callback<Storage, BoolResponse>(this, &Storage::savesSyncDefaultCallback);
+	if (!errorCallback) errorCallback = new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::savesSyncDefaultErrorCallback);
 	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
 	_syncRestartRequestsed = false;
 	_runningRequestsMutex.unlock();	
@@ -192,6 +193,28 @@ void Storage::setSyncTarget(GUI::CommandReceiver *target) {
 	_runningRequestsMutex.unlock();
 }
 
+void Storage::savesSyncDefaultCallback(BoolResponse response) {
+	_runningRequestsMutex.lock();
+	_savesSyncRequest = nullptr;
+	_runningRequestsMutex.unlock();
+
+	if (!response.value) warning("SavesSyncRequest called success callback with `false` argument");
+	g_system->displayMessageOnOSD(_("Saves sync complete."));
+}
+
+void Storage::savesSyncDefaultErrorCallback(Networking::ErrorResponse error) {
+	_runningRequestsMutex.lock();
+	_savesSyncRequest = nullptr;
+	_runningRequestsMutex.unlock();
+
+	printErrorResponse(error);
+
+	if (error.interrupted)
+		g_system->displayMessageOnOSD(_("Saves sync was cancelled."));
+	else
+		g_system->displayMessageOnOSD(_("Saves sync failed.\nCheck your Internet connection."));
+}
+
 ///// DownloadFolderRequest-related /////
 
 bool Storage::startDownload(Common::String remotePath, Common::String localPath) {
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index a577b3c..b33d69e 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -181,6 +181,14 @@ public:
 	/** Sets SavesSyncRequest's target to given CommandReceiver. */
 	virtual void setSyncTarget(GUI::CommandReceiver *target);
 
+protected:
+	/** Finishes the sync. Shows an OSD message. */
+	virtual void savesSyncDefaultCallback(BoolResponse response);
+
+	/** Finishes the sync. Shows an OSD message. */
+	virtual void savesSyncDefaultErrorCallback(Networking::ErrorResponse error);
+
+public:
 	///// DownloadFolderRequest-related /////
 
 	/** Starts a folder download. */


Commit: ab0f2d1a03c9ca45dd30440b8a897d9de480bfbe
    https://github.com/scummvm/scummvm/commit/ab0f2d1a03c9ca45dd30440b8a897d9de480bfbe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix OneDriveUploadRequest

Segfault when given stream is nullptr.

Changed paths:
    backends/cloud/onedrive/onedriveuploadrequest.cpp



diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 55359f8..5b86c6c 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -50,9 +50,15 @@ OneDriveUploadRequest::~OneDriveUploadRequest() {
 void OneDriveUploadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
+	if (_contentsStream == nullptr) {
+		warning("OneDriveUploadRequest: cannot restart because no stream given");
+		finishError(Networking::ErrorResponse(this, false, true, "No stream given", -1));
+		return;
+	}
 	if (!_contentsStream->seek(0)) {
 		warning("OneDriveUploadRequest: cannot restart because stream couldn't seek(0)");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
 	}
 	_ignoreCallback = false;
 


Commit: 1d78d20fcf59d772688f9f32d81670548d6569f9
    https://github.com/scummvm/scummvm/commit/1d78d20fcf59d772688f9f32d81670548d6569f9
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Dropbox and Google Drive UploadRequests

Possible segfault there too.

Changed paths:
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index e530502..a3d7dd0 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -56,6 +56,7 @@ void DropboxUploadRequest::start() {
 	if (!_contentsStream->seek(0)) {
 		warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
 	}
 	_ignoreCallback = false;
 
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index ce7d59a..73a7fbf 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -50,9 +50,10 @@ GoogleDriveUploadRequest::~GoogleDriveUploadRequest() {
 void GoogleDriveUploadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
-	if (!_contentsStream->seek(0)) {
+	if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
 		warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
+		return;
 	}
 	_resolvedId = ""; //used to update file contents
 	_parentId = ""; //used to create file within parent directory


Commit: 63311bac267ed0ebacc10068d8ffd2354a5f4371
    https://github.com/scummvm/scummvm/commit/63311bac267ed0ebacc10068d8ffd2354a5f4371
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add error callback in Options' Cloud tab

Shows OSD message.

Changed paths:
    gui/options.cpp
    gui/options.h



diff --git a/gui/options.cpp b/gui/options.cpp
index f3afd58..ac548f5 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1624,10 +1624,17 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 	case kRefreshStorageCmd:
 	{
-		CloudMan.info(new Common::Callback<GlobalOptionsDialog, Cloud::Storage::StorageInfoResponse>(this, &GlobalOptionsDialog::storageInfoCallback), nullptr);
+		CloudMan.info(
+			new Common::Callback<GlobalOptionsDialog, Cloud::Storage::StorageInfoResponse>(this, &GlobalOptionsDialog::storageInfoCallback),
+			new Common::Callback<GlobalOptionsDialog, Networking::ErrorResponse>(this, &GlobalOptionsDialog::storageErrorCallback)
+		);
 		Common::String dir = CloudMan.savesDirectoryPath();
 		if (dir.lastChar() == '/') dir.deleteLastChar();
-		CloudMan.listDirectory(dir, new Common::Callback<GlobalOptionsDialog, Cloud::Storage::ListDirectoryResponse>(this, &GlobalOptionsDialog::storageListDirectoryCallback), nullptr);
+		CloudMan.listDirectory(
+			dir,
+			new Common::Callback<GlobalOptionsDialog, Cloud::Storage::ListDirectoryResponse>(this, &GlobalOptionsDialog::storageListDirectoryCallback),
+			new Common::Callback<GlobalOptionsDialog, Networking::ErrorResponse>(this, &GlobalOptionsDialog::storageErrorCallback)
+		);
 		break;
 	}
 	case kDownloadStorageCmd:
@@ -1825,6 +1832,14 @@ void GlobalOptionsDialog::storageListDirectoryCallback(Cloud::Storage::ListDirec
 	CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
 	_redrawCloudTab = true;
 }
+
+void GlobalOptionsDialog::storageErrorCallback(Networking::ErrorResponse response) {
+	debug("error response (%s, %ld):", (response.failed ? "failed" : "interrupted"), response.httpResponseCode);
+	debug("%s", response.response.c_str());
+
+	if (!response.interrupted)
+		g_system->displayMessageOnOSD(_("Request failed.\nCheck your Internet connection."));
+}
 #endif
 
 } // End of namespace GUI
diff --git a/gui/options.h b/gui/options.h
index e68778c..a7e98ad 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -277,6 +277,7 @@ protected:
 #ifdef USE_LIBCURL
 	void storageInfoCallback(Cloud::Storage::StorageInfoResponse response);
 	void storageListDirectoryCallback(Cloud::Storage::ListDirectoryResponse response);
+	void storageErrorCallback(Networking::ErrorResponse response);
 #endif
 };
 


Commit: e833c8f65c05a1db182d4982d4e1b8edfe014980
    https://github.com/scummvm/scummvm/commit/e833c8f65c05a1db182d4982d4e1b8edfe014980
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add OSD warning when can't start LocalWebserver

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 29db0b7..1480418 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -28,6 +28,7 @@
 #include "common/str.h"
 #include "common/system.h"
 #include "common/timer.h"
+#include "common/translation.h"
 #include <SDL/SDL_net.h>
 
 #ifdef POSIX
@@ -100,7 +101,11 @@ void LocalWebserver::start() {
 
 	_serverSocket = SDLNet_TCP_Open(&ip);
 	if (!_serverSocket) {
-		error("SDLNet_TCP_Open: %s\n", SDLNet_GetError());		
+		warning("SDLNet_TCP_Open: %s", SDLNet_GetError());
+		stopTimer();
+		g_system->displayMessageOnOSD(_("Failed to start local webserver.\nCheck whether selected port is not used by another application and try again."));
+		_handleMutex.unlock();
+		return;
 	}
 
 	// Create a socket set


Commit: 0200694dd050409eec7a30efd8a616ab6b1fdae1
    https://github.com/scummvm/scummvm/commit/0200694dd050409eec7a30efd8a616ab6b1fdae1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix backends/module.mk

MinGW failed to compile with the latest fix.

Checked this fix with create_project for MSVC, MinGW's make, make under
kubuntu and while building Android apk.

Changed paths:
    backends/module.mk



diff --git a/backends/module.mk b/backends/module.mk
index d7728dc..c402a10 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -150,37 +150,51 @@ MODULE_OBJS += \
 endif
 endif
 
-ifdef POSIX
-MODULE_OBJS += \
-	fs/posix/posix-fs.o \
-	fs/posix/posix-fs-factory.o \
-	fs/chroot/chroot-fs-factory.o \
-	fs/chroot/chroot-fs.o \
-	plugins/posix/posix-provider.o \
-	saves/posix/posix-saves.o \
-	taskbar/unity/unity-taskbar.o
-
+# openUrl
 ifeq ($(BACKEND),android)
 MODULE_OBJS += \
-	networking/browser/openurl-android.o \
-	networking/connection/islimited-android.o
+	networking/browser/openurl-android.o
 else
 ifdef MACOSX
 MODULE_OBJS += \
-	networking/browser/openurl-osx.o \
-	networking/connection/islimited-default.o
+	networking/browser/openurl-osx.o
 else
 ifdef WIN32
 MODULE_OBJS += \
-	networking/browser/openurl-windows.o \
-	networking/connection/islimited-default.o
+	networking/browser/openurl-windows.o
 else
-MODULE_OBJS += \
-	networking/browser/openurl-posix.o \
-	networking/connection/islimited-default.o
+	ifdef POSIX
+	MODULE_OBJS += \
+		networking/browser/openurl-posix.o
+	else
+		# create_project doesn't know something about `else`
+		ifndef WIN32
+		MODULE_OBJS += \
+		networking/browser/openurl-default.o
+		endif
+	endif
 endif
 endif
 endif
+
+# Connection::isLimited
+ifeq ($(BACKEND),android)
+MODULE_OBJS += \
+	networking/connection/islimited-android.o
+else
+MODULE_OBJS += \
+	networking/connection/islimited-default.o
+endif
+
+ifdef POSIX
+MODULE_OBJS += \
+	fs/posix/posix-fs.o \
+	fs/posix/posix-fs-factory.o \
+	fs/chroot/chroot-fs-factory.o \
+	fs/chroot/chroot-fs.o \
+	plugins/posix/posix-provider.o \
+	saves/posix/posix-saves.o \
+	taskbar/unity/unity-taskbar.o
 endif
 
 ifdef MACOSX
@@ -207,18 +221,14 @@ endif
 ifeq ($(BACKEND),androidsdl)
 MODULE_OBJS += \
 	events/androidsdl/androidsdl-events.o \
-	graphics/androidsdl/androidsdl-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/androidsdl/androidsdl-graphics.o
 endif
 
 ifdef AMIGAOS
 MODULE_OBJS += \
 	fs/amigaos4/amigaos4-fs.o \
 	fs/amigaos4/amigaos4-fs-factory.o \
-	midi/camd.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	midi/camd.o
 endif
 
 ifdef PLAYSTATION3
@@ -226,9 +236,7 @@ MODULE_OBJS += \
 	fs/posix/posix-fs.o \
 	fs/posix/posix-fs-factory.o \
 	fs/ps3/ps3-fs-factory.o \
-	events/ps3sdl/ps3sdl-events.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	events/ps3sdl/ps3sdl-events.o
 endif
 
 ifdef USE_LINUXCD
@@ -238,76 +246,58 @@ endif
 
 ifeq ($(BACKEND),tizen)
 MODULE_OBJS += \
-	timer/tizen/timer.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	timer/tizen/timer.o
 endif
 
 ifeq ($(BACKEND),ds)
 MODULE_OBJS += \
 	fs/ds/ds-fs.o \
 	fs/ds/ds-fs-factory.o \
-	plugins/ds/ds-provider.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	plugins/ds/ds-provider.o
 endif
 
 ifeq ($(BACKEND),dingux)
 MODULE_OBJS += \
 	events/dinguxsdl/dinguxsdl-events.o \
-	graphics/dinguxsdl/dinguxsdl-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/dinguxsdl/dinguxsdl-graphics.o
 endif
 
 ifeq ($(BACKEND),gph)
 MODULE_OBJS += \
 	events/gph/gph-events.o \
-	graphics/gph/gph-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/gph/gph-graphics.o
 endif
 
 ifeq ($(BACKEND),linuxmoto)
 MODULE_OBJS += \
 	events/linuxmotosdl/linuxmotosdl-events.o \
-	graphics/linuxmotosdl/linuxmotosdl-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/linuxmotosdl/linuxmotosdl-graphics.o
 endif
 
 ifeq ($(BACKEND),maemo)
 MODULE_OBJS += \
 	events/maemosdl/maemosdl-events.o \
-	graphics/maemosdl/maemosdl-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/maemosdl/maemosdl-graphics.o
 endif
 
 ifeq ($(BACKEND),n64)
 MODULE_OBJS += \
 	fs/n64/n64-fs.o \
 	fs/n64/n64-fs-factory.o \
-	fs/n64/romfsstream.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	fs/n64/romfsstream.o
 endif
 
 ifeq ($(BACKEND),openpandora)
 MODULE_OBJS += \
 	events/openpandora/op-events.o \
-	graphics/openpandora/op-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/openpandora/op-graphics.o
 endif
 
 ifeq ($(BACKEND),ps2)
 MODULE_OBJS += \
 	fs/ps2/ps2-fs.o \
 	fs/ps2/ps2-fs-factory.o \
-	plugins/ps2/ps2-provider.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	plugins/ps2/ps2-provider.o
 endif
 
 ifeq ($(BACKEND),psp)
@@ -317,24 +307,18 @@ MODULE_OBJS += \
 	fs/psp/psp-stream.o \
 	plugins/psp/psp-provider.o \
 	saves/psp/psp-saves.o \
-	timer/psp/timer.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	timer/psp/timer.o
 endif
 
 ifeq ($(BACKEND),samsungtv)
 MODULE_OBJS += \
 	events/samsungtvsdl/samsungtvsdl-events.o \
-	graphics/samsungtvsdl/samsungtvsdl-graphics.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	graphics/samsungtvsdl/samsungtvsdl-graphics.o
 endif
 
 ifeq ($(BACKEND),webos)
 MODULE_OBJS += \
-	events/webossdl/webossdl-events.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	events/webossdl/webossdl-events.o
 endif
 
 ifeq ($(BACKEND),wince)
@@ -344,18 +328,14 @@ MODULE_OBJS += \
 	fs/windows/windows-fs-factory.o \
 	graphics/wincesdl/wincesdl-graphics.o \
 	mixer/wincesdl/wincesdl-mixer.o \
-	plugins/win32/win32-provider.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	plugins/win32/win32-provider.o
 endif
 
 ifeq ($(BACKEND),wii)
 MODULE_OBJS += \
 	fs/wii/wii-fs.o \
 	fs/wii/wii-fs-factory.o \
-	plugins/wii/wii-provider.o \
-	networking/browser/openurl-default.o \
-	networking/connection/islimited-default.o
+	plugins/wii/wii-provider.o
 endif
 
 ifdef ENABLE_EVENTRECORDER


Commit: f743b319633dcd57ea08f5ac86d3daec548d1ab0
    https://github.com/scummvm/scummvm/commit/f743b319633dcd57ea08f5ac86d3daec548d1ab0
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix CloudManager::connectStorage() memory leak

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 9e036b1..8162af2 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -56,12 +56,16 @@ BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
 	_token(accessToken), _refreshToken(refreshToken) {}
 
 BoxStorage::BoxStorage(Common::String code) {
-	getAccessToken(new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete), code);
+	getAccessToken(
+		new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
+		new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
+		code
+	);
 }
 
 BoxStorage::~BoxStorage() {}
 
-void BoxStorage::getAccessToken(BoolCallback callback, Common::String code) {
+void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
 	if (!KEY || !SECRET) loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
@@ -72,7 +76,8 @@ void BoxStorage::getAccessToken(BoolCallback callback, Common::String code) {
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://api.box.com/oauth2/token");
+	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.box.com/oauth2/token");
 	if (codeFlow) {
 		request->addPostField("grant_type=authorization_code");
 		request->addPostField("code=" + code);
@@ -117,6 +122,7 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 void BoxStorage::codeFlowComplete(BoolResponse response) {
 	if (!response.value) {
 		warning("BoxStorage: failed to get access token through code flow");
+		CloudMan.removeStorage(this);
 		return;
 	}
 
@@ -124,6 +130,12 @@ void BoxStorage::codeFlowComplete(BoolResponse response) {
 	ConfMan.flushToDisk();
 }
 
+void BoxStorage::codeFlowFailed(Networking::ErrorResponse error) {
+	debug("Box's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
+	CloudMan.removeStorage(this);
+}
+
 void BoxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 51f2a95..826d6be 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -42,6 +42,7 @@ class BoxStorage: public Id::IdStorage {
 
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
+	void codeFlowFailed(Networking::ErrorResponse error);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
@@ -108,7 +109,7 @@ public:
 	 * Use "" in order to refresh token and pass a callback, so you could
 	 * continue your work when new token is available.
 	 */
-	void getAccessToken(BoolCallback callback, Common::String code = "");
+	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
 	Common::String accessToken() { return _token; }
 };
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index b6a721b..8b7230f 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -45,6 +45,7 @@ CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr)
 CloudManager::~CloudManager() {
 	//TODO: do we have to save storages on manager destruction?	
 	delete _activeStorage;
+	freeStorages();
 }
 
 Common::String CloudManager::getStorageConfigName(uint32 index) const {
@@ -124,6 +125,7 @@ void CloudManager::save() {
 }
 
 void CloudManager::replaceStorage(Storage *storage, uint32 index) {
+	freeStorages();
 	if (!storage) error("CloudManager::replaceStorage: NULL storage passed");
 	if (index >= kStorageTotal) error("CloudManager::replaceStorage: invalid index passed");
 	delete _activeStorage;
@@ -138,6 +140,18 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
 	}
 }
 
+void CloudManager::removeStorage(Storage *storage) {
+	// can't just delete it as it's mostly likely the one who calls the method
+	// it would be freed on freeStorages() call (on next Storage connect or replace)
+	_storagesToRemove.push_back(storage);
+}
+
+void CloudManager::freeStorages() {
+	for (uint32 i = 0; i < _storagesToRemove.size(); ++i)
+		delete _storagesToRemove[i];
+	_storagesToRemove.clear();
+}
+
 Storage *CloudManager::getCurrentStorage() const {
 	return _activeStorage;
 }
@@ -207,6 +221,8 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
 }
 
 void CloudManager::connectStorage(uint32 index, Common::String code) {
+	freeStorages();
+
 	Storage *storage = nullptr;
 	switch (index) {
 	case kStorageDropboxId: storage = new Dropbox::DropboxStorage(code); break;
@@ -214,7 +230,9 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 	case kStorageGoogleDriveId: storage = new GoogleDrive::GoogleDriveStorage(code); break;
 	case kStorageBoxId: storage = new Box::BoxStorage(code); break;
 	}
-	//these would automatically request replaceStorage() when they receive the token
+	// in these constructors Storages request token using the passed code
+	// when the token is received, they call replaceStorage()
+	// or removeStorage(), if some error occurred
 }
 
 void CloudManager::printBool(Storage::BoolResponse response) const {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 15409ee..4442b9b 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -59,6 +59,7 @@ class CloudManager : public Common::Singleton<CloudManager> {
 	Common::Array<StorageConfig> _storages;
 	uint _currentStorageIndex;
 	Storage *_activeStorage;
+	Common::Array<Storage *> _storagesToRemove;
 
 	void printBool(Cloud::Storage::BoolResponse response) const;
 
@@ -66,6 +67,9 @@ class CloudManager : public Common::Singleton<CloudManager> {
 
 	Common::String getStorageConfigName(uint32 index) const;
 
+	/** Frees memory used by storages which failed to connect. */
+	void freeStorages();
+
 public:
 	CloudManager();
 	virtual ~CloudManager();
@@ -91,6 +95,9 @@ public:
 	 */
 	void replaceStorage(Storage *storage, uint32 index);
 
+	/** Adds storage in the list of storages to remove later. */
+	void removeStorage(Storage *storage);
+
 	/**
 	 * Returns active Storage, which could be used to interact
 	 *  with cloud storage.
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index e34912e..4171af0 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -62,8 +62,9 @@ DropboxStorage::~DropboxStorage() {}
 
 void DropboxStorage::getAccessToken(Common::String code) {
 	if (!KEY || !SECRET) loadKeyAndSecret();
-	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);		
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/oauth2/token");
+	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);
+	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::codeFlowFailed);
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, errorCallback, "https://api.dropboxapi.com/oauth2/token");
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
@@ -83,6 +84,7 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 		if (!result.contains("access_token") || !result.contains("uid")) {
 			warning("%s", json->stringify(true).c_str());
 			warning("Bad response, no token/uid passed");
+			CloudMan.removeStorage(this);
 		} else {
 			_token = result.getVal("access_token")->asString();
 			_uid = result.getVal("uid")->asString();			
@@ -94,9 +96,16 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 		delete json;
 	} else {
 		debug("DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
+		CloudMan.removeStorage(this);
 	}
 }
 
+void DropboxStorage::codeFlowFailed(Networking::ErrorResponse error) {
+	debug("Dropbox's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
+	CloudMan.removeStorage(this);
+}
+
 void DropboxStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
 	ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 2c9cf16..9dd28ba 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -42,6 +42,7 @@ class DropboxStorage: public Cloud::Storage {
 
 	void getAccessToken(Common::String code);
 	void codeFlowComplete(Networking::JsonResponse response);
+	void codeFlowFailed(Networking::ErrorResponse error);
 
 public:
 	/** This constructor uses OAuth code flow to get tokens. */
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 2816301..c426409 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -56,12 +56,16 @@ GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::Strin
 	_token(accessToken), _refreshToken(refreshToken) {}
 
 GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
-	getAccessToken(new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete), code);
+	getAccessToken(
+		new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
+		new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
+		code
+	);
 }
 
 GoogleDriveStorage::~GoogleDriveStorage() {}
 
-void GoogleDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
 	if (!KEY || !SECRET) loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
@@ -72,7 +76,8 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Common::String co
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://accounts.google.com/o/oauth2/token"); //TODO
+	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://accounts.google.com/o/oauth2/token"); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
@@ -118,6 +123,7 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 	if (!response.value) {
 		warning("GoogleDriveStorage: failed to get access token through code flow");
+		CloudMan.removeStorage(this);
 		return;
 	}
 
@@ -126,6 +132,12 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 	ConfMan.flushToDisk();
 }
 
+void GoogleDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
+	debug("Google Drive's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
+	CloudMan.removeStorage(this);
+}
+
 void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
 	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 4a7dbab..eee4de9 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -42,6 +42,7 @@ class GoogleDriveStorage: public Id::IdStorage {
 
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
+	void codeFlowFailed(Networking::ErrorResponse error);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
@@ -110,7 +111,7 @@ public:
 	 * Use "" in order to refresh token and pass a callback, so you could
 	 * continue your work when new token is available.
 	 */
-	void getAccessToken(BoolCallback callback, Common::String code = "");
+	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
 	Common::String accessToken() { return _token; }
 };
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 3c8ea5f..e916594 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -57,12 +57,16 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user
 	_token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
 
 OneDriveStorage::OneDriveStorage(Common::String code) {
-	getAccessToken(new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete), code);
+	getAccessToken(
+		new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
+		new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
+		code
+	);
 }
 
 OneDriveStorage::~OneDriveStorage() {}
 
-void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) {
+void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
 	if (!KEY || !SECRET) loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
@@ -73,7 +77,8 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code)
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://login.live.com/oauth20_token.srf"); //TODO
+	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://login.live.com/oauth20_token.srf"); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
@@ -117,6 +122,7 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 	if (!response.value) {
 		warning("OneDriveStorage: failed to get access token through code flow");
+		CloudMan.removeStorage(this);
 		return;
 	}
 
@@ -125,6 +131,12 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 	ConfMan.flushToDisk();
 }
 
+void OneDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
+	debug("OneDrive's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug("%s", error.response.c_str());
+	CloudMan.removeStorage(this);
+}
+
 void OneDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
 	ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 650c240..8ceaaf1 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -41,6 +41,7 @@ class OneDriveStorage: public Cloud::Storage {
 
 	void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
 	void codeFlowComplete(BoolResponse response);
+	void codeFlowFailed(Networking::ErrorResponse error);
 
 	/** Constructs StorageInfo based on JSON response from cloud. */
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
@@ -101,7 +102,7 @@ public:
 	 * Use "" in order to refresh token and pass a callback, so you could
 	 * continue your work when new token is available.
 	 */
-	void getAccessToken(BoolCallback callback, Common::String code = "");
+	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
 	Common::String accessToken() { return _token; }
 };


Commit: b1264df120b6594a79d99cbb1cc5ef944fa2e448
    https://github.com/scummvm/scummvm/commit/b1264df120b6594a79d99cbb1cc5ef944fa2e448
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Check whether Storage is working when replacing it

We do that in CloudManager::replaceStorage(), but I've tried to
eliminate such possibility by adding a check in the StorageWizardDialog.

Changed paths:
    backends/cloud/cloudmanager.cpp
    gui/storagewizarddialog.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 8b7230f..ed48ec5 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -128,7 +128,14 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
 	freeStorages();
 	if (!storage) error("CloudManager::replaceStorage: NULL storage passed");
 	if (index >= kStorageTotal) error("CloudManager::replaceStorage: invalid index passed");
-	delete _activeStorage;
+	if (_activeStorage != nullptr && _activeStorage->isWorking()) {
+		warning("CloudManager::replaceStorage: replacing Storage while the other is working");
+		if (_activeStorage->isDownloading()) _activeStorage->cancelDownload();
+		if (_activeStorage->isSyncing()) _activeStorage->cancelSync();
+		removeStorage(_activeStorage);
+	} else {
+		delete _activeStorage;
+	}
 	_activeStorage = storage;
 	_currentStorageIndex = index;
 	save();
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 393b826..a8574ab 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -74,6 +74,29 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 
 void StorageWizardDialog::open() {
 	Dialog::open();
+
+	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;
+		}
+	}
+
 #ifdef USE_SDL_NET
 	_stopServerOnClose = !LocalServer.isRunning();
 	LocalServer.start();


Commit: 39865e6e6cf67e3b5a3867540dde76969b244246
    https://github.com/scummvm/scummvm/commit/39865e6e6cf67e3b5a3867540dde76969b244246
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add port override for LocalWebserver

It's enabled only when NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE is
defined.

It's not defined, because override means we have to reconfigure our
redirect links somehow to use the override port.

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    gui/options.cpp
    gui/options.h
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 1480418..7c3edfc 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -30,6 +30,7 @@
 #include "common/timer.h"
 #include "common/translation.h"
 #include <SDL/SDL_net.h>
+#include <common/config-manager.h>
 
 #ifdef POSIX
 #include <sys/types.h>
@@ -48,7 +49,7 @@ DECLARE_SINGLETON(Networking::LocalWebserver);
 namespace Networking {
 
 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
-	_stopOnIdle(false), _clients(0), _idlingFrames(0) {
+	_stopOnIdle(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
 	addPathHandler("/", _indexPageHandler.getHandler());
 	addPathHandler("/files", _filesPageHandler.getHandler());
 	addPathHandler("/create", _createDirectoryHandler.getHandler());
@@ -84,6 +85,7 @@ void LocalWebserver::stopTimer() {
 
 void LocalWebserver::start() {
 	_handleMutex.lock();
+	_serverPort = getPort();
 	_stopOnIdle = false;
 	if (_timerStarted) {
 		_handleMutex.unlock();
@@ -93,7 +95,7 @@ void LocalWebserver::start() {
 
 	// Create a listening TCP socket
 	IPaddress ip;	
-	if (SDLNet_ResolveHost(&ip, NULL, SERVER_PORT) == -1) {
+	if (SDLNet_ResolveHost(&ip, NULL, _serverPort) == -1) {
 		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
 
@@ -166,6 +168,14 @@ bool LocalWebserver::isRunning() {
 	return result;
 }
 
+uint32 LocalWebserver::getPort() {
+#ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+	if (ConfMan.hasKey("local_server_port"))
+		return ConfMan.getInt("local_server_port");
+#endif
+	return DEFAULT_SERVER_PORT;
+}
+
 void LocalWebserver::handle() {
 	_handleMutex.lock();
 	int numready = SDLNet_CheckSockets(_set, 0);
@@ -241,7 +251,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 	IPaddress *ip = (IPaddress *)ipAddress;
 
 	// not resolved
-	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", SERVER_PORT);
+	_address = Common::String::format("http://127.0.0.1:%u/ (unresolved)", _serverPort);
 
 	// default way (might work everywhere, surely works on Windows)
 	const char *name = SDLNet_ResolveIP(ip);
@@ -249,13 +259,13 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 		warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	} else {
 		IPaddress localIp;
-		if (SDLNet_ResolveHost(&localIp, name, SERVER_PORT) == -1) {
+		if (SDLNet_ResolveHost(&localIp, name, _serverPort) == -1) {
 			warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 		} else {
 			_address = Common::String::format(
 				"http://%u.%u.%u.%u:%u/",
 				localIp.host & 0xFF, (localIp.host >> 8) & 0xFF, (localIp.host >> 16) & 0xFF, (localIp.host >> 24) & 0xFF,
-				SERVER_PORT
+				_serverPort
 			);
 		}
 	}
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 1185cda..3512583 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -50,7 +50,7 @@ namespace Networking {
 class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	static const uint32 FRAMES_PER_SECOND = 20;
 	static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
-	static const uint32 SERVER_PORT = 12345;
+	static const uint32 DEFAULT_SERVER_PORT = 12345;
 	static const uint32 MAX_CONNECTIONS = 10;
 
 	friend void localWebserverTimer(void *); //calls handle()
@@ -73,6 +73,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	uint32 _idlingFrames;
 	Common::Mutex _handleMutex;
 	Common::String _address;
+	uint32 _serverPort;
 
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
@@ -95,6 +96,7 @@ public:
 	Common::String getAddress();
 	IndexPageHandler &indexPageHandler();
 	bool isRunning();
+	static uint32 getPort();
 
 	static void setClientGetHandler(Client &client, Common::String response, long code = 200, const char *mimeType = nullptr);
 	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
diff --git a/gui/options.cpp b/gui/options.cpp
index ac548f5..cbcb42c 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -43,6 +43,7 @@
 #include "audio/mixer.h"
 #include "audio/fmopl.h"
 #include "widgets/scrollcontainer.h"
+#include "widgets/edittext.h"
 
 #ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
@@ -1317,6 +1318,10 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_runServerButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
 	_serverInfoLabel = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerInfoLabel", _("Not running"));
 
+	uint32 port = Networking::LocalWebserver::getPort();
+	_serverPortDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortDesc", _("Server's port:"), _("Which port is used by server"));
+	_serverPort = new EditTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortEditText", Common::String::format("%u", port), 0);
+
 	setupCloudTab();
 	_redrawCloudTab = false;
 #ifdef USE_SDL_NET
@@ -1489,7 +1494,17 @@ void GlobalOptionsDialog::close() {
 			}
 		}
 #endif
-
+#ifdef USE_SDL_NET
+#ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+		// save server's port
+		uint32 port = Networking::LocalWebserver::getPort();
+		if (_serverPort) {
+			uint64 contents = _serverPort->getEditString().asUint64();
+			if (contents != 0) port = contents;
+		}
+		ConfMan.setInt("local_server_port", port);
+#endif
+#endif
 	}
 	OptionsDialog::close();
 }
@@ -1647,6 +1662,17 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 #ifdef USE_SDL_NET
 	case kRunServerCmd:
 	{
+#ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+		// save server's port
+		uint32 port = Networking::LocalWebserver::getPort();
+		if (_serverPort) {
+			uint64 contents = _serverPort->getEditString().asUint64();
+			if (contents != 0) port = contents;
+		}
+		ConfMan.setInt("local_server_port", port);
+		ConfMan.flushToDisk();
+#endif
+
 		if (LocalServer.isRunning()) LocalServer.stopOnIdle();
 		else LocalServer.start();
 		break;
@@ -1787,13 +1813,19 @@ void GlobalOptionsDialog::setupCloudTab() {
 	//determine original widget's positions
 	int16 x, y;
 	uint16 w, h;
-	int serverButtonY, serverInfoY;
+	int serverButtonY, serverInfoY, serverPortDescY, serverPortY;
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RunServerButton", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.RunServerButton's position is undefined");
 	serverButtonY = y;
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerInfoLabel", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.ServerInfoLabel's position is undefined");
 	serverInfoY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerPortDesc", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.ServerPortDesc's position is undefined");
+	serverPortDescY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerPortEditText", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.ServerPortEditText's position is undefined");
+	serverPortY = y;
 
 	bool serverIsRunning = LocalServer.isRunning();
 		
@@ -1809,9 +1841,26 @@ void GlobalOptionsDialog::setupCloudTab() {
 		if (serverIsRunning) _serverInfoLabel->setLabel(LocalServer.getAddress());
 		else _serverInfoLabel->setLabel(_("Not running"));
 	}
+#ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+	if (_serverPortDesc) {
+		_serverPortDesc->setVisible(true);
+		_serverPortDesc->setPos(_serverPortDesc->getRelX(), serverLabelPosition + serverPortDescY - serverInfoY);
+		_serverPortDesc->setEnabled(!serverIsRunning);
+	}
+	if (_serverPort) {
+		_serverPort->setVisible(true);
+		_serverPort->setPos(_serverPort->getRelX(), serverLabelPosition + serverPortY - serverInfoY);
+		_serverPort->setEnabled(!serverIsRunning);
+	}
+#else
+	if (_serverPortDesc) _serverPortDesc->setVisible(false);
+	if (_serverPort) _serverPort->setVisible(false);
+#endif
 #else
 	if (_runServerButton) _runServerButton->setVisible(false);
 	if (_serverInfoLabel) _serverInfoLabel->setVisible(false);
+	if (_serverPortDesc) _serverPortDesc->setVisible(false);
+	if (_serverPort) _serverPort->setVisible(false);
 #endif
 }
 #endif
diff --git a/gui/options.h b/gui/options.h
index a7e98ad..ea8b252 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -45,6 +45,7 @@ namespace GUI {
 class LauncherDialog;
 
 class CheckboxWidget;
+class EditTextWidget;
 class PopUpWidget;
 class SliderWidget;
 class StaticTextWidget;
@@ -267,6 +268,8 @@ protected:
 	ButtonWidget	 *_storageDownloadButton;
 	ButtonWidget	 *_runServerButton;
 	StaticTextWidget *_serverInfoLabel;
+	StaticTextWidget *_serverPortDesc;
+	EditTextWidget *_serverPort;
 	bool _redrawCloudTab;
 #ifdef USE_SDL_NET
 	bool _serverWasRunning;
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 545b3fe..1e3d09d 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -583,6 +583,15 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'ServerPortDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'ServerPortEditText'
+						width = '70'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 27a3ecd..2ae008a 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -596,6 +596,17 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'ServerPortDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'ServerPortEditText'
+						width = '60'
+						height = '16'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 5f4ef6f..03fa58a 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -597,6 +597,15 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'ServerPortDesc'
+						type = 'OptionsLabel'
+				/>
+				<widget name = 'ServerPortEditText'
+						width = '70'
+						height = 'Globals.Line.Height'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 24476e3..b25edcb 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -594,6 +594,17 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
+				<widget name = 'ServerPortDesc'
+						width = '80'
+						height = 'Globals.Line.Height'
+						textalign = 'right'
+				/>
+				<widget name = 'ServerPortEditText'
+						width = '60'
+						height = '16'
+				/>
+			</layout>
 		</layout>
 	</dialog>
 


Commit: 85f4c69fc981ae1d3807ffb0280b59d627a92ebc
    https://github.com/scummvm/scummvm/commit/85f4c69fc981ae1d3807ffb0280b59d627a92ebc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update StorageWizardDialog

It now hides code fields not just when built with SDL_Net, but also when
LocalWebserver's using default port.

So that's why NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE is defined
in localwebserver.h now.

Changed paths:
    backends/networking/sdl_net/localwebserver.h
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 3512583..107c21e 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -47,10 +47,11 @@ typedef struct _TCPsocket *TCPsocket;
 
 namespace Networking {
 
+#define NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+
 class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	static const uint32 FRAMES_PER_SECOND = 20;
 	static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
-	static const uint32 DEFAULT_SERVER_PORT = 12345;
 	static const uint32 MAX_CONNECTIONS = 10;
 
 	friend void localWebserverTimer(void *); //calls handle()
@@ -84,6 +85,8 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
         void resolveAddress(void *ipAddress);
 	
 public:
+	static const uint32 DEFAULT_SERVER_PORT = 12345;
+
 	LocalWebserver();
 	virtual ~LocalWebserver();
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index a8574ab..299da11 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -61,15 +61,15 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 
-#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
+	if (couldUseLocalServer()) {
+		// 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);
+	}
 }
 
 void StorageWizardDialog::open() {
@@ -97,18 +97,18 @@ void StorageWizardDialog::open() {
 		}
 	}
 
-#ifdef USE_SDL_NET
-	_stopServerOnClose = !LocalServer.isRunning();
-	LocalServer.start();
-	LocalServer.indexPageHandler().setTarget(this);
-#endif
+	if (couldUseLocalServer()) {
+		_stopServerOnClose = !LocalServer.isRunning();
+		LocalServer.start();
+		LocalServer.indexPageHandler().setTarget(this);
+	}
 }
 
 void StorageWizardDialog::close() {
-#ifdef USE_SDL_NET
-	if (_stopServerOnClose) LocalServer.stopOnIdle();
-	LocalServer.indexPageHandler().setTarget(nullptr);
-#endif
+	if (couldUseLocalServer()) {
+		if (_stopServerOnClose) LocalServer.stopOnIdle();
+		LocalServer.indexPageHandler().setTarget(nullptr);
+	}
 	Dialog::close();
 }
 
@@ -182,12 +182,10 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		close();
 		break;
 	}
-#ifdef USE_SDL_NET
 	case kStorageCodePassedCmd:
 		CloudMan.connectStorage(_storageId, LocalServer.indexPageHandler().code());
 		_close = true;		
 		break;
-#endif
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
@@ -210,12 +208,18 @@ Common::String StorageWizardDialog::getUrl() const {
 	case Cloud::kStorageGoogleDriveId: url += "gd"; break;
 	case Cloud::kStorageBoxId: url += "bx"; break;
 	}
-#ifdef USE_SDL_NET
-	url += "s";
-#endif
+	
+	if (couldUseLocalServer()) url += "s";
 	return url;
 }
 
+bool StorageWizardDialog::couldUseLocalServer() const {
+#ifdef USE_SDL_NET
+	return Networking::LocalWebserver::getPort() == Networking::LocalWebserver::DEFAULT_SERVER_PORT;
+#else
+	return false;
+#endif
+}
 
 int StorageWizardDialog::decodeHashchar(char c) {
 	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 6b00d60..26e8cd4 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -51,6 +51,9 @@ class StorageWizardDialog : public Dialog {
 	/** Return short scummvm.org URL for user to navigate to. */
 	Common::String getUrl() const;
 
+	/** Return whether fields should be used or not. */
+	bool couldUseLocalServer() const;
+
 	/**
 	 * Return the value corresponding to the given character.
 	 *


Commit: 52503a2713432aab7663f88fd0be07055ee9125d
    https://github.com/scummvm/scummvm/commit/52503a2713432aab7663f88fd0be07055ee9125d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add "Clear port" button in Cloud tab

Changed paths:
    gui/options.cpp
    gui/options.h
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/options.cpp b/gui/options.cpp
index cbcb42c..abc97aa 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -102,7 +102,8 @@ enum {
 	kRefreshStorageCmd = 'rfst',
 	kDownloadStorageCmd = 'dlst',
 	kRunServerCmd = 'rnsv',
-	kCloudTabContainerReflowCmd = 'ctcr'
+	kCloudTabContainerReflowCmd = 'ctcr',
+	kServerPortClearCmd = 'spcl'
 };
 #endif
 
@@ -1321,6 +1322,7 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	uint32 port = Networking::LocalWebserver::getPort();
 	_serverPortDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortDesc", _("Server's port:"), _("Which port is used by server"));
 	_serverPort = new EditTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortEditText", Common::String::format("%u", port), 0);
+	_serverPortClearButton = addClearButton(container, "GlobalOptions_Cloud_Container.ServerPortClearButton", kServerPortClearCmd);
 
 	setupCloudTab();
 	_redrawCloudTab = false;
@@ -1631,6 +1633,16 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 	case kConfigureStorageCmd:
 	{
+#ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
+		// save server's port
+		uint32 port = Networking::LocalWebserver::getPort();
+		if (_serverPort) {
+			uint64 contents = _serverPort->getEditString().asUint64();
+			if (contents != 0) port = contents;
+		}
+		ConfMan.setInt("local_server_port", port);
+		ConfMan.flushToDisk();
+#endif
 		StorageWizardDialog dialog(_selectedStorageIndex);
 		dialog.runModal();
 		//update container's scrollbar
@@ -1677,6 +1689,14 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		else LocalServer.start();
 		break;
 	}
+
+	case kServerPortClearCmd: {
+		if (_serverPort) {
+			_serverPort->setEditString(Common::String::format("%u", Networking::LocalWebserver::DEFAULT_SERVER_PORT));
+		}
+		draw();
+		break;
+	}
 #endif
 #ifdef GUI_ENABLE_KEYSDIALOG
 	case kChooseKeyMappingCmd:
@@ -1813,7 +1833,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	//determine original widget's positions
 	int16 x, y;
 	uint16 w, h;
-	int serverButtonY, serverInfoY, serverPortDescY, serverPortY;
+	int serverButtonY, serverInfoY, serverPortDescY, serverPortY, serverPortClearButtonY;
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RunServerButton", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.RunServerButton's position is undefined");
 	serverButtonY = y;
@@ -1826,6 +1846,9 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerPortEditText", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.ServerPortEditText's position is undefined");
 	serverPortY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerPortClearButton", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.ServerPortClearButton's position is undefined");
+	serverPortClearButtonY = y;
 
 	bool serverIsRunning = LocalServer.isRunning();
 		
@@ -1852,15 +1875,22 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_serverPort->setPos(_serverPort->getRelX(), serverLabelPosition + serverPortY - serverInfoY);
 		_serverPort->setEnabled(!serverIsRunning);
 	}
+	if (_serverPortClearButton) {
+		_serverPortClearButton->setVisible(true);
+		_serverPortClearButton->setPos(_serverPortClearButton->getRelX(), serverLabelPosition + serverPortClearButtonY - serverInfoY);
+		_serverPortClearButton->setEnabled(!serverIsRunning);
+	}
 #else
 	if (_serverPortDesc) _serverPortDesc->setVisible(false);
 	if (_serverPort) _serverPort->setVisible(false);
+	if (_serverPortClearButton) _serverPortClearButton->setVisible(false);
 #endif
 #else
 	if (_runServerButton) _runServerButton->setVisible(false);
 	if (_serverInfoLabel) _serverInfoLabel->setVisible(false);
 	if (_serverPortDesc) _serverPortDesc->setVisible(false);
 	if (_serverPort) _serverPort->setVisible(false);
+	if (_serverPortClearButton) _serverPortClearButton->setVisible(false);
 #endif
 }
 #endif
diff --git a/gui/options.h b/gui/options.h
index ea8b252..42e8ffb 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -270,6 +270,7 @@ protected:
 	StaticTextWidget *_serverInfoLabel;
 	StaticTextWidget *_serverPortDesc;
 	EditTextWidget *_serverPort;
+	ButtonWidget	 *_serverPortClearButton;
 	bool _redrawCloudTab;
 #ifdef USE_SDL_NET
 	bool _serverWasRunning;
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 1e3d09d..b9c2781 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -591,6 +591,10 @@
 						width = '70'
 						height = 'Globals.Line.Height'
 				/>
+				<widget name = 'ServerPortClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 2ae008a..7c062f7 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -606,6 +606,10 @@
 						width = '60'
 						height = '16'
 				/>
+				<widget name = 'ServerPortClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 03fa58a..e0ea868 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -605,6 +605,10 @@
 						width = '70'
 						height = 'Globals.Line.Height'
 				/>
+				<widget name = 'ServerPortClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index b25edcb..ceb43bb 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -604,6 +604,10 @@
 						width = '60'
 						height = '16'
 				/>
+				<widget name = 'ServerPortClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
 			</layout>
 		</layout>
 	</dialog>


Commit: c7fe842f9acf0eeec5453170699ed10394ef0fa5
    https://github.com/scummvm/scummvm/commit/c7fe842f9acf0eeec5453170699ed10394ef0fa5
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix texts clipping

If it was completely clipped out (empty rectangle), it was drawing the
whole text ("empty means no clipping"), so I had to detect such cases
and change textArea to one small pixel.

Changed paths:
    gui/ThemeEngine.cpp



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index e0563da..78353a0 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -1088,7 +1088,10 @@ void ThemeEngine::queueDDTextClip(TextData type, TextColor color, const Common::
 	area.clip(_screen.w, _screen.h);
 	Common::Rect textArea = drawableTextArea;
 	if (textArea.isEmpty()) textArea = clippingArea;
-	else textArea.clip(clippingArea);
+	else {
+		textArea.clip(clippingArea);
+		if (textArea.isEmpty()) textArea = Common::Rect(0, 0, 1, 1); // one small pixel should be invisible enough
+	}
 
 	ThemeItemTextData *q = new ThemeItemTextData(this, _texts[type], _textColors[color], area, textArea, text, alignH, alignV, ellipsis, restoreBg, deltax);
 


Commit: a449ddce15c512e809b9aedad334db6b52628e61
    https://github.com/scummvm/scummvm/commit/a449ddce15c512e809b9aedad334db6b52628e61
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Cppcheck warnings

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index ed48ec5..3e1c94f 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -240,6 +240,7 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 	// in these constructors Storages request token using the passed code
 	// when the token is received, they call replaceStorage()
 	// or removeStorage(), if some error occurred
+	// thus, no memory leak happens
 }
 
 void CloudManager::printBool(Storage::BoolResponse response) const {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 4171af0..d7aea01 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -135,7 +135,7 @@ Networking::Request *DropboxStorage::streamFileById(Common::String path, Network
 
 	Networking::NetworkReadStreamResponse response = request->execute();
 	if (callback) (*callback)(response);
-	return response.request;
+	return response.request; // no leak here, response.request == request
 }
 
 Networking::Request *DropboxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index a3d7dd0..ee6f665 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -137,10 +137,10 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 			//debug("%s", json->stringify(true).c_str());
 
 			if (object.contains("error") || object.contains("error_summary")) {
-				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
-				delete json;
+				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());				
 				error.response = json->stringify(true);
 				finishError(error);
+				delete json;
 				return;
 			}
 
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 73a7fbf..dfff46c 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -266,9 +266,9 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 
 			if (object.contains("error")) {
 				warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
-				delete json;
 				error.response = json->stringify(true);
 				finishError(error);
+				delete json;
 				return;
 			}
 
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 5b86c6c..500f875 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -127,9 +127,9 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 
 			if (object.contains("error")) {
 				warning("OneDrive returned error: %s", json->stringify(true).c_str());
-				delete json;
 				error.response = json->stringify(true);
 				finishError(error);
+				delete json;
 				return;
 			}
 


Commit: b180c73675846f45abab2190b39e0b9d0d6addbf
    https://github.com/scummvm/scummvm/commit/b180c73675846f45abab2190b39e0b9d0d6addbf
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Do some refactoring/cleanup

Nothing really major.

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/downloadrequest.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/id/idstorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    base/main.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 8162af2..c31c72b 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -278,12 +278,12 @@ BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
-		return 0;
+		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
 		warning("No refresh_token found");
-		return 0;
+		return nullptr;
 	}
 
 	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 826d6be..1373c2f 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -111,7 +111,7 @@ public:
 	 */
 	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
-	Common::String accessToken() { return _token; }
+	Common::String accessToken() const { return _token; }
 };
 
 } // End of namespace Box
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index 9dfbef5..f100e18 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -69,7 +69,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		long httpCode = -1;
 		if (_stream) {
 			httpCode = _stream->httpResponseCode();
-			debug("code %ld", httpCode);
+			debug(9, "code %ld", httpCode);
 		}
 
 		bool irrecoverable = true;
@@ -77,12 +77,12 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (result.contains("code")) {
 			code = result.getVal("code")->asString();
-			debug("code = %s", code.c_str());			
+			debug(9, "code = %s", code.c_str());			
 		}
 
 		if (result.contains("message")) {
 			message = result.getVal("message")->asString();
-			debug("message = %s", message.c_str());
+			debug(9, "message = %s", message.c_str());
 		}
 
 		//TODO: decide when token refreshment will help
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index c94494e..c81a3ab 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -29,7 +29,6 @@
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
-#include "common/debug.h"
 
 namespace Cloud {
 namespace Box {
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 3e1c94f..55dbb2b 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -25,7 +25,6 @@
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
-#include "common/debug.h"
 #include "common/translation.h"
 #include "common/config-manager.h"
 #include "common/str.h"
@@ -243,10 +242,6 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 	// thus, no memory leak happens
 }
 
-void CloudManager::printBool(Storage::BoolResponse response) const {
-	debug("bool = %s", (response.value ? "true" : "false"));
-}
-
 Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->listDirectory(path, callback, errorCallback, recursive);
@@ -295,12 +290,7 @@ SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networ
 	return nullptr;
 }
 
-void CloudManager::testFeature() {
-	//Storage *storage = getCurrentStorage();
-	//if (storage) storage->info(nullptr, nullptr);
-}
-
-bool CloudManager::isWorking() {
+bool CloudManager::isWorking() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->isWorking();
 	return false;
@@ -308,95 +298,95 @@ bool CloudManager::isWorking() {
 
 ///// SavesSyncRequest-related /////
 
-bool CloudManager::isSyncing() {
+bool CloudManager::isSyncing() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->isSyncing();
 	return false;
 }
 
-double CloudManager::getSyncDownloadingProgress() {
+double CloudManager::getSyncDownloadingProgress() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getSyncDownloadingProgress();
 	return 1;
 }
 
-double CloudManager::getSyncProgress() {
+double CloudManager::getSyncProgress() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getSyncProgress();
 	return 1;
 }
 
-Common::Array<Common::String> CloudManager::getSyncingFiles() {
+Common::Array<Common::String> CloudManager::getSyncingFiles() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getSyncingFiles();
 	return Common::Array<Common::String>();
 }
 
-void CloudManager::cancelSync() {
+void CloudManager::cancelSync() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->cancelSync();
 }
 
-void CloudManager::setSyncTarget(GUI::CommandReceiver *target) {
+void CloudManager::setSyncTarget(GUI::CommandReceiver *target) const {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->setSyncTarget(target);
 }
 
 ///// DownloadFolderRequest-related /////
 
-bool CloudManager::startDownload(Common::String remotePath, Common::String localPath) {
+bool CloudManager::startDownload(Common::String remotePath, Common::String localPath) const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->startDownload(remotePath, localPath);
 	return false;
 }
 
-void CloudManager::cancelDownload() {
+void CloudManager::cancelDownload() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->cancelDownload();
 }
 
-void CloudManager::setDownloadTarget(GUI::CommandReceiver *target) {
+void CloudManager::setDownloadTarget(GUI::CommandReceiver *target) const {
 	Storage *storage = getCurrentStorage();
 	if (storage) storage->setDownloadTarget(target);
 }
 
-bool CloudManager::isDownloading() {
+bool CloudManager::isDownloading() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->isDownloading();
 	return false;
 }
 
-double CloudManager::getDownloadingProgress() {
+double CloudManager::getDownloadingProgress() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadingProgress();
 	return 1;
 }
 
-uint64 CloudManager::getDownloadBytesNumber() {
+uint64 CloudManager::getDownloadBytesNumber() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadBytesNumber();
 	return 0;
 }
 
-uint64 CloudManager::getDownloadTotalBytesNumber() {
+uint64 CloudManager::getDownloadTotalBytesNumber() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadTotalBytesNumber();
 	return 0;
 }
 
-uint64 CloudManager::getDownloadSpeed() {
+uint64 CloudManager::getDownloadSpeed() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadSpeed();
 	return 0;
 }
 
-Common::String CloudManager::getDownloadRemoteDirectory() {
+Common::String CloudManager::getDownloadRemoteDirectory() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadRemoteDirectory();
 	return "";
 }
 
-Common::String CloudManager::getDownloadLocalDirectory() {
+Common::String CloudManager::getDownloadLocalDirectory() const {
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->getDownloadLocalDirectory();
 	return "";
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 4442b9b..2941ea6 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -61,8 +61,6 @@ class CloudManager : public Common::Singleton<CloudManager> {
 	Storage *_activeStorage;
 	Common::Array<Storage *> _storagesToRemove;
 
-	void printBool(Cloud::Storage::BoolResponse response) const;
-
 	void loadStorage();
 
 	Common::String getStorageConfigName(uint32 index) const;
@@ -206,65 +204,60 @@ public:
 	 */
 	SavesSyncRequest *syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
 
-	/**
-	 * Starts feature testing (the one I'm working on currently). (Temporary)
-	 */
-	void testFeature();
-
 	/** Returns whether there are any requests running. */
-	bool isWorking();
+	bool isWorking() const;
 
 	///// SavesSyncRequest-related /////
 
 	/** Returns whether there is a SavesSyncRequest running. */
-	bool isSyncing();
+	bool isSyncing() const;
 
 	/** Returns a number in [0, 1] range which represents current sync downloading progress (1 = complete). */
-	double getSyncDownloadingProgress();
+	double getSyncDownloadingProgress() const;
 
 	/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
-	double getSyncProgress();
+	double getSyncProgress() const;
 
 	/** Returns an array of saves names which are not yet synced (thus cannot be used). */
-	Common::Array<Common::String> getSyncingFiles();
+	Common::Array<Common::String> getSyncingFiles() const;
 
 	/** Cancels running sync. */
-	void cancelSync();
+	void cancelSync() const;
 
 	/** Sets SavesSyncRequest's target to given CommandReceiver. */
-	void setSyncTarget(GUI::CommandReceiver *target);
+	void setSyncTarget(GUI::CommandReceiver *target) const;
 
 	///// DownloadFolderRequest-related /////
 
 	/** Starts a folder download. */
-	bool startDownload(Common::String remotePath, Common::String localPath);
+	bool startDownload(Common::String remotePath, Common::String localPath) const;
 
 	/** Cancels running download. */
-	void cancelDownload();
+	void cancelDownload() const;
 
 	/** Sets FolderDownloadRequest's target to given CommandReceiver. */
-	void setDownloadTarget(GUI::CommandReceiver *target);
+	void setDownloadTarget(GUI::CommandReceiver *target) const;
 
 	/** Returns whether there is a FolderDownloadRequest running. */
-	bool isDownloading();
+	bool isDownloading() const;
 
 	/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
-	double getDownloadingProgress();
+	double getDownloadingProgress() const;
 
 	/** Returns a number of bytes that is downloaded in current download progress. */
-	uint64 getDownloadBytesNumber();
+	uint64 getDownloadBytesNumber() const;
 
 	/** Returns a total number of bytes to be downloaded in current download progress. */
-	uint64 getDownloadTotalBytesNumber();
+	uint64 getDownloadTotalBytesNumber() const;
 
 	/** Returns download speed of current download progress. */
-	uint64 getDownloadSpeed();
+	uint64 getDownloadSpeed() const;
 
 	/** Returns remote directory path. */
-	virtual Common::String getDownloadRemoteDirectory();
+	Common::String getDownloadRemoteDirectory() const;
 
 	/** Returns local directory path. */
-	virtual Common::String getDownloadLocalDirectory();
+	Common::String getDownloadLocalDirectory() const;
 };
 
 /** Shortcut for accessing the connection manager. */
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 5efb87e..e8c410a 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -22,7 +22,6 @@
 
 #include "backends/cloud/downloadrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
-#include "common/debug.h"
 #include "common/textconsole.h"
 
 namespace Cloud {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d7aea01..7e3521f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -155,12 +155,12 @@ DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
-		return 0;
+		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
 		warning("No user_id found");
-		return 0;
+		return nullptr;
 	}
 
 	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index ee6f665..29b7f60 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -118,7 +118,7 @@ void DropboxUploadRequest::uploadNextPart() {
 }
 
 void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
-	debug("partUploadedCallback");
+	debug(9, "partUploadedCallback");
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index d506d80..9e05202 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -132,7 +132,7 @@ void FolderDownloadRequest::downloadNextFile() {
 		else
 			localPath = _localDirectoryPath + "/" + localPath;
 	}
-	debug("%s -> %s", remotePath.c_str(), localPath.c_str());
+	debug(9, "%s -> %s", remotePath.c_str(), localPath.c_str());
 	_workingRequest = _storage->downloadById(
 		_currentFile.id(), localPath,
 		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback),
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index 9d8dea4..08fa193 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
 
 #include "backends/networking/curl/request.h"
-#include "backends/networking/curl/networkreadstream.h"
 #include "backends/cloud/storage.h"
 #include "gui/object.h"
 
@@ -69,10 +68,10 @@ public:
 	uint64 getDownloadSpeed() const;
 
 	/** Returns remote directory path. */
-	Common::String getRemotePath() { return _remoteDirectoryPath; }
+	Common::String getRemotePath() const { return _remoteDirectoryPath; }
 
 	/** Returns local directory path. */
-	Common::String getLocalPath() { return _localDirectoryPath; }
+	Common::String getLocalPath() const { return _localDirectoryPath; }
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index c426409..77ac898 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -229,10 +229,10 @@ Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Netwo
 }
 
 void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
-	debug("\nuser info:");
-	debug("\tname: %s", response.value.name().c_str());
-	debug("\temail: %s", response.value.email().c_str());
-	debug("\tdisk usage: %llu/%llu", response.value.used(), response.value.available());
+	debug(9, "\nuser info:");
+	debug(9, "\tname: %s", response.value.name().c_str());
+	debug(9, "\temail: %s", response.value.email().c_str());
+	debug(9, "\tdisk usage: %llu/%llu", response.value.used(), response.value.available());
 }
 
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
@@ -273,12 +273,12 @@ GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix)
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
-		return 0;
+		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
 		warning("No refresh_token found");
-		return 0;
+		return nullptr;
 	}
 
 	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index eee4de9..4c164dd 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
 
 #include "backends/cloud/id/idstorage.h"
-#include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
@@ -87,9 +86,6 @@ public:
 	virtual Networking::Request *streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns the StorageInfo struct. */
@@ -113,7 +109,7 @@ public:
 	 */
 	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
-	Common::String accessToken() { return _token; }
+	Common::String accessToken() const { return _token; }
 };
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 3dfb843..9e26e62 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -67,7 +67,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request		
 		if (_stream) {
-			debug("code %ld", _stream->httpResponseCode());
+			debug(9, "code %ld", _stream->httpResponseCode());
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -77,12 +77,12 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String message;
 		if (error.contains("code") && error.getVal("code")->isIntegerNumber()) {
 			code = error.getVal("code")->asIntegerNumber();
-			debug("code = %u", code);
+			debug(9, "code = %u", code);
 		}
 
 		if (error.contains("message")) {
 			message = error.getVal("message")->asString();
-			debug("message = %s", message.c_str());
+			debug(9, "message = %s", message.c_str());
 		}
 
 		if (code == 401 || message == "Invalid Credentials")
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index dfff46c..b63e989 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -28,7 +28,6 @@
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
-#include "common/debug.h"
 #include "googledrivetokenrefresher.h"
 
 namespace Cloud {
@@ -201,7 +200,6 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
 	if (size != 0) request->setBuffer(buffer, size);
 
-	//request->addHeader(Common::String::format("Content-Length: %u", size));
 	if (_uploadUrl != "") {
 		if (_contentsStream->pos() == 0)
 			request->addHeader(Common::String::format("Content-Length: 0"));
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index 28f8805..5b0f9db 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -36,26 +36,26 @@ namespace Id {
 IdStorage::~IdStorage() {}
 
 void IdStorage::printFiles(FileArrayResponse response) {
-	debug("files:");
+	debug(9, "files:");
 	Common::Array<StorageFile> &files = response.value;
 	for (uint32 i = 0; i < files.size(); ++i) {
-		debug("\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
-		debug("\t%s", files[i].path().c_str());
-		debug("\t%s", files[i].id().c_str());
-		debug(" ");
+		debug(9, "\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
+		debug(9, "\t%s", files[i].path().c_str());
+		debug(9, "\t%s", files[i].id().c_str());
+		debug(9, " ");
 	}
 }
 
 void IdStorage::printBool(BoolResponse response) {
-	debug("bool: %s", response.value ? "true" : "false");
+	debug(9, "bool: %s", response.value ? "true" : "false");
 }
 
 void IdStorage::printFile(UploadResponse response) {
-	debug("\nuploaded file info:");
-	debug("\tid: %s", response.value.path().c_str());
-	debug("\tname: %s", response.value.name().c_str());
-	debug("\tsize: %u", response.value.size());
-	debug("\ttimestamp: %u", response.value.timestamp());
+	debug(9, "\nuploaded file info:");
+	debug(9, "\tid: %s", response.value.path().c_str());
+	debug(9, "\tname: %s", response.value.name().c_str());
+	debug(9, "\tsize: %u", response.value.size());
+	debug(9, "\ttimestamp: %u", response.value.timestamp());
 }
 
 Storage::ListDirectoryCallback IdStorage::getPrintFilesCallback() {
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index e916594..39213e8 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -189,7 +189,7 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
 	if (!response.value) {
 		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 		return;
 	}
 
@@ -199,12 +199,12 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(
 				response.request,
-				new Networking::NetworkReadStream(url, 0, "")
+				new Networking::NetworkReadStream(url, nullptr, "")
 			));
 	} else {
 		warning("downloadUrl not found in passed JSON");
 		debug("%s", response.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0));
+		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 	}
 	delete response.value;
 }
@@ -244,17 +244,17 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
 		warning("No access_token found");
-		return 0;
+		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
 		warning("No user_id found");
-		return 0;
+		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
 		warning("No refresh_token found");
-		return 0;
+		return nullptr;
 	}
 
 	Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 8ceaaf1..60817e6 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -104,7 +104,7 @@ public:
 	 */
 	void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
 
-	Common::String accessToken() { return _token; }
+	Common::String accessToken() const { return _token; }
 };
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 04e155c..9afea3d 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -67,7 +67,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request		
 		if (_stream) {
-			debug("code %ld", _stream->httpResponseCode());
+			debug(9, "code %ld", _stream->httpResponseCode());
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -76,12 +76,12 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (error.contains("code")) {
 			code = error.getVal("code")->asString();
-			debug("code = %s", code.c_str());			
+			debug(9, "code = %s", code.c_str());			
 		}
 
 		if (error.contains("message")) {
 			message = error.getVal("message")->asString();
-			debug("message = %s", message.c_str());
+			debug(9, "message = %s", message.c_str());
 		}
 
 		//determine whether token refreshing would help in this situation
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 500f875..569809d 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -28,7 +28,6 @@
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
-#include "common/debug.h"
 #include "onedrivetokenrefresher.h"
 
 namespace Cloud {
@@ -135,7 +134,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 
 			if (object.contains("id") && object.contains("name")) {
 				//finished				
-				Common::String path = _savePath; //object.getVal("name")->asString();; //object.getVal("id")->asString();
+				Common::String path = _savePath;
 				uint32 size = object.getVal("size")->asIntegerNumber();
 				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
 				finishUpload(StorageFile(path, size, timestamp, false));
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 32b22ed..014e6a0 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -117,17 +117,15 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 		if (i->_value) _filesToUpload.push_back(i->_key);
 	}
 
-	///////
-	debug("\ndownload files:");
+	debug(9, "\ndownload files:");
 	for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
-		debug("%s", _filesToDownload[i].name().c_str());
+		debug(9, "%s", _filesToDownload[i].name().c_str());
 	}
-	debug("\nupload files:");
+	debug(9, "\nupload files:");
 	for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
-		debug("%s", _filesToUpload[i].c_str());
+		debug(9, "%s", _filesToUpload[i].c_str());
 	}
 	_totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
-	///////
 
 	//start downloading files
 	downloadNextFile();
@@ -162,13 +160,11 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 						}
 					}
 				}
-
-				//TODO: Google Drive-related JSON error
 			}
 			delete value;
 		}
 
-		//Google Drive-related ScummVM-based error
+		//Google Drive and Box-related ScummVM-based error
 		if (error.response.contains("subdirectory not found")) {
 			irrecoverable = false; //base "/ScummVM/" folder not found
 		} else if (error.response.contains("no such file found in its parent directory")) {
@@ -184,7 +180,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 	//we're lucky - user just lacks his "/cloud/" folder - let's create one
 	Common::String dir = _storage->savesDirectoryPath();
 	if (dir.lastChar() == '/') dir.deleteLastChar();
-	debug("creating %s", dir.c_str());
+	debug(9, "creating %s", dir.c_str());
 	_workingRequest = _storage->createDirectory(dir,
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
@@ -227,10 +223,8 @@ void SavesSyncRequest::downloadNextFile() {
 	_filesToDownload.pop_back();
 
 	sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
-
-	///////
-	debug("downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
-	///////
+	
+	debug(9, "downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
@@ -275,10 +269,8 @@ void SavesSyncRequest::uploadNextFile() {
 
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
-	
-	///////
-	debug("uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));
-	///////
+		
+	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));	
 	if (_storage->uploadStreamSupported()) {
 		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
@@ -318,7 +310,7 @@ void SavesSyncRequest::handle() {}
 
 void SavesSyncRequest::restart() { start(); }
 
-double SavesSyncRequest::getDownloadingProgress() {
+double SavesSyncRequest::getDownloadingProgress() const {
 	if (_totalFilesToHandle == 0) {
 		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
 		return 0; //directory not listed yet
@@ -331,7 +323,7 @@ double SavesSyncRequest::getDownloadingProgress() {
 	return (double)(totalFilesToDownload - filesLeftToDownload) / (double)(totalFilesToDownload);
 }
 
-double SavesSyncRequest::getProgress() {
+double SavesSyncRequest::getProgress() const {
 	if (_totalFilesToHandle == 0) {
 		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
 		return 0; //directory not listed yet
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index d1a9d4a4..304c444 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -66,10 +66,10 @@ public:
 	virtual void restart();
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
-	double getDownloadingProgress();
+	double getDownloadingProgress() const;
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
-	double getProgress();
+	double getProgress() const;
 
 	/** Returns an array of saves names which are not downloaded yet. */
 	Common::Array<Common::String> getFilesToDownload();
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index d4548b8..100d72b 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -49,7 +49,7 @@ void Storage::printErrorResponse(Networking::ErrorResponse error) {
 Networking::Request *Storage::addRequest(Networking::Request *request) {
 	_runningRequestsMutex.lock();
 	++_runningRequestsCount;
-	if (_runningRequestsCount == 1) debug("Storage is working now");
+	if (_runningRequestsCount == 1) debug(9, "Storage is working now");
 	_runningRequestsMutex.unlock();
 	return ConnMan.addRequest(request, new Common::Callback<Storage, Networking::Request *>(this, &Storage::requestFinishedCallback));
 }
@@ -62,7 +62,7 @@ void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer
 		_savesSyncRequest = nullptr;
 	--_runningRequestsCount;
 	if (_syncRestartRequestsed) restartSync = true;
-	if (_runningRequestsCount == 0 && !restartSync) debug("Storage is not working now");
+	if (_runningRequestsCount == 0 && !restartSync) debug(9, "Storage is not working now");
 	_runningRequestsMutex.unlock();
 
 	if (restartSync)
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index b33d69e..eaa6e9c 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -62,8 +62,12 @@ protected:
 	/** Keeps track of running requests. */
 	uint32 _runningRequestsCount;
 	Common::Mutex _runningRequestsMutex;
+
+	/** SavesSyncRequest-related */
 	SavesSyncRequest *_savesSyncRequest;
 	bool _syncRestartRequestsed;
+
+	/** FolderDownloadRequest-related */
 	FolderDownloadRequest *_downloadFolderRequest;
 
 	/** Returns default error callback (printErrorResponse). */
@@ -122,7 +126,7 @@ public:
 	/** Returns ListDirectoryResponse with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
 	
-	/** Returns UploadStatus struct with info about uploaded file. */
+	/** Returns StorageFile with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
@@ -147,8 +151,8 @@ public:
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
 	/**
-	 * Return the StorageInfo struct via <callback>.
-	 * Call the <errorCallback> if failed to get information.
+	 * Returns the StorageInfo struct via <callback>.
+	 * Calls the <errorCallback> if failed to get information.
 	 *
 	 * @note on success Storage should also call
 	 *	     CloudMan.setStorageUsername().
diff --git a/base/main.cpp b/base/main.cpp
index dd2c3f5..6b6868b 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -486,7 +486,6 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #ifdef USE_LIBCURL
 	CloudMan.init();
 	CloudMan.syncSaves();
-	CloudMan.testFeature(); //TODO: remove later
 #endif
 
 	// Unless a game was specified, show the launcher dialog


Commit: 01161ae7ddbc5f147dd9e71991eb2f1a1c9a7b06
    https://github.com/scummvm/scummvm/commit/01161ae7ddbc5f147dd9e71991eb2f1a1c9a7b06
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Do some refactoring/cleanup in Networking

Changed paths:
    backends/networking/browser/openurl.h
    backends/networking/connection/islimited.h
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/sdl_net/client.h
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/sdl_net/handlers/indexpagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.h
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/sdl_net/reader.h
    backends/networking/sdl_net/uploadfileclienthandler.h



diff --git a/backends/networking/browser/openurl.h b/backends/networking/browser/openurl.h
index fe2f452..15b4bf3 100644
--- a/backends/networking/browser/openurl.h
+++ b/backends/networking/browser/openurl.h
@@ -28,6 +28,11 @@
 namespace Networking {
 namespace Browser {
 
+/**
+ * Opens URL in default browser (if available on the target system).
+ *
+ * Returns true on success.
+ */
 bool openUrl(const Common::String &url);
 
 } // End of namespace Browser
diff --git a/backends/networking/connection/islimited.h b/backends/networking/connection/islimited.h
index 69be267..b23d31d 100644
--- a/backends/networking/connection/islimited.h
+++ b/backends/networking/connection/islimited.h
@@ -26,6 +26,11 @@
 namespace Networking {
 namespace Connection {
 
+/**
+* Returns whether connection's limited (if available on the target system).
+*
+* Returns true if connection seems limited.
+*/
 bool isLimited();
 
 } // End of namespace Connection
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index b553e8f..6affad7 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -63,7 +63,7 @@ ConnectionManager::~ConnectionManager() {
 	_handleMutex.unlock();
 }
 
-void ConnectionManager::registerEasyHandle(CURL *easy) {
+void ConnectionManager::registerEasyHandle(CURL *easy) const {
 	curl_multi_add_handle(_multi, easy);
 }
 
@@ -80,7 +80,7 @@ void ConnectionManager::showCloudDisabledIcon() {
 	startTimer();
 }
 
-Common::String ConnectionManager::urlEncode(Common::String s) {
+Common::String ConnectionManager::urlEncode(Common::String s) const {
 	if (!_multi) return "";
 	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
 	if (output) {
@@ -111,7 +111,7 @@ void ConnectionManager::startTimer(int interval) {
 }
 
 void ConnectionManager::stopTimer() {
-	debug("timer stopped");
+	debug(9, "timer stopped");
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(connectionsThread);
 	_timerStarted = false;
@@ -146,7 +146,7 @@ void ConnectionManager::interateRequests() {
 	_addedRequestsMutex.unlock();
 
 	//call handle() of all running requests (so they can do their work)
-	debug("handling %d request(s)", _requests.size());
+	debug(9, "handling %d request(s)", _requests.size());
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
 		Request *request = i->request;		
 		if (request) {
@@ -182,7 +182,7 @@ void ConnectionManager::processTransfers() {
 		if (stream) stream->finished();
 
 		if (curlMsg->msg == CURLMSG_DONE) {
-			debug("ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
+			debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
 		} else {
 			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
 		}
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 0da5d5a..b4ecbc9 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -98,7 +98,7 @@ public:
 	 * So, if you want to start any libcurl transfer, you must create
 	 * an easy handle and register it using this method.
 	 */
-	void registerEasyHandle(CURL *easy);
+	void registerEasyHandle(CURL *easy) const;
 
 	/**
 	 * Use this method to add new Request into manager's queue.
@@ -119,7 +119,7 @@ public:
 	void showCloudDisabledIcon();
 
 	/** Return URL-encoded version of given string. */
-	Common::String urlEncode(Common::String s);
+	Common::String urlEncode(Common::String s) const;
 
 	static uint32 getCloudRequestsPeriodInMicroseconds();
 };
diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h
index f37d046..134c1be 100644
--- a/backends/networking/sdl_net/client.h
+++ b/backends/networking/sdl_net/client.h
@@ -53,6 +53,26 @@ public:
 	virtual void handle(Client *client) = 0;
 };
 
+/**
+ * Client class represents one client's HTTP request
+ * to the LocalWebserver.
+ *
+ * While in READING_HEADERS state, it's kept in LocalWebserver.
+ * Client must read the headers and decide whether it's
+ * READ_HEADERS (could be handled) or BAD_REQUEST (failed).
+ *
+ * If it's READ_HEADERS, LocalWebserver searches for a corresponding
+ * BaseHandler. These classes use the information from headers -
+ * like method, path, GET parameters - to build the response
+ * for this client's request. When they do, they call setHandler()
+ * and pass a special ClientHandler. Client becomes BEING_HANDLED.
+ *
+ * While in that state, LocalWebserver calls Client's handle() and
+ * it's passed to ClientHandler. The latter does the job: it commands
+ * Client to read or write bytes with its socket or calls
+ * readContent() methods, so Client reads the request through Reader.
+ */
+
 class Client {
 	ClientState _state;
 	SDLNet_SocketSet _set;
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 5ead4f3..2e934a3 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -168,7 +168,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 	return true;
 }
 
-FilesPageHandler::ItemType FilesPageHandler::detectType(bool isDirectory, const Common::String &name) const {
+FilesPageHandler::ItemType FilesPageHandler::detectType(bool isDirectory, const Common::String &name) {
 	if (isDirectory) return IT_DIRECTORY;
 	if (name.hasSuffix(".txt")) return IT_TXT;
 	if (name.hasSuffix(".zip")) return IT_ZIP;
@@ -176,7 +176,7 @@ FilesPageHandler::ItemType FilesPageHandler::detectType(bool isDirectory, const
 	return IT_UNKNOWN;
 }
 
-void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size) {
+void FilesPageHandler::addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size) const {
 	Common::String item = itemTemplate, icon;
 	bool isDirectory = (itemType == IT_DIRECTORY || itemType == IT_PARENT_DIRECTORY);
 	switch (itemType) {
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index f102acd..c66b79e 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -47,10 +47,10 @@ class FilesPageHandler: public FilesBaseHandler {
 	bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate);
 
 	/** Helper method for detecting items' type. */
-	ItemType detectType(bool isDirectory, const Common::String &name) const;
+	static ItemType detectType(bool isDirectory, const Common::String &name);
 
 	/** Helper method for adding items into the files list. */
-	void addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size = "");
+	void addItem(Common::String &content, const Common::String &itemTemplate, ItemType itemType, Common::String path, Common::String name, Common::String size = "") const;
 
 public:
 	FilesPageHandler();
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 9208cd0..e12016a 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -36,7 +36,7 @@ void IndexPageHandler::handle(Client &client) {
 	Common::String code = client.queryParameter("code");
 
 	if (code == "") {
-		// redirect to "/files"
+		// redirect to "/filesAJAX"
 		HandlerUtils::setMessageHandler(
 			client,
 			Common::String::format(
@@ -45,7 +45,7 @@ void IndexPageHandler::handle(Client &client) {
 				client.queryParameter("path").c_str(),
 				_("Open Files manager")
 			),
-			"/files"
+			"/filesAJAX"
 		);
 		return;
 	}
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index 8657ab5..6319485 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -96,7 +96,7 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 	return successResult;
 }
 
-ListAjaxHandler::ItemType ListAjaxHandler::detectType(bool isDirectory, const Common::String &name) const {
+ListAjaxHandler::ItemType ListAjaxHandler::detectType(bool isDirectory, const Common::String &name) {
 	if (isDirectory) return IT_DIRECTORY;
 	if (name.hasSuffix(".txt")) return IT_TXT;
 	if (name.hasSuffix(".zip")) return IT_ZIP;
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.h b/backends/networking/sdl_net/handlers/listajaxhandler.h
index cef6a07..c157e1b 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.h
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.h
@@ -48,10 +48,10 @@ class ListAjaxHandler: public FilesBaseHandler {
 	Common::JSONObject listDirectory(Common::String path);
 
 	/** Helper method for detecting items' type. */
-	ItemType detectType(bool isDirectory, const Common::String &name) const;
+	static ItemType detectType(bool isDirectory, const Common::String &name);
 
 	/** Helper method for adding items into the files list. */
-	void addItem(Common::JSONArray &responseItemsList, ItemType itemType, Common::String path, Common::String name, Common::String size = "");
+	static void addItem(Common::JSONArray &responseItemsList, ItemType itemType, Common::String path, Common::String name, Common::String size = "");
 
 public:
 	ListAjaxHandler();
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index f249ee1..cdc505b 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -110,8 +110,9 @@ void HandlerUtils::setMessageHandler(Client &client, Common::String message, Com
 void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
 	setMessageHandler(client,
 		Common::String::format(
-			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			"%s<br/><a href=\"files%s?path=%s\">%s</a>",
 			message.c_str(),
+			client.queryParameter("ajax") == "true" ? "AJAX": "",
 			"%2F", //that's encoded "/"
 			_("Back to the files manager")
 		), redirectTo
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 7c3edfc..baa2974 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -323,7 +323,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 			*/
 			
 			// use the address found
-			_address = "http://" + addr + Common::String::format(":%u/", SERVER_PORT);
+			_address = "http://" + addr + Common::String::format(":%u/", _serverPort);
 		}
 		
 		if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 107c21e..9c677f3 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -81,8 +81,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handle();
 	void handleClient(uint32 i);
 	void acceptClient();
-        
-        void resolveAddress(void *ipAddress);
+	void resolveAddress(void *ipAddress);
 	
 public:
 	static const uint32 DEFAULT_SERVER_PORT = 12345;
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index d234a82..d4a0ba4 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -40,6 +40,38 @@ enum ReaderState {
 	RS_READING_CONTENT
 };
 
+/**
+ * This is a helper class for Client.
+ *
+ * It parses HTTP request and finds headers
+ * and content. It also supports POST form/multipart.
+ *
+ * One might pass the request even byte by byte,
+ * Reader will always be able to continue from the
+ * state it stopped on.
+ *
+ * Main headers/content must be read with
+ * readFirstHeaders() and readFirstContent() methods.
+ * Further headers/content blocks (POST form/multipart)
+ * must be read with readBlockHeaders() and readBlockContent().
+ *
+ * Main headers and parsed URL components could be accessed
+ * with special methods after reading.
+ *
+ * To use the object, call setContent() and then one of those
+ * reading methods. It would return whether reading is over
+ * or not. If reading is over, content stream still could
+ * contain bytes to read with other methods.
+ *
+ * If reading is not over, Reader awaits you to call the
+ * same reading method when you'd get more content.
+ *
+ * If it's over, you should check whether Reader awaits
+ * more content with noMoreContent() and call the other
+ * reading method, if it is. When headers are read, one
+ * must read contents, and vice versa.
+ */
+
 class Reader {
 	ReaderState _state;
 	Common::MemoryReadWriteStream *_content;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h
index f61a2fa..4aa6929 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.h
+++ b/backends/networking/sdl_net/uploadfileclienthandler.h
@@ -36,6 +36,16 @@ enum UploadFileHandlerState {
 	UFH_STOP
 };
 
+/**
+ * This class handles POST form/multipart upload.
+ *
+ * handleBlockHeaders() looks for filename and, if it's found,
+ * handleBlockContent() saves content into the file with such name.
+ *
+ * If no file found or other error occurs, it sets
+ * default error message handler.
+ */
+
 class UploadFileClientHandler: public ClientHandler {
 	UploadFileHandlerState _state;
 	Common::MemoryReadWriteStream *_headersStream;


Commit: 438ba985a4a97a8695a6e6fdda6930694976c07b
    https://github.com/scummvm/scummvm/commit/438ba985a4a97a8695a6e6fdda6930694976c07b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
JANITORIAL: Remove spaces at the end of the line

I knew there were some, but I wanted to fix them once, instead of doing
it all the time.

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/cloud/box/boxlistdirectorybyidrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/box/boxtokenrefresher.h
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/box/boxuploadrequest.h
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
    backends/cloud/dropbox/dropboxinforequest.cpp
    backends/cloud/dropbox/dropboxinforequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.h
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/googledrive/googledriveuploadrequest.h
    backends/cloud/id/idcreatedirectoryrequest.cpp
    backends/cloud/id/iddownloadrequest.cpp
    backends/cloud/id/idlistdirectoryrequest.cpp
    backends/cloud/id/idresolveidrequest.cpp
    backends/cloud/id/idresolveidrequest.h
    backends/cloud/id/idstorage.h
    backends/cloud/id/idstreamfilerequest.cpp
    backends/cloud/iso8601.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/onedrive/onedriveuploadrequest.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    backends/cloud/storageinfo.h
    backends/networking/browser/openurl-android.cpp
    backends/networking/browser/openurl-posix.cpp
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/curljsonrequest.h
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/wwwroot/.filesAJAX.html
    backends/networking/wwwroot/ajax.js
    backends/platform/android/jni.cpp
    backends/saves/default/default-saves.cpp
    common/config-manager.cpp
    common/file.cpp
    common/json.cpp
    common/memstream.h
    common/str.h
    common/xmlparser.cpp
    engines/testbed/misc.cpp
    gui/debugger.cpp
    gui/downloaddialog.cpp
    gui/downloaddialog.h
    gui/options.cpp
    gui/remotebrowser.cpp
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/storagewizarddialog.cpp
    gui/themes/scummclassic/classic_gfx.stx
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx
    gui/widgets/scrollcontainer.cpp



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index d4606b1..537666b 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -87,7 +87,7 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		Common::JSONObject responseObject = json->asObject();
 
 		//debug("%s", json->stringify(true).c_str());
-		
+
 		//TODO: check that error is returned the right way
 		/*
 		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
@@ -100,7 +100,7 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		}
 		*/
 
-		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
 		if (responseObject.contains("entries") && responseObject.getVal("entries")->isArray()) {
 			Common::JSONArray items = responseObject.getVal("entries")->asArray();
@@ -160,7 +160,7 @@ void BoxListDirectoryByIdRequest::restart() { start(); }
 
 Common::String BoxListDirectoryByIdRequest::date() const { return _date; }
 
-void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {	
+void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.h b/backends/cloud/box/boxlistdirectorybyidrequest.h
index ccf8d2e..13f1ba0 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.h
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.h
@@ -34,7 +34,7 @@ namespace Box {
 class BoxStorage;
 
 class BoxListDirectoryByIdRequest: public Networking::Request {
-	Common::String _requestedId;	
+	Common::String _requestedId;
 	BoxStorage *_storage;
 
 	Storage::ListDirectoryCallback _listDirectoryCallback;
@@ -42,7 +42,7 @@ class BoxListDirectoryByIdRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _date;
-	
+
 	void start();
 	void makeRequest(uint32 offset);
 	void responseCallback(Networking::JsonResponse response);
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index c31c72b..6d4b5ef 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -152,7 +152,7 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 		delete outerCallback;
 		return;
 	}
-	
+
 	Common::JSONObject info = json->asObject();
 
 	Common::String uid, name, email;
@@ -224,7 +224,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
 	Common::JSONObject parentObject;
 	parentObject.setVal("id", new Common::JSONValue(parentId));
 
-	Common::JSONObject jsonRequestParameters;	
+	Common::JSONObject jsonRequestParameters;
 	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
 	jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
 
@@ -248,7 +248,7 @@ Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableRea
 }
 
 bool BoxStorage::uploadStreamSupported() {
-	return false;	
+	return false;
 }
 
 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 1373c2f..89bc470 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -48,7 +48,7 @@ class BoxStorage: public Id::IdStorage {
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
 	void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response);
-public:	
+public:
 	/** This constructor uses OAuth code flow to get tokens. */
 	BoxStorage(Common::String code);
 	virtual ~BoxStorage();
@@ -97,7 +97,7 @@ public:
 	virtual Common::String savesDirectoryPath();
 
 	/**
-	 * Load token and user id from configs and return BoxStorage for those.	
+	 * Load token and user id from configs and return BoxStorage for those.
 	 * @return pointer to the newly created BoxStorage or 0 if some problem occured.
 	 */
 	static BoxStorage *loadFromConfig(Common::String keyPrefix);
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index f100e18..65964f3 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -52,7 +52,7 @@ void BoxTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
 	}
 	setHeaders(_headers);
 
-	//successfully received refreshed token, can restart the original request now	
+	//successfully received refreshed token, can restart the original request now
 	retry(0);
 }
 
@@ -77,7 +77,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (result.contains("code")) {
 			code = result.getVal("code")->asString();
-			debug(9, "code = %s", code.c_str());			
+			debug(9, "code = %s", code.c_str());
 		}
 
 		if (result.contains("message")) {
@@ -88,13 +88,13 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		//TODO: decide when token refreshment will help
 		//if (code == "unauthenticated") irrecoverable = false;
 
-		if (irrecoverable) {			
+		if (irrecoverable) {
 			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
 			delete json;
 			return;
 		}
 
-		pause();		
+		pause();
 		delete json;
 		_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
 		return;
@@ -117,7 +117,7 @@ void BoxTokenRefresher::finishError(Networking::ErrorResponse error) {
 	Request::finishError(error);
 }
 
-void BoxTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+void BoxTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
 	_headers = headers;
 	curl_slist_free_all(_headersList);
 	_headersList = 0;
diff --git a/backends/cloud/box/boxtokenrefresher.h b/backends/cloud/box/boxtokenrefresher.h
index 7dedefd..c08e846 100644
--- a/backends/cloud/box/boxtokenrefresher.h
+++ b/backends/cloud/box/boxtokenrefresher.h
@@ -33,13 +33,13 @@ class BoxStorage;
 
 class BoxTokenRefresher: public Networking::CurlJsonRequest {
 	BoxStorage *_parentStorage;
-	Common::Array<Common::String> _headers;	
-	
+	Common::Array<Common::String> _headers;
+
 	void tokenRefreshed(Storage::BoolResponse response);
 
 	virtual void finishJson(Common::JSONValue *json);
 	virtual void finishError(Networking::ErrorResponse error);
-public:	
+public:
 	BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~BoxTokenRefresher();
 
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index c81a3ab..d1f68f1 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -123,12 +123,12 @@ void BoxUploadRequest::upload() {
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {	
+void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-		
+
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;	
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
@@ -193,7 +193,7 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 	delete json;
 }
 
-void BoxUploadRequest::notUploadedCallback(Networking::ErrorResponse error) {	
+void BoxUploadRequest::notUploadedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	finishError(error);
diff --git a/backends/cloud/box/boxuploadrequest.h b/backends/cloud/box/boxuploadrequest.h
index 3d15aa7..1bc8690 100644
--- a/backends/cloud/box/boxuploadrequest.h
+++ b/backends/cloud/box/boxuploadrequest.h
@@ -39,7 +39,7 @@ class BoxUploadRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _resolvedId, _parentId;
-	
+
 	void start();
 	void resolveId();
 	void idResolvedCallback(Storage::UploadResponse response);
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 55dbb2b..a4763db 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -42,7 +42,7 @@ const char *const CloudManager::kStoragePrefix = "storage_";
 CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr) {}
 
 CloudManager::~CloudManager() {
-	//TODO: do we have to save storages on manager destruction?	
+	//TODO: do we have to save storages on manager destruction?
 	delete _activeStorage;
 	freeStorages();
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 2941ea6..0146a41 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -89,7 +89,7 @@ public:
 	 * @note this method automatically saves the changes with ConfMan.
 	 *
 	 * @param	storage Cloud::Storage to replace active storage with.
-	 * @param	index   one of Cloud::StorageID enum values to indicate what storage type is replaced.	 
+	 * @param	index   one of Cloud::StorageID enum values to indicate what storage type is replaced.
 	 */
 	void replaceStorage(Storage *storage, uint32 index);
 
@@ -160,7 +160,7 @@ public:
 	 */
 	void setStorageUsername(uint32 index, Common::String name);
 
-	/**	
+	/**
 	 * Set Storage's used space field.
 	 * Automatically saves changes to the config.
 	 *
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index e8c410a..33ac811 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -66,7 +66,7 @@ void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) {
 	finishError(error);
 }
 
-void DownloadRequest::handle() {	
+void DownloadRequest::handle() {
 	if (!_localFile) {
 		warning("DownloadRequest: no file to write");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
@@ -96,12 +96,12 @@ void DownloadRequest::handle() {
 	if (_remoteFileStream->eos()) {
 		if (_remoteFileStream->httpResponseCode() != 200) {
 			warning("HTTP response code is not 200 OK (it's %ld)", _remoteFileStream->httpResponseCode());
-			//TODO: do something about it actually			
+			//TODO: do something about it actually
 		}
 
 		finishDownload(_remoteFileStream->httpResponseCode() == 200);
 
-		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()		
+		_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
 	}
 }
 
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index 138616a..7e58098 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -32,8 +32,8 @@ namespace Cloud {
 
 #define DOWNLOAD_REQUEST_BUFFER_SIZE 1 * 1024 * 1024
 
-class DownloadRequest: public Networking::Request {	
-	Storage::BoolCallback _boolCallback;	
+class DownloadRequest: public Networking::Request {
+	Storage::BoolCallback _boolCallback;
 	Common::DumpFile *_localFile;
 	Common::String _remoteFileId;
 	Storage *_storage;
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 81ace75..6d22822 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -44,7 +44,7 @@ DropboxCreateDirectoryRequest::~DropboxCreateDirectoryRequest() {
 
 void DropboxCreateDirectoryRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();	
+	if (_workingRequest) _workingRequest->finish();
 	_ignoreCallback = false;
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback);
@@ -74,13 +74,13 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
-	
+
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
-	
+
 	Common::JSONObject info = json->asObject();
 	if (info.contains("id")) finishCreation(true);
 	else {
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
index 4bdc6db..0ef6a22 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
@@ -37,7 +37,7 @@ class DropboxCreateDirectoryRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _date;
-	
+
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index f5a14ab..e147ac5 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -45,7 +45,7 @@ DropboxInfoRequest::~DropboxInfoRequest() {
 
 void DropboxInfoRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();	
+	if (_workingRequest) _workingRequest->finish();
 	_ignoreCallback = false;
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback);
@@ -70,7 +70,7 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
-	
+
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		finishError(error);
diff --git a/backends/cloud/dropbox/dropboxinforequest.h b/backends/cloud/dropbox/dropboxinforequest.h
index a91d016..68a3e13 100644
--- a/backends/cloud/dropbox/dropboxinforequest.h
+++ b/backends/cloud/dropbox/dropboxinforequest.h
@@ -36,7 +36,7 @@ class DropboxInfoRequest: public Networking::Request {
 	Storage::StorageInfoCallback _infoCallback;
 	Request *_workingRequest;
 	bool _ignoreCallback;
-	
+
 	void start();
 	void userResponseCallback(Networking::JsonResponse response);
 	void quotaResponseCallback(Networking::JsonResponse response);
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index dcbca31..cc82f12 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -81,7 +81,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 	Common::JSONValue *json = response.value;
 	if (json) {
 		Common::JSONObject responseObjecct = json->asObject();
-		
+
 		if (responseObjecct.contains("error") || responseObjecct.contains("error_summary")) {
 			warning("Dropbox returned error: %s", responseObjecct.getVal("error_summary")->asString().c_str());
 			error.failed = true;
@@ -91,7 +91,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 			return;
 		}
 
-		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
 		if (responseObjecct.contains("entries")) {
 			Common::JSONArray items = responseObjecct.getVal("entries")->asArray();
@@ -110,7 +110,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 
 		bool hasMore = (responseObjecct.contains("has_more") && responseObjecct.getVal("has_more")->asBool());
 
-		if (hasMore) {			
+		if (hasMore) {
 			Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
 			Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
 			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
@@ -124,9 +124,9 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 			request->addPostField(Common::JSON::stringify(&value));
 
 			_workingRequest = ConnMan.addRequest(request);
-		} else {			
+		} else {
 			finishListing(_files);
-		}		
+		}
 	} else {
 		warning("null, not json");
 		error.failed = true;
@@ -149,7 +149,7 @@ void DropboxListDirectoryRequest::restart() { start(); }
 
 Common::String DropboxListDirectoryRequest::date() const { return _date; }
 
-void DropboxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {	
+void DropboxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 62dde71..5c0d8df 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -41,7 +41,7 @@ class DropboxListDirectoryRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _date;
-	
+
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 7e3521f..7ef07a5 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -87,7 +87,7 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 			CloudMan.removeStorage(this);
 		} else {
 			_token = result.getVal("access_token")->asString();
-			_uid = result.getVal("uid")->asString();			
+			_uid = result.getVal("uid")->asString();
 			ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
 			CloudMan.replaceStorage(this, kStorageDropboxId);
 			ConfMan.flushToDisk();
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 9dd28ba..0d308f4 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -72,9 +72,9 @@ public:
 
 	/** Returns ListDirectoryStatus struct with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
-	
+
 	/** Returns UploadStatus struct with info about uploaded file. */
-	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);	
+	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
@@ -92,7 +92,7 @@ public:
 	virtual Common::String savesDirectoryPath();
 
 	/**
-	 * Load token and user id from configs and return DropboxStorage for those.	
+	 * Load token and user id from configs and return DropboxStorage for those.
 	 * @return pointer to the newly created DropboxStorage or 0 if some problem occured.
 	 */
 	static DropboxStorage *loadFromConfig(Common::String keyPrefix);
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 29b7f60..eaa945a 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -63,9 +63,9 @@ void DropboxUploadRequest::start() {
 	uploadNextPart();
 }
 
-void DropboxUploadRequest::uploadNextPart() {	
+void DropboxUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
-	
+
 	Common::String url = "https://content.dropboxapi.com/2/files/upload_session/";
 	Common::JSONObject jsonRequestParameters;
 
@@ -82,8 +82,8 @@ void DropboxUploadRequest::uploadNextPart() {
 		}
 	} else {
 		if ((uint32)(_contentsStream->size() - _contentsStream->pos()) <= UPLOAD_PER_ONE_REQUEST) {
-			url += "finish";			
-			Common::JSONObject jsonCursor, jsonCommit;			
+			url += "finish";
+			Common::JSONObject jsonCursor, jsonCommit;
 			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
 			jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
 			jsonCommit.setVal("path", new Common::JSONValue(_savePath));
@@ -98,22 +98,22 @@ void DropboxUploadRequest::uploadNextPart() {
 			jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
 			jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
 			jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
-			jsonRequestParameters.setVal("close", new Common::JSONValue(false));			
+			jsonRequestParameters.setVal("close", new Common::JSONValue(false));
 		}
 	}
-	
+
 	Common::JSONValue value(jsonRequestParameters);
 	Networking::JsonCallback callback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxUploadRequest, Networking::ErrorResponse>(this, &DropboxUploadRequest::partUploadedErrorCallback);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, url);
 	request->addHeader("Authorization: Bearer " + _token);
-	request->addHeader("Content-Type: application/octet-stream");	
-	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));	
+	request->addHeader("Content-Type: application/octet-stream");
+	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 
 	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
 	request->setBuffer(buffer, size);
-	
+
 	_workingRequest = ConnMan.addRequest(request);
 }
 
@@ -137,7 +137,7 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 			//debug("%s", json->stringify(true).c_str());
 
 			if (object.contains("error") || object.contains("error_summary")) {
-				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());				
+				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
 				error.response = json->stringify(true);
 				finishError(error);
 				delete json;
@@ -162,14 +162,14 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 			}
 		}
 
-		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {			
+		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {
 			warning("no file info to return");
 			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");		
+		warning("null, not json");
 		finishError(error);
 	}
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
index 8d9a3e2..5adf5a6 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.h
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -39,7 +39,7 @@ class DropboxUploadRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _sessionId;
-	
+
 	void start();
 	void uploadNextPart();
 	void partUploadedCallback(Networking::JsonResponse response);
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 9e05202..de60480 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -46,7 +46,7 @@ FolderDownloadRequest::~FolderDownloadRequest() {
 void FolderDownloadRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();	
+	if (_workingRequest) _workingRequest->finish();
 	_currentFile = StorageFile();
 	_pendingFiles.clear();
 	_failedFiles.clear();
@@ -108,7 +108,7 @@ void FolderDownloadRequest::downloadNextFile() {
 			finishDownload(_failedFiles);
 			return;
 		}
-	
+
 		_currentFile = _pendingFiles.back();
 		_pendingFiles.pop_back();
 	} while (_currentFile.isDirectory()); //TODO: may be create these directories (in case those are empty)
@@ -124,7 +124,7 @@ void FolderDownloadRequest::downloadNextFile() {
 	} else {
 		warning("Can't process the following paths:");
 		warning("remote directory: %s", _remoteDirectoryPath.c_str());
-		warning("remote file under that directory: %s", remotePath.c_str());		
+		warning("remote file under that directory: %s", remotePath.c_str());
 	}
 	if (_localDirectoryPath != "") {
 		if (_localDirectoryPath.lastChar() == '/' || _localDirectoryPath.lastChar() == '\\')
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 36bc390..7b05d48 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -82,7 +82,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 		Common::JSONObject responseObject = json->asObject();
 
 		///debug("%s", json->stringify(true).c_str());
-		
+
 		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
 			warning("GoogleDrive returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
 			error.failed = true;
@@ -92,7 +92,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 			return;
 		}
 
-		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults		
+		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
 		if (responseObject.contains("files") && responseObject.getVal("files")->isArray()) {
 			Common::JSONArray items = responseObject.getVal("files")->asArray();
@@ -117,9 +117,9 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 		if (hasMore) {
 			Common::String token = responseObject.getVal("nextPageToken")->asString();
 			makeRequest(token);
-		} else {			
+		} else {
 			finishListing(_files);
-		}		
+		}
 	} else {
 		warning("null, not json");
 		error.failed = true;
@@ -142,7 +142,7 @@ void GoogleDriveListDirectoryByIdRequest::restart() { start(); }
 
 Common::String GoogleDriveListDirectoryByIdRequest::date() const { return _date; }
 
-void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {	
+void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
 	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
index ecde880..e94c6b1 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
@@ -34,7 +34,7 @@ namespace GoogleDrive {
 class GoogleDriveStorage;
 
 class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
-	Common::String _requestedId;	
+	Common::String _requestedId;
 	GoogleDriveStorage *_storage;
 
 	Storage::ListDirectoryCallback _listDirectoryCallback;
@@ -42,7 +42,7 @@ class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _date;
-	
+
 	void start();
 	void makeRequest(Common::String pageToken);
 	void responseCallback(Networking::JsonResponse response);
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 77ac898..2d9f33c 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -138,7 +138,7 @@ void GoogleDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
 	CloudMan.removeStorage(this);
 }
 
-void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {	
+void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
 	ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
 	ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
 }
@@ -173,11 +173,11 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 		//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
 		Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
 		Common::String usage = storageQuota.getVal("usage")->asString();
-		Common::String limit = storageQuota.getVal("limit")->asString();			
+		Common::String limit = storageQuota.getVal("limit")->asString();
 		quotaUsed = usage.asUint64();
 		quotaAllocated = limit.asUint64();
 	}
-		
+
 	CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
 
 	if (outerCallback) {
@@ -197,7 +197,7 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
 	}
 
 	if (outerCallback) {
-		Common::JSONObject info = json->asObject();		
+		Common::JSONObject info = json->asObject();
 		(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
 		delete outerCallback;
 	}
@@ -212,7 +212,7 @@ Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, Li
 }
 
 Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));	
+	return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));
 }
 
 Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
@@ -221,7 +221,7 @@ Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Netwo
 		Common::String header = "Authorization: Bearer " + _token;
 		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
-		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));		
+		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
 	}
 	delete callback;
 	delete errorCallback;
@@ -237,8 +237,8 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
 	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	
-	Common::String url = "https://www.googleapis.com/drive/v3/files";	
+
+	Common::String url = "https://www.googleapis.com/drive/v3/files";
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
@@ -263,7 +263,7 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user");
 	request->addHeader("Authorization: Bearer " + _token);
-	return addRequest(request);	
+	return addRequest(request);
 }
 
 Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; }
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index 4c164dd..e60719c 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -95,7 +95,7 @@ public:
 	virtual Common::String savesDirectoryPath();
 
 	/**
-	 * Load token and user id from configs and return GoogleDriveStorage for those.	
+	 * Load token and user id from configs and return GoogleDriveStorage for those.
 	 * @return pointer to the newly created GoogleDriveStorage or 0 if some problem occured.
 	 */
 	static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix);
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 9e26e62..b876bc2 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -52,7 +52,7 @@ void GoogleDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
 	}
 	setHeaders(_headers);
 
-	//successfully received refreshed token, can restart the original request now	
+	//successfully received refreshed token, can restart the original request now
 	retry(0);
 }
 
@@ -65,13 +65,13 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 
 	Common::JSONObject result = json->asObject();
 	if (result.contains("error")) {
-		//new token needed => request token & then retry original request		
+		//new token needed => request token & then retry original request
 		if (_stream) {
 			debug(9, "code %ld", _stream->httpResponseCode());
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
-		bool irrecoverable = true;		
+		bool irrecoverable = true;
 
 		uint32 code = -1;
 		Common::String message;
@@ -88,13 +88,13 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		if (code == 401 || message == "Invalid Credentials")
 			irrecoverable = false;
 
-		if (irrecoverable) {			
+		if (irrecoverable) {
 			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
 			delete json;
 			return;
 		}
 
-		pause();		
+		pause();
 		delete json;
 		_parentStorage->getAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
 		return;
@@ -104,7 +104,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	CurlJsonRequest::finishJson(json);
 }
 
-void GoogleDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+void GoogleDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
 	_headers = headers;
 	curl_slist_free_all(_headersList);
 	_headersList = 0;
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h
index 3bc5fbd..6cb3e41 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.h
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.h
@@ -33,12 +33,12 @@ class GoogleDriveStorage;
 
 class GoogleDriveTokenRefresher: public Networking::CurlJsonRequest {
 	GoogleDriveStorage *_parentStorage;
-	Common::Array<Common::String> _headers;	
-	
+	Common::Array<Common::String> _headers;
+
 	void tokenRefreshed(Storage::BoolResponse response);
 
 	virtual void finishJson(Common::JSONValue *json);
-public:	
+public:
 	GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~GoogleDriveTokenRefresher();
 
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index b63e989..4a7c30e 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -164,7 +164,7 @@ void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse resp
 		}
 	}
 
-	Common::JSONValue *json = response.value;	
+	Common::JSONValue *json = response.value;
 	delete json;
 
 	finishError(error);
@@ -179,11 +179,11 @@ void GoogleDriveUploadRequest::startUploadErrorCallback(Networking::ErrorRespons
 void GoogleDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 	Common::String url = _uploadUrl;
-	
+
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::partUploadedErrorCallback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
-	request->addHeader("Authorization: Bearer " + _storage->accessToken());	
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->usePut();
 
 	uint32 oldPos = _contentsStream->pos();
@@ -206,17 +206,17 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 		else
 			request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos() - 1, _contentsStream->size()));
 	}
-	
+
 	_workingRequest = ConnMan.addRequest(request);
 }
 
 bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
 	//308 Resume Incomplete, with Range: X-Y header
 	if (!stream) return false;
-	if (stream->httpResponseCode() != 308) return false; //seriously	
-	
+	if (stream->httpResponseCode() != 308) return false; //seriously
+
 	Common::String headers = stream->responseHeaders();
-	const char *cstr = headers.c_str();	
+	const char *cstr = headers.c_str();
 	for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
 		const char *needle = (rangeTry==0 ? "Range: 0-" : "Range: bytes=0-");
 		uint32 needleLength = (rangeTry == 0 ? 9 : 15);
@@ -239,12 +239,12 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
 	return false;
 }
 
-void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {	
+void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-		
+
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;	
+	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
@@ -301,16 +301,16 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 	delete json;
 }
 
-void GoogleDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {	
+void GoogleDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	
+
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)error.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
-			long code = stream->httpResponseCode();			
-			if (code == 308 && handleHttp308(stream)) {				
+			long code = stream->httpResponseCode();
+			if (code == 308 && handleHttp308(stream)) {
 				return;
 			}
 		}
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.h b/backends/cloud/googledrive/googledriveuploadrequest.h
index 8cc4079..73acab5 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.h
+++ b/backends/cloud/googledrive/googledriveuploadrequest.h
@@ -42,7 +42,7 @@ class GoogleDriveUploadRequest: public Networking::Request {
 	Common::String _resolvedId, _parentId;
 	Common::String _uploadUrl;
 	uint64 _serverReceivedBytes;
-	
+
 	void start();
 	void resolveId();
 	void idResolvedCallback(Storage::UploadResponse response);
diff --git a/backends/cloud/id/idcreatedirectoryrequest.cpp b/backends/cloud/id/idcreatedirectoryrequest.cpp
index 7968a4b..f64133c 100644
--- a/backends/cloud/id/idcreatedirectoryrequest.cpp
+++ b/backends/cloud/id/idcreatedirectoryrequest.cpp
@@ -56,7 +56,7 @@ void IdCreateDirectoryRequest::start() {
 		_workingRequest = _storage->createDirectory("ScummVM", callback, failureCallback);
 		return;
 	}
-	
+
 	resolveId();
 }
 
@@ -97,9 +97,9 @@ void IdCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	if (error.request) _date = error.request->date();
-	
+
 	//not resolved => folder not exists
-	if (error.response.contains("no such file found in its parent directory")) {		
+	if (error.response.contains("no such file found in its parent directory")) {
 		//parent's id after the '\n'
 		Common::String parentId = error.response;
 		for (uint32 i = 0; i < parentId.size(); ++i)
diff --git a/backends/cloud/id/iddownloadrequest.cpp b/backends/cloud/id/iddownloadrequest.cpp
index 7166a1e..edd2d26 100644
--- a/backends/cloud/id/iddownloadrequest.cpp
+++ b/backends/cloud/id/iddownloadrequest.cpp
@@ -48,7 +48,7 @@ void IdDownloadRequest::start() {
 
 	//find file's id
 	Storage::UploadCallback innerCallback = new Common::Callback<IdDownloadRequest, Storage::UploadResponse>(this, &IdDownloadRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, Networking::ErrorResponse>(this, &IdDownloadRequest::idResolveFailedCallback);	
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, Networking::ErrorResponse>(this, &IdDownloadRequest::idResolveFailedCallback);
 	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
 }
 
diff --git a/backends/cloud/id/idlistdirectoryrequest.cpp b/backends/cloud/id/idlistdirectoryrequest.cpp
index 012065d..6c70ed5 100644
--- a/backends/cloud/id/idlistdirectoryrequest.cpp
+++ b/backends/cloud/id/idlistdirectoryrequest.cpp
@@ -83,7 +83,7 @@ void IdListDirectoryRequest::listNextDirectory() {
 	_directoriesQueue.pop_back();
 
 	Storage::FileArrayCallback callback = new Common::Callback<IdListDirectoryRequest, Storage::FileArrayResponse>(this, &IdListDirectoryRequest::listedDirectoryCallback);
-	Networking::ErrorCallback failureCallback = new Common::Callback<IdListDirectoryRequest, Networking::ErrorResponse>(this, &IdListDirectoryRequest::listedDirectoryErrorCallback);	
+	Networking::ErrorCallback failureCallback = new Common::Callback<IdListDirectoryRequest, Networking::ErrorResponse>(this, &IdListDirectoryRequest::listedDirectoryErrorCallback);
 	_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
 }
 
diff --git a/backends/cloud/id/idresolveidrequest.cpp b/backends/cloud/id/idresolveidrequest.cpp
index fc61137..abd64df 100644
--- a/backends/cloud/id/idresolveidrequest.cpp
+++ b/backends/cloud/id/idresolveidrequest.cpp
@@ -47,7 +47,7 @@ void IdResolveIdRequest::start() {
 	_currentDirectory = "";
 	_currentDirectoryId = _storage->getRootDirectoryId();
 	_ignoreCallback = false;
-	
+
 	listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
 }
 
@@ -62,10 +62,10 @@ void IdResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
 	_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
 }
 
-void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {	
+void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	
+
 	Common::String currentLevelName = _requestedPath;
 	///debug("'%s'", currentLevelName.c_str());
 	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
diff --git a/backends/cloud/id/idresolveidrequest.h b/backends/cloud/id/idresolveidrequest.h
index 94d4af5..e735e96 100644
--- a/backends/cloud/id/idresolveidrequest.h
+++ b/backends/cloud/id/idresolveidrequest.h
@@ -33,7 +33,7 @@ namespace Id {
 class IdStorage;
 
 class IdResolveIdRequest: public Networking::Request {
-	Common::String _requestedPath;	
+	Common::String _requestedPath;
 	IdStorage *_storage;
 	Storage::UploadCallback _uploadCallback;
 	Common::String _currentDirectory;
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
index ccadc0e..7e64fd4 100644
--- a/backends/cloud/id/idstorage.h
+++ b/backends/cloud/id/idstorage.h
@@ -50,7 +50,7 @@ protected:
 	void printFile(UploadResponse response);
 
 	ListDirectoryCallback getPrintFilesCallback();
-	
+
 public:
 	virtual ~IdStorage();
 
diff --git a/backends/cloud/id/idstreamfilerequest.cpp b/backends/cloud/id/idstreamfilerequest.cpp
index 08060b9..cc1ce2c 100644
--- a/backends/cloud/id/idstreamfilerequest.cpp
+++ b/backends/cloud/id/idstreamfilerequest.cpp
@@ -47,7 +47,7 @@ void IdStreamFileRequest::start() {
 
 	//find file's id
 	Storage::UploadCallback innerCallback = new Common::Callback<IdStreamFileRequest, Storage::UploadResponse>(this, &IdStreamFileRequest::idResolvedCallback);
-	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, Networking::ErrorResponse>(this, &IdStreamFileRequest::idResolveFailedCallback);	
+	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, Networking::ErrorResponse>(this, &IdStreamFileRequest::idResolveFailedCallback);
 	_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
 }
 
diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
index 1675c7c..b2483fd 100644
--- a/backends/cloud/iso8601.cpp
+++ b/backends/cloud/iso8601.cpp
@@ -44,9 +44,9 @@ int find(const char *cstr, uint32 startPosition, char needle) {
 namespace Cloud {
 namespace ISO8601 {
 
-uint32 convertToTimestamp(const Common::String &iso8601Date) {		
+uint32 convertToTimestamp(const Common::String &iso8601Date) {
 	//2015-05-12T15:50:38Z
-	const char *cstr = iso8601Date.c_str();	
+	const char *cstr = iso8601Date.c_str();
 	int firstHyphen = find(cstr, 0, '-');
 	int secondHyphen = find(cstr, firstHyphen + 1, '-');
 	int tSeparator = find(cstr, secondHyphen + 1, 'T');
@@ -54,7 +54,7 @@ uint32 convertToTimestamp(const Common::String &iso8601Date) {
 	int secondColon = find(cstr, firstColon + 1, ':');
 	int zSeparator = find(cstr, secondColon + 1, 'Z');
 	if (zSeparator == -1) zSeparator = find(cstr, secondColon + 1, '-'); // Box's RFC 3339
-	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char	
+	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char
 
 	Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
 	Common::String month = getSubstring(iso8601Date, firstHyphen + 1, secondHyphen);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index 6173465..c934f2e 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -45,7 +45,7 @@ OneDriveCreateDirectoryRequest::~OneDriveCreateDirectoryRequest() {
 
 void OneDriveCreateDirectoryRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();	
+	if (_workingRequest) _workingRequest->finish();
 	_ignoreCallback = false;
 
 	Common::String name = _path, parent = _path;
@@ -54,7 +54,7 @@ void OneDriveCreateDirectoryRequest::start() {
 		while (true) {
 			parent.deleteLastChar();
 			if (name[i] == '/' || name[i] == '\\') {
-				name.erase(0, i + 1);				
+				name.erase(0, i + 1);
 				break;
 			}
 			if (i == 0) break;
@@ -62,9 +62,9 @@ void OneDriveCreateDirectoryRequest::start() {
 		}
 	}
 
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";	
+	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";
 	if (parent != "") url += ":/" + ConnMan.urlEncode(parent) + ":";
-	url += "/children";	
+	url += "/children";
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, url.c_str());
@@ -93,13 +93,13 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
-	
+
 	if (!json) {
 		warning("NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
-	
+
 	Common::JSONObject info = json->asObject();
 	if (info.contains("id")) finishCreation(true);
 	else {
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
index 83c72c4..acaca2b 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -39,7 +39,7 @@ class OneDriveCreateDirectoryRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _date;
-	
+
 	void start();
 	void responseCallback(Networking::JsonResponse response);
 	void errorCallback(Networking::ErrorResponse error);
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index baccdf4..d0b2714 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -77,7 +77,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
 	makeRequest(url);
 }
 
-void OneDriveListDirectoryRequest::makeRequest(Common::String url) {	
+void OneDriveListDirectoryRequest::makeRequest(Common::String url) {
 	Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
@@ -107,18 +107,18 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 		return;
 	}
 
-	Common::JSONObject object = json->asObject();		
-	
+	Common::JSONObject object = json->asObject();
+
 	//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
 
 	Common::JSONArray items = object.getVal("value")->asArray();
 	for (uint32 i = 0; i < items.size(); ++i) {
-		Common::JSONObject item = items[i]->asObject();	
+		Common::JSONObject item = items[i]->asObject();
 
 		Common::String path = _currentDirectory + item.getVal("name")->asString();
-		bool isDirectory = item.contains("folder");		
+		bool isDirectory = item.contains("folder");
 		uint32 size = item.getVal("size")->asIntegerNumber();
-		uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());		
+		uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());
 
 		StorageFile file(path, size, timestamp, isDirectory);
 		_files.push_back(file);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 39213e8..5fc10b7 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -154,7 +154,7 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 		delete outerCallback;
 		return;
 	}
-	
+
 	Common::JSONObject info = json->asObject();
 
 	Common::String uid, name, email;
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 60817e6..4985f3f 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -47,7 +47,7 @@ class OneDriveStorage: public Cloud::Storage {
 	void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
 
 	void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
-public:	
+public:
 	/** This constructor uses OAuth code flow to get tokens. */
 	OneDriveStorage(Common::String code);
 	virtual ~OneDriveStorage();
@@ -92,7 +92,7 @@ public:
 	virtual Common::String savesDirectoryPath();
 
 	/**
-	 * Load token and user id from configs and return OneDriveStorage for those.	
+	 * Load token and user id from configs and return OneDriveStorage for those.
 	 * @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
 	 */
 	static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 9afea3d..88b5154 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -52,7 +52,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
 	}
 	setHeaders(_headers);
 
-	//successfully received refreshed token, can restart the original request now	
+	//successfully received refreshed token, can restart the original request now
 	retry(0);
 }
 
@@ -65,7 +65,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 
 	Common::JSONObject result = json->asObject();
 	if (result.contains("error")) {
-		//new token needed => request token & then retry original request		
+		//new token needed => request token & then retry original request
 		if (_stream) {
 			debug(9, "code %ld", _stream->httpResponseCode());
 		}
@@ -76,7 +76,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (error.contains("code")) {
 			code = error.getVal("code")->asString();
-			debug(9, "code = %s", code.c_str());			
+			debug(9, "code = %s", code.c_str());
 		}
 
 		if (error.contains("message")) {
@@ -92,13 +92,13 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 
 		if (code == "unauthenticated") irrecoverable = false;
 
-		if (irrecoverable) {			
+		if (irrecoverable) {
 			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
 			delete json;
 			return;
 		}
 
-		pause();		
+		pause();
 		delete json;
 		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
 		return;
@@ -108,7 +108,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	CurlJsonRequest::finishJson(json);
 }
 
-void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {	
+void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
 	_headers = headers;
 	curl_slist_free_all(_headersList);
 	_headersList = 0;
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 4be1fa7..77e34d4 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -33,12 +33,12 @@ class OneDriveStorage;
 
 class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
 	OneDriveStorage *_parentStorage;
-	Common::Array<Common::String> _headers;	
-	
+	Common::Array<Common::String> _headers;
+
 	void tokenRefreshed(Storage::BoolResponse response);
 
 	virtual void finishJson(Common::JSONValue *json);
-public:	
+public:
 	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	virtual ~OneDriveTokenRefresher();
 
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 569809d..2af4d06 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -64,7 +64,7 @@ void OneDriveUploadRequest::start() {
 	uploadNextPart();
 }
 
-void OneDriveUploadRequest::uploadNextPart() {	
+void OneDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
 	if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
@@ -79,16 +79,16 @@ void OneDriveUploadRequest::uploadNextPart() {
 	}
 
 	Common::String url;
-	if (_uploadUrl == "") {		
+	if (_uploadUrl == "") {
 		url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/content";
-	} else {		
+	} else {
 		url = _uploadUrl;
 	}
-	
+
 	Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
-	request->addHeader("Authorization: Bearer " + _storage->accessToken());	
+	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->usePut();
 
 	uint32 oldPos = _contentsStream->pos();
@@ -106,18 +106,18 @@ void OneDriveUploadRequest::uploadNextPart() {
 			delete request;
 			return;
 		}
-	
+
 	_workingRequest = ConnMan.addRequest(request);
 }
 
-void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {	
+void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	
+
 	Networking::ErrorResponse error(this, false, true, "", -1);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
-		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();		
+		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	Common::JSONValue *json = response.value;
 	if (json) {
@@ -133,7 +133,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 			}
 
 			if (object.contains("id") && object.contains("name")) {
-				//finished				
+				//finished
 				Common::String path = _savePath;
 				uint32 size = object.getVal("size")->asIntegerNumber();
 				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
@@ -145,7 +145,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 				if (object.contains("uploadUrl"))
 					_uploadUrl = object.getVal("uploadUrl")->asString();
 				else
-					warning("no uploadUrl found in OneDrive's response");				
+					warning("no uploadUrl found in OneDrive's response");
 			}
 		}
 
@@ -156,14 +156,14 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");		
+		warning("null, not json");
 		finishError(error);
 	}
 
 	delete json;
 }
 
-void OneDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {	
+void OneDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
 	finishError(error);
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.h b/backends/cloud/onedrive/onedriveuploadrequest.h
index f613c41..4e2cb24 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.h
+++ b/backends/cloud/onedrive/onedriveuploadrequest.h
@@ -40,7 +40,7 @@ class OneDriveUploadRequest: public Networking::Request {
 	Request *_workingRequest;
 	bool _ignoreCallback;
 	Common::String _uploadUrl;
-	
+
 	void start();
 	void uploadNextPart();
 	void partUploadedCallback(Networking::JsonResponse response);
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 014e6a0..957b7e6 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -48,7 +48,7 @@ SavesSyncRequest::~SavesSyncRequest() {
 void SavesSyncRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();	
+	if (_workingRequest) _workingRequest->finish();
 	_currentDownloadingFile = StorageFile();
 	_currentUploadingFile = "";
 	_filesToDownload.clear();
@@ -78,7 +78,7 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 	if (response.request) _date = response.request->date();
 
 	Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
-	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {		
+	for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
 		localFileNotAvailableInCloud[i->_key] = true;
 	}
 
@@ -96,7 +96,7 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 			_filesToDownload.push_back(file);
 		else {
 			localFileNotAvailableInCloud[name] = false;
-			
+
 			if (_localFilesTimestamps[name] == file.timestamp())
 				continue;
 
@@ -145,7 +145,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 				//Dropbox-related error:
 				if (object.contains("error_summary")) {
 					Common::String summary = object.getVal("error_summary")->asString();
-					if (summary.contains("not_found")) {						
+					if (summary.contains("not_found")) {
 						irrecoverable = false;
 					}
 				}
@@ -223,7 +223,7 @@ void SavesSyncRequest::downloadNextFile() {
 	_filesToDownload.pop_back();
 
 	sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
-	
+
 	debug(9, "downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
@@ -258,7 +258,7 @@ void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse err
 	if (_ignoreCallback) return;
 
 	//stop syncing if download failed
-	finishError(error);	
+	finishError(error);
 }
 
 void SavesSyncRequest::uploadNextFile() {
@@ -269,8 +269,8 @@ void SavesSyncRequest::uploadNextFile() {
 
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
-		
-	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));	
+
+	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));
 	if (_storage->uploadStreamSupported()) {
 		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
@@ -288,7 +288,7 @@ void SavesSyncRequest::uploadNextFile() {
 void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
 	if (_ignoreCallback) return;
-	
+
 	//update local timestamp for the uploaded file
 	_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
 	_localFilesTimestamps[_currentUploadingFile] = response.value.timestamp();
@@ -345,7 +345,7 @@ void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	debug("SavesSync::finishError");
 	//if we were downloading a file - remember the name
 	//and make the Request close() it, so we can delete it
-	Common::String name = _currentDownloadingFile.name();	
+	Common::String name = _currentDownloadingFile.name();
 	if (_workingRequest) {
 		_ignoreCallback = true;
 		_workingRequest->finish();
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 304c444..e891e93 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -57,7 +57,7 @@ class SavesSyncRequest: public Networking::Request, public GUI::CommandSender {
 	void uploadNextFile();
 	virtual void finishError(Networking::ErrorResponse error);
 	void finishSync(bool success);
-	
+
 public:
 	SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb);
 	virtual ~SavesSyncRequest();
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 100d72b..dc403df 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -132,7 +132,7 @@ SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCal
 	if (!errorCallback) errorCallback = new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::savesSyncDefaultErrorCallback);
 	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
 	_syncRestartRequestsed = false;
-	_runningRequestsMutex.unlock();	
+	_runningRequestsMutex.unlock();
 	return (SavesSyncRequest *)addRequest(_savesSyncRequest); //who knows what that ConnMan could return in the future
 }
 
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index eaa6e9c..8582a75 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -110,7 +110,7 @@ public:
 	virtual void saveConfig(Common::String keyPrefix) = 0;
 
 	/**
-	* Return unique storage name.		
+	* Return unique storage name.
 	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const = 0;
@@ -125,7 +125,7 @@ public:
 
 	/** Returns ListDirectoryResponse with list of files. */
 	virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
-	
+
 	/** Returns StorageFile with info about uploaded file. */
 	virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
 	virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
index e0666bc..469f040 100644
--- a/backends/cloud/storageinfo.h
+++ b/backends/cloud/storageinfo.h
@@ -32,7 +32,7 @@ namespace Cloud {
 * It's disk quota usage, owner name, and such.
 */
 
-class StorageInfo {	
+class StorageInfo {
 	Common::String _uid, _name, _email;
 	uint64 _usedBytes, _allocatedBytes;
 
diff --git a/backends/networking/browser/openurl-android.cpp b/backends/networking/browser/openurl-android.cpp
index 98fc803..64e6832 100644
--- a/backends/networking/browser/openurl-android.cpp
+++ b/backends/networking/browser/openurl-android.cpp
@@ -26,7 +26,7 @@
 namespace Networking {
 namespace Browser {
 
-bool openUrl(const Common::String &url) {	
+bool openUrl(const Common::String &url) {
 	return JNI::openUrl(url.c_str());
 }
 
diff --git a/backends/networking/browser/openurl-posix.cpp b/backends/networking/browser/openurl-posix.cpp
index 562c1ad..0e56119 100644
--- a/backends/networking/browser/openurl-posix.cpp
+++ b/backends/networking/browser/openurl-posix.cpp
@@ -29,7 +29,7 @@
 namespace Networking {
 namespace Browser {
 
-namespace {    
+namespace {
 bool launch(const Common::String client, const Common::String &url) {
     // FIXME: system's input must be heavily escaped
     // well, when url's specified by user
@@ -41,10 +41,10 @@ bool launch(const Common::String client, const Common::String &url) {
 
 bool openUrl(const Common::String &url) {
     // inspired by Qt's "qdesktopservices_x11.cpp"
-    
+
     // try "standards"
     if (launch("xdg-open", url))
-        return true;    
+        return true;
     if (launch(getenv("DEFAULT_BROWSER"), url))
         return true;
     if (launch(getenv("BROWSER"), url))
@@ -67,7 +67,7 @@ bool openUrl(const Common::String &url) {
         return true;
     if (launch("opera", url))
         return true;
-    
+
     warning("Networking::Browser::openUrl() (POSIX) failed to open URL");
     return false;
 }
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 554fc79..5178522 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -44,8 +44,8 @@ CloudIcon::~CloudIcon() {}
 bool CloudIcon::draw() {
 	bool stop = false;
 	initIcons();
-	
-	if (CloudMan.isWorking() || _disabledFrames > 0) {		
+
+	if (CloudMan.isWorking() || _disabledFrames > 0) {
 		if (g_system) {
 			if (!_wasVisible) {
 				g_system->clearOSD();
@@ -109,8 +109,8 @@ void CloudIcon::initIcons() {
 	_iconsInited = true;
 }
 
-void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size) {	
-	Image::PNGDecoder decoder;	
+void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size) {
+	Image::PNGDecoder decoder;
 	Common::MemoryReadStream stream(data, size);
 	if (!decoder.loadStream(stream))
 		error("CloudIcon::loadIcon: error decoding PNG");
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 6affad7..cb0e246 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -54,7 +54,7 @@ ConnectionManager::~ConnectionManager() {
 		delete request;
 		if (callback) (*callback)(request);
 	}
-	_requests.clear();	
+	_requests.clear();
 
 	//cleanup
 	curl_multi_cleanup(_multi);
@@ -68,9 +68,9 @@ void ConnectionManager::registerEasyHandle(CURL *easy) const {
 }
 
 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
-	_addedRequestsMutex.lock();	
+	_addedRequestsMutex.lock();
 	_addedRequests.push_back(RequestWithCallback(request, callback));
-	if (!_timerStarted) startTimer();	
+	if (!_timerStarted) startTimer();
 	_addedRequestsMutex.unlock();
 	return request;
 }
@@ -130,7 +130,7 @@ void ConnectionManager::handle() {
 	++_frame;
 	if (_frame % CLOUD_PERIOD == 0) interateRequests();
 	if (_frame % CURL_PERIOD == 0) processTransfers();
-	
+
 	if (_icon.draw() && _requests.empty() && !hasAddedRequests())
 		stopTimer();
 	_handleMutex.unlock();
@@ -148,7 +148,7 @@ void ConnectionManager::interateRequests() {
 	//call handle() of all running requests (so they can do their work)
 	debug(9, "handling %d request(s)", _requests.size());
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
-		Request *request = i->request;		
+		Request *request = i->request;
 		if (request) {
 			if (request->state() == PROCESSING) request->handle();
 			else if (request->state() == RETRY) request->handleRetry();
@@ -161,7 +161,7 @@ void ConnectionManager::interateRequests() {
 			continue;
 		}
 
-		++i;		
+		++i;
 	}
 }
 
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index b4ecbc9..5fd19d7 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -71,17 +71,17 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	struct RequestWithCallback {
 		Request *request;
 		RequestCallback onDeleteCallback;
-		
+
 		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), onDeleteCallback(cb) {}
 	};
 
-	CURLM *_multi;	
+	CURLM *_multi;
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests, _addedRequests;
 	Common::Mutex _handleMutex, _addedRequestsMutex;
 	CloudIcon _icon;
 	uint32 _frame;
-	
+
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
 	void handle();
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 5a51065..7858c89 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -50,7 +50,7 @@ public:
 	CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url);
 	virtual ~CurlJsonRequest();
 
-	virtual void handle(); 
+	virtual void handle();
 	virtual void restart();
 };
 
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 05dfc4a..4420b6f 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -48,7 +48,7 @@ NetworkReadStream *CurlRequest::makeStream() {
 }
 
 void CurlRequest::handle() {
-	if (!_stream) _stream = makeStream();	
+	if (!_stream) _stream = makeStream();
 
 	if (_stream && _stream->eos()) {
 		if (_stream->httpResponseCode() != 200) {
@@ -68,7 +68,7 @@ void CurlRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
-Common::String CurlRequest::date() const {	
+Common::String CurlRequest::date() const {
 	if (_stream) {
 		Common::String headers = _stream->responseHeaders();
 		const char *cstr = headers.c_str();
@@ -114,7 +114,7 @@ void CurlRequest::addPostField(Common::String keyValuePair) {
 void CurlRequest::addFormField(Common::String name, Common::String value) {
 	if (_bytesBuffer)
 		warning("CurlRequest: added POST form fields would be ignored, because there is buffer present");
-	
+
 	if (_formFields.contains(name))
 		warning("CurlRequest: form field '%s' already had a value", name.c_str());
 
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 9b34aaa..d5f6288 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -29,13 +29,13 @@
 
 namespace Networking {
 
-static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {	
+static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
 	if (stream) return stream->write(d, n*l);
 	return 0;
 }
 
-static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {	
+static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
 	if (stream) return stream->fillWithSendingContents(d, n*l);
 	return 0;
@@ -79,7 +79,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
 		curl_easy_setopt(_easy, CURLOPT_READFUNCTION, curlReadDataCallback);
 		_sendingContentsBuffer = buffer;
 		_sendingContentsSize = bufferSize;
-	} else if (usingPatch) {		
+	} else if (usingPatch) {
 		curl_easy_setopt(_easy, CURLOPT_CUSTOMREQUEST, "PATCH");
 	} else {
 		if (post || bufferSize != 0) {
@@ -110,7 +110,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
 	curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
 	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
-	
+
 	// set POST multipart upload form fields/files
 	struct curl_httppost *formpost = NULL;
 	struct curl_httppost *lastptr = NULL;
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index a322101..0138da1 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -34,7 +34,7 @@ struct curl_slist;
 
 namespace Networking {
 
-class NetworkReadStream: public Common::MemoryReadWriteStream {	
+class NetworkReadStream: public Common::MemoryReadWriteStream {
 	CURL *_easy;
 	bool _eos, _requestComplete;
 	const byte *_sendingContentsBuffer;
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 9d49480..6cef5ff 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -111,7 +111,7 @@ bool Client::readBlockContent(Common::WriteStream *stream) {
 	return _reader.readBlockContent(stream);
 }
 
-void Client::setHandler(ClientHandler *handler) {	
+void Client::setHandler(ClientHandler *handler) {
 	if (_handler) {
 		if (_previousHandler) delete _previousHandler;
 		_previousHandler = _handler; //can't just delete it, as setHandler() could've been called by handler itself
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index ff45517..20577de 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -67,9 +67,9 @@ void DownloadFileHandler::handle(Client &client) {
 
 	GetClientHandler *handler = new GetClientHandler(stream);
 	handler->setResponseCode(200);
-	handler->setHeader("Content-Type", "application/force-download");	
+	handler->setHeader("Content-Type", "application/force-download");
 	handler->setHeader("Content-Disposition", "attachment; filename=\"" + node->getDisplayName() + "\"");
-	handler->setHeader("Content-Transfer-Encoding", "binary");	
+	handler->setHeader("Content-Transfer-Encoding", "binary");
 	client.setHandler(handler);
 }
 
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index df44821..7d48458 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -53,7 +53,7 @@ void FilesAjaxPageHandler::handle(Client &client) {
 		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The page is not available without the resources."));
 		return;
 	}
-		
+
 	Common::String response = HandlerUtils::readEverythingFromStream(stream);
 	Common::String path = client.queryParameter("path");
 
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index d4b5c3b..9546c5c 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -45,7 +45,7 @@ Common::String FilesBaseHandler::parentPath(Common::String path) {
 }
 
 bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd, bool isDirectory) {
-	// <path> is not empty, but could lack the trailing slash	
+	// <path> is not empty, but could lack the trailing slash
 	if (isDirectory && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
 
 	if (path.hasPrefix("/root")) {
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 2e934a3..d3fa459 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -66,7 +66,7 @@ Common::String getDisplayPath(Common::String s) {
 }
 
 void FilesPageHandler::handle(Client &client) {
-	Common::String response = 
+	Common::String response =
 		"<html>" \
 			"<head><title>ScummVM</title></head>" \
 			"<body>" \
@@ -97,7 +97,7 @@ void FilesPageHandler::handle(Client &client) {
 
 	Common::String path = client.queryParameter("path");
 	Common::String content = "";
-	
+
 	// show an error message if failed to list directory
 	if (!listDirectory(path, content, itemTemplate)) {
 		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ScummVM couldn't list the directory you specified."));
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index 6319485..2e2e138 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -116,7 +116,7 @@ void ListAjaxHandler::addItem(Common::JSONArray &responseItemsList, ItemType ite
 	default: icon = "unk.png";
 	}
 
-	Common::JSONObject item;	
+	Common::JSONObject item;
 	item.setVal("name", new Common::JSONValue(name));
 	item.setVal("path", new Common::JSONValue(path));
 	item.setVal("isDirectory", new Common::JSONValue(isDirectory));
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index e2f6143..5642e3f 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -59,7 +59,7 @@ void UploadFileHandler::handle(Client &client) {
 		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!"));
 		return;
 	}
-	
+
 	// if all OK, set special handler
 	client.setHandler(new UploadFileClientHandler(path));
 }
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h
index bba7fdf..4cd6115 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.h
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h
@@ -29,7 +29,7 @@ namespace Networking {
 
 class UploadFileHandler: public FilesBaseHandler {
 	void handle(Client &client);
-	
+
 public:
 	UploadFileHandler();
 	virtual ~UploadFileHandler();
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index baa2974..0ad1335 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -77,7 +77,7 @@ void LocalWebserver::startTimer(int interval) {
 	}
 }
 
-void LocalWebserver::stopTimer() {	
+void LocalWebserver::stopTimer() {
 	Common::TimerManager *manager = g_system->getTimerManager();
 	manager->removeTimerProc(localWebserverTimer);
 	_timerStarted = false;
@@ -94,7 +94,7 @@ void LocalWebserver::start() {
 	startTimer();
 
 	// Create a listening TCP socket
-	IPaddress ip;	
+	IPaddress ip;
 	if (SDLNet_ResolveHost(&ip, NULL, _serverPort) == -1) {
 		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
@@ -113,9 +113,9 @@ void LocalWebserver::start() {
 	// Create a socket set
 	_set = SDLNet_AllocSocketSet(MAX_CONNECTIONS + 1); //one more for our server socket
 	if (!_set) {
-		error("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());		
+		error("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
 	}
-	
+
 	int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
 	if (numused == -1) {
 		error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
@@ -193,7 +193,7 @@ void LocalWebserver::handle() {
 
 	if (_clients == 0) ++_idlingFrames;
 	else _idlingFrames = 0;
-	
+
 	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) {
 		_handleMutex.unlock();
 		stop();
@@ -203,7 +203,7 @@ void LocalWebserver::handle() {
 	_handleMutex.unlock();
 }
 
-void LocalWebserver::handleClient(uint32 i) {	
+void LocalWebserver::handleClient(uint32 i) {
 	switch (_client[i].state()) {
 	case INVALID: return;
 	case READING_HEADERS: _client[i].readHeaders(); break;
@@ -214,7 +214,7 @@ void LocalWebserver::handleClient(uint32 i) {
 			(*_pathHandlers[_client[i].path()])(_client[i]);
 		else if (_defaultHandler)
 			(*_defaultHandler)(_client[i]); //try default handler
-		
+
 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break;
 
 		//if no handler, answer with default BAD REQUEST
@@ -287,18 +287,18 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 			if (!i->ifa_addr) {
 				continue;
 			}
-			
+
 			Common::String addr;
-			
+
 			// IPv4
-			if (i->ifa_addr->sa_family == AF_INET) { 
+			if (i->ifa_addr->sa_family == AF_INET) {
 				tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
 				char addressBuffer[INET_ADDRSTRLEN];
 				inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
 				debug("%s IP Address %s", i->ifa_name, addressBuffer);
 				addr = addressBuffer;
 			}
-			
+
 			// IPv6
 			/*
 			if (i->ifa_addr->sa_family == AF_INET6) {
@@ -309,9 +309,9 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 				addr = addressBuffer;
 			}
 			*/
-			
+
 			if (addr.empty()) continue;
-			
+
 			// ignored IPv4 addresses
 			if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
 				continue;
@@ -321,11 +321,11 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 			if (addr.equals("::1"))
 				continue;
 			*/
-			
+
 			// use the address found
 			_address = "http://" + addr + Common::String::format(":%u/", _serverPort);
 		}
-		
+
 		if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
 #endif
 }
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 9c677f3..d118509 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -82,7 +82,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handleClient(uint32 i);
 	void acceptClient();
 	void resolveAddress(void *ipAddress);
-	
+
 public:
 	static const uint32 DEFAULT_SERVER_PORT = 12345;
 
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 1e5693c..8edda70 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -230,7 +230,7 @@ void Reader::parseQueryParameters() {
 				value = "";
 			} else key += _query[i];
 		} else {
-			if (_query[i] == '&') {				
+			if (_query[i] == '&') {
 				if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!", key.c_str());
 				else _queryParameters[key] = LocalWebserver::urlDecode(value);
 				readingKey = true;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 7e93573..4ecd095 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -164,11 +164,11 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 				(client->queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
 					LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
 			);
-			_state = UFH_STOP;		
+			_state = UFH_STOP;
 			return;
 		}
 	}
-	
+
 	// no more content avaiable
 	if (client->noMoreContent()) {
 		// if no file field was found - failure
diff --git a/backends/networking/wwwroot/.filesAJAX.html b/backends/networking/wwwroot/.filesAJAX.html
index f736b74..d45c730 100644
--- a/backends/networking/wwwroot/.filesAJAX.html
+++ b/backends/networking/wwwroot/.filesAJAX.html
@@ -166,7 +166,7 @@
 						if (!slashes) {
 							currentPath += crumb;
 							b.appendChild(makeBreadcrumb(crumb, currentPath+'/'));
-							slashes = true;					
+							slashes = true;
 						}
 					} else {
 						if (slashes) {
diff --git a/backends/networking/wwwroot/ajax.js b/backends/networking/wwwroot/ajax.js
index 12c50f1..c01d7e9 100644
--- a/backends/networking/wwwroot/ajax.js
+++ b/backends/networking/wwwroot/ajax.js
@@ -6,7 +6,7 @@ ajax.x = function () { return new XMLHttpRequest(); }; // "no one uses IE6"
 
 ajax.send = function (url, callback, errorCallback, method, data, async) {
     if (async === undefined) async = true;
-    
+
     var x = ajax.x();
     x.open(method, url, async);
     x.onreadystatechange = function () {
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 91d9a57..256ae09 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -265,7 +265,7 @@ bool JNI::isConnectionLimited() {
 		env->ExceptionClear();
 		limited = true;
 	}
-	
+
 	return limited;
 }
 
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 7f96868..8a7fba4 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -88,7 +88,7 @@ Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &
 	}
 
 	Common::StringArray results;
-	for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) {		
+	for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) {
 		if (!locked.contains(file->_key) && file->_key.matchString(pattern, true)) {
 			results.push_back(file->_key);
 		}
@@ -142,7 +142,7 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 		return nullptr;
 
 	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
-		if (filename == *i) {			
+		if (filename == *i) {
 			return nullptr; //file is locked, no saving available
 		}
 	}
@@ -238,7 +238,7 @@ void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
 	checkPath(Common::FSNode(savePathName));
 
 #ifdef USE_LIBCURL
-	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
+	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 	if (!files.empty()) updateSavefilesList(files); //makes this cache invalid
 	else _lockedFiles = files;
 #endif
@@ -287,7 +287,7 @@ Common::HashMap<Common::String, uint32> DefaultSaveFileManager::loadTimestamps()
 	Common::Array<Common::String> files;
 	g_system->getSavefileManager()->updateSavefilesList(files);
 
-	//start with listing all the files in saves/ directory and setting invalid timestamp to them	
+	//start with listing all the files in saves/ directory and setting invalid timestamp to them
 	Common::StringArray localFiles = g_system->getSavefileManager()->listSavefiles("*");
 	for (uint32 i = 0; i < localFiles.size(); ++i)
 		timestamps[localFiles[i]] = INVALID_TIMESTAMP;
@@ -329,7 +329,7 @@ Common::HashMap<Common::String, uint32> DefaultSaveFileManager::loadTimestamps()
 
 		//parse timestamp
 		uint32 timestamp = buffer.asUint64();
-		if (buffer == "" || timestamp == 0) break;		
+		if (buffer == "" || timestamp == 0) break;
 		timestamps[filename] = timestamp;
 	}
 
diff --git a/common/config-manager.cpp b/common/config-manager.cpp
index 40d8aca..fdd0c6f 100644
--- a/common/config-manager.cpp
+++ b/common/config-manager.cpp
@@ -129,7 +129,7 @@ void ConfigManager::addDomain(const String &domainName, const ConfigManager::Dom
 		_keymapperDomain = domain;
 #endif
 #ifdef USE_CLOUD
-	} else if (domainName == kCloudDomain) {		
+	} else if (domainName == kCloudDomain) {
 		_cloudDomain = domain;
 #endif
 	} else if (domain.contains("gameid")) {
diff --git a/common/file.cpp b/common/file.cpp
index 5f3402e..9797bca 100644
--- a/common/file.cpp
+++ b/common/file.cpp
@@ -161,14 +161,14 @@ bool DumpFile::open(const String &filename, bool createPath) {
 				Common::String subpath = filename;
 				subpath.erase(i);
 				if (subpath.empty()) continue;
-				AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(subpath);				
-				if (node->exists()) continue;				
+				AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(subpath);
+				if (node->exists()) continue;
 				if (!node->create(true)) warning("DumpFile: unable to create directories from path prefix");
 			}
 		}
 	}
 
-	FSNode node(filename);	
+	FSNode node(filename);
 	return open(node);
 }
 
diff --git a/common/json.cpp b/common/json.cpp
index f95a44f..d0e585a 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -281,7 +281,7 @@ JSONValue *JSONValue::parse(const char **data) {
 			return new JSONValue(str);
 	}
 
-	// Is it a boolean?	
+	// Is it a boolean?
 	else if ((simplejson_wcsnlen(*data, 4) && scumm_strnicmp(*data, "true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && scumm_strnicmp(*data, "false", 5) == 0)) {
 		bool value = scumm_strnicmp(*data, "true", 4) == 0;
 		(*data) += value ? 4 : 5;
@@ -989,7 +989,7 @@ String JSONValue::stringifyImpl(size_t const indentDepth) const {
 	case JSONType_IntegerNumber: {
 		char str[80];
 		sprintf(str, "%lld", _integerValue);
-		ret_string = str;		
+		ret_string = str;
 		break;
 	}
 
diff --git a/common/memstream.h b/common/memstream.h
index 76fa07c..94b8d19 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -230,7 +230,7 @@ private:
 		_capacity = MAX(new_len + 32, _capacity * 2);
 		_data = (byte *)malloc(_capacity);
 
-		if (old_data) {			
+		if (old_data) {
 			// Copy old data
 			if (_readPos < _writePos) {
 				memcpy(_data, old_data + _readPos, _writePos - _readPos);
@@ -289,7 +289,7 @@ public:
 	uint32 pos() const { return _pos - _length; } //'read' position in the stream
 	uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
 
-	byte *getData() { return _data; }	
+	byte *getData() { return _data; }
 };
 
 } // End of namespace Common
diff --git a/common/str.h b/common/str.h
index 1293d89..d55ba07 100644
--- a/common/str.h
+++ b/common/str.h
@@ -236,12 +236,12 @@ public:
 	void trim();
 
 	uint hash() const;
-	 
+
 	/**@{
 	 * Functions to replace some amount of chars with chars from some other string.
 	 *
 	 * @note The implementation follows that of the STL's std::string:
-	 *       http://www.cplusplus.com/reference/string/string/replace/        
+	 *       http://www.cplusplus.com/reference/string/string/replace/
 	 *
 	 * @param pos Starting position for the replace in the original string.
 	 * @param count Number of chars to replace from the original string.
@@ -250,7 +250,7 @@ public:
 	 * @param countOri Same as count
 	 * @param posDest Initial position to read str from.
 	 * @param countDest Number of chars to read from str. npos by default.
-	 */ 
+	 */
 	// Replace 'count' bytes, starting from 'pos' with str.
 	void replace(uint32 pos, uint32 count, const String &str);
 	// The same as above, but accepts a C-like array of characters.
@@ -267,7 +267,7 @@ public:
 	// str[posDest, posDest + countDest)
 	void replace(uint32 posOri, uint32 countOri, const char *str,
 					uint32 posDest, uint32 countDest);
-	/**@}*/ 
+	/**@}*/
 
 	/**
 	 * Print formatted data into a String object. Similar to sprintf,
diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp
index da4f577..4470180 100644
--- a/common/xmlparser.cpp
+++ b/common/xmlparser.cpp
@@ -128,7 +128,7 @@ bool XMLParser::parserError(const String &errStr) {
 		while (currentPosition--)
 			errorMessage += (char)_stream->readByte();
 	}
-	
+
 	errorMessage += "\n\nParser error: ";
 	errorMessage += errStr;
 	errorMessage += "\n\n";
diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp
index 3dde71e..20651e7 100644
--- a/engines/testbed/misc.cpp
+++ b/engines/testbed/misc.cpp
@@ -174,7 +174,7 @@ TestExitStatus MiscTests::testOpenUrl() {
 		Testsuite::logPrintf("Info! openUrl() says it couldn't open the url (probably not supported on this platform)\n");
 		return kTestFailed;
 	}
-	
+
 	if (Testsuite::handleInteractiveInput("Was ScummVM able to open 'http://scummvm.org/' in your default browser?", "Yes", "No", kOptionRight)) {
 		Testsuite::logDetailedPrintf("Error! openUrl() is not working!\n");
 		return kTestFailed;
diff --git a/gui/debugger.cpp b/gui/debugger.cpp
index 72d05e2..7595efc 100644
--- a/gui/debugger.cpp
+++ b/gui/debugger.cpp
@@ -584,7 +584,7 @@ bool Debugger::cmdMd5(int argc, const char **argv) {
 			length = atoi(argv[2]);
 			paramOffset = 2;
 		}
-		
+
 		// Assume that spaces are part of a single filename.
 		Common::String filename = argv[1 + paramOffset];
 		for (int i = 2 + paramOffset; i < argc; i++) {
@@ -624,7 +624,7 @@ bool Debugger::cmdMd5Mac(int argc, const char **argv) {
 			length = atoi(argv[2]);
 			paramOffset = 2;
 		}
-		
+
 		// Assume that spaces are part of a single filename.
 		Common::String filename = argv[1 + paramOffset];
 		for (int i = 2 + paramOffset; i < argc; i++) {
diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index df87837..a90783d 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -119,7 +119,7 @@ bool DownloadDialog::selectDirectories() {
 		if (alert.runModal() != GUI::kMessageOK) return false;
 	}
 
-	//first user should select remote directory to download	
+	//first user should select remote directory to download
 	if (_remoteBrowser->runModal() <= 0) return false;
 
 	Cloud::StorageFile remoteDirectory = _remoteBrowser->getResult();
@@ -248,7 +248,7 @@ Common::String DownloadDialog::getSpeedLabelText() {
 	return Common::String::format("Download speed: %s %s", speed.c_str(), _(speedUnits.c_str()));
 }
 
-void DownloadDialog::refreshWidgets() {	
+void DownloadDialog::refreshWidgets() {
 	_localDirectory = CloudMan.getDownloadLocalDirectory();
 	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
 	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
diff --git a/gui/downloaddialog.h b/gui/downloaddialog.h
index cbeb78a..9080717 100644
--- a/gui/downloaddialog.h
+++ b/gui/downloaddialog.h
@@ -46,7 +46,7 @@ class DownloadDialog : public Dialog {
 	LauncherDialog *_launcher;
 	BrowserDialog *_browser;
 	RemoteBrowserDialog *_remoteBrowser;
-	
+
 	StaticTextWidget *_remoteDirectoryLabel;
 	StaticTextWidget *_localDirectoryLabel;
 	StaticTextWidget *_percentLabel;
diff --git a/gui/options.cpp b/gui/options.cpp
index abc97aa..ee7e584 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1779,7 +1779,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	int serverLabelPosition = -1; //no override
 #ifdef USE_LIBCURL
 	_selectedStorageIndex = _storagePopUp->getSelectedTag();
-	
+
 	if (_storagePopUpDesc) _storagePopUpDesc->setVisible(true);
 	if (_storagePopUp) _storagePopUp->setVisible(true);
 
@@ -1813,7 +1813,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (!shown) serverLabelPosition = (_storageUsernameDesc ? _storageUsernameDesc->getRelY() : 0);
 #else
 	_selectedStorageIndex = 0;
-	
+
 	if (_storagePopUpDesc) _storagePopUpDesc->setVisible(false);
 	if (_storagePopUp) _storagePopUp->setVisible(false);
 	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(false);
@@ -1822,7 +1822,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageUsedSpaceDesc) _storageUsedSpaceDesc->setVisible(false);
 	if (_storageUsedSpace) _storageUsedSpace->setVisible(false);
 	if (_storageLastSyncDesc) _storageLastSyncDesc->setVisible(false);
-	if (_storageLastSync) _storageLastSync->setVisible(false);	
+	if (_storageLastSync) _storageLastSync->setVisible(false);
 	if (_storageConnectButton) _storageConnectButton->setVisible(false);
 	if (_storageRefreshButton) _storageRefreshButton->setVisible(false);
 	if (_storageDownloadButton) _storageDownloadButton->setVisible(false);
@@ -1851,7 +1851,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 	serverPortClearButtonY = y;
 
 	bool serverIsRunning = LocalServer.isRunning();
-		
+
 	if (serverLabelPosition < 0) serverLabelPosition = serverInfoY;
 	if (_runServerButton) {
 		_runServerButton->setVisible(true);
@@ -1907,7 +1907,7 @@ void GlobalOptionsDialog::storageListDirectoryCallback(Cloud::Storage::ListDirec
 	uint64 totalSize = 0;
 	for (uint32 i = 0; i < files.size(); ++i)
 		if (!files[i].isDirectory())
-			totalSize += files[i].size();	
+			totalSize += files[i].size();
 	CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
 	_redrawCloudTab = true;
 }
diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index d176d29..49f35ed 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -67,7 +67,7 @@ RemoteBrowserDialog::~RemoteBrowserDialog() {
 	}
 }
 
-void RemoteBrowserDialog::open() {	
+void RemoteBrowserDialog::open() {
 	Dialog::open();
 	listDirectory(Cloud::StorageFile());
 }
@@ -176,7 +176,7 @@ void RemoteBrowserDialog::goUp() {
 			if (i == 0 || path[i] == '/' || path[i] == '\\') {
 				path.erase(i);
 				break;
-			}		
+			}
 	}
 
 	listDirectory(Cloud::StorageFile(path, 0, 0, true));
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 222f637..0a39f59 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -57,7 +57,7 @@ SaveLoadCloudSyncProgressDialog::SaveLoadCloudSyncProgressDialog(bool canRunInBa
 	_progressBar->setValue(progress);
 	_progressBar->setEnabled(false);
 	_percentLabel = new StaticTextWidget(this, "SaveLoadCloudSyncProgress.PercentText", Common::String::format("%u %%", progress));
-	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Cancel", "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog																																				
+	new ButtonWidget(this, "SaveLoadCloudSyncProgress.Cancel", "Cancel", 0, kCancelSyncCmd, Common::ASCII_ESCAPE);	// Cancel dialog
 	ButtonWidget *backgroundButton = new ButtonWidget(this, "SaveLoadCloudSyncProgress.Background", "Run in background", 0, kBackgroundSyncCmd, Common::ASCII_RETURN);	// Confirm dialog
 	backgroundButton->setEnabled(canRunInBackground);
 	draw();
@@ -69,7 +69,7 @@ SaveLoadCloudSyncProgressDialog::~SaveLoadCloudSyncProgressDialog() {
 
 void SaveLoadCloudSyncProgressDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch(cmd) {
-	case kSavesSyncProgressCmd:				
+	case kSavesSyncProgressCmd:
 		_percentLabel->setLabel(Common::String::format("%u%%", data));
 		_progressBar->setValue(data);
 		_progressBar->draw();
@@ -280,7 +280,7 @@ void SaveLoadChooserDialog::reflowLayout() {
 
 void SaveLoadChooserDialog::updateSaveList() {
 #ifdef USE_LIBCURL
-	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing	
+	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 	g_system->getSavefileManager()->updateSavefilesList(files);
 #endif
 	listSaves();
@@ -300,7 +300,7 @@ void SaveLoadChooserDialog::listSaves() {
 
 			//make up some slot number
 			int slotNum = 0;
-			for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars			
+			for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars
 				char c = files[i][j];
 				if (c < '0' || c > '9') continue;
 				slotNum = slotNum * 10 + (c - '0');
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 4e375bf..fb28333 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -84,7 +84,7 @@ public:
 #ifdef USE_LIBCURL
 	virtual void runSaveSync(bool hasSavepathOverride);
 #endif
-	
+
 	virtual void handleTickle();
 
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
@@ -148,8 +148,8 @@ public:
 
 	virtual void open();
 	virtual void close();
-protected:	
-	virtual void updateSaveList();	
+protected:
+	virtual void updateSaveList();
 private:
 	virtual int runIntern();
 
@@ -163,7 +163,7 @@ private:
 	StaticTextWidget	*_playtime;
 
 	String					_resultString;
-	
+
 	void updateSelection(bool redraw);
 };
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 299da11..b411a2e 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -46,7 +46,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 
 	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:"));
 	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", getUrl());
 
@@ -114,7 +114,7 @@ void StorageWizardDialog::close() {
 
 void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
-	case kCodeBoxCmd: {		
+	case kCodeBoxCmd: {
 		Common::String code, message;
 		uint32 correctFields = 0;
 		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
@@ -184,7 +184,7 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 	case kStorageCodePassedCmd:
 		CloudMan.connectStorage(_storageId, LocalServer.indexPageHandler().code());
-		_close = true;		
+		_close = true;
 		break;
 	default:
 		Dialog::handleCommand(sender, cmd, data);
@@ -208,7 +208,7 @@ Common::String StorageWizardDialog::getUrl() const {
 	case Cloud::kStorageGoogleDriveId: url += "gd"; break;
 	case Cloud::kStorageBoxId: url += "bx"; break;
 	}
-	
+
 	if (couldUseLocalServer()) url += "s";
 	return url;
 }
@@ -222,7 +222,7 @@ bool StorageWizardDialog::couldUseLocalServer() const {
 }
 
 int StorageWizardDialog::decodeHashchar(char c) {
-	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";	
+	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";
 	for (uint32 i = 0; i < 64; ++i)
 		if (c == HASHCHARS[i])
 			return i;
@@ -232,10 +232,10 @@ int StorageWizardDialog::decodeHashchar(char c) {
 bool StorageWizardDialog::correctChecksum(Common::String s) {
 	if (s.size() == 0) return false; //no last char
 	int providedChecksum = decodeHashchar(s.lastChar());
-	int calculatedChecksum = 0x2A; //any initial value would do, but it must equal to the one used on the page where these checksums were generated 
+	int calculatedChecksum = 0x2A; //any initial value would do, but it must equal to the one used on the page where these checksums were generated
 	for (uint32 i = 0; i < s.size()-1; ++i) {
 		calculatedChecksum = calculatedChecksum ^ s[i];
-	}	
+	}
 	return providedChecksum == (calculatedChecksum % 64);
 }
 
diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx
index 49ecea2..1311345 100644
--- a/gui/themes/scummclassic/classic_gfx.stx
+++ b/gui/themes/scummclassic/classic_gfx.stx
@@ -192,7 +192,7 @@
 					orientation = 'top'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'scrollbar_button_idle' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'bevelsq'
 					bevel = '2'
@@ -226,7 +226,7 @@
 					orientation = 'top'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'scrollbar_button_hover' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'bevelsq'
 					bevel = '2'
@@ -314,7 +314,7 @@
 					bevel = '2'
 					fill = 'none'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -325,7 +325,7 @@
 					padding = '0, 0, 7, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -336,20 +336,20 @@
 					padding = '0, 0, 7, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_idle' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'bevelsq'
 					bevel = '2'
 					fill = 'none'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -360,7 +360,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -371,7 +371,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -394,7 +394,7 @@
 					padding = '0, 0, 7, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -411,13 +411,13 @@
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_disabled' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'bevelsq'
 					bevel = '2'
 					fill = 'none'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -428,7 +428,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -439,7 +439,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -462,7 +462,7 @@
 					padding = '0, 0, 7, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -479,13 +479,13 @@
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_hover' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'bevelsq'
 					bevel = '2'
 					fill = 'none'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -496,7 +496,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					fg_color = 'green'
 					fill = 'foreground'
@@ -507,7 +507,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -552,7 +552,7 @@
 					fill = 'foreground'
 					fg_color = 'green'
 		/>
-	</drawdata> 
+	</drawdata>
 
 	<drawdata id = 'button_idle' cache = 'false'>
 		<text	font = 'text_button'
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index b9c2781..2e37b00 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -610,7 +610,7 @@
 			<widget name = 'ProgressBar'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					height = 'Globals.Line.Height'
 					textalign = 'center'
@@ -657,7 +657,7 @@
 					/>
 					<widget name = 'ReturnLine2'
 							height = 'Globals.Line.Height'
-					/>					
+					/>
 					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
 						<widget name = 'CodeBox1'
 							width = '70'
@@ -699,7 +699,7 @@
 					/>
 					<space size = '6' />
 				</layout>
-			</layout>			
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<widget name = 'CancelButton'
 						type = 'Button'
@@ -1244,7 +1244,7 @@
 					width = '496'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					width = '496'
 					height = 'Globals.Line.Height'
@@ -1255,7 +1255,7 @@
 				<widget name = 'Cancel'
 						width = '150'
 						height = 'Globals.Button.Height'
-				/>				
+				/>
 				<widget name = 'Background'
 						width = '150'
 						height = 'Globals.Button.Height'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 7c062f7..06ce6f6 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -538,7 +538,7 @@
 	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
-				<widget name = 'StoragePopupDesc'						
+				<widget name = 'StoragePopupDesc'
 						width = '80'
 						height = 'Globals.Line.Height'
 						textalign = 'right'
@@ -625,7 +625,7 @@
 			<widget name = 'ProgressBar'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					height = 'Globals.Line.Height'
 					textalign = 'center'
@@ -709,7 +709,7 @@
 							height = 'Globals.Line.Height'
 					/>
 				<space size = '4' />
-			</layout>		
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'CancelButton'
 						type = 'Button'
@@ -1247,7 +1247,7 @@
 					width = '240'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					width = '240'
 					height = 'Globals.Line.Height'
@@ -1258,7 +1258,7 @@
 				<widget name = 'Cancel'
 						width = '100'
 						height = 'Globals.Button.Height'
-				/>				
+				/>
 				<widget name = 'Background'
 						width = '100'
 						height = 'Globals.Button.Height'
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index 3a1ec5a..31b194d 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -181,7 +181,7 @@
 		<text_color	id = 'color_button_hover'
 				color = 'white'
 		/>
-	
+
 		<text_color	id = 'color_alternative_inverted'
 				color = 'white'
 		/>
@@ -327,7 +327,7 @@
 					orientation = 'top'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'scrollbar_button_hover' cache = 'false' resolution = 'y>399'>
 		<drawstep	func = 'roundedsq'
 					radius = '10'
@@ -350,7 +350,7 @@
 					orientation = 'top'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'scrollbar_button_hover' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'roundedsq'
 					radius = '10'
@@ -476,7 +476,7 @@
 					bg_color = 'xtrabrightred'
 					shadow = '1'
 		/>
-	
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -487,7 +487,7 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -498,14 +498,14 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_idle' cache = 'false' resolution ='y<400'>
 		<drawstep	func = 'roundedsq'
 					radius = '5'
@@ -515,7 +515,7 @@
 					bg_color = 'xtrabrightred'
 					shadow = '1'
 		/>
-	
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -526,7 +526,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -537,7 +537,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -566,7 +566,7 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -577,14 +577,14 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal_hover'
 				vertical_align = 'center'
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_disabled' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'roundedsq'
 					radius = '5'
@@ -594,7 +594,7 @@
 					bg_color = 'xtrabrightred'
 					shadow = '2'
 		/>
-	
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -605,7 +605,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -616,7 +616,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -645,7 +645,7 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -656,14 +656,14 @@
 					padding = '0, 0, 6, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal_hover'
 				vertical_align = 'center'
 				horizontal_align = 'left'
 		/>
 	</drawdata>
-	
+
 	<drawdata id = 'popup_hover' cache = 'false' resolution = 'y<400'>
 		<drawstep	func = 'roundedsq'
 					radius = '5'
@@ -673,7 +673,7 @@
 					bg_color = 'xtrabrightred'
 					shadow = '1'
 		/>
-	
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -684,7 +684,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'bottom'
 		/>
-		
+
 		<drawstep	func = 'triangle'
 					bg_color = 'shadowcolor'
 					fill = 'background'
@@ -695,7 +695,7 @@
 					padding = '0, 0, 3, 0'
 					orientation = 'top'
 		/>
-		
+
 		<text	font = 'text_default'
 				text_color = 'color_normal'
 				vertical_align = 'center'
@@ -777,7 +777,7 @@
 					bevel = '1'
 					bevel_color = 'black'
 		/>
-	</drawdata> 
+	</drawdata>
 
 	<!-- Idle button -->
 	<drawdata id = 'button_idle' cache = 'false'>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index e0ea868..49876be 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -624,7 +624,7 @@
 			<widget name = 'ProgressBar'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					height = 'Globals.Line.Height'
 					textalign = 'center'
@@ -671,7 +671,7 @@
 					/>
 					<widget name = 'ReturnLine2'
 							height = 'Globals.Line.Height'
-					/>					
+					/>
 					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
 						<widget name = 'CodeBox1'
 							width = '70'
@@ -713,7 +713,7 @@
 					/>
 					<space size = '6' />
 				</layout>
-			</layout>			
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<widget name = 'CancelButton'
 						type = 'Button'
@@ -1258,7 +1258,7 @@
 					width = '496'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					width = '496'
 					height = 'Globals.Line.Height'
@@ -1269,7 +1269,7 @@
 				<widget name = 'Cancel'
 						width = '150'
 						height = 'Globals.Button.Height'
-				/>				
+				/>
 				<widget name = 'Background'
 						width = '150'
 						height = 'Globals.Button.Height'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index ceb43bb..e10ce4a 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -536,7 +536,7 @@
 	<dialog name = 'GlobalOptions_Cloud_Container' overlays = 'GlobalOptions_Cloud.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
-				<widget name = 'StoragePopupDesc'						
+				<widget name = 'StoragePopupDesc'
 						width = '80'
 						height = 'Globals.Line.Height'
 						textalign = 'right'
@@ -623,7 +623,7 @@
 			<widget name = 'ProgressBar'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					height = 'Globals.Line.Height'
 					textalign = 'center'
@@ -707,7 +707,7 @@
 							height = 'Globals.Line.Height'
 					/>
 				<space size = '4' />
-			</layout>		
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'CancelButton'
 						type = 'Button'
@@ -1245,7 +1245,7 @@
 					width = '240'
 					height = 'Globals.Button.Height'
 			/>
-			<space size = '1'/>	
+			<space size = '1'/>
 			<widget name = 'PercentText'
 					width = '240'
 					height = 'Globals.Line.Height'
@@ -1256,7 +1256,7 @@
 				<widget name = 'Cancel'
 						width = '100'
 						height = 'Globals.Button.Height'
-				/>				
+				/>
 				<widget name = 'Background'
 						width = '100'
 						height = 'Globals.Button.Height'
diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 820c00f..9a77927 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -52,7 +52,7 @@ void ScrollContainerWidget::init() {
 void ScrollContainerWidget::recalc() {
 	int scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0);
 	_limitH = _h;
-	
+
 	//calculate virtual height
 	const int spacing = g_gui.xmlEval()->getVar("Global.Font.Height", 16); //on the bottom
 	int h = 0;
@@ -120,7 +120,7 @@ void ScrollContainerWidget::reflowLayout() {
 
 	//hide and move widgets, if needed
 	sendCommand(_reflowCmd, 0);
-	
+
 	//recalculate height
 	recalc();
 


Commit: 772d8ee42b820a5c19a8d9a9efb215f17606fb8f
    https://github.com/scummvm/scummvm/commit/772d8ee42b820a5c19a8d9a9efb215f17606fb8f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix `redirect_uri` selection code

Now it's not hardcoded based on USE_SDL_NET, but one or another value is
used depending on currently selected LocalWebserver's port.

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    gui/options.cpp
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 6d4b5ef..873e4fa 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -88,11 +88,11 @@ void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
 	/*
-#ifdef USE_SDL_NET
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
-#else
-	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
-#endif
+	if (Cloud::CloudManager::couldUseLocalServer()) {
+		request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
+	} else {
+		request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+	}
 	*/
 	addRequest(request);
 }
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a4763db..f5b7c97 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -28,6 +28,9 @@
 #include "common/translation.h"
 #include "common/config-manager.h"
 #include "common/str.h"
+#ifdef USE_SDL_NET
+#include "backends/networking/sdl_net/localwebserver.h"
+#endif
 
 namespace Common {
 
@@ -296,6 +299,14 @@ bool CloudManager::isWorking() const {
 	return false;
 }
 
+bool CloudManager::couldUseLocalServer() {
+#ifdef USE_SDL_NET
+	return Networking::LocalWebserver::getPort() == Networking::LocalWebserver::DEFAULT_SERVER_PORT;
+#else
+	return false;
+#endif
+}
+
 ///// SavesSyncRequest-related /////
 
 bool CloudManager::isSyncing() const {
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 0146a41..42aa718 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -207,6 +207,9 @@ public:
 	/** Returns whether there are any requests running. */
 	bool isWorking() const;
 
+	/** Returns whether LocalWebserver is available to use for auth. */
+	static bool couldUseLocalServer();
+
 	///// SavesSyncRequest-related /////
 
 	/** Returns whether there is a SavesSyncRequest running. */
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 7ef07a5..d35e29f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -69,11 +69,11 @@ void DropboxStorage::getAccessToken(Common::String code) {
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
-#ifdef USE_SDL_NET
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
-#else
-	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
-#endif
+	if (Cloud::CloudManager::couldUseLocalServer()) {
+		request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+	} else {
+		request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+	}
 	addRequest(request);
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 2d9f33c..fbb5e69 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -87,11 +87,11 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::Error
 	}
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
-#ifdef USE_SDL_NET
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
-#else
-	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
-#endif
+	if (Cloud::CloudManager::couldUseLocalServer()) {
+		request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
+	} else {
+		request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+	}
 	addRequest(request);
 }
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 5fc10b7..7c1849e 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -88,11 +88,11 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCal
 	}
 	request->addPostField("client_id=" + Common::String(KEY));
 	request->addPostField("client_secret=" + Common::String(SECRET));
-#ifdef USE_SDL_NET
-	request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
-#else
-	request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
-#endif
+	if (Cloud::CloudManager::couldUseLocalServer()) {
+		request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+	} else {
+		request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+	}
 	addRequest(request);
 }
 
diff --git a/gui/options.cpp b/gui/options.cpp
index ee7e584..34044b7 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1320,7 +1320,7 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_serverInfoLabel = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerInfoLabel", _("Not running"));
 
 	uint32 port = Networking::LocalWebserver::getPort();
-	_serverPortDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortDesc", _("Server's port:"), _("Which port is used by server"));
+	_serverPortDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortDesc", _("Server's port:"), _("Which port is used by server\nAuth with server is not available with non-default port"));
 	_serverPort = new EditTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortEditText", Common::String::format("%u", port), 0);
 	_serverPortClearButton = addClearButton(container, "GlobalOptions_Cloud_Container.ServerPortClearButton", kServerPortClearCmd);
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index b411a2e..5838dd1 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -61,7 +61,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 
-	if (couldUseLocalServer()) {
+	if (Cloud::CloudManager::couldUseLocalServer()) {
 		// 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."));
@@ -97,7 +97,7 @@ void StorageWizardDialog::open() {
 		}
 	}
 
-	if (couldUseLocalServer()) {
+	if (Cloud::CloudManager::couldUseLocalServer()) {
 		_stopServerOnClose = !LocalServer.isRunning();
 		LocalServer.start();
 		LocalServer.indexPageHandler().setTarget(this);
@@ -105,7 +105,7 @@ void StorageWizardDialog::open() {
 }
 
 void StorageWizardDialog::close() {
-	if (couldUseLocalServer()) {
+	if (Cloud::CloudManager::couldUseLocalServer()) {
 		if (_stopServerOnClose) LocalServer.stopOnIdle();
 		LocalServer.indexPageHandler().setTarget(nullptr);
 	}
@@ -209,18 +209,10 @@ Common::String StorageWizardDialog::getUrl() const {
 	case Cloud::kStorageBoxId: url += "bx"; break;
 	}
 
-	if (couldUseLocalServer()) url += "s";
+	if (Cloud::CloudManager::couldUseLocalServer()) url += "s";
 	return url;
 }
 
-bool StorageWizardDialog::couldUseLocalServer() const {
-#ifdef USE_SDL_NET
-	return Networking::LocalWebserver::getPort() == Networking::LocalWebserver::DEFAULT_SERVER_PORT;
-#else
-	return false;
-#endif
-}
-
 int StorageWizardDialog::decodeHashchar(char c) {
 	const char HASHCHARS[65] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!";
 	for (uint32 i = 0; i < 64; ++i)
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 26e8cd4..6b00d60 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -51,9 +51,6 @@ class StorageWizardDialog : public Dialog {
 	/** Return short scummvm.org URL for user to navigate to. */
 	Common::String getUrl() const;
 
-	/** Return whether fields should be used or not. */
-	bool couldUseLocalServer() const;
-
 	/**
 	 * Return the value corresponding to the given character.
 	 *


Commit: 9254df2d9614b2cc8e35a3abbdc593e54616a322
    https://github.com/scummvm/scummvm/commit/9254df2d9614b2cc8e35a3abbdc593e54616a322
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix code formatting

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/cloudmanager.h
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/id/idcreatedirectoryrequest.cpp
    backends/cloud/id/idresolveidrequest.cpp
    backends/cloud/iso8601.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.h
    backends/cloud/storagefile.cpp
    backends/networking/browser/openurl-posix.cpp
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/cloudicon_data.h
    backends/networking/curl/cloudicon_disabled_data.h
    backends/networking/curl/connectionmanager.h
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/networkreadstream.h
    backends/networking/curl/request.h
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 537666b..5a4e062 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -37,7 +37,7 @@ namespace Box {
 
 BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
-	 _workingRequest(nullptr), _ignoreCallback(false) {
+	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
 
@@ -91,12 +91,12 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		//TODO: check that error is returned the right way
 		/*
 		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
-			warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
-			error.failed = true;
-			error.response = json->stringify();
-			finishError(error);
-			delete json;
-			return;
+		    warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
+		    error.failed = true;
+		    error.response = json->stringify();
+		    finishError(error);
+		    delete json;
+		    return;
 		}
 		*/
 
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 873e4fa..cd61e04 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -57,9 +57,9 @@ BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
 
 BoxStorage::BoxStorage(Common::String code) {
 	getAccessToken(
-		new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
-		new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
-		code
+	    new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
+	    new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
+	    code
 	);
 }
 
@@ -89,9 +89,9 @@ void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback
 	request->addPostField("client_secret=" + Common::String(SECRET));
 	/*
 	if (Cloud::CloudManager::couldUseLocalServer()) {
-		request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
+	    request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
 	} else {
-		request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+	    request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
 	}
 	*/
 	addRequest(request);
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 89bc470..80a572c 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -68,7 +68,7 @@ public:
 
 	/**
 	* Return unique storage name.
-	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	* @returns  some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const;
 
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index d1f68f1..1449aa9 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -174,11 +174,11 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 			//TODO: check errors
 			/*
 			if (object.contains("error")) {
-				warning("Box returned error: %s", json->stringify(true).c_str());
-				delete json;
-				error.response = json->stringify(true);
-				finishError(error);
-				return;
+			    warning("Box returned error: %s", json->stringify(true).c_str());
+			    delete json;
+			    error.response = json->stringify(true);
+			    finishError(error);
+			    return;
 			}
 			*/
 		}
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 42aa718..aad0133 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -88,8 +88,8 @@ public:
 	 * Replace active Storage.
 	 * @note this method automatically saves the changes with ConfMan.
 	 *
-	 * @param	storage Cloud::Storage to replace active storage with.
-	 * @param	index   one of Cloud::StorageID enum values to indicate what storage type is replaced.
+	 * @param   storage Cloud::Storage to replace active storage with.
+	 * @param   index   one of Cloud::StorageID enum values to indicate what storage type is replaced.
 	 */
 	void replaceStorage(Storage *storage, uint32 index);
 
@@ -100,54 +100,54 @@ public:
 	 * Returns active Storage, which could be used to interact
 	 *  with cloud storage.
 	 *
-	 * @return	active Cloud::Storage or null, if there is no active Storage.
+	 * @return  active Cloud::Storage or null, if there is no active Storage.
 	 */
 	Cloud::Storage *getCurrentStorage() const;
 
 	/**
 	 * Return active Storage's index.
 	 *
-	 * @return	active Storage's index.
+	 * @return  active Storage's index.
 	 */
 	uint32 getStorageIndex() const;
 
 	/**
 	 * Return Storages names as list.
 	 *
-	 * @return	a list of Storages names.
+	 * @return  a list of Storages names.
 	 */
 	Common::StringArray listStorages() const;
 
 	/**
 	 * Changes the storage to the one with given index.
 	 *
-	 * @param	new Storage's index.
+	 * @param   new Storage's index.
 	 */
 	bool switchStorage(uint32 index);
 
 	/**
 	 * Return username used by Storage.
 	 *
-	 * @param	Storage's index.
-	 * @returns	username or "" if index is invalid (no such Storage).
+	 * @param   Storage's index.
+	 * @returns username or "" if index is invalid (no such Storage).
 	 */
 	Common::String getStorageUsername(uint32 index);
 
 	/**
 	 * Return space used by Storage.
 	 *
-	 * @param	Storage's index.
-	 * @returns	used space in bytes or 0 if index is invalid (no such Storage).
+	 * @param   Storage's index.
+	 * @returns used space in bytes or 0 if index is invalid (no such Storage).
 	 */
 	uint64 getStorageUsedSpace(uint32 index);
 
 	/**
 	 * Return Storage's last sync date.
 	 *
-	 * @param	Storage's index.
-	 * @returns	last sync date or "" if index is invalid (no such Storage).
-				It also returns "" if there never was any sync
-				or if storage is syncing right now.
+	 * @param   Storage's index.
+	 * @returns last sync date or "" if index is invalid (no such Storage).
+	            It also returns "" if there never was any sync
+	            or if storage is syncing right now.
 	 */
 	Common::String getStorageLastSync(uint32 index);
 
@@ -155,8 +155,8 @@ public:
 	 * Set Storage's username.
 	 * Automatically saves changes to the config.
 	 *
-	 * @param	index	Storage's index.
-	 * @param	name	username to set
+	 * @param   index   Storage's index.
+	 * @param   name    username to set
 	 */
 	void setStorageUsername(uint32 index, Common::String name);
 
@@ -164,8 +164,8 @@ public:
 	 * Set Storage's used space field.
 	 * Automatically saves changes to the config.
 	 *
-	 * @param	index	Storage's index.
-	 * @param	used	value to set
+	 * @param   index   Storage's index.
+	 * @param   used    value to set
 	 */
 	void setStorageUsedSpace(uint32 index, uint64 used);
 
@@ -173,8 +173,8 @@ public:
 	 * Set Storage's last sync date.
 	 * Automatically saves changes to the config.
 	 *
-	 * @param	index	Storage's index.
-	 * @param	date	date to set
+	 * @param   index   Storage's index.
+	 * @param   date    date to set
 	 */
 	void setStorageLastSync(uint32 index, Common::String date);
 
@@ -182,8 +182,8 @@ public:
 	 * Replace Storage which has given index with a
 	 * storage created with given code.
 	 *
-	 * @param	index	Storage's index
-	 * @param	code	OAuth2 code received from user
+	 * @param   index   Storage's index
+	 * @param   code    OAuth2 code received from user
 	 */
 	void connectStorage(uint32 index, Common::String code);
 
@@ -264,7 +264,7 @@ public:
 };
 
 /** Shortcut for accessing the connection manager. */
-#define CloudMan		Cloud::CloudManager::instance()
+#define CloudMan        Cloud::CloudManager::instance()
 
 } // End of namespace Cloud
 
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 0d308f4..1f46c51 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -64,7 +64,7 @@ public:
 
 	/**
 	* Return unique storage name.
-	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	* @returns  some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const;
 
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index de60480..ebcd167 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -69,7 +69,7 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 	_pendingFiles = response.value;
 
 	// remove all directories
-	for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end(); )
+	for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end();)
 		if (i->isDirectory())
 			_pendingFiles.erase(i);
 		else {
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 7b05d48..3228dde 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -35,7 +35,7 @@ namespace GoogleDrive {
 
 GoogleDriveListDirectoryByIdRequest::GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
-	 _workingRequest(nullptr), _ignoreCallback(false) {
+	_workingRequest(nullptr), _ignoreCallback(false) {
 	start();
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index fbb5e69..3d87be3 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -57,9 +57,9 @@ GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::Strin
 
 GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
 	getAccessToken(
-		new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
-		new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
-		code
+	    new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
+	    new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
+	    code
 	);
 }
 
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index e60719c..6a834c4 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -70,7 +70,7 @@ public:
 
 	/**
 	* Return unique storage name.
-	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	* @returns  some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const;
 
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 4a7c30e..90dc8af 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -218,7 +218,7 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
 	Common::String headers = stream->responseHeaders();
 	const char *cstr = headers.c_str();
 	for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
-		const char *needle = (rangeTry==0 ? "Range: 0-" : "Range: bytes=0-");
+		const char *needle = (rangeTry == 0 ? "Range: 0-" : "Range: bytes=0-");
 		uint32 needleLength = (rangeTry == 0 ? 9 : 15);
 
 		const char *position = strstr(cstr, needle); //if it lost the first part, I refuse to talk with it
diff --git a/backends/cloud/id/idcreatedirectoryrequest.cpp b/backends/cloud/id/idcreatedirectoryrequest.cpp
index f64133c..11f6503 100644
--- a/backends/cloud/id/idcreatedirectoryrequest.cpp
+++ b/backends/cloud/id/idcreatedirectoryrequest.cpp
@@ -104,7 +104,7 @@ void IdCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse
 		Common::String parentId = error.response;
 		for (uint32 i = 0; i < parentId.size(); ++i)
 			if (parentId[i] == '\n') {
-				parentId.erase(0, i+1);
+				parentId.erase(0, i + 1);
 				break;
 			}
 
diff --git a/backends/cloud/id/idresolveidrequest.cpp b/backends/cloud/id/idresolveidrequest.cpp
index abd64df..38478fa 100644
--- a/backends/cloud/id/idresolveidrequest.cpp
+++ b/backends/cloud/id/idresolveidrequest.cpp
@@ -101,7 +101,7 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 	}
 
 	if (!found) {
-		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n")+_currentDirectoryId, 404));
+		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n") + _currentDirectoryId, 404));
 		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
 	}
 }
diff --git a/backends/cloud/iso8601.h b/backends/cloud/iso8601.h
index 6e11322..cdd817b 100644
--- a/backends/cloud/iso8601.h
+++ b/backends/cloud/iso8601.h
@@ -28,8 +28,8 @@
 namespace Cloud {
 namespace ISO8601 {
 
-	/** Returns timestamp corresponding to given ISO 8601 date */
-	uint32 convertToTimestamp(const Common::String &iso8601Date);
+/** Returns timestamp corresponding to given ISO 8601 date */
+uint32 convertToTimestamp(const Common::String &iso8601Date);
 
 } // End of namespace ISO8601
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 7c1849e..a26dec3 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -58,9 +58,9 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user
 
 OneDriveStorage::OneDriveStorage(Common::String code) {
 	getAccessToken(
-		new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
-		new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
-		code
+	    new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
+	    new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
+	    code
 	);
 }
 
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 4985f3f..59c6107 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -67,7 +67,7 @@ public:
 
 	/**
 	* Return unique storage name.
-	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	* @returns  some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const;
 
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 2af4d06..a22dbf0 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -68,7 +68,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
 	if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
-		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/upload.createSession"; //folder must exist
+		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(_savePath) + ":/upload.createSession"; //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
 		Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
@@ -80,7 +80,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 	Common::String url;
 	if (_uploadUrl == "") {
-		url = "https://api.onedrive.com/v1.0/drive/special/approot:/"+ConnMan.urlEncode(_savePath)+":/content";
+		url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(_savePath) + ":/content";
 	} else {
 		url = _uploadUrl;
 	}
@@ -98,14 +98,13 @@ void OneDriveUploadRequest::uploadNextPart() {
 	request->setBuffer(buffer, size);
 
 	if (_uploadUrl != "")
-		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos()-1, _contentsStream->size()));
-	else
-		if (_contentsStream->size() == 0) {
-			warning("\"Sorry, OneDrive can't upload empty files\"");
-			finishUpload(StorageFile(_savePath, 0, 0, false));
-			delete request;
-			return;
-		}
+		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos() - 1, _contentsStream->size()));
+	else if (_contentsStream->size() == 0) {
+		warning("\"Sorry, OneDrive can't upload empty files\"");
+		finishUpload(StorageFile(_savePath, 0, 0, false));
+		delete request;
+		return;
+	}
 
 	_workingRequest = ConnMan.addRequest(request);
 }
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 957b7e6..e3b3aa2 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -181,7 +181,8 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 	Common::String dir = _storage->savesDirectoryPath();
 	if (dir.lastChar() == '/') dir.deleteLastChar();
 	debug(9, "creating %s", dir.c_str());
-	_workingRequest = _storage->createDirectory(dir,
+	_workingRequest = _storage->createDirectory(
+		dir,
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
 	);
@@ -225,7 +226,9 @@ void SavesSyncRequest::downloadNextFile() {
 	sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
 
 	debug(9, "downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
-	_workingRequest = _storage->downloadById(_currentDownloadingFile.id(), DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
+	_workingRequest = _storage->downloadById(
+		_currentDownloadingFile.id(),
+		DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
@@ -270,14 +273,18 @@ void SavesSyncRequest::uploadNextFile() {
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
 
-	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress()*100));
+	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
 	if (_storage->uploadStreamSupported()) {
-		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
+		_workingRequest = _storage->upload(
+			_storage->savesDirectoryPath() + _currentUploadingFile,
+			g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
 			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
 			new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
 		);
 	} else {
-		_workingRequest = _storage->upload(_storage->savesDirectoryPath() + _currentUploadingFile, DefaultSaveFileManager::concatWithSavesPath(_currentUploadingFile),
+		_workingRequest = _storage->upload(
+			_storage->savesDirectoryPath() + _currentUploadingFile,
+			DefaultSaveFileManager::concatWithSavesPath(_currentUploadingFile),
 			new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
 			new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
 		);
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 8582a75..6a5765f 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -111,7 +111,7 @@ public:
 
 	/**
 	* Return unique storage name.
-	* @returns	some unique storage name (for example, "Dropbox (user at example.com)")
+	* @returns  some unique storage name (for example, "Dropbox (user at example.com)")
 	*/
 	virtual Common::String name() const = 0;
 
@@ -155,7 +155,7 @@ public:
 	 * Calls the <errorCallback> if failed to get information.
 	 *
 	 * @note on success Storage should also call
-	 *	     CloudMan.setStorageUsername().
+	 *       CloudMan.setStorageUsername().
 	 */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
 
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index 4bee92e..c1107ea 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -42,7 +42,7 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 		uint32 i = _name.size() - 1;
 		while (true) {
 			if (_name[i] == '/' || _name[i] == '\\') {
-				_name.erase(0, i+1);
+				_name.erase(0, i + 1);
 				break;
 			}
 			if (i == 0) break;
diff --git a/backends/networking/browser/openurl-posix.cpp b/backends/networking/browser/openurl-posix.cpp
index 0e56119..429a379 100644
--- a/backends/networking/browser/openurl-posix.cpp
+++ b/backends/networking/browser/openurl-posix.cpp
@@ -31,45 +31,45 @@ namespace Browser {
 
 namespace {
 bool launch(const Common::String client, const Common::String &url) {
-    // FIXME: system's input must be heavily escaped
-    // well, when url's specified by user
-    // it's OK now (urls are hardcoded somewhere in GUI)
-    Common::String cmd = client + " " + url;
-    return (system(cmd.c_str()) != -1);
+	// FIXME: system's input must be heavily escaped
+	// well, when url's specified by user
+	// it's OK now (urls are hardcoded somewhere in GUI)
+	Common::String cmd = client + " " + url;
+	return (system(cmd.c_str()) != -1);
 }
 }
 
 bool openUrl(const Common::String &url) {
-    // inspired by Qt's "qdesktopservices_x11.cpp"
+	// inspired by Qt's "qdesktopservices_x11.cpp"
 
-    // try "standards"
-    if (launch("xdg-open", url))
-        return true;
-    if (launch(getenv("DEFAULT_BROWSER"), url))
-        return true;
-    if (launch(getenv("BROWSER"), url))
-        return true;
+	// try "standards"
+	if (launch("xdg-open", url))
+		return true;
+	if (launch(getenv("DEFAULT_BROWSER"), url))
+		return true;
+	if (launch(getenv("BROWSER"), url))
+		return true;
 
-    // try desktop environment specific tools
-    if (launch("gnome-open", url)) // gnome
-        return true;
-    if (launch("kfmclient openURL", url)) // kde
-        return true;
-    if (launch("exo-open", url)) // xfce
-        return true;
+	// try desktop environment specific tools
+	if (launch("gnome-open", url)) // gnome
+		return true;
+	if (launch("kfmclient openURL", url)) // kde
+		return true;
+	if (launch("exo-open", url)) // xfce
+		return true;
 
-    // try browser names
-    if (launch("firefox", url))
-        return true;
-    if (launch("mozilla", url))
-        return true;
-    if (launch("netscape", url))
-        return true;
-    if (launch("opera", url))
-        return true;
+	// try browser names
+	if (launch("firefox", url))
+		return true;
+	if (launch("mozilla", url))
+		return true;
+	if (launch("netscape", url))
+		return true;
+	if (launch("opera", url))
+		return true;
 
-    warning("Networking::Browser::openUrl() (POSIX) failed to open URL");
-    return false;
+	warning("Networking::Browser::openUrl() (POSIX) failed to open URL");
+	return false;
 }
 
 } // End of namespace Browser
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 5178522..80e2616 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -82,7 +82,7 @@ bool CloudIcon::draw() {
 
 	if (g_system) {
 		Graphics::TransparentSurface *surface = &_icon;
-		makeAlphaIcon((_showingDisabled? _disabledIcon:_icon), _currentAlpha);
+		makeAlphaIcon((_showingDisabled ? _disabledIcon : _icon), _currentAlpha);
 		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
 		if (surface && surface->getPixels()) {
 			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
diff --git a/backends/networking/curl/cloudicon_data.h b/backends/networking/curl/cloudicon_data.h
index a924dc8..21d8818 100644
--- a/backends/networking/curl/cloudicon_data.h
+++ b/backends/networking/curl/cloudicon_data.h
@@ -25,87 +25,87 @@
 // The tool is from https://github.com/pinard/Recode
 
 byte cloudicon_data[] = {
-137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
- 82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
-122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
-124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
- 18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
- 69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
-101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
-  0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
-  0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
-115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4,  50,  73,  68,
- 65,  84,  88, 133, 197, 151, 109, 104, 150, 101,  20, 199, 127, 247, 227,
-179, 105,  51,  23,  65, 181, 150, 224, 154, 214, 132, 194, 249,  33, 165,
- 22, 189, 231, 194, 210, 250,  16, 171, 180,  55,  42, 152,  68,  65, 100,
- 52, 233,   5, 146, 144, 144,  26, 249, 169,  62, 164,  80,  89, 152,  25,
- 18, 226,  42,  49,  87,  88, 180,  94,  96,  96, 246, 234, 180,  70,  50,
- 66, 214,  55, 247,  22, 133, 247, 255, 244, 225,  58, 247, 158, 107, 143,
-219, 243,  60, 186, 192,   3, 135, 251, 220, 215, 117, 223, 231, 127, 238,
-235, 252, 239, 235,  58,  39, 105, 250, 206,  56, 147, 146,  55,  85, 252,
-108,  13, 112,  59, 176,  12,  88,   2, 204,   7, 230, 248, 220,  48, 208,
- 15, 244,   2, 221,  64,  23,  48,  86, 137, 211, 228, 146, 158, 178,  43,
- 80,   7, 172,   3, 218, 129, 179,  43, 241,   9, 140,   0, 155, 129,  87,
-128, 193, 146,  15,  47, 248, 178, 100,   0, 237,  64,  39, 112,  14,  96,
-174,  20, 217, 153, 228, 162,   0,  50,  25,   2,  58, 128,  45,  83,   1,
-228,  53, 121,  10, 170, 129, 183, 128, 213, 126,  47, 215,  49, 224,  39,
-224,  19, 160,   7,  56,   2, 204,   0, 154, 128,  27, 128, 229, 110, 215,
-120,  64, 181, 132, 149, 184,  30, 120,   4, 248, 183,  24,  40, 185, 248,
-243, 147,  86, 160, 154, 144, 195,  91, 252,  43,   5, 140,   2,  31,   2,
- 27, 129, 195,  83, 125, 141,  75,  19, 240,   2, 129,  47, 179,  61, 144,
-  4, 216,   7, 172,  44,  14,  34, 105, 232,  62,  41, 128, 109, 192, 189,
- 14, 126,   2,  24,   0,  30,   3, 246, 150,   1,  46, 150,  54,  15, 184,
-  1, 200, 251, 216, 123, 192, 253,  19,   2, 152, 183, 119,  66,   0, 237,
-192,  27, 110, 159,   0, 250, 128, 187, 128,  67, 167,   8, 158, 201,  98,
-224,   3, 160, 209, 131,  72, 128,  53,  68, 156, 200, 153, 192, 181, 206,
- 68, 167, 219,  50,  49,  96, 226,  78,  19, 135, 162, 103, 138, 117, 169,
-137,  46,  19, 163,  38,  82,  19,  63, 152, 120, 220, 196,  12, 159,  63,
-104,  98, 149, 137,  99, 238, 211,  28, 163,  46, 243, 145, 147, 192, 117,
-157,  68, 173, 219, 195,  18, 143,  74,  28, 137, 230, 139, 181,  77, 162,
- 71,  98, 165,  68, 141,  68,  78,  98, 145, 196, 107,  18,  59, 252,  30,
-137,   3,  18, 207,  72, 140, 249, 125, 173,  99,  33,  65, 114, 209, 110,
-131, 192, 218,  65,   2, 105,   4, 108, 245, 165,  74, 125, 165, 206,   5,
-214,   2, 151,   3,   7, 128, 119, 128,  95, 253, 189, 169, 228, 105, 224,
- 85, 183, 171, 128,  29, 192,  29,   4,  82, 142,  18, 246, 151, 177, 164,
-126, 151,   1, 220,   3, 188,  79,  32, 222,  40,  97, 167, 235, 243, 151,
-207,   2, 190,   5, 154,  35, 231, 223,   0,  45, 101, 242, 127,  12, 152,
- 75,  97, 191,  88,  12, 124, 237, 254,  18,  96,  21, 176,  35, 227, 192,
- 50, 207,  15,  38, 126,  49, 113,  56, 202, 243, 221,  38, 154, 139, 114,
-223,  82, 130,  23, 153, 214, 155,  88,  20, 221, 255, 104, 226, 104, 116,
-223, 106,  42, 144, 112, 169, 137, 196, 131, 248,  56,  10, 166, 222, 196,
-141,  21, 128,  77, 165, 223, 155, 120,  42,  34, 246, 158, 200, 247,  18,
- 19, 228,  21, 178,  60, 223, 151,  41,   1, 190, 112, 251,  60, 224, 171,
-104, 238, 116,  36,  33, 240, 224,  32, 240,  25, 176,  31, 120, 194, 199,
- 27, 161, 112,  26, 102, 167, 154,   1, 127, 186, 253,  98,   9, 240,  81,
-  2,  97,  43, 149, 231,  61, 128, 129, 104, 108,  78,  28,  64,  44,  25,
-105, 218,  74,  56, 156,  13, 252,  76, 248,  43,  42, 145, 140, 176, 249,
-226, 137, 188, 133,  20,  12, 123,  68,   9, 225, 171, 127,   7,  46,  40,
-227, 180,  82, 112, 128, 153, 126, 189, 148, 194, 105,  57,  12,   5,  18,
-246,  71,  68, 185, 214, 237, 191, 166,  65, 190,  98, 237, 243, 235, 205,
-209,  88, 127, 188,  19, 246, 250,  53,  39, 177, 194, 237, 157,  37, 118,
-193,  83, 213, 183,  37, 102,  73,  92,  39, 145, 196, 152,  57,  75, 193,
- 82, 246, 249,  21,  75,  89, 104,  41, 205, 150, 178, 222,  82, 250, 163,
-241, 211, 213, 253, 150, 178, 201,  82, 174, 180, 148, 185, 150, 146, 248,
-120, 183, 165, 133,  20, 116, 153,  24, 113, 123, 150, 137, 103,  77,  28,
- 55, 113, 141, 137, 173,  38, 134,  60,  61, 167, 178, 236,   3,  38,  54,
-152, 184, 213, 255, 253,  39, 221,  55, 142, 213, 101,  42, 252,   5,  99,
-132, 202, 101,  45,  97, 175,  94,  14, 172,   0, 118,   1,  15, 185,  78,
- 71,  86,  19, 138, 217, 196, 117, 179,  99, 146, 204, 126, 125, 188,  30,
-168,  35, 236, 255, 181, 132,   3, 233,  15, 224,  54, 202,  87,  64, 229,
-164,   5, 216,   9,  92, 232, 224,  67, 192,  66, 188,  88, 205,  69, 185,
- 26, 180, 148,  14, 207,  81, 206,  82, 230,  89, 202, 110,  75, 185, 108,
- 26, 249, 191, 202,  82, 222, 181, 148, 243, 163, 220, 119,  56,  22, 150,
- 78, 172,   7, 144, 216,  34, 177, 205, 237, 188, 196,   2, 137,  61,  18,
- 15, 158,   6, 243,  31, 144, 216,  37, 209, 224, 190,  18, 137, 237, 142,
- 49, 254,  92,  50, 115, 211, 164,  69, 233,  71,  64,  43, 140,  23, 165,
-127,  19,  26, 142, 231,   8, 117, 192,  84,  82,  69,  56, 118, 215,   3,
- 55,  17,  54, 160, 172,  40, 253, 148,  80, 168,  78,  44,  74, 171,  59,
- 39, 237,  11, 170, 129,  55, 129, 251,  40, 108, 205,   2, 254,   1, 126,
-243,  96, 122, 129, 163,  62, 223,   0,  92, 237, 160, 141,  17, 112,  38,
-219, 129, 135, 139, 193,   1, 146, 170, 151,  43, 106,  76, 106, 139, 198,
-229, 192,  73,  20,  96,  12, 152, 177, 253,  56, 101,  26, 147,  36, 191,
-177, 226, 214, 108,  13, 133, 214, 172, 220,  75,  35,  14,  90, 190,  53,
-203, 189,  84, 113, 119, 156,  53, 167, 173, 192,  21, 252,  95, 205, 105,
-178, 225, 204, 182, 231, 255,   1, 200,  91, 112, 221, 160, 249,  68,  42,
-  0,   0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
+	137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
+	82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
+	122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
+	124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
+	18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
+	69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
+	101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
+	0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
+	0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
+	115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4,  50,  73,  68,
+	65,  84,  88, 133, 197, 151, 109, 104, 150, 101,  20, 199, 127, 247, 227,
+	179, 105,  51,  23,  65, 181, 150, 224, 154, 214, 132, 194, 249,  33, 165,
+	22, 189, 231, 194, 210, 250,  16, 171, 180,  55,  42, 152,  68,  65, 100,
+	52, 233,   5, 146, 144, 144,  26, 249, 169,  62, 164,  80,  89, 152,  25,
+	18, 226,  42,  49,  87,  88, 180,  94,  96,  96, 246, 234, 180,  70,  50,
+	66, 214,  55, 247,  22, 133, 247, 255, 244, 225,  58, 247, 158, 107, 143,
+	219, 243,  60, 186, 192,   3, 135, 251, 220, 215, 117, 223, 231, 127, 238,
+	235, 252, 239, 235,  58,  39, 105, 250, 206,  56, 147, 146,  55,  85, 252,
+	108,  13, 112,  59, 176,  12,  88,   2, 204,   7, 230, 248, 220,  48, 208,
+	15, 244,   2, 221,  64,  23,  48,  86, 137, 211, 228, 146, 158, 178,  43,
+	80,   7, 172,   3, 218, 129, 179,  43, 241,   9, 140,   0, 155, 129,  87,
+	128, 193, 146,  15,  47, 248, 178, 100,   0, 237,  64,  39, 112,  14,  96,
+	174,  20, 217, 153, 228, 162,   0,  50,  25,   2,  58, 128,  45,  83,   1,
+	228,  53, 121,  10, 170, 129, 183, 128, 213, 126,  47, 215,  49, 224,  39,
+	224,  19, 160,   7,  56,   2, 204,   0, 154, 128,  27, 128, 229, 110, 215,
+	120,  64, 181, 132, 149, 184,  30, 120,   4, 248, 183,  24,  40, 185, 248,
+	243, 147,  86, 160, 154, 144, 195,  91, 252,  43,   5, 140,   2,  31,   2,
+	27, 129, 195,  83, 125, 141,  75,  19, 240,   2, 129,  47, 179,  61, 144,
+	4, 216,   7, 172,  44,  14,  34, 105, 232,  62,  41, 128, 109, 192, 189,
+	14, 126,   2,  24,   0,  30,   3, 246, 150,   1,  46, 150,  54,  15, 184,
+	1, 200, 251, 216, 123, 192, 253,  19,   2, 152, 183, 119,  66,   0, 237,
+	192,  27, 110, 159,   0, 250, 128, 187, 128,  67, 167,   8, 158, 201,  98,
+	224,   3, 160, 209, 131,  72, 128,  53,  68, 156, 200, 153, 192, 181, 206,
+	68, 167, 219,  50,  49,  96, 226,  78,  19, 135, 162, 103, 138, 117, 169,
+	137,  46,  19, 163,  38,  82,  19,  63, 152, 120, 220, 196,  12, 159,  63,
+	104,  98, 149, 137,  99, 238, 211,  28, 163,  46, 243, 145, 147, 192, 117,
+	157,  68, 173, 219, 195,  18, 143,  74,  28, 137, 230, 139, 181,  77, 162,
+	71,  98, 165,  68, 141,  68,  78,  98, 145, 196, 107,  18,  59, 252,  30,
+	137,   3,  18, 207,  72, 140, 249, 125, 173,  99,  33,  65, 114, 209, 110,
+	131, 192, 218,  65,   2, 105,   4, 108, 245, 165,  74, 125, 165, 206,   5,
+	214,   2, 151,   3,   7, 128, 119, 128,  95, 253, 189, 169, 228, 105, 224,
+	85, 183, 171, 128,  29, 192,  29,   4,  82, 142,  18, 246, 151, 177, 164,
+	126, 151,   1, 220,   3, 188,  79,  32, 222,  40,  97, 167, 235, 243, 151,
+	207,   2, 190,   5, 154,  35, 231, 223,   0,  45, 101, 242, 127,  12, 152,
+	75,  97, 191,  88,  12, 124, 237, 254,  18,  96,  21, 176,  35, 227, 192,
+	50, 207,  15,  38, 126,  49, 113,  56, 202, 243, 221,  38, 154, 139, 114,
+	223,  82, 130,  23, 153, 214, 155,  88,  20, 221, 255, 104, 226, 104, 116,
+	223, 106,  42, 144, 112, 169, 137, 196, 131, 248,  56,  10, 166, 222, 196,
+	141,  21, 128,  77, 165, 223, 155, 120,  42,  34, 246, 158, 200, 247,  18,
+	19, 228,  21, 178,  60, 223, 151,  41,   1, 190, 112, 251,  60, 224, 171,
+	104, 238, 116,  36,  33, 240, 224,  32, 240,  25, 176,  31, 120, 194, 199,
+	27, 161, 112,  26, 102, 167, 154,   1, 127, 186, 253,  98,   9, 240,  81,
+	2,  97,  43, 149, 231,  61, 128, 129, 104, 108,  78,  28,  64,  44,  25,
+	105, 218,  74,  56, 156,  13, 252,  76, 248,  43,  42, 145, 140, 176, 249,
+	226, 137, 188, 133,  20,  12, 123,  68,   9, 225, 171, 127,   7,  46,  40,
+	227, 180,  82, 112, 128, 153, 126, 189, 148, 194, 105,  57,  12,   5,  18,
+	246,  71,  68, 185, 214, 237, 191, 166,  65, 190,  98, 237, 243, 235, 205,
+	209,  88, 127, 188,  19, 246, 250,  53,  39, 177, 194, 237, 157,  37, 118,
+	193,  83, 213, 183,  37, 102,  73,  92,  39, 145, 196, 152,  57,  75, 193,
+	82, 246, 249,  21,  75,  89, 104,  41, 205, 150, 178, 222,  82, 250, 163,
+	241, 211, 213, 253, 150, 178, 201,  82, 174, 180, 148, 185, 150, 146, 248,
+	120, 183, 165, 133,  20, 116, 153,  24, 113, 123, 150, 137, 103,  77,  28,
+	55, 113, 141, 137, 173,  38, 134,  60,  61, 167, 178, 236,   3,  38,  54,
+	152, 184, 213, 255, 253,  39, 221,  55, 142, 213, 101,  42, 252,   5,  99,
+	132, 202, 101,  45,  97, 175,  94,  14, 172,   0, 118,   1,  15, 185,  78,
+	71,  86,  19, 138, 217, 196, 117, 179,  99, 146, 204, 126, 125, 188,  30,
+	168,  35, 236, 255, 181, 132,   3, 233,  15, 224,  54, 202,  87,  64, 229,
+	164,   5, 216,   9,  92, 232, 224,  67, 192,  66, 188,  88, 205,  69, 185,
+	26, 180, 148,  14, 207,  81, 206,  82, 230,  89, 202, 110,  75, 185, 108,
+	26, 249, 191, 202,  82, 222, 181, 148, 243, 163, 220, 119,  56,  22, 150,
+	78, 172,   7, 144, 216,  34, 177, 205, 237, 188, 196,   2, 137,  61,  18,
+	15, 158,   6, 243,  31, 144, 216,  37, 209, 224, 190,  18, 137, 237, 142,
+	49, 254,  92,  50, 115, 211, 164,  69, 233,  71,  64,  43, 140,  23, 165,
+	127,  19,  26, 142, 231,   8, 117, 192,  84,  82,  69,  56, 118, 215,   3,
+	55,  17,  54, 160, 172,  40, 253, 148,  80, 168,  78,  44,  74, 171,  59,
+	39, 237,  11, 170, 129,  55, 129, 251,  40, 108, 205,   2, 254,   1, 126,
+	243,  96, 122, 129, 163,  62, 223,   0,  92, 237, 160, 141,  17, 112,  38,
+	219, 129, 135, 139, 193,   1, 146, 170, 151,  43, 106,  76, 106, 139, 198,
+	229, 192,  73,  20,  96,  12, 152, 177, 253,  56, 101,  26, 147,  36, 191,
+	177, 226, 214, 108,  13, 133, 214, 172, 220,  75,  35,  14,  90, 190,  53,
+	203, 189,  84, 113, 119, 156,  53, 167, 173, 192,  21, 252,  95, 205, 105,
+	178, 225, 204, 182, 231, 255,   1, 200,  91, 112, 221, 160, 249,  68,  42,
+	0,   0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
 };
diff --git a/backends/networking/curl/cloudicon_disabled_data.h b/backends/networking/curl/cloudicon_disabled_data.h
index fc1638c..4340a8a 100644
--- a/backends/networking/curl/cloudicon_disabled_data.h
+++ b/backends/networking/curl/cloudicon_disabled_data.h
@@ -25,93 +25,93 @@
 // The tool is from https://github.com/pinard/Recode
 
 byte cloudicon_disabled_data[] = {
-137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
- 82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
-122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
-124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
- 18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
- 69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
-101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
-  0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
-  0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
-115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4, 139,  73,  68,
- 65,  84,  88, 133, 197, 215,  91, 168,  86,  69,  20,   7, 240, 223, 254,
-244, 120, 236, 120,  57,  26,  94,  10,  31,  34,  35,  11,  36, 203,  74,
-212, 160, 204, 212, 110, 166, 248, 208, 205, 172, 151, 144, 136, 158,  82,
- 80, 168,  32, 233, 165, 160,  32, 130, 236, 165,  34, 204,  74,  18,  52,
- 36, 169, 232,  34,  24, 221, 243, 218, 133,  74,  77, 195,  44, 200,  91,
-122, 188, 156, 172, 102, 159, 221, 195, 204, 246, 219, 126, 231, 226,  17,
-138,  22,  12, 123, 246, 236, 153, 245,  95, 107, 205, 127, 205, 172, 157,
- 21, 254,  95, 233,  27, 122,  63, 183,   5, 179,  48,  13,  87,  98,  52,
-  6, 167, 111,  71, 176,  11,  27, 177,  14, 107, 209, 222,  27, 165, 217,
-137, 211, 207,  25, 137, 197, 152, 143,  65, 189, 209, 137, 163, 120,   1,
- 79,  98, 111, 143, 147, 143, 247, 172, 108,  62, 158, 194,  16,  20, 169,
-105, 232, 151,  82, 171,  24,  80,  74,  27,  22, 225, 197, 110,  13, 104,
-235, 122, 188,  31,  94, 194,  93,  21, 192,  14,  49, 172, 223, 226, 109,
-124, 130,  29, 232, 131,  49, 184,  22,  55, 166, 126,  75, 131,  65, 175,
-225,  94, 252, 213, 201, 128, 131,  93, 131, 191, 137,  27,  42, 192, 199,
-241,   6, 158, 192, 246, 238, 188,  73,  50,   6, 143,  98,  78,  50, 164,
-140, 200, 123,  34, 135,  78,  49,  34, 219, 215,  89, 193, 171, 152, 151,
-192,   3, 246, 224,   1, 188, 123,  26, 224, 170,  92, 134, 143,  49, 160,
- 50,  86,  96,   5, 238, 174,  78, 108, 204, 130, 249,  98, 216,  75, 240,
-109, 184,  13,  63, 156,   1, 248,  68,  44,  21, 189,  47, 165,  67, 140,
-196,  60, 172,  87, 225,  68, 182, 167,  62, 105, 100,   2, 106,  77,  11,
-118, 139, 123, 186, 163,  59, 164,  53,  76,  16, 195, 125,  29, 250,  15,
-227, 167,  73,  12, 237,  27,  73,  91,  75, 142,  28,  21, 185,  51,  60,
-141, 181, 225,  98,  41,  59, 106,  33, 185,  26,  88,  28, 104,  77, 253,
-163, 129, 251,   3,  59,  42, 223,  79, 105, 171, 184,  53, 240,  73, 224,
-150,  64, 203,  32, 106,  99, 185, 160, 224, 236,  16, 245, 118,   4,  54,
-  5, 166,   4,  22,   6, 218, 211, 218, 214, 132,  37,  32,  75, 238, 181,
-224,  55,  12,  76, 222, 191, 140, 251, 144,  39,  79, 135,  98,   1, 198,
- 98,  11, 150, 227, 251,  50, 204,  67, 113, 121,  90, 156,  57, 185, 127,
-135, 154, 184,   9,  95, 160,   9,  43,  49,  59,  69, 225,  24, 206,  65,
-123, 150,  54, 247,  14, 188, 158, 214,  30,  23,  79, 186, 109,   9, 252,
- 44, 124, 142, 113, 149, 232, 127, 134, 201, 196,  88,  79,  66, 115,   5,
-252,  24, 182, 114, 224,  32,  35, 230, 212, 207, 139,  75, 241, 169, 168,
- 47, 195, 157,  88,  89, 110, 193, 180,  64, 145, 250, 223,   5, 182,  87,
-194, 125, 123,  96,  92, 195,  22,  76,  14, 201, 227, 177, 226,  65, 144,
-227, 111,  28, 198, 151, 113, 131, 135,   5,  46, 169, 172, 249,  38, 176,
-187, 242,  62,  35, 160, 150, 199, 197,  19, 114, 178, 156,  34, 231, 173,
-244, 180, 154, 115, 115, 166, 166,  57, 167, 180, 193,  34,   3,   7, 165,
- 61,  11, 137, 105,  27, 112, 160,  62, 111, 235, 106,  22, 166, 126,  71,
-206,  59, 165, 238, 156,  43, 114, 100, 155,  98, 120, 218, 146, 206,   2,
- 83, 241, 225,  26, 134, 165, 253,  27, 173,  65, 134, 138, 137, 222, 154,
-222, 139, 228, 249,  87, 233, 217, 133,  76, 159,  19,  47, 169,  89, 226,
-129, 214,  71, 188, 192, 134, 148, 231,  64, 121, 171,  21, 248,  85, 244,
-232, 177,  30, 192,  59,  90,  82,   6, 193,   9, 108, 234,  30,  28,  30,
-  9, 209, 128,  74, 214,  71, 204, 190, 121, 231, 201,   5, 228, 220, 218,
-248,  97, 136, 200, 246, 102, 106,  29, 234,  73, 190,   5, 135, 186,   7,
-135, 201,   9, 167, 111, 227, 135,  50,   2,  71, 146,  69, 153, 232, 245,
-206, 192, 136, 234, 196,  86,  49,  13, 154, 162, 113,  39, 211, 101,  51,
-126, 239,  25,  28, 154,  19, 206, 133, 234, 119, 195,  17, 234,  36, 220,
- 85,  33, 202, 213, 169, 191, 175,  36,  92, 139, 152, 106,   3, 212,   9,
-119,  76, 100, 251, 126, 157,   9, 218,  69, 219, 150, 158, 211,  42,  99,
-187, 114, 245, 147, 112,  83, 122, 214,   2,  51,  83, 127,  85,  16,  89,
- 62,  94,  60,  61, 202,  20,  58,  36,  38, 244, 126,  93, 159, 146,  93,
-180, 101, 129, 254, 129, 107,   2,  89,  26, 219,  24,  42,   6, 188,  95,
-153, 124,  81, 202, 251,  37,   3, 249, 101, 188, 120, 114, 148, 223, 219,
-197, 212,  56, 208, 123, 240, 245, 129, 167,   3,  19,   3, 163,  42,   6,
-172,  11, 234,  36,  92,  43, 242, 105,  32, 250, 227, 161,  89,  44,  45,
- 98, 196, 139,  14, 178, 146, 112,  27, 210, 179,  23, 178,   7, 203, 240,
-248, 156, 152, 251,  15,  38, 221, 146, 138, 181, 212,  73, 216,  46, 214,
-112,  11,  82, 180, 103, 138,  71, 237,  40, 145,  52,  29, 216, 221, 194,
-220,  41,  49,   0, 103,  36, 129, 185, 152, 158, 116, 101,   9, 171,  29,
-178,  85, 245, 121, 213, 235, 184,  90, 215, 229, 248,  89, 172, 112, 190,
- 62,  83, 112, 209, 145,  85, 226, 229, 147, 105, 188, 142,  43, 172, 220,
-155, 179,  40,  29, 201,  85,   6, 239, 204, 153, 155, 243, 117,  47, 216,
-222, 216,  38, 229, 188, 146,  51, 188, 162, 119,  81, 194,  82, 205, 130,
-178, 189,  24, 120, 173,  97, 108,  80,  34, 102, 111,  73,  87, 182, 123,
-  2, 107,   2, 231, 133, 184, 213,  89,  96,  69, 194,  56,  57,  47,  91,
-222,  57, 100, 253,  68, 130,  92, 175,  94, 148, 254, 129,  15, 240, 176,
- 88,   7, 116,  39,  77, 226, 181, 187,  68, 172, 146, 154,  69,  78, 101,
- 98,  77,  57,  91,  99,  81, 250,  82, 215, 138, 202, 178, 188,  44,  78,
- 37,  67, 254, 196, 143, 201, 152, 141,  98, 217,  86, 224,  60,  92, 149,
- 64, 207, 175,   0, 151, 178,  66, 119, 101, 249, 243,  61, 184, 163, 254,
- 99, 210, 218,  48,  94,  94,   5, 101,  13, 162,   1, 176, 100, 251,  97,
-167, 249,  49, 233, 115, 179, 250, 111,  78,  23, 109, 115, 193, 178, 130,
- 62,   5, 151,  20,  52,  23, 241,  76, 200,  10, 106, 149, 103, 173,  50,
-158,  21,  28,  45, 120, 174,  96, 110, 193,  71,  61, 232, 151,  61, 219,
-115,   4, 170,  82, 254, 156, 206,  16,  47, 197, 127, 231, 231, 244, 153,
-222,  27, 240, 159, 200,  63, 153, 185,  24, 191, 162, 246,  71, 153,   0,
-  0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
+	137,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,
+	82,   0,   0,   0,  32,   0,   0,   0,  32,   8,   6,   0,   0,   0, 115,
+	122, 122, 244,   0,   0,   0,   4, 115,  66,  73,  84,   8,   8,   8,   8,
+	124,   8, 100, 136,   0,   0,   0,   9, 112,  72,  89, 115,   0,   0,  11,
+	18,   0,   0,  11,  18,   1, 210, 221, 126, 252,   0,   0,   0,  22, 116,
+	69,  88, 116,  67, 114, 101,  97, 116, 105, 111, 110,  32,  84, 105, 109,
+	101,   0,  48,  54,  47,  48,  51,  47,  49,  54, 159, 192, 233, 192,   0,
+	0,   0,  28, 116,  69,  88, 116,  83, 111, 102, 116, 119,  97, 114, 101,
+	0,  65, 100, 111,  98, 101,  32,  70, 105, 114, 101, 119, 111, 114, 107,
+	115,  32,  67,  83,  54, 232, 188, 178, 140,   0,   0,   4, 139,  73,  68,
+	65,  84,  88, 133, 197, 215,  91, 168,  86,  69,  20,   7, 240, 223, 254,
+	244, 120, 236, 120,  57,  26,  94,  10,  31,  34,  35,  11,  36, 203,  74,
+	212, 160, 204, 212, 110, 166, 248, 208, 205, 172, 151, 144, 136, 158,  82,
+	80, 168,  32, 233, 165, 160,  32, 130, 236, 165,  34, 204,  74,  18,  52,
+	36, 169, 232,  34,  24, 221, 243, 218, 133,  74,  77, 195,  44, 200,  91,
+	122, 188, 156, 172, 102, 159, 221, 195, 204, 246, 219, 126, 231, 226,  17,
+	138,  22,  12, 123, 246, 236, 153, 245,  95, 107, 205, 127, 205, 172, 157,
+	21, 254,  95, 233,  27, 122,  63, 183,   5, 179,  48,  13,  87,  98,  52,
+	6, 167, 111,  71, 176,  11,  27, 177,  14, 107, 209, 222,  27, 165, 217,
+	137, 211, 207,  25, 137, 197, 152, 143,  65, 189, 209, 137, 163, 120,   1,
+	79,  98, 111, 143, 147, 143, 247, 172, 108,  62, 158, 194,  16,  20, 169,
+	105, 232, 151,  82, 171,  24,  80,  74,  27,  22, 225, 197, 110,  13, 104,
+	235, 122, 188,  31,  94, 194,  93,  21, 192,  14,  49, 172, 223, 226, 109,
+	124, 130,  29, 232, 131,  49, 184,  22,  55, 166, 126,  75, 131,  65, 175,
+	225,  94, 252, 213, 201, 128, 131,  93, 131, 191, 137,  27,  42, 192, 199,
+	241,   6, 158, 192, 246, 238, 188,  73,  50,   6, 143,  98,  78,  50, 164,
+	140, 200, 123,  34, 135,  78,  49,  34, 219, 215,  89, 193, 171, 152, 151,
+	192,   3, 246, 224,   1, 188, 123,  26, 224, 170,  92, 134, 143,  49, 160,
+	50,  86,  96,   5, 238, 174,  78, 108, 204, 130, 249,  98, 216,  75, 240,
+	109, 184,  13,  63, 156,   1, 248,  68,  44,  21, 189,  47, 165,  67, 140,
+	196,  60, 172,  87, 225,  68, 182, 167,  62, 105, 100,   2, 106,  77,  11,
+	118, 139, 123, 186, 163,  59, 164,  53,  76,  16, 195, 125,  29, 250,  15,
+	227, 167,  73,  12, 237,  27,  73,  91,  75, 142,  28,  21, 185,  51,  60,
+	141, 181, 225,  98,  41,  59, 106,  33, 185,  26,  88,  28, 104,  77, 253,
+	163, 129, 251,   3,  59,  42, 223,  79, 105, 171, 184,  53, 240,  73, 224,
+	150,  64, 203,  32, 106,  99, 185, 160, 224, 236,  16, 245, 118,   4,  54,
+	5, 166,   4,  22,   6, 218, 211, 218, 214, 132,  37,  32,  75, 238, 181,
+	224,  55,  12,  76, 222, 191, 140, 251, 144,  39,  79, 135,  98,   1, 198,
+	98,  11, 150, 227, 251,  50, 204,  67, 113, 121,  90, 156,  57, 185, 127,
+	135, 154, 184,   9,  95, 160,   9,  43,  49,  59,  69, 225,  24, 206,  65,
+	123, 150,  54, 247,  14, 188, 158, 214,  30,  23,  79, 186, 109,   9, 252,
+	44, 124, 142, 113, 149, 232, 127, 134, 201, 196,  88,  79,  66, 115,   5,
+	252,  24, 182, 114, 224,  32,  35, 230, 212, 207, 139,  75, 241, 169, 168,
+	47, 195, 157,  88,  89, 110, 193, 180,  64, 145, 250, 223,   5, 182,  87,
+	194, 125, 123,  96,  92, 195,  22,  76,  14, 201, 227, 177, 226,  65, 144,
+	227, 111,  28, 198, 151, 113, 131, 135,   5,  46, 169, 172, 249,  38, 176,
+	187, 242,  62,  35, 160, 150, 199, 197,  19, 114, 178, 156,  34, 231, 173,
+	244, 180, 154, 115, 115, 166, 166,  57, 167, 180, 193,  34,   3,   7, 165,
+	61,  11, 137, 105,  27, 112, 160,  62, 111, 235, 106,  22, 166, 126,  71,
+	206,  59, 165, 238, 156,  43, 114, 100, 155,  98, 120, 218, 146, 206,   2,
+	83, 241, 225,  26, 134, 165, 253,  27, 173,  65, 134, 138, 137, 222, 154,
+	222, 139, 228, 249,  87, 233, 217, 133,  76, 159,  19,  47, 169,  89, 226,
+	129, 214,  71, 188, 192, 134, 148, 231,  64, 121, 171,  21, 248,  85, 244,
+	232, 177,  30, 192,  59,  90,  82,   6, 193,   9, 108, 234,  30,  28,  30,
+	9, 209, 128,  74, 214,  71, 204, 190, 121, 231, 201,   5, 228, 220, 218,
+	248,  97, 136, 200, 246, 102, 106,  29, 234,  73, 190,   5, 135, 186,   7,
+	135, 201,   9, 167, 111, 227, 135,  50,   2,  71, 146,  69, 153, 232, 245,
+	206, 192, 136, 234, 196,  86,  49,  13, 154, 162, 113,  39, 211, 101,  51,
+	126, 239,  25,  28, 154,  19, 206, 133, 234, 119, 195,  17, 234,  36, 220,
+	85,  33, 202, 213, 169, 191, 175,  36,  92, 139, 152, 106,   3, 212,   9,
+	119,  76, 100, 251, 126, 157,   9, 218,  69, 219, 150, 158, 211,  42,  99,
+	187, 114, 245, 147, 112,  83, 122, 214,   2,  51,  83, 127,  85,  16,  89,
+	62,  94,  60,  61, 202,  20,  58,  36,  38, 244, 126,  93, 159, 146,  93,
+	180, 101, 129, 254, 129, 107,   2,  89,  26, 219,  24,  42,   6, 188,  95,
+	153, 124,  81, 202, 251,  37,   3, 249, 101, 188, 120, 114, 148, 223, 219,
+	197, 212,  56, 208, 123, 240, 245, 129, 167,   3,  19,   3, 163,  42,   6,
+	172,  11, 234,  36,  92,  43, 242, 105,  32, 250, 227, 161,  89,  44,  45,
+	98, 196, 139,  14, 178, 146, 112,  27, 210, 179,  23, 178,   7, 203, 240,
+	248, 156, 152, 251,  15,  38, 221, 146, 138, 181, 212,  73, 216,  46, 214,
+	112,  11,  82, 180, 103, 138,  71, 237,  40, 145,  52,  29, 216, 221, 194,
+	220,  41,  49,   0, 103,  36, 129, 185, 152, 158, 116, 101,   9, 171,  29,
+	178,  85, 245, 121, 213, 235, 184,  90, 215, 229, 248,  89, 172, 112, 190,
+	62,  83, 112, 209, 145,  85, 226, 229, 147, 105, 188, 142,  43, 172, 220,
+	155, 179,  40,  29, 201,  85,   6, 239, 204, 153, 155, 243, 117,  47, 216,
+	222, 216,  38, 229, 188, 146,  51, 188, 162, 119,  81, 194,  82, 205, 130,
+	178, 189,  24, 120, 173,  97, 108,  80,  34, 102, 111,  73,  87, 182, 123,
+	2, 107,   2, 231, 133, 184, 213,  89,  96,  69, 194,  56,  57,  47,  91,
+	222,  57, 100, 253,  68, 130,  92, 175,  94, 148, 254, 129,  15, 240, 176,
+	88,   7, 116,  39,  77, 226, 181, 187,  68, 172, 146, 154,  69,  78, 101,
+	98,  77,  57,  91,  99,  81, 250,  82, 215, 138, 202, 178, 188,  44,  78,
+	37,  67, 254, 196, 143, 201, 152, 141,  98, 217,  86, 224,  60,  92, 149,
+	64, 207, 175,   0, 151, 178,  66, 119, 101, 249, 243,  61, 184, 163, 254,
+	99, 210, 218,  48,  94,  94,   5, 101,  13, 162,   1, 176, 100, 251,  97,
+	167, 249,  49, 233, 115, 179, 250, 111,  78,  23, 109, 115, 193, 178, 130,
+	62,   5, 151,  20,  52,  23, 241,  76, 200,  10, 106, 149, 103, 173,  50,
+	158,  21,  28,  45, 120, 174,  96, 110, 193,  71,  61, 232, 151,  61, 219,
+	115,   4, 170,  82, 254, 156, 206,  16,  47, 197, 127, 231, 231, 244, 153,
+	222,  27, 240, 159, 200,  63, 153, 185,  24, 191, 162, 246,  71, 153,   0,
+	0,   0,   0,  73,  69,  78,  68, 174,  66,  96, 130
 };
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
index 5fd19d7..826bef6 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/curl/connectionmanager.h
@@ -125,7 +125,7 @@ public:
 };
 
 /** Shortcut for accessing the connection manager. */
-#define ConnMan		Networking::ConnectionManager::instance()
+#define ConnMan     Networking::ConnectionManager::instance()
 
 } // End of namespace Networking
 
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index d5f6288..1764a93 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -31,19 +31,19 @@ namespace Networking {
 
 static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->write(d, n*l);
+	if (stream) return stream->write(d, n * l);
 	return 0;
 }
 
 static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->fillWithSendingContents(d, n*l);
+	if (stream) return stream->fillWithSendingContents(d, n * l);
 	return 0;
 }
 
 static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->addResponseHeaders(d, n*l);
+	if (stream) return stream->addResponseHeaders(d, n * l);
 	return 0;
 }
 
@@ -112,30 +112,33 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
 
 	// set POST multipart upload form fields/files
-	struct curl_httppost *formpost = NULL;
-	struct curl_httppost *lastptr = NULL;
+	struct curl_httppost *formpost = nullptr;
+	struct curl_httppost *lastptr = nullptr;
 
 	for (Common::HashMap<Common::String, Common::String>::iterator i = formFields.begin(); i != formFields.end(); ++i) {
-		if (0 != curl_formadd(&formpost, &lastptr,
+		CURLFORMcode code = curl_formadd(
+			&formpost,
+			&lastptr,
 			CURLFORM_COPYNAME, i->_key.c_str(),
 			CURLFORM_COPYCONTENTS, i->_value.c_str(),
-			CURLFORM_END)) debug("file failed formadd");
-	}
+			CURLFORM_END
+		);
 
-	/*
-	curl_formadd(&formpost, &lastptr,
-		CURLFORM_COPYNAME, "fieldname",
-		CURLFORM_BUFFER, "filename",
-		CURLFORM_BUFFERPTR, buffer,
-		CURLFORM_BUFFERLENGTH, bufferSize,
-		CURLFORM_END);
-	*/
+		if (code != CURL_FORMADD_OK)
+			debug("field failed formadd");
+	}
 
 	for (Common::HashMap<Common::String, Common::String>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
-		if (0 != curl_formadd(&formpost, &lastptr,
+		CURLFORMcode code = curl_formadd(
+			&formpost,
+			&lastptr,
 			CURLFORM_COPYNAME, i->_key.c_str(),
 			CURLFORM_FILE, i->_value.c_str(),
-			CURLFORM_END)) debug("file failed formadd");
+			CURLFORM_END
+		);
+
+		if (code != CURL_FORMADD_OK)
+			debug("file failed formadd");
 	}
 
 	curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
index 0138da1..2be6d59 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/curl/networkreadstream.h
@@ -50,9 +50,9 @@ public:
 	NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);
 	/** Send <formFields>, <formFiles>, using POST multipart/form. */
 	NetworkReadStream(
-		const char *url, curl_slist *headersList,
-		Common::HashMap<Common::String, Common::String> formFields,
-		Common::HashMap<Common::String, Common::String> formFiles);
+	    const char *url, curl_slist *headersList,
+	    Common::HashMap<Common::String, Common::String> formFields,
+	    Common::HashMap<Common::String, Common::String> formFiles);
 	/** Send <buffer, using POST by default. */
 	NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
 	virtual ~NetworkReadStream();
@@ -77,8 +77,8 @@ public:
 	 * supposed to match those of ISO C fread(), in particular where
 	 * it concerns setting error and end of file/stream flags.
 	 *
-	 * @param dataPtr	pointer to a buffer into which the data is read
-	 * @param dataSize	number of bytes to be read
+	 * @param dataPtr   pointer to a buffer into which the data is read
+	 * @param dataSize  number of bytes to be read
 	 * @return the number of bytes which were actually read.
 	 */
 	virtual uint32 read(void *dataPtr, uint32 dataSize);
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
index 6a1bc12..9b366ea 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/curl/request.h
@@ -161,7 +161,7 @@ public:
 	/** Method, which does actual work. Depends on what this Request is doing. */
 	virtual void handle() = 0;
 
-	/** Method, which is called by ConnectionManager when Request's state is RETRY.	 */
+	/** Method, which is called by ConnectionManager when Request's state is RETRY.  */
 	virtual void handleRetry();
 
 	/** Method, which is used to restart the Request. */
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index 2ded978..b16827d 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -91,9 +91,8 @@ void CreateDirectoryHandler::handle(Client &client) {
 			client.queryParameter("path").c_str(),
 			_("Back to parent directory")
 		),
-
 		(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
-			LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+		LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
 	);
 }
 
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index 7d48458..6cf18ae 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -34,6 +34,7 @@ FilesAjaxPageHandler::FilesAjaxPageHandler() {}
 FilesAjaxPageHandler::~FilesAjaxPageHandler() {}
 
 namespace {
+
 Common::String encodeDoubleQuotesAndSlashes(Common::String s) {
 	Common::String result = "";
 	for (uint32 i = 0; i < s.size(); ++i)
@@ -42,8 +43,9 @@ Common::String encodeDoubleQuotesAndSlashes(Common::String s) {
 		} else if (s[i] == '\\') {
 			result += "\\\\";
 		} else result += s[i];
-		return result;
+	return result;
 }
+
 }
 
 void FilesAjaxPageHandler::handle(Client &client) {
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index cdc505b..bb6a036 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -108,14 +108,16 @@ void HandlerUtils::setMessageHandler(Client &client, Common::String message, Com
 }
 
 void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
-	setMessageHandler(client,
+	setMessageHandler(
+		client,
 		Common::String::format(
 			"%s<br/><a href=\"files%s?path=%s\">%s</a>",
 			message.c_str(),
-			client.queryParameter("ajax") == "true" ? "AJAX": "",
+			client.queryParameter("ajax") == "true" ? "AJAX" : "",
 			"%2F", //that's encoded "/"
 			_("Back to the files manager")
-		), redirectTo
+		),
+		redirectTo
 	);
 }
 
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 0ad1335..9b62bb8 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -205,8 +205,11 @@ void LocalWebserver::handle() {
 
 void LocalWebserver::handleClient(uint32 i) {
 	switch (_client[i].state()) {
-	case INVALID: return;
-	case READING_HEADERS: _client[i].readHeaders(); break;
+	case INVALID:
+		return;
+	case READING_HEADERS:
+		_client[i].readHeaders();
+		break;
 	case READ_HEADERS: //decide what to do next with that client
 		//if GET, check whether we know a handler for such URL
 		//if PUT, check whether we know a handler for that URL
@@ -216,7 +219,6 @@ void LocalWebserver::handleClient(uint32 i) {
 			(*_defaultHandler)(_client[i]); //try default handler
 
 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break;
-
 		//if no handler, answer with default BAD REQUEST
 		//fallthrough
 	case BAD_REQUEST:
@@ -278,55 +280,55 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 
 	// if not - try platform-specific
 #ifdef POSIX
-		struct ifaddrs *ifAddrStruct = NULL;
-		void *tmpAddrPtr = NULL;
+	struct ifaddrs *ifAddrStruct = NULL;
+	void *tmpAddrPtr = NULL;
 
-		getifaddrs(&ifAddrStruct);
+	getifaddrs(&ifAddrStruct);
 
-		for (struct ifaddrs *i = ifAddrStruct; i != NULL; i = i->ifa_next) {
-			if (!i->ifa_addr) {
-				continue;
-			}
+	for (struct ifaddrs *i = ifAddrStruct; i != NULL; i = i->ifa_next) {
+		if (!i->ifa_addr) {
+			continue;
+		}
 
-			Common::String addr;
+		Common::String addr;
 
-			// IPv4
-			if (i->ifa_addr->sa_family == AF_INET) {
-				tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
-				char addressBuffer[INET_ADDRSTRLEN];
-				inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
-				debug("%s IP Address %s", i->ifa_name, addressBuffer);
-				addr = addressBuffer;
-			}
+		// IPv4
+		if (i->ifa_addr->sa_family == AF_INET) {
+			tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
+			char addressBuffer[INET_ADDRSTRLEN];
+			inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
+			debug("%s IP Address %s", i->ifa_name, addressBuffer);
+			addr = addressBuffer;
+		}
 
-			// IPv6
-			/*
-			if (i->ifa_addr->sa_family == AF_INET6) {
-				tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
-				char addressBuffer[INET6_ADDRSTRLEN];
-				inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
-				debug("%s IP Address %s", i->ifa_name, addressBuffer);
-				addr = addressBuffer;
-			}
-			*/
+		// IPv6
+		/*
+		if (i->ifa_addr->sa_family == AF_INET6) {
+		    tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
+		    char addressBuffer[INET6_ADDRSTRLEN];
+		    inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
+		    debug("%s IP Address %s", i->ifa_name, addressBuffer);
+		    addr = addressBuffer;
+		}
+		*/
 
-			if (addr.empty()) continue;
+		if (addr.empty()) continue;
 
-			// ignored IPv4 addresses
-			if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
-				continue;
+		// ignored IPv4 addresses
+		if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
+			continue;
 
-			// ignored IPv6 addresses
-			/*
-			if (addr.equals("::1"))
-				continue;
-			*/
+		// ignored IPv6 addresses
+		/*
+		if (addr.equals("::1"))
+		    continue;
+		*/
 
-			// use the address found
-			_address = "http://" + addr + Common::String::format(":%u/", _serverPort);
-		}
+		// use the address found
+		_address = "http://" + addr + Common::String::format(":%u/", _serverPort);
+	}
 
-		if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
+	if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
 #endif
 }
 
@@ -397,9 +399,9 @@ Common::String LocalWebserver::urlDecode(Common::String value) {
 			continue;
 		}
 
-		if (value[i] == '%' && i+2 < size) {
-			int d1 = hexDigit(value[i+1]);
-			int d2 = hexDigit(value[i+2]);
+		if (value[i] == '%' && i + 2 < size) {
+			int d1 = hexDigit(value[i + 1]);
+			int d2 = hexDigit(value[i + 2]);
 			if (0 <= d1 && d1 < 16 && 0 <= d2 && d2 < 16) {
 				result += (char)(d1 * 16 + d2);
 				i = i + 2;
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index d118509..f4c59c6 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -110,7 +110,7 @@ public:
 };
 
 /** Shortcut for accessing the local webserver. */
-#define LocalServer		Networking::LocalWebserver::instance()
+#define LocalServer     Networking::LocalWebserver::instance()
 
 } // End of namespace Networking
 
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 8edda70..66efeec 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -163,7 +163,7 @@ void Reader::parseFirstLine(const Common::String &headers) {
 			const char *cstr = headers.c_str();
 			const char *position = strstr(cstr, "\r\n");
 			if (position) { //we have at least one line - and we want the first one
-							//"<METHOD> <path> HTTP/<VERSION>\r\n"
+				//"<METHOD> <path> HTTP/<VERSION>\r\n"
 				Common::String method, path, http, buf;
 				uint32 length = position - cstr;
 				if (headersSize > length) headersSize = length;
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 4ecd095..3c51c55 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -153,16 +153,16 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 
 		if (client->noMoreContent()) {
 			// success - redirect back to directory listing
-			HandlerUtils::setMessageHandler(*client,
+			HandlerUtils::setMessageHandler(
+				*client,
 				Common::String::format(
 					"%s<br/><a href=\"files?path=%s\">%s</a>",
 					_("Uploaded successfully!"),
 					client->queryParameter("path").c_str(),
 					_("Back to parent directory")
 				),
-
 				(client->queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
-					LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
+				LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
 			);
 			_state = UFH_STOP;
 			return;


Commit: efebb5b90dd84f962572f3e8df161f6284465cbc
    https://github.com/scummvm/scummvm/commit/efebb5b90dd84f962572f3e8df161f6284465cbc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove DropboxStorage::remove()

Changed paths:
    backends/cloud/dropbox/dropboxstorage.h



diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 1f46c51..0a0043a 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -80,9 +80,6 @@ public:
 	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
-	/** Calls the callback when finished. */
 	virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
 
 	/** Returns the StorageInfo struct. */


Commit: 8c62993769dd12d3ee96eba84f3b35d8c63f9269
    https://github.com/scummvm/scummvm/commit/8c62993769dd12d3ee96eba84f3b35d8c63f9269
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove remove() from BoxStorage

Changed paths:
    backends/cloud/box/boxstorage.h



diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 80a572c..0185623 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -87,9 +87,6 @@ public:
 	/** Returns pointer to Networking::NetworkReadStream. */
 	virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
 
-	/** Calls the callback when finished. */
-	virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO
-
 	/** Returns the StorageInfo struct. */
 	virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
 


Commit: bd8f2ed8250f902b9061f2c3688857390ceebe97
    https://github.com/scummvm/scummvm/commit/bd8f2ed8250f902b9061f2c3688857390ceebe97
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix some TODOs in CloudManager

"No Storage connected!" error message is passed to the error callback
now when there is no Storage connected to the CloudManager.

Changed paths:
    backends/cloud/cloudmanager.cpp
    backends/cloud/cloudmanager.h



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index f5b7c97..056eb08 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -45,7 +45,6 @@ const char *const CloudManager::kStoragePrefix = "storage_";
 CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr) {}
 
 CloudManager::~CloudManager() {
-	//TODO: do we have to save storages on manager destruction?
 	delete _activeStorage;
 	freeStorages();
 }
@@ -161,6 +160,11 @@ void CloudManager::freeStorages() {
 	_storagesToRemove.clear();
 }
 
+void CloudManager::passNoStorageConnected(Networking::ErrorCallback errorCallback) const {
+	if (errorCallback == nullptr) return;
+	(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "No Storage connected!", -1));
+}
+
 Storage *CloudManager::getCurrentStorage() const {
 	return _activeStorage;
 }
@@ -249,9 +253,9 @@ Networking::Request *CloudManager::listDirectory(Common::String path, Storage::L
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->listDirectory(path, callback, errorCallback, recursive);
 	else {
+		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
-		//TODO: should we call errorCallback?
 	}
 	return nullptr;
 }
@@ -260,9 +264,9 @@ Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Com
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
 	else {
+		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
-		//TODO: should we call errorCallback?
 	}
 	return nullptr;
 }
@@ -271,9 +275,9 @@ Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, N
 	Storage *storage = getCurrentStorage();
 	if (storage) return storage->info(callback, errorCallback);
 	else {
+		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
-		//TODO: should we call errorCallback?
 	}
 	return nullptr;
 }
@@ -289,6 +293,10 @@ SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networ
 	if (storage) {
 		setStorageLastSync(_currentStorageIndex, "???"); //TODO get the date
 		return storage->syncSaves(callback, errorCallback);
+	} else {
+		passNoStorageConnected(errorCallback);
+		delete callback;
+		delete errorCallback;
 	}
 	return nullptr;
 }
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index aad0133..c504ff3 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -68,6 +68,9 @@ class CloudManager : public Common::Singleton<CloudManager> {
 	/** Frees memory used by storages which failed to connect. */
 	void freeStorages();
 
+	/** Calls the error callback with a special "no storage connected" message. */
+	void passNoStorageConnected(Networking::ErrorCallback errorCallback) const;
+
 public:
 	CloudManager();
 	virtual ~CloudManager();


Commit: 5f9beb76cdb7b34acc258cd30c4c7390036f25c1
    https://github.com/scummvm/scummvm/commit/5f9beb76cdb7b34acc258cd30c4c7390036f25c1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix HTTP response code TODOs

Changed paths:
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp



diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index b876bc2..1b85850 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -64,10 +64,12 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	}
 
 	Common::JSONObject result = json->asObject();
+	long httpResponseCode = -1;
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request
 		if (_stream) {
-			debug(9, "code %ld", _stream->httpResponseCode());
+			httpResponseCode = _stream->httpResponseCode();
+			debug(9, "code %ld", httpResponseCode);
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -89,7 +91,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 			irrecoverable = false;
 
 		if (irrecoverable) {
-			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
+			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
 			delete json;
 			return;
 		}
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 88b5154..3e429f1 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -64,10 +64,12 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	}
 
 	Common::JSONObject result = json->asObject();
+	long httpResponseCode = -1;
 	if (result.contains("error")) {
 		//new token needed => request token & then retry original request
 		if (_stream) {
-			debug(9, "code %ld", _stream->httpResponseCode());
+			httpResponseCode = _stream->httpResponseCode();
+			debug(9, "code %ld", httpResponseCode);
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -93,7 +95,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		if (code == "unauthenticated") irrecoverable = false;
 
 		if (irrecoverable) {
-			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode
+			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
 			delete json;
 			return;
 		}


Commit: a30d0d19945890acae27a9fc3a2520f9b36fc04c
    https://github.com/scummvm/scummvm/commit/a30d0d19945890acae27a9fc3a2520f9b36fc04c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update DownloadRequest's TODO

We need a way to reopen DumpFile if we want DownloadRequest to support
restarting.

Changed paths:
    backends/cloud/downloadrequest.cpp



diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 33ac811..49a8a1e 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -44,7 +44,7 @@ void DownloadRequest::start() {
 	_ignoreCallback = true;
 	if (_workingRequest) _workingRequest->finish();
 	_remoteFileStream = nullptr;
-	//TODO: reopen DumpFile
+	//TODO: add some way to reopen DumpFile, so DownloadRequest could be restarted
 	_ignoreCallback = false;
 
 	_workingRequest = _storage->streamFileById(


Commit: 758f46ddf0ad312858fa0260c7e92a2d242dc40a
    https://github.com/scummvm/scummvm/commit/758f46ddf0ad312858fa0260c7e92a2d242dc40a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix FolderDownloadRequest TODO

Changed paths:
    backends/cloud/folderdownloadrequest.cpp



diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index ebcd167..1b3c1c5 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -69,6 +69,7 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 	_pendingFiles = response.value;
 
 	// remove all directories
+	// non-empty directories would be created by DumpFile, and empty ones are just ignored
 	for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end();)
 		if (i->isDirectory())
 			_pendingFiles.erase(i);
@@ -111,7 +112,7 @@ void FolderDownloadRequest::downloadNextFile() {
 
 		_currentFile = _pendingFiles.back();
 		_pendingFiles.pop_back();
-	} while (_currentFile.isDirectory()); //TODO: may be create these directories (in case those are empty)
+	} while (_currentFile.isDirectory()); // directories are actually removed earlier, in the directoryListedCallback()
 
 	sendCommand(GUI::kDownloadProgressCmd, (int)(getProgress() * 100));
 


Commit: 409dd27e76f705941f6f702d55244d24aa06651f
    https://github.com/scummvm/scummvm/commit/409dd27e76f705941f6f702d55244d24aa06651f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Regenerate themes

Changed paths:
    gui/themes/default.inc
    gui/themes/scummclassic.zip
    gui/themes/scummmodern.zip



diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index c0ea733..d46a603 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -1092,6 +1092,192 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "/>"
 "</layout>"
 "</dialog>"
+"<dialog name='GlobalOptions_Cloud' overlays='Dialog.GlobalOptions.TabWidget'>"
+"<layout type='vertical' padding='0,0,0,0'>"
+"<widget name='Container'/>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_Container' overlays='GlobalOptions_Cloud.Container'>"
+"<layout type='vertical' padding='16,16,16,16' spacing='8'>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='StoragePopupDesc' "
+"type='OptionsLabel' "
+"/>"
+"<widget name='StoragePopup' "
+"type='PopUp' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='StorageUsernameDesc' "
+"type='OptionsLabel' "
+"/>"
+"<widget name='StorageUsernameLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='StorageUsedSpaceDesc' "
+"type='OptionsLabel' "
+"/>"
+"<widget name='StorageUsedSpaceLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='StorageLastSyncDesc' "
+"type='OptionsLabel' "
+"/>"
+"<widget name='StorageLastSyncLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='ConnectButton' "
+"type='Button' "
+"/>"
+"<widget name='RefreshButton' "
+"type='Button' "
+"/>"
+"<widget name='DownloadButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='RunServerButton' "
+"type='Button' "
+"/>"
+"<widget name='ServerInfoLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='ServerPortDesc' "
+"type='OptionsLabel' "
+"/>"
+"<widget name='ServerPortEditText' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='ServerPortClearButton' "
+"height='Globals.Line.Height' "
+"width='Globals.Line.Height' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_DownloadDialog' overlays='Dialog.GlobalOptions'>"
+"<layout type='vertical' padding='16,16,16,8' spacing='8'>"
+"<widget name='RemoteDirectory' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='LocalDirectory' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='ProgressBar' "
+"height='Globals.Button.Height' "
+"/>"
+"<space size='1'/>"
+"<widget name='PercentText' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<widget name='DownloadSize' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='DownloadSpeed' "
+"height='Globals.Line.Height' "
+"/>"
+"<space/>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'>"
+"<widget name='MainButton' "
+"type='Button' "
+"/>"
+"<space/>"
+"<widget name='CloseButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_ConnectionWizard' overlays='Dialog.GlobalOptions'>"
+"<layout type='vertical' padding='16,16,16,16' spacing='8'>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='Picture' "
+"type='OptionsLabel' "
+"/>"
+"<layout type='vertical' padding='0,0,0,0' spacing='6'>"
+"<widget name='Headline' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='4' />"
+"<widget name='NavigateLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='URLLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='4' />"
+"<widget name='ReturnLine1' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='ReturnLine2' "
+"height='Globals.Line.Height' "
+"/>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='4' center='true'>"
+"<widget name='CodeBox1' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox2' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox3' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox4' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='4' center='true'>"
+"<widget name='CodeBox5' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox6' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox7' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='CodeBox8' "
+"width='70' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<widget name='MessageLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='6' />"
+"</layout>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'>"
+"<widget name='CancelButton' "
+"type='Button' "
+"/>"
+"<widget name='OpenUrlButton' "
+"type='Button' "
+"/>"
+"<widget name='ConnectButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>"
 "<layout type='vertical' padding='8,8,8,8' center='true'>"
 "<widget name='Action' "
@@ -1592,6 +1778,37 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='SaveLoadCloudSyncProgress' overlays='screen_center' inset='8' shading='dim'>"
+"<layout type='vertical' padding='8,8,8,8' center='true'>"
+"<widget name='TitleText' "
+"width='496' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<space size='1'/>"
+"<widget name='ProgressBar' "
+"width='496' "
+"height='Globals.Button.Height' "
+"/>"
+"<space size='1'/>"
+"<widget name='PercentText' "
+"width='496' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<space size='1'/>"
+"<layout type='horizontal' padding='0,0,0,0' center='true' spacing='10'>"
+"<widget name='Cancel' "
+"width='150' "
+"height='Globals.Button.Height' "
+"/>"
+"<widget name='Background' "
+"width='150' "
+"height='Globals.Button.Height' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "<dialog name='SavenameDialog' overlays='screen_center'>"
 "<layout type='vertical' padding='8,8,8,8'>"
 "<widget name='DescriptionText' "
@@ -2394,6 +2611,197 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "/>"
 "</layout>"
 "</dialog>"
+"<dialog name='GlobalOptions_Cloud' overlays='Dialog.GlobalOptions.TabWidget'>"
+"<layout type='vertical' padding='0,0,0,0'>"
+"<widget name='Container'/>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_Container' overlays='GlobalOptions_Cloud.Container'>"
+"<layout type='vertical' padding='16,16,16,16' spacing='8'>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='StoragePopupDesc' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
+"/>"
+"<widget name='StoragePopup' "
+"type='PopUp' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='StorageUsernameDesc' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
+"/>"
+"<widget name='StorageUsernameLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='StorageUsedSpaceDesc' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
+"/>"
+"<widget name='StorageUsedSpaceLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='StorageLastSyncDesc' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
+"/>"
+"<widget name='StorageLastSyncLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='ConnectButton' "
+"type='Button' "
+"/>"
+"<widget name='RefreshButton' "
+"type='Button' "
+"/>"
+"<widget name='DownloadButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='RunServerButton' "
+"type='Button' "
+"/>"
+"<widget name='ServerInfoLabel' "
+"height='Globals.Line.Height' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='ServerPortDesc' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
+"/>"
+"<widget name='ServerPortEditText' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='ServerPortClearButton' "
+"height='Globals.Line.Height' "
+"width='Globals.Line.Height' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_DownloadDialog' overlays='Dialog.GlobalOptions'>"
+"<layout type='vertical' padding='8,8,8,4' spacing='8'>"
+"<widget name='RemoteDirectory' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='LocalDirectory' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='ProgressBar' "
+"height='Globals.Button.Height' "
+"/>"
+"<space size='1'/>"
+"<widget name='PercentText' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<widget name='DownloadSize' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='DownloadSpeed' "
+"height='Globals.Line.Height' "
+"/>"
+"<space/>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6'>"
+"<widget name='MainButton' "
+"type='Button' "
+"/>"
+"<space/>"
+"<widget name='CloseButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
+"<dialog name='GlobalOptions_Cloud_ConnectionWizard' overlays='Dialog.GlobalOptions'>"
+"<layout type='vertical' padding='16,16,16,16' spacing='8'>"
+"<layout type='vertical' padding='0,0,0,0' spacing='4'>"
+"<widget name='Headline' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='2' />"
+"<widget name='NavigateLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='URLLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='2' />"
+"<widget name='ReturnLine1' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='ReturnLine2' "
+"height='Globals.Line.Height' "
+"/>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='4' center='true'>"
+"<widget name='CodeBox1' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox2' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox3' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox4' "
+"width='60' "
+"height='16' "
+"/>"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='4' center='true'>"
+"<widget name='CodeBox5' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox6' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox7' "
+"width='60' "
+"height='16' "
+"/>"
+"<widget name='CodeBox8' "
+"width='60' "
+"height='16' "
+"/>"
+"</layout>"
+"<widget name='MessageLine' "
+"height='Globals.Line.Height' "
+"/>"
+"<space size='4' />"
+"</layout>"
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'>"
+"<widget name='CancelButton' "
+"type='Button' "
+"/>"
+"<widget name='OpenUrlButton' "
+"type='Button' "
+"/>"
+"<widget name='ConnectButton' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>"
 "<layout type='vertical' padding='8,8,8,8' center='true'>"
 "<widget name='Action' "
@@ -2888,6 +3296,37 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='SaveLoadCloudSyncProgress' overlays='screen_center' inset='8' shading='dim'>"
+"<layout type='vertical' padding='8,8,8,8' center='true'>"
+"<widget name='TitleText' "
+"width='240' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<space size='1'/>"
+"<widget name='ProgressBar' "
+"width='240' "
+"height='Globals.Button.Height' "
+"/>"
+"<space size='1'/>"
+"<widget name='PercentText' "
+"width='240' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<space size='1'/>"
+"<layout type='horizontal' padding='0,0,0,0' center='true' spacing='10'>"
+"<widget name='Cancel' "
+"width='100' "
+"height='Globals.Button.Height' "
+"/>"
+"<widget name='Background' "
+"width='100' "
+"height='Globals.Button.Height' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "<dialog name='SavenameDialog' overlays='screen_center'>"
 "<layout type='vertical' padding='8,8,8,8'>"
 "<widget name='DescriptionText' "
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 561f2a5..4a36e7d 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index d80c481..fcf35d9 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ


Commit: a65682a828ff0213b60b983f0957c504ec2d2ec1
    https://github.com/scummvm/scummvm/commit/a65682a828ff0213b60b983f0957c504ec2d2ec1
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix warnings

Changed paths:
    gui/ThemeEngine.cpp
    gui/launcher.cpp



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 78353a0..ac209d6 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -1449,6 +1449,9 @@ void ThemeEngine::drawDialogBackgroundClip(const Common::Rect &r, const Common::
 	case kDialogBackgroundDefault:
 		queueDDClip(kDDDefaultBackground, r, clip);
 		break;
+	case kDialogBackgroundNone:
+		// no op
+		break;
 	}
 }
 
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index d1a226f..50f060d 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -333,9 +333,13 @@ void LauncherDialog::addGame() {
 			String bannedDirectory = CloudMan.getDownloadLocalDirectory();
 			if (selectedDirectory.size() && selectedDirectory.lastChar() != '/' && selectedDirectory.lastChar() != '\\')
 				selectedDirectory += '/';
-			if (bannedDirectory.size() && bannedDirectory.lastChar() != '/' && bannedDirectory.lastChar() != '\\')
-				if (selectedDirectory.size()) bannedDirectory += selectedDirectory.lastChar();
-				else bannedDirectory += '/';
+			if (bannedDirectory.size() && bannedDirectory.lastChar() != '/' && bannedDirectory.lastChar() != '\\') {
+				if (selectedDirectory.size()) {
+					bannedDirectory += selectedDirectory.lastChar();
+				} else {
+					bannedDirectory += '/';
+				}
+			}
 			if (selectedDirectory.equalsIgnoreCase(bannedDirectory)) {
 				MessageDialog alert(_("This directory cannot be used yet, it is being downloaded into!"));
 				alert.runModal();


Commit: e114d1a697b21203b09ca2c607cbd86c3db05328
    https://github.com/scummvm/scummvm/commit/e114d1a697b21203b09ca2c607cbd86c3db05328
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix format warning

Changed paths:
    gui/downloaddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index a90783d..495e9c1 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -199,12 +199,12 @@ void DownloadDialog::reflowLayout() {
 
 namespace {
 Common::String getHumanReadableBytes(uint64 bytes, Common::String &unitsOut) {
-	Common::String result = Common::String::format("%u", bytes);
+	Common::String result = Common::String::format("%lu", bytes);
 	unitsOut = "B";
 
 	if (bytes >= 1024) {
 		bytes /= 1024;
-		result = Common::String::format("%u", bytes);
+		result = Common::String::format("%lu", bytes);
 		unitsOut = "KB";
 	}
 


Commit: 876b8616afc290028a714108ec476486d4ef9b8f
    https://github.com/scummvm/scummvm/commit/876b8616afc290028a714108ec476486d4ef9b8f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix format warning

Changed paths:
    backends/cloud/googledrive/googledrivestorage.cpp



diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 3d87be3..33b2bf5 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -232,7 +232,7 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 	debug(9, "\nuser info:");
 	debug(9, "\tname: %s", response.value.name().c_str());
 	debug(9, "\temail: %s", response.value.email().c_str());
-	debug(9, "\tdisk usage: %llu/%llu", response.value.used(), response.value.available());
+	debug(9, "\tdisk usage: %lu/%lu", response.value.used(), response.value.available());
 }
 
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {


Commit: 7c9912e3d8b5cfff6851d3c8695b3f1db8112b8c
    https://github.com/scummvm/scummvm/commit/7c9912e3d8b5cfff6851d3c8695b3f1db8112b8c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix IndexPageHandler warning

Changed paths:
    backends/networking/sdl_net/handlers/indexpagehandler.cpp



diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index e12016a..116090b 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -42,7 +42,6 @@ void IndexPageHandler::handle(Client &client) {
 			Common::String::format(
 				"%s<br/><a href=\"files\">%s</a>",
 				_("This is a local webserver index page."),
-				client.queryParameter("path").c_str(),
 				_("Open Files manager")
 			),
 			"/filesAJAX"


Commit: 43c940c98579e669826de838eff9dcb372d453f7
    https://github.com/scummvm/scummvm/commit/43c940c98579e669826de838eff9dcb372d453f7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Updated BoxListDirectoryByIdRequest

It now checks for all keys in JSON to avoid segfaults and prints
warnings if passed keys are missing or have wrong types.

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 5a4e062..3e51806 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -83,67 +83,135 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	Common::JSONValue *json = response.value;
-	if (json) {
-		Common::JSONObject responseObject = json->asObject();
-
-		//debug("%s", json->stringify(true).c_str());
-
-		//TODO: check that error is returned the right way
-		/*
-		if (responseObject.contains("error") || responseObject.contains("error_summary")) {
-		    warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
-		    error.failed = true;
-		    error.response = json->stringify();
-		    finishError(error);
-		    delete json;
-		    return;
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
+
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
+	Common::JSONObject responseObject = json->asObject();
+	//debug(9, "%s", json->stringify(true).c_str());
+
+	//TODO: check that error is returned the right way
+	/*
+	if (responseObject.contains("error") || responseObject.contains("error_summary")) {
+		warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
+		error.failed = true;
+		error.response = json->stringify();
+		finishError(error);
+		delete json;
+		return;
+	}
+	*/
+
+	//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
+	if (responseObject.contains("entries")) {
+		if (!responseObject.getVal("entries")->isArray()) {
+			error.response = Common::String::format(
+				"\"entries\" found, but that's not an array!\n%s",
+				responseObject.getVal("entries")->stringify(true).c_str()
+			);
+			finishError(error);
+			delete json;
+			return;
 		}
-		*/
-
-		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
-
-		if (responseObject.contains("entries") && responseObject.getVal("entries")->isArray()) {
-			Common::JSONArray items = responseObject.getVal("entries")->asArray();
-			for (uint32 i = 0; i < items.size(); ++i) {
-				Common::JSONObject item = items[i]->asObject();
-				Common::String id = item.getVal("id")->asString();
-				Common::String name = item.getVal("name")->asString();
-				bool isDirectory = (item.getVal("type")->asString() == "folder");
-				uint32 size = 0, timestamp = 0;
+
+		Common::JSONArray items = responseObject.getVal("entries")->asArray();
+		for (uint32 i = 0; i < items.size(); ++i) {
+			if (!items[i]->isObject()) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item is not an object!");
+				debug(9, "%s", items[i]->stringify(true).c_str());
+				continue;
+			}
+
+			Common::JSONObject item = items[i]->asObject();
+
+			if (!item.contains("id") || !item.getVal("id")->isString()) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"id\"!");
+				if (item.contains("id")) {
+					debug(9, "%s", item.getVal("id")->stringify(true).c_str());
+				} else {
+					debug(9, "(not available)");
+				}
+				continue;
+			}
+
+			if (!item.contains("name") || !item.getVal("name")->isString()) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"name\"!");
+				if (item.contains("name")) {
+					debug(9, "%s", item.getVal("name")->stringify(true).c_str());
+				} else {
+					debug(9, "(not available)");
+				}
+				continue;
+			}
+
+			if (!item.contains("type") || !item.getVal("type")->isString()) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"type\"!");
+				if (item.contains("type")) {
+					debug(9, "%s", item.getVal("type")->stringify(true).c_str());
+				} else {
+					debug(9, "(not available)");
+				}
+				continue;
+			}
+
+			if (!item.contains("size") || (!item.getVal("size")->isString() && !item.getVal("size")->isIntegerNumber())) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"size\"!");
 				if (item.contains("size")) {
-					if (item.getVal("size")->isString())
-						size = item.getVal("size")->asString().asUint64();
-					else if (item.getVal("size")->isIntegerNumber())
-						size = item.getVal("size")->asIntegerNumber();
-					else
-						warning("strange type for field 'size'");
+					debug(9, "%s", item.getVal("size")->stringify(true).c_str());
+				} else {
+					debug(9, "(not available)");
+				}
+				continue;
+			}
+
+			if (!item.contains("modified_at") || !item.getVal("modified_at")->isString()) {
+				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"modified_at\"!");
+				if (item.contains("modified_at")) {
+					debug(9, "%s", item.getVal("modified_at")->stringify(true).c_str());
+				} else {
+					debug(9, "(not available)");
 				}
-				if (item.contains("modified_at") && item.getVal("modified_at")->isString())
-					timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
+				continue;
+			}
 
-				//as we list directory by id, we can't determine full path for the file, so we leave it empty
-				_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
+			Common::String id = item.getVal("id")->asString();
+			Common::String name = item.getVal("name")->asString();
+			bool isDirectory = (item.getVal("type")->asString() == "folder");
+			uint32 size;
+			if (item.getVal("size")->isString()) {
+				size = item.getVal("size")->asString().asUint64();
+			} else {
+				size = item.getVal("size")->asIntegerNumber();
 			}
-		}
+			uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
 
-		uint32 received = 0;
-		uint32 totalCount = 0;
-		if (responseObject.contains("total_count") && responseObject.getVal("total_count")->isIntegerNumber())
-			totalCount = responseObject.getVal("total_count")->asIntegerNumber();
-		if (responseObject.contains("offset") && responseObject.getVal("offset")->isIntegerNumber())
-			received = responseObject.getVal("offset")->asIntegerNumber();
-		if (responseObject.contains("limit") && responseObject.getVal("limit")->isIntegerNumber())
-			received += responseObject.getVal("limit")->asIntegerNumber();
-		bool hasMore = (received < totalCount);
-
-		if (hasMore) makeRequest(received);
-		else finishListing(_files);
-	} else {
-		warning("null, not json");
-		error.failed = true;
-		finishError(error);
+			//as we list directory by id, we can't determine full path for the file, so we leave it empty
+			_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
+		}
 	}
 
+	uint32 received = 0;
+	uint32 totalCount = 0;
+	if (responseObject.contains("total_count") && responseObject.getVal("total_count")->isIntegerNumber())
+		totalCount = responseObject.getVal("total_count")->asIntegerNumber();
+	if (responseObject.contains("offset") && responseObject.getVal("offset")->isIntegerNumber())
+		received = responseObject.getVal("offset")->asIntegerNumber();
+	if (responseObject.contains("limit") && responseObject.getVal("limit")->isIntegerNumber())
+		received += responseObject.getVal("limit")->asIntegerNumber();
+	bool hasMore = (received < totalCount);
+
+	if (hasMore) makeRequest(received);
+	else finishListing(_files);
+
 	delete json;
 }
 


Commit: cccfe7c247fed9b00c100e64433439386d6ef077
    https://github.com/scummvm/scummvm/commit/cccfe7c247fed9b00c100e64433439386d6ef077
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update BoxListDirectoryByIdRequest

It now uses special CurlJsonRequest static methods to check whether JSON
is an object, has a string or integer parameter.

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 3e51806..776ee71 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -125,63 +125,16 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 
 		Common::JSONArray items = responseObject.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!items[i]->isObject()) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item is not an object!");
-				debug(9, "%s", items[i]->stringify(true).c_str());
-				continue;
-			}
+			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
 
 			Common::JSONObject item = items[i]->asObject();
 
-			if (!item.contains("id") || !item.getVal("id")->isString()) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"id\"!");
-				if (item.contains("id")) {
-					debug(9, "%s", item.getVal("id")->stringify(true).c_str());
-				} else {
-					debug(9, "(not available)");
-				}
-				continue;
-			}
-
-			if (!item.contains("name") || !item.getVal("name")->isString()) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"name\"!");
-				if (item.contains("name")) {
-					debug(9, "%s", item.getVal("name")->stringify(true).c_str());
-				} else {
-					debug(9, "(not available)");
-				}
-				continue;
-			}
-
-			if (!item.contains("type") || !item.getVal("type")->isString()) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"type\"!");
-				if (item.contains("type")) {
-					debug(9, "%s", item.getVal("type")->stringify(true).c_str());
-				} else {
-					debug(9, "(not available)");
-				}
-				continue;
-			}
-
-			if (!item.contains("size") || (!item.getVal("size")->isString() && !item.getVal("size")->isIntegerNumber())) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"size\"!");
-				if (item.contains("size")) {
-					debug(9, "%s", item.getVal("size")->stringify(true).c_str());
-				} else {
-					debug(9, "(not available)");
-				}
-				continue;
-			}
-
-			if (!item.contains("modified_at") || !item.getVal("modified_at")->isString()) {
-				warning("BoxListDirectoryByIdRequest: \"entries\" item's \"modified_at\"!");
-				if (item.contains("modified_at")) {
-					debug(9, "%s", item.getVal("modified_at")->stringify(true).c_str());
-				} else {
-					debug(9, "(not available)");
-				}
-				continue;
-			}
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "size", "BoxListDirectoryByIdRequest") &&
+				!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
 
 			Common::String id = item.getVal("id")->asString();
 			Common::String name = item.getVal("name")->asString();
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 875f5e7..7f7cbca 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -31,7 +31,7 @@
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url):
+CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url) :
 	CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES),
 	_buffer(new byte[CURL_JSON_REQUEST_BUFFER_SIZE]) {}
 
@@ -97,4 +97,43 @@ void CurlJsonRequest::finishJson(Common::JSONValue *json) {
 	else delete json;
 }
 
+bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningPrefix) {
+	if (item == nullptr) {
+		warning("%s: passed item is NULL", warningPrefix);
+		return false;
+	}
+
+	if (item->isObject()) return true;
+
+	warning("%s: passed item is not an object!", warningPrefix);
+	debug(9, "%s", item->stringify(true).c_str());
+	return false;
+}
+
+bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix) {
+	if (!item.contains(key)) {
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	if (item.getVal(key)->isString()) return true;
+
+	warning("%s: passed item's \"%s\" attribute is not a string!", warningPrefix, key);
+	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+	return false;
+}
+
+bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix) {
+	if (!item.contains(key)) {
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	if (item.getVal(key)->isIntegerNumber()) return true;
+
+	warning("%s: passed item's \"%s\" attribute is not an integer!", warningPrefix, key);
+	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+	return false;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 7858c89..1d1409e 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -52,6 +52,10 @@ public:
 
 	virtual void handle();
 	virtual void restart();
+
+	static bool jsonIsObject(Common::JSONValue *item, const char *warningPrefix);
+	static bool jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix);
+	static bool jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix);
 };
 
 } // End of namespace Networking


Commit: d57fca4665e65bca1cf487fb109d020d1233e800
    https://github.com/scummvm/scummvm/commit/d57fca4665e65bca1cf487fb109d020d1233e800
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: JANITORIAL: Fix code formatting

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/cloudmanager.cpp
    backends/cloud/downloadrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxinforequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/id/idcreatedirectoryrequest.cpp
    backends/cloud/id/iddownloadrequest.cpp
    backends/cloud/id/idlistdirectoryrequest.cpp
    backends/cloud/id/idresolveidrequest.cpp
    backends/cloud/id/idstorage.cpp
    backends/cloud/id/idstreamfilerequest.cpp
    backends/cloud/iso8601.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.cpp
    backends/cloud/storagefile.cpp
    backends/networking/curl/cloudicon.cpp
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/networkreadstream.cpp
    backends/networking/curl/request.cpp
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/getclienthandler.cpp
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/resourcehandler.cpp
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index cd61e04..2b2be70 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -57,16 +57,17 @@ BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
 
 BoxStorage::BoxStorage(Common::String code) {
 	getAccessToken(
-	    new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
-	    new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
-	    code
+		new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
+		new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
+		code
 	);
 }
 
 BoxStorage::~BoxStorage() {}
 
 void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
-	if (!KEY || !SECRET) loadKeyAndSecret();
+	if (!KEY || !SECRET)
+		loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
 	if (!codeFlow && _refreshToken == "") {
@@ -76,7 +77,9 @@ void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback);
-	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	if (errorCallback == nullptr)
+		errorCallback = getErrorPrintingCallback();
+
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.box.com/oauth2/token");
 	if (codeFlow) {
 		request->addPostField("grant_type=authorization_code");
@@ -109,12 +112,14 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 	if (!result.contains("access_token") || !result.contains("refresh_token")) {
 		warning("Bad response, no token passed");
 		debug("%s", json->stringify().c_str());
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		_refreshToken = result.getVal("refresh_token")->asString();
 		CloudMan.save(); //ask CloudManager to save our new refreshToken
-		if (callback) (*callback)(BoolResponse(nullptr, true));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
 }
@@ -190,8 +195,10 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 }
 
 Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = getPrintFilesCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
+	if (!callback)
+		callback = getPrintFilesCallback();
 	return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
 }
 
@@ -213,7 +220,8 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
 }
 
 Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 
 	Common::String url = "https://api.box.com/2.0/folders";
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::createDirectoryInnerCallback, callback);
@@ -235,13 +243,15 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
 }
 
 Networking::Request *BoxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 	return addRequest(new BoxUploadRequest(this, remotePath, localPath, callback, errorCallback));
 }
 
 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
 	warning("BoxStorage::upload(ReadStream) not implemented");
-	if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "BoxStorage::upload(ReadStream) not implemented", -1));
+	if (errorCallback)
+		(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "BoxStorage::upload(ReadStream) not implemented", -1));
 	delete callback;
 	delete errorCallback;
 	return nullptr;
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index 1449aa9..c308ddb 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -41,13 +41,15 @@ BoxUploadRequest::BoxUploadRequest(BoxStorage *storage, Common::String path, Com
 
 BoxUploadRequest::~BoxUploadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _uploadCallback;
 }
 
 void BoxUploadRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_resolvedId = ""; //used to update file contents
 	_parentId = ""; //used to create file within parent directory
 	_ignoreCallback = false;
@@ -101,7 +103,8 @@ void BoxUploadRequest::upload() {
 	}
 
 	Common::String url = "https://upload.box.com/api/2.0/files";
-	if (_resolvedId != "") url += "/" + _resolvedId;
+	if (_resolvedId != "")
+		url += "/" + _resolvedId;
 	url += "/content";
 	Networking::JsonCallback callback = new Common::Callback<BoxUploadRequest, Networking::JsonResponse>(this, &BoxUploadRequest::uploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<BoxUploadRequest, Networking::ErrorResponse>(this, &BoxUploadRequest::notUploadedCallback);
@@ -174,11 +177,11 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 			//TODO: check errors
 			/*
 			if (object.contains("error")) {
-			    warning("Box returned error: %s", json->stringify(true).c_str());
-			    delete json;
-			    error.response = json->stringify(true);
-			    finishError(error);
-			    return;
+				warning("Box returned error: %s", json->stringify(true).c_str());
+				delete json;
+				error.response = json->stringify(true);
+				finishError(error);
+				return;
 			}
 			*/
 		}
@@ -205,7 +208,8 @@ void BoxUploadRequest::restart() { start(); }
 
 void BoxUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+	if (_uploadCallback)
+		(*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace Box
diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index 056eb08..a68a872 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -112,7 +112,8 @@ void CloudManager::init() {
 
 void CloudManager::save() {
 	for (uint32 i = 0; i < _storages.size(); ++i) {
-		if (i == kStorageNoneId) continue;
+		if (i == kStorageNoneId)
+			continue;
 		Common::String name = getStorageConfigName(i);
 		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, ConfMan.kCloudDomain);
 		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, ConfMan.kCloudDomain);
@@ -127,12 +128,16 @@ void CloudManager::save() {
 
 void CloudManager::replaceStorage(Storage *storage, uint32 index) {
 	freeStorages();
-	if (!storage) error("CloudManager::replaceStorage: NULL storage passed");
-	if (index >= kStorageTotal) error("CloudManager::replaceStorage: invalid index passed");
+	if (!storage)
+		error("CloudManager::replaceStorage: NULL storage passed");
+	if (index >= kStorageTotal)
+		error("CloudManager::replaceStorage: invalid index passed");
 	if (_activeStorage != nullptr && _activeStorage->isWorking()) {
 		warning("CloudManager::replaceStorage: replacing Storage while the other is working");
-		if (_activeStorage->isDownloading()) _activeStorage->cancelDownload();
-		if (_activeStorage->isSyncing()) _activeStorage->cancelSync();
+		if (_activeStorage->isDownloading())
+			_activeStorage->cancelDownload();
+		if (_activeStorage->isSyncing())
+			_activeStorage->cancelSync();
 		removeStorage(_activeStorage);
 	} else {
 		delete _activeStorage;
@@ -161,7 +166,8 @@ void CloudManager::freeStorages() {
 }
 
 void CloudManager::passNoStorageConnected(Networking::ErrorCallback errorCallback) const {
-	if (errorCallback == nullptr) return;
+	if (errorCallback == nullptr)
+		return;
 	(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "No Storage connected!", -1));
 }
 
@@ -200,35 +206,42 @@ bool CloudManager::switchStorage(uint32 index) {
 }
 
 Common::String CloudManager::getStorageUsername(uint32 index) {
-	if (index >= _storages.size()) return "";
+	if (index >= _storages.size())
+		return "";
 	return _storages[index].username;
 }
 
 uint64 CloudManager::getStorageUsedSpace(uint32 index) {
-	if (index >= _storages.size()) return 0;
+	if (index >= _storages.size())
+		return 0;
 	return _storages[index].usedBytes;
 }
 
 Common::String CloudManager::getStorageLastSync(uint32 index) {
-	if (index >= _storages.size()) return "";
-	if (index == _currentStorageIndex && isSyncing()) return "";
+	if (index >= _storages.size())
+		return "";
+	if (index == _currentStorageIndex && isSyncing())
+		return "";
 	return _storages[index].lastSyncDate;
 }
 
 void CloudManager::setStorageUsername(uint32 index, Common::String name) {
-	if (index >= _storages.size()) return;
+	if (index >= _storages.size())
+		return;
 	_storages[index].username = name;
 	save();
 }
 
 void CloudManager::setStorageUsedSpace(uint32 index, uint64 used) {
-	if (index >= _storages.size()) return;
+	if (index >= _storages.size())
+		return;
 	_storages[index].usedBytes = used;
 	save();
 }
 
 void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
-	if (index >= _storages.size()) return;
+	if (index >= _storages.size())
+		return;
 	_storages[index].lastSyncDate = date;
 	save();
 }
@@ -238,10 +251,18 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 
 	Storage *storage = nullptr;
 	switch (index) {
-	case kStorageDropboxId: storage = new Dropbox::DropboxStorage(code); break;
-	case kStorageOneDriveId: storage = new OneDrive::OneDriveStorage(code); break;
-	case kStorageGoogleDriveId: storage = new GoogleDrive::GoogleDriveStorage(code); break;
-	case kStorageBoxId: storage = new Box::BoxStorage(code); break;
+	case kStorageDropboxId:
+		storage = new Dropbox::DropboxStorage(code);
+		break;
+	case kStorageOneDriveId:
+		storage = new OneDrive::OneDriveStorage(code);
+		break;
+	case kStorageGoogleDriveId:
+		storage = new GoogleDrive::GoogleDriveStorage(code);
+		break;
+	case kStorageBoxId:
+		storage = new Box::BoxStorage(code);
+		break;
 	}
 	// in these constructors Storages request token using the passed code
 	// when the token is received, they call replaceStorage()
@@ -251,8 +272,9 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
 
 Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->listDirectory(path, callback, errorCallback, recursive);
-	else {
+	if (storage) {
+		return storage->listDirectory(path, callback, errorCallback, recursive);
+	} else {
 		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
@@ -262,8 +284,9 @@ Networking::Request *CloudManager::listDirectory(Common::String path, Storage::L
 
 Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Common::String localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
-	else {
+	if (storage) {
+		return storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
+	} else {
 		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
@@ -273,8 +296,9 @@ Networking::Request *CloudManager::downloadFolder(Common::String remotePath, Com
 
 Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->info(callback, errorCallback);
-	else {
+	if (storage) {
+		return storage->info(callback, errorCallback);
+	} else {
 		passNoStorageConnected(errorCallback);
 		delete callback;
 		delete errorCallback;
@@ -284,7 +308,8 @@ Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, N
 
 Common::String CloudManager::savesDirectoryPath() {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->savesDirectoryPath();
+	if (storage)
+		return storage->savesDirectoryPath();
 	return "";
 }
 
@@ -303,7 +328,8 @@ SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networ
 
 bool CloudManager::isWorking() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->isWorking();
+	if (storage)
+		return storage->isWorking();
 	return false;
 }
 
@@ -319,95 +345,111 @@ bool CloudManager::couldUseLocalServer() {
 
 bool CloudManager::isSyncing() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->isSyncing();
+	if (storage)
+		return storage->isSyncing();
 	return false;
 }
 
 double CloudManager::getSyncDownloadingProgress() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getSyncDownloadingProgress();
+	if (storage)
+		return storage->getSyncDownloadingProgress();
 	return 1;
 }
 
 double CloudManager::getSyncProgress() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getSyncProgress();
+	if (storage)
+		return storage->getSyncProgress();
 	return 1;
 }
 
 Common::Array<Common::String> CloudManager::getSyncingFiles() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getSyncingFiles();
+	if (storage)
+		return storage->getSyncingFiles();
 	return Common::Array<Common::String>();
 }
 
 void CloudManager::cancelSync() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->cancelSync();
+	if (storage)
+		storage->cancelSync();
 }
 
 void CloudManager::setSyncTarget(GUI::CommandReceiver *target) const {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->setSyncTarget(target);
+	if (storage)
+		storage->setSyncTarget(target);
 }
 
 ///// DownloadFolderRequest-related /////
 
 bool CloudManager::startDownload(Common::String remotePath, Common::String localPath) const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->startDownload(remotePath, localPath);
+	if (storage)
+		return storage->startDownload(remotePath, localPath);
 	return false;
 }
 
 void CloudManager::cancelDownload() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->cancelDownload();
+	if (storage)
+		storage->cancelDownload();
 }
 
 void CloudManager::setDownloadTarget(GUI::CommandReceiver *target) const {
 	Storage *storage = getCurrentStorage();
-	if (storage) storage->setDownloadTarget(target);
+	if (storage)
+		storage->setDownloadTarget(target);
 }
 
 bool CloudManager::isDownloading() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->isDownloading();
+	if (storage)
+		return storage->isDownloading();
 	return false;
 }
 
 double CloudManager::getDownloadingProgress() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadingProgress();
+	if (storage)
+		return storage->getDownloadingProgress();
 	return 1;
 }
 
 uint64 CloudManager::getDownloadBytesNumber() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadBytesNumber();
+	if (storage)
+		return storage->getDownloadBytesNumber();
 	return 0;
 }
 
 uint64 CloudManager::getDownloadTotalBytesNumber() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadTotalBytesNumber();
+	if (storage)
+		return storage->getDownloadTotalBytesNumber();
 	return 0;
 }
 
 uint64 CloudManager::getDownloadSpeed() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadSpeed();
+	if (storage)
+		return storage->getDownloadSpeed();
 	return 0;
 }
 
 Common::String CloudManager::getDownloadRemoteDirectory() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadRemoteDirectory();
+	if (storage)
+		return storage->getDownloadRemoteDirectory();
 	return "";
 }
 
 Common::String CloudManager::getDownloadLocalDirectory() const {
 	Storage *storage = getCurrentStorage();
-	if (storage) return storage->getDownloadLocalDirectory();
+	if (storage)
+		return storage->getDownloadLocalDirectory();
 	return "";
 }
 
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index 49a8a1e..f706ed6 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -34,7 +34,8 @@ DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callbac
 
 DownloadRequest::~DownloadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 	delete _localFile;
 	delete[] _buffer;
@@ -42,7 +43,8 @@ DownloadRequest::~DownloadRequest() {
 
 void DownloadRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_remoteFileStream = nullptr;
 	//TODO: add some way to reopen DumpFile, so DownloadRequest could be restarted
 	_ignoreCallback = false;
@@ -56,13 +58,15 @@ void DownloadRequest::start() {
 
 void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	_remoteFileStream = (Networking::NetworkReadStream *)response.value;
 }
 
 void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -113,11 +117,13 @@ void DownloadRequest::restart() {
 
 void DownloadRequest::finishDownload(bool success) {
 	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 void DownloadRequest::finishError(Networking::ErrorResponse error) {
-	if (_localFile) _localFile->close();
+	if (_localFile)
+		_localFile->close();
 	Request::finishError(error);
 }
 
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 6d22822..968fb45 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -38,13 +38,15 @@ DropboxCreateDirectoryRequest::DropboxCreateDirectoryRequest(Common::String toke
 
 DropboxCreateDirectoryRequest::~DropboxCreateDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 }
 
 void DropboxCreateDirectoryRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_ignoreCallback = false;
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback);
@@ -82,8 +84,9 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 	}
 
 	Common::JSONObject info = json->asObject();
-	if (info.contains("id")) finishCreation(true);
-	else {
+	if (info.contains("id")) {
+		finishCreation(true);
+	} else {
 		if (info.contains("error_summary") && info.getVal("error_summary")->isString()) {
 			Common::String summary = info.getVal("error_summary")->asString();
 			if (summary.contains("path") && summary.contains("conflict") && summary.contains("folder")) {
@@ -101,8 +104,10 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 
 void DropboxCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -114,7 +119,8 @@ Common::String DropboxCreateDirectoryRequest::date() const { return _date; }
 
 void DropboxCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index e147ac5..37700ea 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -39,13 +39,15 @@ DropboxInfoRequest::DropboxInfoRequest(Common::String token, Storage::StorageInf
 
 DropboxInfoRequest::~DropboxInfoRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _infoCallback;
 }
 
 void DropboxInfoRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_ignoreCallback = false;
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback);
@@ -136,7 +138,8 @@ void DropboxInfoRequest::restart() { start(); }
 
 void DropboxInfoRequest::finishInfo(StorageInfo info) {
 	Request::finishSuccess();
-	if (_infoCallback) (*_infoCallback)(Storage::StorageInfoResponse(this, info));
+	if (_infoCallback)
+		(*_infoCallback)(Storage::StorageInfoResponse(this, info));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index cc82f12..def9155 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -39,13 +39,15 @@ DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, C
 
 DropboxListDirectoryRequest::~DropboxListDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void DropboxListDirectoryRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_files.clear();
 	_ignoreCallback = false;
 
@@ -69,9 +71,11 @@ void DropboxListDirectoryRequest::start() {
 
 void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
-	if (response.request) _date = response.request->date();
+	if (response.request)
+		_date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -138,8 +142,10 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 
 void DropboxListDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -151,7 +157,8 @@ Common::String DropboxListDirectoryRequest::date() const { return _date; }
 
 void DropboxListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback)
+		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index d35e29f..cd1dff8 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -61,7 +61,8 @@ DropboxStorage::DropboxStorage(Common::String code) {
 DropboxStorage::~DropboxStorage() {}
 
 void DropboxStorage::getAccessToken(Common::String code) {
-	if (!KEY || !SECRET) loadKeyAndSecret();
+	if (!KEY || !SECRET)
+		loadKeyAndSecret();
 	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);
 	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::codeFlowFailed);
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, errorCallback, "https://api.dropboxapi.com/oauth2/token");
@@ -134,17 +135,20 @@ Networking::Request *DropboxStorage::streamFileById(Common::String path, Network
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
 
 	Networking::NetworkReadStreamResponse response = request->execute();
-	if (callback) (*callback)(response);
+	if (callback)
+		(*callback)(response);
 	return response.request; // no leak here, response.request == request
 }
 
 Networking::Request *DropboxStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 	return addRequest(new DropboxCreateDirectoryRequest(_token, path, callback, errorCallback));
 }
 
 Networking::Request *DropboxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 	return addRequest(new DropboxInfoRequest(_token, callback, errorCallback));
 }
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index eaa945a..03c3fbc 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -40,14 +40,16 @@ DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String
 
 DropboxUploadRequest::~DropboxUploadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _contentsStream;
 	delete _uploadCallback;
 }
 
 void DropboxUploadRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	if (!_contentsStream) {
 		warning("DropboxUploadRequest: cannot start because stream is invalid");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
@@ -120,7 +122,8 @@ void DropboxUploadRequest::uploadNextPart() {
 void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
 	debug(9, "partUploadedCallback");
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -179,7 +182,8 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 void DropboxUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
 	debug("partUploadedErrorCallback");
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -189,7 +193,8 @@ void DropboxUploadRequest::restart() { start(); }
 
 void DropboxUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+	if (_uploadCallback)
+		(*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 1b3c1c5..fe9704c 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -39,14 +39,16 @@ FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArra
 FolderDownloadRequest::~FolderDownloadRequest() {
 	sendCommand(GUI::kDownloadEndedCmd, 0);
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _fileArrayCallback;
 }
 
 void FolderDownloadRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_currentFile = StorageFile();
 	_pendingFiles.clear();
 	_failedFiles.clear();
@@ -65,7 +67,8 @@ void FolderDownloadRequest::start() {
 
 void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	_pendingFiles = response.value;
 
 	// remove all directories
@@ -84,13 +87,15 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
 
 void FolderDownloadRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
 void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	if (!response.value) _failedFiles.push_back(_currentFile);
 	_downloadedBytes += _currentFile.size();
 	downloadNextFile();
@@ -98,7 +103,8 @@ void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse respons
 
 void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	fileDownloadedCallback(Storage::BoolResponse(error.request, false));
 }
 
@@ -153,23 +159,28 @@ void FolderDownloadRequest::restart() { start(); }
 
 void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
+	if (_fileArrayCallback)
+		(*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
 }
 
 double FolderDownloadRequest::getProgress() const {
-	if (_totalFiles == 0 || _totalBytes == 0) return 0;
+	if (_totalFiles == 0 || _totalBytes == 0)
+		return 0;
 	return (double)getDownloadedBytes() / (double)getTotalBytesToDownload();
 }
 
 uint64 FolderDownloadRequest::getDownloadedBytes() const {
-	if (_totalFiles == 0) return 0;
+	if (_totalFiles == 0)
+		return 0;
 
 	double currentFileProgress = 0;
 	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
-	if (downloadRequest != nullptr) currentFileProgress = downloadRequest->getProgress();
-	else {
+	if (downloadRequest != nullptr) {
+		currentFileProgress = downloadRequest->getProgress();
+	} else {
 		Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest);
-		if (idDownloadRequest != nullptr) currentFileProgress = idDownloadRequest->getProgress();
+		if (idDownloadRequest != nullptr)
+			currentFileProgress = idDownloadRequest->getProgress();
 	}
 
 	return _downloadedBytes + (uint64)(currentFileProgress * _currentFile.size());
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 3228dde..d2e94a7 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -41,13 +41,15 @@ GoogleDriveListDirectoryByIdRequest::GoogleDriveListDirectoryByIdRequest(GoogleD
 
 GoogleDriveListDirectoryByIdRequest::~GoogleDriveListDirectoryByIdRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void GoogleDriveListDirectoryByIdRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_files.clear();
 	_ignoreCallback = false;
 
@@ -57,7 +59,8 @@ void GoogleDriveListDirectoryByIdRequest::start() {
 void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken) {
 	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken&orderBy=folder,name";
 	//files(id,mimeType,modifiedTime,name,size),nextPageToken
-	if (pageToken != "") url += "&pageToken=" + pageToken;
+	if (pageToken != "")
+		url += "&pageToken=" + pageToken;
 	url += "&q=%27" + _requestedId + "%27+in+parents";
 
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, Networking::JsonResponse>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
@@ -69,8 +72,10 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken)
 
 void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -131,8 +136,10 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
 
 void GoogleDriveListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -144,7 +151,8 @@ Common::String GoogleDriveListDirectoryByIdRequest::date() const { return _date;
 
 void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback)
+		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 33b2bf5..2201321 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -57,9 +57,9 @@ GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::Strin
 
 GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
 	getAccessToken(
-	    new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
-	    new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
-	    code
+		new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
+		new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
+		code
 	);
 }
 
@@ -71,12 +71,14 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::Error
 
 	if (!codeFlow && _refreshToken == "") {
 		warning("GoogleDriveStorage: no refresh token available to get new access token.");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 		return;
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
-	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	if (errorCallback == nullptr)
+		errorCallback = getErrorPrintingCallback();
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://accounts.google.com/o/oauth2/token"); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
@@ -99,7 +101,8 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("GoogleDriveStorage: got NULL instead of JSON");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 		return;
 	}
 
@@ -107,7 +110,8 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 	if (!result.contains("access_token")) {
 		warning("Bad response, no token passed");
 		debug("%s", json->stringify().c_str());
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		if (!result.contains("refresh_token"))
@@ -115,7 +119,8 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 		else
 			_refreshToken = result.getVal("refresh_token")->asString();
 		CloudMan.save(); //ask CloudManager to save our new refreshToken
-		if (callback) (*callback)(BoolResponse(nullptr, true));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
 }
@@ -206,8 +211,10 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
 }
 
 Networking::Request *GoogleDriveStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
+	if (!callback)
+		callback = new Common::Callback<GoogleDriveStorage, FileArrayResponse>(this, &GoogleDriveStorage::printFiles);
 	return addRequest(new GoogleDriveListDirectoryByIdRequest(this, id, callback, errorCallback));
 }
 
@@ -236,7 +243,8 @@ void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
 }
 
 Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 
 	Common::String url = "https://www.googleapis.com/drive/v3/files";
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
@@ -259,7 +267,8 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str
 }
 
 Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!callback) callback = new Common::Callback<GoogleDriveStorage, StorageInfoResponse>(this, &GoogleDriveStorage::printInfo);
+	if (!callback)
+		callback = new Common::Callback<GoogleDriveStorage, StorageInfoResponse>(this, &GoogleDriveStorage::printInfo);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::infoInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user");
 	request->addHeader("Authorization: Bearer " + _token);
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 90dc8af..3cdee26 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -41,14 +41,16 @@ GoogleDriveUploadRequest::GoogleDriveUploadRequest(GoogleDriveStorage *storage,
 
 GoogleDriveUploadRequest::~GoogleDriveUploadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _contentsStream;
 	delete _uploadCallback;
 }
 
 void GoogleDriveUploadRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
 		warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
 		finishError(Networking::ErrorResponse(this, false, true, "", -1));
@@ -71,14 +73,16 @@ void GoogleDriveUploadRequest::resolveId() {
 
 void GoogleDriveUploadRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	_resolvedId = response.value.id();
 	startUpload();
 }
 
 void GoogleDriveUploadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//not resolved => error or no such file
 	if (error.response.contains("no such file found in its parent directory")) {
@@ -108,18 +112,21 @@ void GoogleDriveUploadRequest::startUpload() {
 	}
 
 	Common::String url = "https://www.googleapis.com/upload/drive/v3/files";
-	if (_resolvedId != "") url += "/" + ConnMan.urlEncode(_resolvedId);
+	if (_resolvedId != "")
+		url += "/" + ConnMan.urlEncode(_resolvedId);
 	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::startUploadCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
-	if (_resolvedId != "") request->usePatch();
+	if (_resolvedId != "")
+		request->usePatch();
 
 	Common::JSONObject jsonRequestParameters;
-	if (_resolvedId != "") jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
-	else {
+	if (_resolvedId != "") {
+		jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
+	} else {
 		Common::JSONArray parentsArray;
 		parentsArray.push_back(new Common::JSONValue(_parentId));
 		jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
@@ -134,7 +141,8 @@ void GoogleDriveUploadRequest::startUpload() {
 
 void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -151,7 +159,8 @@ void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse resp
 					Common::String result = "";
 					char c;
 					for (const char *i = position + 10; c = *i, c != 0; ++i) {
-						if (c == '\n' || c == '\r') break;
+						if (c == '\n' || c == '\r')
+							break;
 						result += c;
 					}
 					_uploadUrl = result;
@@ -172,7 +181,8 @@ void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse resp
 
 void GoogleDriveUploadRequest::startUploadErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -198,7 +208,8 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 
 	byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
-	if (size != 0) request->setBuffer(buffer, size);
+	if (size != 0)
+		request->setBuffer(buffer, size);
 
 	if (_uploadUrl != "") {
 		if (_contentsStream->pos() == 0)
@@ -212,8 +223,10 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 
 bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
 	//308 Resume Incomplete, with Range: X-Y header
-	if (!stream) return false;
-	if (stream->httpResponseCode() != 308) return false; //seriously
+	if (!stream)
+		return false;
+	if (stream->httpResponseCode() != 308)
+		return false; //seriously
 
 	Common::String headers = stream->responseHeaders();
 	const char *cstr = headers.c_str();
@@ -227,7 +240,8 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
 			Common::String result = "";
 			char c;
 			for (const char *i = position + needleLength; c = *i, c != 0; ++i) {
-				if (c == '\n' || c == '\r') break;
+				if (c == '\n' || c == '\r')
+					break;
 				result += c;
 			}
 			_serverReceivedBytes = result.asUint64() + 1;
@@ -241,7 +255,8 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
 
 void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -303,7 +318,8 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 
 void GoogleDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)error.request;
 	if (rq) {
@@ -325,7 +341,8 @@ void GoogleDriveUploadRequest::restart() { start(); }
 
 void GoogleDriveUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+	if (_uploadCallback)
+		(*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/id/idcreatedirectoryrequest.cpp b/backends/cloud/id/idcreatedirectoryrequest.cpp
index 11f6503..37f417f 100644
--- a/backends/cloud/id/idcreatedirectoryrequest.cpp
+++ b/backends/cloud/id/idcreatedirectoryrequest.cpp
@@ -36,20 +36,23 @@ IdCreateDirectoryRequest::IdCreateDirectoryRequest(IdStorage *storage, Common::S
 
 IdCreateDirectoryRequest::~IdCreateDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 }
 
 void IdCreateDirectoryRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_ignoreCallback = false;
 
 	//the only exception when we create parent folder - is when it's ScummVM/ base folder
 	Common::String prefix = _requestedParentPath;
-	if (prefix.size() > 7) prefix.erase(7);
+	if (prefix.size() > 7)
+		prefix.erase(7);
 	if (prefix.equalsIgnoreCase("ScummVM")) {
 		Storage::BoolCallback callback = new Common::Callback<IdCreateDirectoryRequest, Storage::BoolResponse>(this, &IdCreateDirectoryRequest::createdBaseDirectoryCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<IdCreateDirectoryRequest, Networking::ErrorResponse>(this, &IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback);
@@ -62,15 +65,19 @@ void IdCreateDirectoryRequest::start() {
 
 void IdCreateDirectoryRequest::createdBaseDirectoryCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 	resolveId();
 }
 
 void IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -79,15 +86,18 @@ void IdCreateDirectoryRequest::resolveId() {
 	Storage::UploadCallback innerCallback = new Common::Callback<IdCreateDirectoryRequest, Storage::UploadResponse>(this, &IdCreateDirectoryRequest::idResolvedCallback);
 	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdCreateDirectoryRequest, Networking::ErrorResponse>(this, &IdCreateDirectoryRequest::idResolveFailedCallback);
 	Common::String path = _requestedParentPath;
-	if (_requestedParentPath != "") path += "/";
+	if (_requestedParentPath != "")
+		path += "/";
 	path += _requestedDirectoryName;
 	_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
 }
 
 void IdCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 
 	//resolved => folder already exists
 	finishCreation(false);
@@ -95,8 +105,10 @@ void IdCreateDirectoryRequest::idResolvedCallback(Storage::UploadResponse respon
 
 void IdCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 
 	//not resolved => folder not exists
 	if (error.response.contains("no such file found in its parent directory")) {
@@ -119,15 +131,19 @@ void IdCreateDirectoryRequest::idResolveFailedCallback(Networking::ErrorResponse
 
 void IdCreateDirectoryRequest::createdDirectoryCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 	finishCreation(response.value);
 }
 
 void IdCreateDirectoryRequest::createdDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -139,7 +155,8 @@ Common::String IdCreateDirectoryRequest::date() const { return _date; }
 
 void IdCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 } // End of namespace Id
diff --git a/backends/cloud/id/iddownloadrequest.cpp b/backends/cloud/id/iddownloadrequest.cpp
index edd2d26..2532d61 100644
--- a/backends/cloud/id/iddownloadrequest.cpp
+++ b/backends/cloud/id/iddownloadrequest.cpp
@@ -35,14 +35,16 @@ IdDownloadRequest::IdDownloadRequest(IdStorage *storage, Common::String remotePa
 
 IdDownloadRequest::~IdDownloadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 }
 
 void IdDownloadRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_ignoreCallback = false;
 
@@ -54,7 +56,8 @@ void IdDownloadRequest::start() {
 
 void IdDownloadRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Storage::BoolCallback innerCallback = new Common::Callback<IdDownloadRequest, Storage::BoolResponse>(this, &IdDownloadRequest::downloadCallback);
 	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, Networking::ErrorResponse>(this, &IdDownloadRequest::downloadErrorCallback);
@@ -63,19 +66,22 @@ void IdDownloadRequest::idResolvedCallback(Storage::UploadResponse response) {
 
 void IdDownloadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
 void IdDownloadRequest::downloadCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishDownload(response.value);
 }
 
 void IdDownloadRequest::downloadErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -85,12 +91,14 @@ void IdDownloadRequest::restart() { start(); }
 
 void IdDownloadRequest::finishDownload(bool success) {
 	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 double IdDownloadRequest::getProgress() const {
 	DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
-	if (downloadRequest == nullptr) return 0; // resolving id still
+	if (downloadRequest == nullptr)
+		return 0; // resolving id still
 
 	// id resolve is 10 % and download is the other 90 %
 	return 0.1 + 0.9 * downloadRequest->getProgress(); // downloading
diff --git a/backends/cloud/id/idlistdirectoryrequest.cpp b/backends/cloud/id/idlistdirectoryrequest.cpp
index 6c70ed5..4e63709 100644
--- a/backends/cloud/id/idlistdirectoryrequest.cpp
+++ b/backends/cloud/id/idlistdirectoryrequest.cpp
@@ -35,14 +35,16 @@ IdListDirectoryRequest::IdListDirectoryRequest(IdStorage *storage, Common::Strin
 
 IdListDirectoryRequest::~IdListDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void IdListDirectoryRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_files.clear();
 	_directoriesQueue.clear();
@@ -57,8 +59,10 @@ void IdListDirectoryRequest::start() {
 
 void IdListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 
 	StorageFile directory = response.value;
 	directory.setPath(_requestedPath);
@@ -68,8 +72,10 @@ void IdListDirectoryRequest::idResolvedCallback(Storage::UploadResponse response
 
 void IdListDirectoryRequest::idResolveErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -89,13 +95,16 @@ void IdListDirectoryRequest::listNextDirectory() {
 
 void IdListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 
 	for (uint32 i = 0; i < response.value.size(); ++i) {
 		StorageFile &file = response.value[i];
 		Common::String path = _currentDirectory.path();
-		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+		if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\')
+			path += '/';
 		path += file.name();
 		file.setPath(path);
 		_files.push_back(file);
@@ -109,8 +118,10 @@ void IdListDirectoryRequest::listedDirectoryCallback(Storage::FileArrayResponse
 
 void IdListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -122,7 +133,8 @@ Common::String IdListDirectoryRequest::date() const { return _date; }
 
 void IdListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback)
+		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace Id
diff --git a/backends/cloud/id/idresolveidrequest.cpp b/backends/cloud/id/idresolveidrequest.cpp
index 38478fa..6742a58 100644
--- a/backends/cloud/id/idresolveidrequest.cpp
+++ b/backends/cloud/id/idresolveidrequest.cpp
@@ -35,14 +35,16 @@ IdResolveIdRequest::IdResolveIdRequest(IdStorage *storage, Common::String path,
 
 IdResolveIdRequest::~IdResolveIdRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _uploadCallback;
 }
 
 void IdResolveIdRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_currentDirectory = "";
 	_currentDirectoryId = _storage->getRootDirectoryId();
@@ -64,12 +66,15 @@ void IdResolveIdRequest::listNextDirectory(StorageFile fileToReturn) {
 
 void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Common::String currentLevelName = _requestedPath;
 	///debug("'%s'", currentLevelName.c_str());
-	if (_currentDirectory.size()) currentLevelName.erase(0, _currentDirectory.size());
-	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\')) currentLevelName.erase(0, 1);
+	if (_currentDirectory.size())
+		currentLevelName.erase(0, _currentDirectory.size());
+	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\'))
+		currentLevelName.erase(0, 1);
 	///debug("'%s'", currentLevelName.c_str());
 	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
 		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
@@ -80,7 +85,8 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 	}
 
 	Common::String path = _currentDirectory;
-	if (path != "") path += "/";
+	if (path != "")
+		path += "/";
 	path += currentLevelName;
 	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
 
@@ -90,7 +96,8 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 	bool found = false;
 	for (uint32 i = 0; i < files.size(); ++i) {
 		if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
-			if (_currentDirectory != "") _currentDirectory += "/";
+			if (_currentDirectory != "")
+				_currentDirectory += "/";
 			_currentDirectory += files[i].name();
 			_currentDirectoryId = files[i].id();
 			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
@@ -101,14 +108,17 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 	}
 
 	if (!found) {
-		if (lastLevel) finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n") + _currentDirectoryId, 404));
-		else finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
+		if (lastLevel)
+			finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n") + _currentDirectoryId, 404));
+		else
+			finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
 	}
 }
 
 void IdResolveIdRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -118,7 +128,8 @@ void IdResolveIdRequest::restart() { start(); }
 
 void IdResolveIdRequest::finishFile(StorageFile file) {
 	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+	if (_uploadCallback)
+		(*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace Id
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index 5b0f9db..857e3fb 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -63,20 +63,26 @@ Storage::ListDirectoryCallback IdStorage::getPrintFilesCallback() {
 }
 
 Networking::Request *IdStorage::resolveFileId(Common::String path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<IdStorage, UploadResponse>(this, &IdStorage::printFile);
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
+	if (!callback)
+		callback = new Common::Callback<IdStorage, UploadResponse>(this, &IdStorage::printFile);
 	return addRequest(new IdResolveIdRequest(this, path, callback, errorCallback));
 }
 
 Networking::Request *IdStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<IdStorage, FileArrayResponse>(this, &IdStorage::printFiles);
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
+	if (!callback)
+		callback = new Common::Callback<IdStorage, FileArrayResponse>(this, &IdStorage::printFiles);
 	return addRequest(new IdListDirectoryRequest(this, path, callback, errorCallback, recursive));
 }
 
 Networking::Request *IdStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
-	if (!callback) callback = new Common::Callback<IdStorage, BoolResponse>(this, &IdStorage::printBool);
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
+	if (!callback)
+		callback = new Common::Callback<IdStorage, BoolResponse>(this, &IdStorage::printBool);
 
 	//find out the parent path and directory name
 	Common::String parentPath = "", directoryName = path;
diff --git a/backends/cloud/id/idstreamfilerequest.cpp b/backends/cloud/id/idstreamfilerequest.cpp
index cc1ce2c..2e68b15 100644
--- a/backends/cloud/id/idstreamfilerequest.cpp
+++ b/backends/cloud/id/idstreamfilerequest.cpp
@@ -34,14 +34,16 @@ IdStreamFileRequest::IdStreamFileRequest(IdStorage *storage, Common::String path
 
 IdStreamFileRequest::~IdStreamFileRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _streamCallback;
 }
 
 void IdStreamFileRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_ignoreCallback = false;
 
@@ -53,7 +55,8 @@ void IdStreamFileRequest::start() {
 
 void IdStreamFileRequest::idResolvedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::NetworkReadStreamCallback innerCallback = new Common::Callback<IdStreamFileRequest, Networking::NetworkReadStreamResponse>(this, &IdStreamFileRequest::streamFileCallback);
 	Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, Networking::ErrorResponse>(this, &IdStreamFileRequest::streamFileErrorCallback);
@@ -62,19 +65,22 @@ void IdStreamFileRequest::idResolvedCallback(Storage::UploadResponse response) {
 
 void IdStreamFileRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
 void IdStreamFileRequest::streamFileCallback(Networking::NetworkReadStreamResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishStream(response.value);
 }
 
 void IdStreamFileRequest::streamFileErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -84,7 +90,8 @@ void IdStreamFileRequest::restart() { start(); }
 
 void IdStreamFileRequest::finishStream(Networking::NetworkReadStream *stream) {
 	Request::finishSuccess();
-	if (_streamCallback) (*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
+	if (_streamCallback)
+		(*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
 }
 
 } // End of namespace Id
diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp
index b2483fd..177ef67 100644
--- a/backends/cloud/iso8601.cpp
+++ b/backends/cloud/iso8601.cpp
@@ -35,7 +35,8 @@ Common::String getSubstring(const Common::String &s, uint32 beginning, uint32 en
 
 int find(const char *cstr, uint32 startPosition, char needle) {
 	const char *res = strchr(cstr + startPosition, needle);
-	if (res == nullptr) return -1;
+	if (res == nullptr)
+		return -1;
 	return res - cstr;
 }
 
@@ -53,7 +54,8 @@ uint32 convertToTimestamp(const Common::String &iso8601Date) {
 	int firstColon = find(cstr, tSeparator + 1, ':');
 	int secondColon = find(cstr, firstColon + 1, ':');
 	int zSeparator = find(cstr, secondColon + 1, 'Z');
-	if (zSeparator == -1) zSeparator = find(cstr, secondColon + 1, '-'); // Box's RFC 3339
+	if (zSeparator == -1)
+		zSeparator = find(cstr, secondColon + 1, '-'); // Box's RFC 3339
 	//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char
 
 	Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index c934f2e..bd612d6 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -39,13 +39,15 @@ OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *
 
 OneDriveCreateDirectoryRequest::~OneDriveCreateDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 }
 
 void OneDriveCreateDirectoryRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_ignoreCallback = false;
 
 	Common::String name = _path, parent = _path;
@@ -57,13 +59,15 @@ void OneDriveCreateDirectoryRequest::start() {
 				name.erase(0, i + 1);
 				break;
 			}
-			if (i == 0) break;
+			if (i == 0)
+				break;
 			--i;
 		}
 	}
 
 	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";
-	if (parent != "") url += ":/" + ConnMan.urlEncode(parent) + ":";
+	if (parent != "")
+		url += ":/" + ConnMan.urlEncode(parent) + ":";
 	url += "/children";
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
@@ -87,7 +91,8 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 		delete json;
 		return;
 	}
-	if (response.request) _date = response.request->date();
+	if (response.request)
+		_date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -101,8 +106,9 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 	}
 
 	Common::JSONObject info = json->asObject();
-	if (info.contains("id")) finishCreation(true);
-	else {
+	if (info.contains("id")) {
+		finishCreation(true);
+	} else {
 		error.response = json->stringify(true);
 		finishError(error);
 	}
@@ -112,8 +118,10 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 
 void OneDriveCreateDirectoryRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -125,7 +133,8 @@ Common::String OneDriveCreateDirectoryRequest::date() const { return _date; }
 
 void OneDriveCreateDirectoryRequest::finishCreation(bool success) {
 	Request::finishSuccess();
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index d0b2714..30a89a7 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -40,14 +40,16 @@ OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *stor
 
 OneDriveListDirectoryRequest::~OneDriveListDirectoryRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void OneDriveListDirectoryRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_workingRequest = nullptr;
 	_files.clear();
 	_directoriesQueue.clear();
@@ -94,7 +96,8 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 		return;
 	}
 
-	if (response.request) _date = response.request->date();
+	if (response.request)
+		_date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -139,8 +142,10 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 
 void OneDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -152,7 +157,8 @@ Common::String OneDriveListDirectoryRequest::date() const { return _date; }
 
 void OneDriveListDirectoryRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback)
+		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index a26dec3..5612cbf 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -58,26 +58,29 @@ OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String user
 
 OneDriveStorage::OneDriveStorage(Common::String code) {
 	getAccessToken(
-	    new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
-	    new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
-	    code
+		new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
+		new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
+		code
 	);
 }
 
 OneDriveStorage::~OneDriveStorage() {}
 
 void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
-	if (!KEY || !SECRET) loadKeyAndSecret();
+	if (!KEY || !SECRET)
+		loadKeyAndSecret();
 	bool codeFlow = (code != "");
 
 	if (!codeFlow && _refreshToken == "") {
 		warning("OneDriveStorage: no refresh token available to get new access token.");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 		return;
 	}
 
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
-	if (errorCallback == nullptr) errorCallback = getErrorPrintingCallback();
+	if (errorCallback == nullptr)
+		errorCallback = getErrorPrintingCallback();
 	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://login.live.com/oauth20_token.srf"); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
@@ -108,13 +111,15 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
 		warning("Bad response, no token or user_id passed");
 		debug("%s", json->stringify().c_str());
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		_uid = result.getVal("user_id")->asString();
 		_refreshToken = result.getVal("refresh_token")->asString();
 		CloudMan.save(); //ask CloudManager to save our new refreshToken
-		if (callback) (*callback)(BoolResponse(nullptr, true));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
 }
@@ -174,8 +179,10 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 	}
 
 	Common::String username = email;
-	if (username == "") username = name;
-	if (username == "") username = uid;
+	if (username == "")
+		username = name;
+	if (username == "")
+		username = uid;
 	CloudMan.setStorageUsername(kStorageOneDriveId, username);
 
 	if (outerCallback) {
@@ -189,7 +196,8 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
 	if (!response.value) {
 		warning("fileInfoCallback: NULL");
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
+		if (outerCallback)
+			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 		return;
 	}
 
@@ -204,7 +212,8 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 	} else {
 		warning("downloadUrl not found in passed JSON");
 		debug("%s", response.value->stringify().c_str());
-		if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
+		if (outerCallback)
+			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 	}
 	delete response.value;
 }
@@ -226,7 +235,8 @@ Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networ
 }
 
 Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 	return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
 }
 
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 3e429f1..f759759 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -92,7 +92,8 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 				irrecoverable = false;
 		}
 
-		if (code == "unauthenticated") irrecoverable = false;
+		if (code == "unauthenticated")
+			irrecoverable = false;
 
 		if (irrecoverable) {
 			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index a22dbf0..331800a 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -41,14 +41,16 @@ OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, Common::S
 
 OneDriveUploadRequest::~OneDriveUploadRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _contentsStream;
 	delete _uploadCallback;
 }
 
 void OneDriveUploadRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	if (_contentsStream == nullptr) {
 		warning("OneDriveUploadRequest: cannot restart because no stream given");
 		finishError(Networking::ErrorResponse(this, false, true, "No stream given", -1));
@@ -97,9 +99,9 @@ void OneDriveUploadRequest::uploadNextPart() {
 	uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
 	request->setBuffer(buffer, size);
 
-	if (_uploadUrl != "")
+	if (_uploadUrl != "") {
 		request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos() - 1, _contentsStream->size()));
-	else if (_contentsStream->size() == 0) {
+	} else if (_contentsStream->size() == 0) {
 		warning("\"Sorry, OneDrive can't upload empty files\"");
 		finishUpload(StorageFile(_savePath, 0, 0, false));
 		delete request;
@@ -111,7 +113,8 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -164,7 +167,8 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 
 void OneDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 	finishError(error);
 }
 
@@ -174,7 +178,8 @@ void OneDriveUploadRequest::restart() { start(); }
 
 void OneDriveUploadRequest::finishUpload(StorageFile file) {
 	Request::finishSuccess();
-	if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file));
+	if (_uploadCallback)
+		(*_uploadCallback)(Storage::UploadResponse(this, file));
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index e3b3aa2..7776804 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -41,14 +41,16 @@ SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callb
 
 SavesSyncRequest::~SavesSyncRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _boolCallback;
 }
 
 void SavesSyncRequest::start() {
 	//cleanup
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_currentDownloadingFile = StorageFile();
 	_currentUploadingFile = "";
 	_filesToDownload.clear();
@@ -62,7 +64,8 @@ void SavesSyncRequest::start() {
 
 	//list saves directory
 	Common::String dir = _storage->savesDirectoryPath();
-	if (dir.lastChar() == '/') dir.deleteLastChar();
+	if (dir.lastChar() == '/')
+		dir.deleteLastChar();
 	_workingRequest = _storage->listDirectory(
 		dir,
 		new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
@@ -73,7 +76,8 @@ void SavesSyncRequest::start() {
 
 void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	if (response.request) _date = response.request->date();
 
@@ -87,14 +91,16 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 	uint64 totalSize = 0;
 	for (uint32 i = 0; i < remoteFiles.size(); ++i) {
 		StorageFile &file = remoteFiles[i];
-		if (file.isDirectory()) continue;
+		if (file.isDirectory())
+			continue;
 		totalSize += file.size();
-		if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME) continue;
+		if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
+			continue;
 
 		Common::String name = file.name();
-		if (!_localFilesTimestamps.contains(name))
+		if (!_localFilesTimestamps.contains(name)) {
 			_filesToDownload.push_back(file);
-		else {
+		} else {
 			localFileNotAvailableInCloud[name] = false;
 
 			if (_localFilesTimestamps[name] == file.timestamp())
@@ -113,8 +119,10 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 
 	//upload files which are unavailable in cloud
 	for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
-		if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME) continue;
-		if (i->_value) _filesToUpload.push_back(i->_key);
+		if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
+			continue;
+		if (i->_value)
+			_filesToUpload.push_back(i->_key);
 	}
 
 	debug(9, "\ndownload files:");
@@ -133,7 +141,8 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 
 void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	bool irrecoverable = error.interrupted || error.failed;
 	if (error.failed) {
@@ -179,19 +188,22 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 
 	//we're lucky - user just lacks his "/cloud/" folder - let's create one
 	Common::String dir = _storage->savesDirectoryPath();
-	if (dir.lastChar() == '/') dir.deleteLastChar();
+	if (dir.lastChar() == '/')
+		dir.deleteLastChar();
 	debug(9, "creating %s", dir.c_str());
 	_workingRequest = _storage->createDirectory(
 		dir,
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
 	);
-	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
+	if (!_workingRequest)
+		finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//stop syncing if failed to create saves directory
 	if (!response.value) {
@@ -206,7 +218,8 @@ void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response)
 
 void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//stop syncing if failed to create saves directory
 	finishError(error);
@@ -232,12 +245,14 @@ void SavesSyncRequest::downloadNextFile() {
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
 		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
 	);
-	if (!_workingRequest) finishError(Networking::ErrorResponse(this));
+	if (!_workingRequest)
+		finishError(Networking::ErrorResponse(this));
 }
 
 void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//stop syncing if download failed
 	if (!response.value) {
@@ -258,7 +273,8 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
 
 void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//stop syncing if download failed
 	finishError(error);
@@ -294,7 +310,8 @@ void SavesSyncRequest::uploadNextFile() {
 
 void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//update local timestamp for the uploaded file
 	_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
@@ -307,7 +324,8 @@ void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
 
 void SavesSyncRequest::fileUploadedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	//stop syncing if upload failed
 	finishError(error);
@@ -319,11 +337,13 @@ void SavesSyncRequest::restart() { start(); }
 
 double SavesSyncRequest::getDownloadingProgress() const {
 	if (_totalFilesToHandle == 0) {
-		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
+		if (_state == Networking::FINISHED)
+			return 1; //nothing to upload and download => Request ends soon
 		return 0; //directory not listed yet
 	}
 
-	if (_totalFilesToHandle == _filesToUpload.size()) return 1; //nothing to download => download complete
+	if (_totalFilesToHandle == _filesToUpload.size())
+		return 1; //nothing to download => download complete
 
 	uint32 totalFilesToDownload = _totalFilesToHandle - _filesToUpload.size();
 	uint32 filesLeftToDownload = _filesToDownload.size() + (_currentDownloadingFile.name() != "" ? 1 : 0);
@@ -332,7 +352,8 @@ double SavesSyncRequest::getDownloadingProgress() const {
 
 double SavesSyncRequest::getProgress() const {
 	if (_totalFilesToHandle == 0) {
-		if (_state == Networking::FINISHED) return 1; //nothing to upload and download => Request ends soon
+		if (_state == Networking::FINISHED)
+			return 1; //nothing to upload and download => Request ends soon
 		return 0; //directory not listed yet
 	}
 
@@ -363,7 +384,8 @@ void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
 	_currentDownloadingFile = StorageFile();
 	_filesToDownload.clear();
 	//delete the incomplete file
-	if (name != "") g_system->getSavefileManager()->removeSavefile(name);
+	if (name != "")
+		g_system->getSavefileManager()->removeSavefile(name);
 	Request::finishError(error);
 }
 
@@ -373,7 +395,8 @@ void SavesSyncRequest::finishSync(bool success) {
 	//update last successful sync date
 	CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
 
-	if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
+	if (_boolCallback)
+		(*_boolCallback)(Storage::BoolResponse(this, success));
 }
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index dc403df..b085401 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -49,7 +49,8 @@ void Storage::printErrorResponse(Networking::ErrorResponse error) {
 Networking::Request *Storage::addRequest(Networking::Request *request) {
 	_runningRequestsMutex.lock();
 	++_runningRequestsCount;
-	if (_runningRequestsCount == 1) debug(9, "Storage is working now");
+	if (_runningRequestsCount == 1)
+		debug(9, "Storage is working now");
 	_runningRequestsMutex.unlock();
 	return ConnMan.addRequest(request, new Common::Callback<Storage, Networking::Request *>(this, &Storage::requestFinishedCallback));
 }
@@ -61,8 +62,10 @@ void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer
 	if (invalidRequestPointer == _savesSyncRequest)
 		_savesSyncRequest = nullptr;
 	--_runningRequestsCount;
-	if (_syncRestartRequestsed) restartSync = true;
-	if (_runningRequestsCount == 0 && !restartSync) debug(9, "Storage is not working now");
+	if (_syncRestartRequestsed)
+		restartSync = true;
+	if (_runningRequestsCount == 0 && !restartSync)
+		debug(9, "Storage is not working now");
 	_runningRequestsMutex.unlock();
 
 	if (restartSync)
@@ -75,7 +78,8 @@ Networking::Request *Storage::upload(Common::String remotePath, Common::String l
 	Common::File *f = new Common::File();
 	if (!f->open(localPath)) {
 		warning("Storage: unable to open file to upload from");
-		if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
+		if (errorCallback)
+			(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
 		delete errorCallback;
 		delete callback;
 		delete f;
@@ -116,7 +120,8 @@ Networking::Request *Storage::downloadById(Common::String remoteId, Common::Stri
 }
 
 Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
-	if (!errorCallback) errorCallback = getErrorPrintingCallback();
+	if (!errorCallback)
+		errorCallback = getErrorPrintingCallback();
 	return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
 }
 
@@ -128,8 +133,10 @@ SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCal
 		_runningRequestsMutex.unlock();
 		return _savesSyncRequest;
 	}
-	if (!callback) callback = new Common::Callback<Storage, BoolResponse>(this, &Storage::savesSyncDefaultCallback);
-	if (!errorCallback) errorCallback = new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::savesSyncDefaultErrorCallback);
+	if (!callback)
+		callback = new Common::Callback<Storage, BoolResponse>(this, &Storage::savesSyncDefaultCallback);
+	if (!errorCallback)
+		errorCallback = new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::savesSyncDefaultErrorCallback);
 	_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
 	_syncRestartRequestsed = false;
 	_runningRequestsMutex.unlock();
@@ -198,7 +205,8 @@ void Storage::savesSyncDefaultCallback(BoolResponse response) {
 	_savesSyncRequest = nullptr;
 	_runningRequestsMutex.unlock();
 
-	if (!response.value) warning("SavesSyncRequest called success callback with `false` argument");
+	if (!response.value)
+		warning("SavesSyncRequest called success callback with `false` argument");
 	g_system->displayMessageOnOSD(_("Saves sync complete."));
 }
 
@@ -332,4 +340,3 @@ void Storage::directoryDownloadedErrorCallback(Networking::ErrorResponse error)
 }
 
 } // End of namespace Cloud
-
diff --git a/backends/cloud/storagefile.cpp b/backends/cloud/storagefile.cpp
index c1107ea..62d4922 100644
--- a/backends/cloud/storagefile.cpp
+++ b/backends/cloud/storagefile.cpp
@@ -45,7 +45,8 @@ StorageFile::StorageFile(Common::String pth, uint32 sz, uint32 ts, bool dir) {
 				_name.erase(0, i + 1);
 				break;
 			}
-			if (i == 0) break;
+			if (i == 0)
+				break;
 			--i;
 		}
 	}
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
index 80e2616..1c1ecf2 100644
--- a/backends/networking/curl/cloudicon.cpp
+++ b/backends/networking/curl/cloudicon.cpp
@@ -83,14 +83,16 @@ bool CloudIcon::draw() {
 	if (g_system) {
 		Graphics::TransparentSurface *surface = &_icon;
 		makeAlphaIcon((_showingDisabled ? _disabledIcon : _icon), _currentAlpha);
-		if (_alphaIcon.getPixels()) surface = &_alphaIcon;
+		if (_alphaIcon.getPixels())
+			surface = &_alphaIcon;
 		if (surface && surface->getPixels()) {
 			int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
 			g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
 		}
 	}
 
-	if (stop) _showingDisabled = false;
+	if (stop)
+		_showingDisabled = false;
 	return stop;
 }
 
@@ -103,7 +105,8 @@ void CloudIcon::showDisabled() {
 #include "backends/networking/curl/cloudicon_disabled_data.h"
 
 void CloudIcon::initIcons() {
-	if (_iconsInited) return;
+	if (_iconsInited)
+		return;
 	loadIcon(_icon, cloudicon_data, ARRAYSIZE(cloudicon_data));
 	loadIcon(_disabledIcon, cloudicon_disabled_data, ARRAYSIZE(cloudicon_disabled_data));
 	_iconsInited = true;
@@ -120,14 +123,18 @@ void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32
 		Graphics::PixelFormat f = g_system->getOSDFormat();
 		if (f != s->format) {
 			Graphics::TransparentSurface *s2 = s->convertTo(f);
-			if (s2) icon.copyFrom(*s2);
-			else warning("CloudIcon::loadIcon: failed converting TransparentSurface");
+			if (s2)
+				icon.copyFrom(*s2);
+			else
+				warning("CloudIcon::loadIcon: failed converting TransparentSurface");
 			delete s2;
 		} else {
 			icon.copyFrom(*s);
 		}
 		delete s;
-	} else warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
+	} else {
+		warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
+	}
 }
 
 void CloudIcon::makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha) {
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index cb0e246..1d5fd0d 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -50,9 +50,11 @@ ConnectionManager::~ConnectionManager() {
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
 		Request *request = i->request;
 		RequestCallback callback = i->onDeleteCallback;
-		if (request) request->finish();
+		if (request)
+			request->finish();
 		delete request;
-		if (callback) (*callback)(request);
+		if (callback)
+			(*callback)(request);
 	}
 	_requests.clear();
 
@@ -70,7 +72,8 @@ void ConnectionManager::registerEasyHandle(CURL *easy) const {
 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
 	_addedRequestsMutex.lock();
 	_addedRequests.push_back(RequestWithCallback(request, callback));
-	if (!_timerStarted) startTimer();
+	if (!_timerStarted)
+		startTimer();
 	_addedRequestsMutex.unlock();
 	return request;
 }
@@ -81,7 +84,8 @@ void ConnectionManager::showCloudDisabledIcon() {
 }
 
 Common::String ConnectionManager::urlEncode(Common::String s) const {
-	if (!_multi) return "";
+	if (!_multi)
+		return "";
 	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
 	if (output) {
 		Common::String result = output;
@@ -128,8 +132,10 @@ void ConnectionManager::handle() {
 	//lock mutex here (in case another handle() would be called before this one ends)
 	_handleMutex.lock();
 	++_frame;
-	if (_frame % CLOUD_PERIOD == 0) interateRequests();
-	if (_frame % CURL_PERIOD == 0) processTransfers();
+	if (_frame % CLOUD_PERIOD == 0)
+		interateRequests();
+	if (_frame % CURL_PERIOD == 0)
+		processTransfers();
 
 	if (_icon.draw() && _requests.empty() && !hasAddedRequests())
 		stopTimer();
@@ -150,13 +156,16 @@ void ConnectionManager::interateRequests() {
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
 		Request *request = i->request;
 		if (request) {
-			if (request->state() == PROCESSING) request->handle();
-			else if (request->state() == RETRY) request->handleRetry();
+			if (request->state() == PROCESSING)
+				request->handle();
+			else if (request->state() == RETRY)
+				request->handleRetry();
 		}
 
 		if (!request || request->state() == FINISHED) {
 			delete (i->request);
-			if (i->onDeleteCallback) (*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
+			if (i->onDeleteCallback)
+				(*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
 			_requests.erase(i);
 			continue;
 		}
@@ -179,7 +188,8 @@ void ConnectionManager::processTransfers() {
 
 		NetworkReadStream *stream;
 		curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
-		if (stream) stream->finished();
+		if (stream)
+			stream->finished();
 
 		if (curlMsg->msg == CURLMSG_DONE) {
 			debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 7f7cbca..4d87aa2 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -49,7 +49,8 @@ char *CurlJsonRequest::getPreparedContents() {
 	byte *result = _contentsStream.getData();
 	uint32 size = _contentsStream.size();
 	for (uint32 i = 0; i < size; ++i) {
-		if (result[i] == '\n') result[i] = ' '; //yeah, kinda stupid
+		if (result[i] == '\n')
+				result[i] = ' '; //yeah, kinda stupid
 		else if (result[i] < 0x20 || result[i] > 0x7f)
 			result[i] = '.';
 	}
@@ -85,7 +86,8 @@ void CurlJsonRequest::handle() {
 }
 
 void CurlJsonRequest::restart() {
-	if (_stream) delete _stream;
+	if (_stream)
+		delete _stream;
 	_stream = nullptr;
 	_contentsStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
 	//with no stream available next handle() will create another one
@@ -93,8 +95,10 @@ void CurlJsonRequest::restart() {
 
 void CurlJsonRequest::finishJson(Common::JSONValue *json) {
 	Request::finishSuccess();
-	if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
-	else delete json;
+	if (_jsonCallback)
+		(*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
+	else
+		delete json;
 }
 
 bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningPrefix) {
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 4420b6f..2d5bb1f 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -63,7 +63,8 @@ void CurlRequest::handle() {
 }
 
 void CurlRequest::restart() {
-	if (_stream) delete _stream;
+	if (_stream)
+		delete _stream;
 	_stream = nullptr;
 	//with no stream available next handle() will create another one
 }
@@ -78,7 +79,8 @@ Common::String CurlRequest::date() const {
 			Common::String result = "";
 			char c;
 			for (const char *i = position + 6; c = *i, c != 0; ++i) {
-				if (c == '\n' || c == '\r') break;
+				if (c == '\n' || c == '\r')
+					break;
 				result += c;
 			}
 			return result;
@@ -135,7 +137,8 @@ void CurlRequest::setBuffer(byte *buffer, uint32 size) {
 	if (_postFields != "")
 		warning("CurlRequest: added POST fields would be ignored, because buffer added");
 
-	if (_bytesBuffer) delete _bytesBuffer;
+	if (_bytesBuffer)
+		delete _bytesBuffer;
 
 	_bytesBuffer = buffer;
 	_bytesBufferSize = size;
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 1764a93..5c760d9 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -31,25 +31,29 @@ namespace Networking {
 
 static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->write(d, n * l);
+	if (stream)
+		return stream->write(d, n * l);
 	return 0;
 }
 
 static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->fillWithSendingContents(d, n * l);
+	if (stream)
+		return stream->fillWithSendingContents(d, n * l);
 	return 0;
 }
 
 static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) return stream->addResponseHeaders(d, n * l);
+	if (stream)
+		return stream->addResponseHeaders(d, n * l);
 	return 0;
 }
 
 static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
 	NetworkReadStream *stream = (NetworkReadStream *)p;
-	if (stream) stream->setProgress(dlnow, dltotal);
+	if (stream)
+		stream->setProgress(dlnow, dltotal);
 	return 0;
 }
 
@@ -171,7 +175,8 @@ uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
 	uint32 actuallyRead = MemoryReadWriteStream::read(dataPtr, dataSize);
 
 	if (actuallyRead == 0) {
-		if (_requestComplete) _eos = true;
+		if (_requestComplete)
+			_eos = true;
 		return 0;
 	}
 
@@ -205,7 +210,8 @@ Common::String NetworkReadStream::responseHeaders() const {
 
 uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
 	uint32 size = _sendingContentsSize - _sendingContentsPos;
-	if (size > maxSize) size = maxSize;
+	if (size > maxSize)
+		size = maxSize;
 	for (uint32 i = 0; i < size; ++i) {
 		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
 	}
@@ -219,7 +225,8 @@ uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) {
 }
 
 double NetworkReadStream::getProgress() const {
-	if (_progressTotal < 1) return 0;
+	if (_progressTotal < 1)
+		return 0;
 	return (double)_progressDownloaded / (double)_progressTotal;
 }
 
diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp
index 4a2704f..30af48a 100644
--- a/backends/networking/curl/request.cpp
+++ b/backends/networking/curl/request.cpp
@@ -39,8 +39,9 @@ Request::~Request() {
 }
 
 void Request::handleRetry() {
-	if (_retryInSeconds > 0) --_retryInSeconds;
-	else {
+	if (_retryInSeconds > 0) {
+		--_retryInSeconds;
+	} else {
 		_state = PROCESSING;
 		restart();
 	}
@@ -64,7 +65,8 @@ Common::String Request::date() const { return ""; }
 
 void Request::finishError(ErrorResponse error) {
 	_state = FINISHED;
-	if (_errorCallback) (*_errorCallback)(error);
+	if (_errorCallback)
+		(*_errorCallback)(error);
 }
 
 void Request::finishSuccess() { _state = FINISHED; }
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 6cef5ff..0026d0e 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -45,15 +45,18 @@ Client::~Client() {
 }
 
 void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
-	if (_state != INVALID) close();
+	if (_state != INVALID)
+		close();
 	_state = READING_HEADERS;
 	_socket = socket;
 	_set = set;
 	Reader cleanReader;
 	_reader = cleanReader;
-	if (_handler) delete _handler;
+	if (_handler)
+		delete _handler;
 	_handler = nullptr;
-	if (_previousHandler) delete _previousHandler;
+	if (_previousHandler)
+		delete _previousHandler;
 	_previousHandler = nullptr;
 	_stream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
 	if (set) {
@@ -65,10 +68,14 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 }
 
 bool Client::readMoreIfNeeded() {
-	if (_stream == nullptr) return false; //nothing to read into
-	if (_stream->size() - _stream->pos() > 0) return true; //not needed, some data left in the stream
-	if (!_socket) return false;
-	if (!SDLNet_SocketReady(_socket)) return false;
+	if (_stream == nullptr)
+		return false; //nothing to read into
+	if (_stream->size() - _stream->pos() > 0)
+		return true; //not needed, some data left in the stream
+	if (!_socket)
+		return false;
+	if (!SDLNet_SocketReady(_socket))
+		return false;
 
 	int bytes = SDLNet_TCP_Recv(_socket, _buffer, CLIENT_BUFFER_SIZE);
 	if (bytes <= 0) {
@@ -87,33 +94,38 @@ bool Client::readMoreIfNeeded() {
 }
 
 void Client::readHeaders() {
-	if (!readMoreIfNeeded()) return;
+	if (!readMoreIfNeeded())
+		return;
 	_reader.setContent(_stream);
 	if (_reader.readFirstHeaders())
 		_state = (_reader.badRequest() ? BAD_REQUEST : READ_HEADERS);
 }
 
 bool Client::readContent(Common::WriteStream *stream) {
-	if (!readMoreIfNeeded()) return false;
+	if (!readMoreIfNeeded())
+		return false;
 	_reader.setContent(_stream);
 	return _reader.readFirstContent(stream);
 }
 
 bool Client::readBlockHeaders(Common::WriteStream *stream) {
-	if (!readMoreIfNeeded()) return false;
+	if (!readMoreIfNeeded())
+		return false;
 	_reader.setContent(_stream);
 	return _reader.readBlockHeaders(stream);
 }
 
 bool Client::readBlockContent(Common::WriteStream *stream) {
-	if (!readMoreIfNeeded()) return false;
+	if (!readMoreIfNeeded())
+		return false;
 	_reader.setContent(_stream);
 	return _reader.readBlockContent(stream);
 }
 
 void Client::setHandler(ClientHandler *handler) {
 	if (_handler) {
-		if (_previousHandler) delete _previousHandler;
+		if (_previousHandler)
+			delete _previousHandler;
 		_previousHandler = _handler; //can't just delete it, as setHandler() could've been called by handler itself
 	}
 	_state = BEING_HANDLED;
@@ -121,9 +133,12 @@ void Client::setHandler(ClientHandler *handler) {
 }
 
 void Client::handle() {
-	if (_state != BEING_HANDLED) warning("handle() called in a wrong Client's state");
-	if (!_handler) warning("Client doesn't have handler to be handled by");
-	if (_handler) _handler->handle(this);
+	if (_state != BEING_HANDLED)
+		warning("handle() called in a wrong Client's state");
+	if (!_handler)
+		warning("Client doesn't have handler to be handled by");
+	if (_handler)
+		_handler->handle(this);
 }
 
 void Client::close() {
diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index cdbcd96..1c4f5db 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -121,15 +121,18 @@ void GetClientHandler::prepareHeaders() {
 }
 
 void GetClientHandler::handle(Client *client) {
-	if (!client) return;
-	if (!_headersPrepared) prepareHeaders();
+	if (!client)
+		return;
+	if (!_headersPrepared)
+		prepareHeaders();
 
 	uint32 readBytes;
 
 	// send headers first
 	if (_headers.size() > 0) {
 		readBytes = _headers.size();
-		if (readBytes > CLIENT_HANDLER_BUFFER_SIZE) readBytes = CLIENT_HANDLER_BUFFER_SIZE;
+		if (readBytes > CLIENT_HANDLER_BUFFER_SIZE)
+			readBytes = CLIENT_HANDLER_BUFFER_SIZE;
 		memcpy(_buffer, _headers.c_str(), readBytes);
 		_headers.erase(0, readBytes);
 	} else {
@@ -149,7 +152,8 @@ void GetClientHandler::handle(Client *client) {
 		}
 
 	// we're done here!
-	if (_stream->eos()) client->close();
+	if (_stream->eos())
+		client->close();
 }
 
 void GetClientHandler::setHeader(Common::String name, Common::String value) { _specialHeaders[name] = value; }
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index b16827d..474ac74 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -61,7 +61,8 @@ void CreateDirectoryHandler::handle(Client &client) {
 	}
 
 	// check that <directory_name> doesn't exist or is directory
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	if (path.lastChar() != '/' && path.lastChar() != '\\')
+		path += '/';
 	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
 	if (node->exists()) {
 		if (!node->isDirectory()) {
@@ -97,8 +98,10 @@ void CreateDirectoryHandler::handle(Client &client) {
 }
 
 void CreateDirectoryHandler::handleError(Client &client, Common::String message) const {
-	if (client.queryParameter("answer_json") == "true") setJsonResponseHandler(client, "error", message);
-	else HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
+	if (client.queryParameter("answer_json") == "true")
+		setJsonResponseHandler(client, "error", message);
+	else
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
 }
 
 void CreateDirectoryHandler::setJsonResponseHandler(Client &client, Common::String type, Common::String message) const {
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index 6cf18ae..e0ecae9 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -42,7 +42,9 @@ Common::String encodeDoubleQuotesAndSlashes(Common::String s) {
 			result += "\\\"";
 		} else if (s[i] == '\\') {
 			result += "\\\\";
-		} else result += s[i];
+		} else {
+			result += s[i];
+		}
 	return result;
 }
 
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index 9546c5c..68d7919 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -40,20 +40,24 @@ Common::String FilesBaseHandler::parentPath(Common::String path) {
 				break;
 			}
 	}
-	if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\')
+		path += '/';
 	return path;
 }
 
 bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefixToRemove, Common::String &prefixToAdd, bool isDirectory) {
 	// <path> is not empty, but could lack the trailing slash
-	if (isDirectory && path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	if (isDirectory && path.lastChar() != '/' && path.lastChar() != '\\')
+		path += '/';
 
 	if (path.hasPrefix("/root")) {
 		prefixToAdd = "/root/";
 		prefixToRemove = "";
 		path.erase(0, 5);
-		if (path == "") path = "/"; // absolute root is '/'
-		if (path != "/") path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
+		if (path == "")
+			path = "/"; // absolute root is '/'
+		if (path != "/")
+			path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
 		return true;
 	}
 
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index d3fa459..abe6f42 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -40,17 +40,23 @@ Common::String encodeDoubleQuotes(Common::String s) {
 	for (uint32 i = 0; i < s.size(); ++i)
 		if (s[i] == '"') {
 			result += "\\\"";
-		} else result += s[i];
+		} else {
+			result += s[i];
+		}
 	return result;
 }
 
 Common::String encodeHtmlEntities(Common::String s) {
 	Common::String result = "";
 	for (uint32 i = 0; i < s.size(); ++i)
-		if (s[i] == '<') result += "<";
-		else if (s[i] == '>') result += ">";
-		else if (s[i] == '&') result += "&";
-		else if (s[i] > 0x7F) result += Common::String::format("&#%d;", (int)s[i]);
+		if (s[i] == '<')
+			result += "<";
+		else if (s[i] == '>')
+			result += ">";
+		else if (s[i] == '&')
+			result += "&";
+		else if (s[i] > 0x7F)
+			result += Common::String::format("&#%d;", (int)s[i]);
 		else result += s[i];
 	return result;
 }
@@ -58,9 +64,12 @@ Common::String encodeHtmlEntities(Common::String s) {
 Common::String getDisplayPath(Common::String s) {
 	Common::String result = "";
 	for (uint32 i = 0; i < s.size(); ++i)
-		if (s[i] == '\\') result += '/';
-		else result += s[i];
-	if (result == "") return "/";
+		if (s[i] == '\\')
+			result += '/';
+		else
+			result += s[i];
+	if (result == "")
+		return "/";
 	return result;
 }
 }
@@ -93,7 +102,8 @@ void FilesPageHandler::handle(Client &client) {
 
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
-	if (stream) response = HandlerUtils::readEverythingFromStream(stream);
+	if (stream)
+		response = HandlerUtils::readEverythingFromStream(stream);
 
 	Common::String path = client.queryParameter("path");
 	Common::String content = "";
@@ -127,11 +137,14 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 	}
 
 	Common::String prefixToRemove = "", prefixToAdd = "";
-	if (!transformPath(path, prefixToRemove, prefixToAdd)) return false;
+	if (!transformPath(path, prefixToRemove, prefixToAdd))
+		return false;
 
 	Common::FSNode node = Common::FSNode(path);
-	if (path == "/") node = node.getParent(); // absolute root
-	if (!node.isDirectory()) return false;
+	if (path == "/")
+		node = node.getParent(); // absolute root
+	if (!node.isDirectory())
+		return false;
 
 	// list directory
 	Common::FSList _nodeContent;
@@ -155,7 +168,8 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 	// fill the content
 	for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) {
 		Common::String name = i->getDisplayName();
-		if (i->isDirectory()) name += "/";
+		if (i->isDirectory())
+			name += "/";
 
 		Common::String filePath = i->getPath();
 		if (filePath.hasPrefix(prefixToRemove))
@@ -169,10 +183,14 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 }
 
 FilesPageHandler::ItemType FilesPageHandler::detectType(bool isDirectory, const Common::String &name) {
-	if (isDirectory) return IT_DIRECTORY;
-	if (name.hasSuffix(".txt")) return IT_TXT;
-	if (name.hasSuffix(".zip")) return IT_ZIP;
-	if (name.hasSuffix(".7z")) return IT_7Z;
+	if (isDirectory)
+		return IT_DIRECTORY;
+	if (name.hasSuffix(".txt"))
+		return IT_TXT;
+	if (name.hasSuffix(".zip"))
+		return IT_ZIP;
+	if (name.hasSuffix(".7z"))
+		return IT_7Z;
 	return IT_UNKNOWN;
 }
 
@@ -180,12 +198,23 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
 	Common::String item = itemTemplate, icon;
 	bool isDirectory = (itemType == IT_DIRECTORY || itemType == IT_PARENT_DIRECTORY);
 	switch (itemType) {
-	case IT_DIRECTORY: icon = "dir.png"; break;
-	case IT_PARENT_DIRECTORY: icon = "up.png"; break;
-	case IT_TXT: icon = "txt.png"; break;
-	case IT_ZIP: icon = "zip.png"; break;
-	case IT_7Z: icon = "7z.png"; break;
-	default: icon = "unk.png";
+	case IT_DIRECTORY:
+		icon = "dir.png";
+		break;
+	case IT_PARENT_DIRECTORY:
+		icon = "up.png";
+		break;
+	case IT_TXT:
+		icon = "txt.png";
+		break;
+	case IT_ZIP:
+		icon = "zip.png";
+		break;
+	case IT_7Z:
+		icon = "7z.png";
+		break;
+	default:
+		icon = "unk.png";
 	}
 	replace(item, "{icon}", icon);
 	replace(item, "{link}", (isDirectory ? "files?path=" : "download?path=") + LocalWebserver::urlEncodeQueryParameterValue(path));
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index 2e2e138..de349bd 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -54,11 +54,14 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 	}
 
 	Common::String prefixToRemove = "", prefixToAdd = "";
-	if (!transformPath(path, prefixToRemove, prefixToAdd)) return errorResult;
+	if (!transformPath(path, prefixToRemove, prefixToAdd))
+		return errorResult;
 
 	Common::FSNode node = Common::FSNode(path);
-	if (path == "/") node = node.getParent(); // absolute root
-	if (!node.isDirectory()) return errorResult;
+	if (path == "/")
+		node = node.getParent(); // absolute root
+	if (!node.isDirectory())
+		return errorResult;
 
 	// list directory
 	Common::FSList _nodeContent;
@@ -97,10 +100,14 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 }
 
 ListAjaxHandler::ItemType ListAjaxHandler::detectType(bool isDirectory, const Common::String &name) {
-	if (isDirectory) return IT_DIRECTORY;
-	if (name.hasSuffix(".txt")) return IT_TXT;
-	if (name.hasSuffix(".zip")) return IT_ZIP;
-	if (name.hasSuffix(".7z")) return IT_7Z;
+	if (isDirectory)
+		return IT_DIRECTORY;
+	if (name.hasSuffix(".txt"))
+		return IT_TXT;
+	if (name.hasSuffix(".zip"))
+		return IT_ZIP;
+	if (name.hasSuffix(".7z"))
+		return IT_7Z;
 	return IT_UNKNOWN;
 }
 
@@ -108,12 +115,23 @@ void ListAjaxHandler::addItem(Common::JSONArray &responseItemsList, ItemType ite
 	Common::String icon;
 	bool isDirectory = (itemType == IT_DIRECTORY || itemType == IT_PARENT_DIRECTORY);
 	switch (itemType) {
-	case IT_DIRECTORY: icon = "dir.png"; break;
-	case IT_PARENT_DIRECTORY: icon = "up.png"; break;
-	case IT_TXT: icon = "txt.png"; break;
-	case IT_ZIP: icon = "zip.png"; break;
-	case IT_7Z: icon = "7z.png"; break;
-	default: icon = "unk.png";
+	case IT_DIRECTORY:
+		icon = "dir.png";
+		break;
+	case IT_PARENT_DIRECTORY:
+		icon = "up.png";
+		break;
+	case IT_TXT:
+		icon = "txt.png";
+		break;
+	case IT_ZIP:
+		icon = "zip.png";
+		break;
+	case IT_7Z:
+		icon = "7z.png";
+		break;
+	default:
+		icon = "unk.png";
 	}
 
 	Common::JSONObject item;
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
index 1205c4a..d6c419d 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.cpp
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -35,11 +35,13 @@ void ResourceHandler::handle(Client &client) {
 	filename.deleteChar(0);
 
 	// if archive hidden file is requested, ignore
-	if (filename.size() && filename[0] == '.') return;
+	if (filename.size() && filename[0] == '.')
+		return;
 
 	// if file not found, don't set handler either
 	Common::SeekableReadStream *file = HandlerUtils::getArchiveFile(filename);
-	if (file == nullptr) return;
+	if (file == nullptr)
+		return;
 
 	LocalWebserver::setClientGetHandler(client, file, 200, LocalWebserver::determineMimeType(filename));
 }
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index bb6a036..edb1484 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -45,7 +45,8 @@ Common::Archive *HandlerUtils::getZipArchive() {
 		if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) {
 			Common::SeekableReadStream *const stream = fileNode.createReadStream();
 			Common::Archive *zipArchive = Common::makeZipArchive(stream);
-			if (zipArchive) return zipArchive;
+			if (zipArchive)
+				return zipArchive;
 		}
 	}
 
@@ -56,7 +57,8 @@ Common::Archive *HandlerUtils::getZipArchive() {
 		Common::ArchiveMember       const &m = **it;
 		Common::SeekableReadStream *const stream = m.createReadStream();
 		Common::Archive *zipArchive = Common::makeZipArchive(stream);
-		if (zipArchive) return zipArchive;
+		if (zipArchive)
+			return zipArchive;
 	}
 
 	return nullptr;
@@ -77,7 +79,8 @@ Common::SeekableReadStream *HandlerUtils::getArchiveFile(Common::String name) {
 	Common::Archive *zipArchive = getZipArchive();
 	if (zipArchive) {
 		const Common::ArchiveMemberPtr ptr = zipArchive->getMember(name);
-		if (ptr.get() == nullptr) return nullptr;
+		if (ptr.get() == nullptr)
+			return nullptr;
 		result = ptr->createReadStream();
 		delete zipArchive;
 	}
@@ -100,11 +103,14 @@ void HandlerUtils::setMessageHandler(Client &client, Common::String message, Com
 
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
-	if (stream) response = readEverythingFromStream(stream);
+	if (stream)
+		response = readEverythingFromStream(stream);
 
 	replace(response, "{message}", message);
-	if (redirectTo.empty()) LocalWebserver::setClientGetHandler(client, response);
-	else LocalWebserver::setClientRedirectHandler(client, response, redirectTo);
+	if (redirectTo.empty())
+		LocalWebserver::setClientGetHandler(client, response);
+	else
+		LocalWebserver::setClientRedirectHandler(client, response, redirectTo);
 }
 
 void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 9b62bb8..75dbea0 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -125,7 +125,8 @@ void LocalWebserver::start() {
 
 void LocalWebserver::stop() {
 	_handleMutex.lock();
-	if (_timerStarted) stopTimer();
+	if (_timerStarted)
+		stopTimer();
 
 	if (_serverSocket) {
 		SDLNet_TCP_Close(_serverSocket);
@@ -147,12 +148,14 @@ void LocalWebserver::stop() {
 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
 
 void LocalWebserver::addPathHandler(Common::String path, ClientHandlerCallback handler) {
-	if (_pathHandlers.contains(path)) warning("LocalWebserver::addPathHandler: path already had a handler");
+	if (_pathHandlers.contains(path))
+		warning("LocalWebserver::addPathHandler: path already had a handler");
 	_pathHandlers[path] = handler;
 }
 
 void LocalWebserver::removePathHandler(Common::String path) {
-	if (!_pathHandlers.contains(path)) warning("LocalWebserver::removePathHandler: no handler known for this path");
+	if (!_pathHandlers.contains(path))
+		warning("LocalWebserver::removePathHandler: no handler known for this path");
 	_pathHandlers.erase(path);
 }
 
@@ -181,7 +184,9 @@ void LocalWebserver::handle() {
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
 		error("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
-	} else if (numready) acceptClient();
+	} else if (numready) {
+		acceptClient();
+	}
 
 	for (uint32 i = 0; i < MAX_CONNECTIONS; ++i)
 		handleClient(i);
@@ -191,8 +196,10 @@ void LocalWebserver::handle() {
 		if (_client[i].state() != INVALID)
 			++_clients;
 
-	if (_clients == 0) ++_idlingFrames;
-	else _idlingFrames = 0;
+	if (_clients == 0)
+		++_idlingFrames;
+	else
+		_idlingFrames = 0;
 
 	if (_idlingFrames > FRAMES_PER_SECOND && _stopOnIdle) {
 		_handleMutex.unlock();
@@ -218,7 +225,8 @@ void LocalWebserver::handleClient(uint32 i) {
 		else if (_defaultHandler)
 			(*_defaultHandler)(_client[i]); //try default handler
 
-		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break;
+		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID)
+			break;
 		//if no handler, answer with default BAD REQUEST
 		//fallthrough
 	case BAD_REQUEST:
@@ -231,10 +239,12 @@ void LocalWebserver::handleClient(uint32 i) {
 }
 
 void LocalWebserver::acceptClient() {
-	if (!SDLNet_SocketReady(_serverSocket)) return;
+	if (!SDLNet_SocketReady(_serverSocket))
+		return;
 
 	TCPsocket client = SDLNet_TCP_Accept(_serverSocket);
-	if (!client) return;
+	if (!client)
+		return;
 
 	if (_clients == MAX_CONNECTIONS) { //drop the connection
 		SDLNet_TCP_Close(client);
@@ -304,15 +314,16 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 		// IPv6
 		/*
 		if (i->ifa_addr->sa_family == AF_INET6) {
-		    tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
-		    char addressBuffer[INET6_ADDRSTRLEN];
-		    inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
-		    debug("%s IP Address %s", i->ifa_name, addressBuffer);
-		    addr = addressBuffer;
+			tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
+			char addressBuffer[INET6_ADDRSTRLEN];
+			inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
+			debug("%s IP Address %s", i->ifa_name, addressBuffer);
+			addr = addressBuffer;
 		}
 		*/
 
-		if (addr.empty()) continue;
+		if (addr.empty())
+			continue;
 
 		// ignored IPv4 addresses
 		if (addr.equals("127.0.0.1") || addr.equals("0.0.0.0") || addr.equals("localhost"))
@@ -321,7 +332,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 		// ignored IPv6 addresses
 		/*
 		if (addr.equals("::1"))
-		    continue;
+			continue;
 		*/
 
 		// use the address found
@@ -342,7 +353,8 @@ void LocalWebserver::setClientGetHandler(Client &client, Common::String response
 void LocalWebserver::setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code, const char *mimeType) {
 	GetClientHandler *handler = new GetClientHandler(responseStream);
 	handler->setResponseCode(code);
-	if (mimeType) handler->setHeader("Content-Type", mimeType);
+	if (mimeType)
+		handler->setHeader("Content-Type", mimeType);
 	client.setHandler(handler);
 }
 
@@ -357,7 +369,8 @@ void LocalWebserver::setClientRedirectHandler(Client &client, Common::SeekableRe
 	GetClientHandler *handler = new GetClientHandler(responseStream);
 	handler->setResponseCode(302); //redirect
 	handler->setHeader("Location", location);
-	if (mimeType) handler->setHeader("Content-Type", mimeType);
+	if (mimeType)
+		handler->setHeader("Content-Type", mimeType);
 	client.setHandler(handler);
 }
 
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 66efeec..828f583 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -51,7 +51,8 @@ Reader::~Reader() {
 }
 
 Reader &Reader::operator=(Reader &r) {
-	if (this == &r) return *this;
+	if (this == &r)
+		return *this;
 	cleanup();
 
 	_state = r._state;
@@ -83,7 +84,8 @@ Reader &Reader::operator=(Reader &r) {
 void Reader::cleanup() {
 	//_content is not to be freed, it's not owned by Reader
 
-	if (_window != nullptr) freeWindow();
+	if (_window != nullptr)
+		freeWindow();
 }
 
 bool Reader::readAndHandleFirstHeaders() {
@@ -94,7 +96,8 @@ bool Reader::readAndHandleFirstHeaders() {
 	}
 
 	while (readOneByteInString(_headers, boundary)) {
-		if (!bytesLeft()) return false;
+		if (!bytesLeft())
+			return false;
 	}
 	handleFirstHeaders(_headers);
 
@@ -108,7 +111,8 @@ bool Reader::readBlockHeadersIntoStream(Common::WriteStream *stream) {
 	if (_window == nullptr) makeWindow(boundary.size());
 
 	while (readOneByteInStream(stream, boundary)) {
-		if (!bytesLeft()) return false;
+		if (!bytesLeft())
+			return false;
 	}
 	if (stream) stream->flush();
 
@@ -124,7 +128,8 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
 	if (position) {
 		char c;
 		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
-			if (c == '\n' || c == '\r') break;
+			if (c == '\n' || c == '\r')
+				break;
 			result += c;
 		}
 	}
@@ -166,14 +171,19 @@ void Reader::parseFirstLine(const Common::String &headers) {
 				//"<METHOD> <path> HTTP/<VERSION>\r\n"
 				Common::String method, path, http, buf;
 				uint32 length = position - cstr;
-				if (headersSize > length) headersSize = length;
+				if (headersSize > length)
+					headersSize = length;
 				for (uint32 i = 0; i < headersSize; ++i) {
-					if (headers[i] != ' ') buf += headers[i];
+					if (headers[i] != ' ')
+						buf += headers[i];
 					if (headers[i] == ' ' || i == headersSize - 1) {
-						if (method == "") method = buf;
-						else if (path == "") path = buf;
-						else if (http == "") http = buf;
-						else {
+						if (method == "") {
+							method = buf;
+						} else if (path == "") {
+							path = buf;
+						} else if (http == "") {
+							http = buf;
+						} else {
 							bad = true;
 							break;
 						}
@@ -182,10 +192,12 @@ void Reader::parseFirstLine(const Common::String &headers) {
 				}
 
 				//check that method is supported
-				if (method != "GET" && method != "PUT" && method != "POST") bad = true;
+				if (method != "GET" && method != "PUT" && method != "POST")
+					bad = true;
 
 				//check that HTTP/<VERSION> is OK
-				if (!http.hasPrefix("HTTP/")) bad = true;
+				if (!http.hasPrefix("HTTP/"))
+					bad = true;
 
 				_method = method;
 				parsePathQueryAndAnchor(path);
@@ -208,12 +220,18 @@ void Reader::parsePathQueryAndAnchor(Common::String path) {
 			if (path[i] == '?') {
 				readingPath = false;
 				readingQuery = true;
-			} else _path += path[i];
+			} else {
+				_path += path[i];
+			}
 		} else if (readingQuery) {
 			if (path[i] == '#') {
 				readingQuery = false;
-			} else _query += path[i];
-		} else _anchor += path[i];
+			} else {
+				_query += path[i];
+			}
+		} else {
+			_anchor += path[i];
+		}
 	}
 
 	parseQueryParameters();
@@ -228,35 +246,48 @@ void Reader::parseQueryParameters() {
 			if (_query[i] == '=') {
 				readingKey = false;
 				value = "";
-			} else key += _query[i];
+			} else {
+				key += _query[i];
+			}
 		} else {
 			if (_query[i] == '&') {
-				if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!", key.c_str());
-				else _queryParameters[key] = LocalWebserver::urlDecode(value);
+				if (_queryParameters.contains(key))
+					warning("Query parameter \"%s\" is already set!", key.c_str());
+				else
+					_queryParameters[key] = LocalWebserver::urlDecode(value);
 				readingKey = true;
 				key = "";
-			} else value += _query[i];
+			} else {
+				value += _query[i];
+			}
 		}
 	}
 
 	if (!key.empty()) {
-		if (_queryParameters.contains(key)) warning("Query parameter \"%s\" is already set!", key.c_str());
-		else _queryParameters[key] = LocalWebserver::urlDecode(value);
+		if (_queryParameters.contains(key))
+			warning("Query parameter \"%s\" is already set!", key.c_str());
+		else
+			_queryParameters[key] = LocalWebserver::urlDecode(value);
 	}
 }
 
 bool Reader::readContentIntoStream(Common::WriteStream *stream) {
 	Common::String boundary = "--" + _boundary;
-	if (!_firstBlock) boundary = "\r\n" + boundary;
-	if (_boundary.empty()) boundary = "\r\n";
-	if (_window == nullptr) makeWindow(boundary.size());
+	if (!_firstBlock)
+		boundary = "\r\n" + boundary;
+	if (_boundary.empty())
+		boundary = "\r\n";
+	if (_window == nullptr)
+		makeWindow(boundary.size());
 
 	while (readOneByteInStream(stream, boundary)) {
-		if (!bytesLeft()) return false;
+		if (!bytesLeft())
+			return false;
 	}
 
 	_firstBlock = false;
-	if (stream) stream->flush();
+	if (stream)
+		stream->flush();
 
 	freeWindow();
 	_state = RS_READING_HEADERS;
@@ -280,14 +311,16 @@ void Reader::freeWindow() {
 bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
 	byte b = readOne();
 	_window[_windowUsed++] = b;
-	if (_windowUsed < _windowSize) return true;
+	if (_windowUsed < _windowSize)
+		return true;
 
 	//when window is filled, check whether that's the boundary
 	if (Common::String((char *)_window, _windowSize) == boundary)
 		return false;
 
 	//if not, add the first byte of the window to the string
-	if (stream) stream->writeByte(_window[0]);
+	if (stream)
+		stream->writeByte(_window[0]);
 	for (uint32 i = 1; i < _windowSize; ++i)
 		_window[i - 1] = _window[i];
 	--_windowUsed;
@@ -297,7 +330,8 @@ bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::Stri
 bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary) {
 	byte b = readOne();
 	_window[_windowUsed++] = b;
-	if (_windowUsed < _windowSize) return true;
+	if (_windowUsed < _windowSize)
+		return true;
 
 	//when window is filled, check whether that's the boundary
 	if (Common::String((char *)_window, _windowSize) == boundary)
@@ -322,9 +356,11 @@ byte Reader::readOne() {
 /// public
 
 bool Reader::readFirstHeaders() {
-	if (_state == RS_NONE) _state = RS_READING_HEADERS;
+	if (_state == RS_NONE)
+		_state = RS_READING_HEADERS;
 
-	if (!bytesLeft()) return false;
+	if (!bytesLeft())
+		return false;
 
 	if (_state == RS_READING_HEADERS)
 		return readAndHandleFirstHeaders();
@@ -349,7 +385,8 @@ bool Reader::readBlockHeaders(Common::WriteStream *stream) {
 		return false;
 	}
 
-	if (!bytesLeft()) return false;
+	if (!bytesLeft())
+		return false;
 
 	return readBlockHeadersIntoStream(stream);
 }
@@ -360,7 +397,8 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
 		return false;
 	}
 
-	if (!bytesLeft()) return false;
+	if (!bytesLeft())
+		return false;
 
 	if (!readContentIntoStream(stream))
 		return false;
@@ -369,7 +407,8 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
 		Common::String bts;
 		bts += readOne();
 		bts += readOne();
-		if (bts == "--") _allContentRead = true;
+		if (bts == "--")
+			_allContentRead = true;
 		else if (bts != "\r\n")
 			warning("strange bytes: \"%s\"", bts.c_str());
 	} else {
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 3c51c55..048ff39 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -89,7 +89,8 @@ void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Commo
 	if (position) {
 		char c;
 		for (const char *i = position + needle.size(); c = *i, c != 0; ++i) {
-			if (c == '"') break;
+			if (c == '"')
+				break;
 			result += c;
 		}
 	}
@@ -101,7 +102,8 @@ Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *str
 	uint32 readBytes;
 	while (true) {
 		readBytes = stream->read(buf, 1024);
-		if (readBytes == 0) break;
+		if (readBytes == 0)
+			break;
 		result += Common::String(buf, readBytes);
 	}
 	return result;
@@ -115,17 +117,20 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	Common::String headers = readEverythingFromMemoryStream(_headersStream);
 	Common::String fieldName = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
-	if (!fieldName.hasPrefix("upload_file")) return;
+	if (!fieldName.hasPrefix("upload_file"))
+		return;
 
 	Common::String filename = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "filename=\"", filename);
 
 	// skip block if <filename> is empty
-	if (filename.empty()) return;
+	if (filename.empty())
+		return;
 
 	// check that <path>/<filename> doesn't exist
 	Common::String path = _parentDirectoryPath;
-	if (path.lastChar() != '/' && path.lastChar() != '\\') path += '/';
+	if (path.lastChar() != '/' && path.lastChar() != '\\')
+		path += '/';
 	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
 	if (originalNode->exists()) {
 		setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
@@ -174,7 +179,9 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 		// if no file field was found - failure
 		if (_uploadedFiles == 0) {
 			setErrorMessageHandler(*client, _("No file was passed!"));
-		} else _state = UFH_STOP;
+		} else {
+			_state = UFH_STOP;
+		}
 		return;
 	}
 }


Commit: eb268cd14ff936b5afa2c39ca51acfaea51a8a90
    https://github.com/scummvm/scummvm/commit/eb268cd14ff936b5afa2c39ca51acfaea51a8a90
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix warning

Changed paths:
    backends/cloud/cloudmanager.cpp



diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp
index a68a872..826fc61 100644
--- a/backends/cloud/cloudmanager.cpp
+++ b/backends/cloud/cloudmanager.cpp
@@ -117,7 +117,7 @@ void CloudManager::save() {
 		Common::String name = getStorageConfigName(i);
 		ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, ConfMan.kCloudDomain);
 		ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, ConfMan.kCloudDomain);
-		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", _storages[i].usedBytes), ConfMan.kCloudDomain);
+		ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%lu", _storages[i].usedBytes), ConfMan.kCloudDomain);
 	}
 
 	ConfMan.set("current_storage", Common::String::format("%u", _currentStorageIndex), ConfMan.kCloudDomain);


Commit: 53aa0c46f1a5d6aefe3f2a087c60d9a59439aedf
    https://github.com/scummvm/scummvm/commit/53aa0c46f1a5d6aefe3f2a087c60d9a59439aedf
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: JANITORIAL: Fix code formatting

Changed paths:
    gui/downloaddialog.cpp
    gui/options.cpp
    gui/remotebrowser.cpp
    gui/saveload-dialog.cpp
    gui/storagewizarddialog.cpp



diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp
index 495e9c1..ea7ace2 100644
--- a/gui/downloaddialog.cpp
+++ b/gui/downloaddialog.cpp
@@ -92,12 +92,12 @@ void DownloadDialog::close() {
 void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kDownloadDialogButtonCmd:
-	{
-		CloudMan.setDownloadTarget(nullptr);
-		CloudMan.cancelDownload();
-		close();
-		break;
-	}
+		{
+			CloudMan.setDownloadTarget(nullptr);
+			CloudMan.cancelDownload();
+			close();
+			break;
+		}
 	case kDownloadProgressCmd:
 		if (!_close) {
 			refreshWidgets();
@@ -116,16 +116,19 @@ bool DownloadDialog::selectDirectories() {
 	if (Networking::Connection::isLimited()) {
 		MessageDialog alert(_("It looks like your connection is limited. "
 			"Do you really want to download files with it?"), _("Yes"), _("No"));
-		if (alert.runModal() != GUI::kMessageOK) return false;
+		if (alert.runModal() != GUI::kMessageOK)
+			return false;
 	}
 
 	//first user should select remote directory to download
-	if (_remoteBrowser->runModal() <= 0) return false;
+	if (_remoteBrowser->runModal() <= 0)
+		return false;
 
 	Cloud::StorageFile remoteDirectory = _remoteBrowser->getResult();
 
 	//now user should select local directory to download into
-	if (_browser->runModal() <= 0) return false;
+	if (_browser->runModal() <= 0)
+		return false;
 
 	Common::FSNode dir(_browser->getResult());
 	Common::FSList files;
@@ -149,7 +152,8 @@ bool DownloadDialog::selectDirectories() {
 				_("Yes"),
 				_("No")
 				);
-			if (alert.runModal() != GUI::kMessageOK) return false;
+			if (alert.runModal() != GUI::kMessageOK)
+				return false;
 			break;
 		}
 	}
@@ -161,12 +165,18 @@ bool DownloadDialog::selectDirectories() {
 	if (localPath.size() && localPath.lastChar() != '/' && localPath.lastChar() != '\\') {
 		int backslashes = 0;
 		for (uint32 i = 0; i < localPath.size(); ++i)
-			if (localPath[i] == '/') --backslashes;
-			else if (localPath[i] == '\\') ++backslashes;
-
-			if (backslashes > 0) localPath += '\\' + remoteDirectory.name();
-			else localPath += '/' + remoteDirectory.name();
-	} else localPath += remoteDirectory.name();
+			if (localPath[i] == '/')
+				--backslashes;
+			else if (localPath[i] == '\\')
+				++backslashes;
+
+			if (backslashes > 0)
+				localPath += '\\' + remoteDirectory.name();
+			else
+				localPath += '/' + remoteDirectory.name();
+	} else {
+		localPath += remoteDirectory.name();
+	}
 
 	CloudMan.startDownload(remoteDirectory.path(), localPath);
 	CloudMan.setDownloadTarget(this);
diff --git a/gui/options.cpp b/gui/options.cpp
index 34044b7..603fc23 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1502,7 +1502,8 @@ void GlobalOptionsDialog::close() {
 		uint32 port = Networking::LocalWebserver::getPort();
 		if (_serverPort) {
 			uint64 contents = _serverPort->getEditString().asUint64();
-			if (contents != 0) port = contents;
+			if (contents != 0)
+				port = contents;
 		}
 		ConfMan.setInt("local_server_port", port);
 #endif
@@ -1638,7 +1639,8 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		uint32 port = Networking::LocalWebserver::getPort();
 		if (_serverPort) {
 			uint64 contents = _serverPort->getEditString().asUint64();
-			if (contents != 0) port = contents;
+			if (contents != 0)
+				port = contents;
 		}
 		ConfMan.setInt("local_server_port", port);
 		ConfMan.flushToDisk();
@@ -1656,7 +1658,8 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 			new Common::Callback<GlobalOptionsDialog, Networking::ErrorResponse>(this, &GlobalOptionsDialog::storageErrorCallback)
 		);
 		Common::String dir = CloudMan.savesDirectoryPath();
-		if (dir.lastChar() == '/') dir.deleteLastChar();
+		if (dir.lastChar() == '/')
+			dir.deleteLastChar();
 		CloudMan.listDirectory(
 			dir,
 			new Common::Callback<GlobalOptionsDialog, Cloud::Storage::ListDirectoryResponse>(this, &GlobalOptionsDialog::storageListDirectoryCallback),
@@ -1665,30 +1668,34 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 	case kDownloadStorageCmd:
-	{
-		DownloadDialog dialog(_selectedStorageIndex, _launcher);
-		dialog.runModal();
-		break;
-	}
+		{
+			DownloadDialog dialog(_selectedStorageIndex, _launcher);
+			dialog.runModal();
+			break;
+		}
 #endif
 #ifdef USE_SDL_NET
 	case kRunServerCmd:
-	{
+		{
 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
-		// save server's port
-		uint32 port = Networking::LocalWebserver::getPort();
-		if (_serverPort) {
-			uint64 contents = _serverPort->getEditString().asUint64();
-			if (contents != 0) port = contents;
-		}
-		ConfMan.setInt("local_server_port", port);
-		ConfMan.flushToDisk();
+			// save server's port
+			uint32 port = Networking::LocalWebserver::getPort();
+			if (_serverPort) {
+				uint64 contents = _serverPort->getEditString().asUint64();
+				if (contents != 0)
+					port = contents;
+			}
+			ConfMan.setInt("local_server_port", port);
+			ConfMan.flushToDisk();
 #endif
 
-		if (LocalServer.isRunning()) LocalServer.stopOnIdle();
-		else LocalServer.start();
-		break;
-	}
+			if (LocalServer.isRunning())
+				LocalServer.stopOnIdle();
+			else
+				LocalServer.start();
+
+			break;
+		}
 
 	case kServerPortClearCmd: {
 		if (_serverPort) {
@@ -1787,7 +1794,8 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(shown);
 	if (_storageUsername) {
 		Common::String username = CloudMan.getStorageUsername(_selectedStorageIndex);
-		if (username == "") username = _("<none>");
+		if (username == "")
+			username = _("<none>");
 		_storageUsername->setLabel(username);
 		_storageUsername->setVisible(shown);
 	}
@@ -1801,31 +1809,49 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_storageLastSync) {
 		Common::String sync = CloudMan.getStorageLastSync(_selectedStorageIndex);
 		if (sync == "") {
-			if (_selectedStorageIndex == CloudMan.getStorageIndex() && CloudMan.isSyncing()) sync = _("<right now>");
-			else sync = _("<never>");
+			if (_selectedStorageIndex == CloudMan.getStorageIndex() && CloudMan.isSyncing())
+				sync = _("<right now>");
+			else
+				sync = _("<never>");
 		}
 		_storageLastSync->setLabel(sync);
 		_storageLastSync->setVisible(shown);
 	}
-	if (_storageConnectButton) _storageConnectButton->setVisible(shown);
-	if (_storageRefreshButton) _storageRefreshButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
-	if (_storageDownloadButton) _storageDownloadButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
-	if (!shown) serverLabelPosition = (_storageUsernameDesc ? _storageUsernameDesc->getRelY() : 0);
+	if (_storageConnectButton)
+		_storageConnectButton->setVisible(shown);
+	if (_storageRefreshButton)
+		_storageRefreshButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
+	if (_storageDownloadButton)
+		_storageDownloadButton->setVisible(shown && _selectedStorageIndex == CloudMan.getStorageIndex());
+	if (!shown)
+		serverLabelPosition = (_storageUsernameDesc ? _storageUsernameDesc->getRelY() : 0);
 #else
 	_selectedStorageIndex = 0;
 
-	if (_storagePopUpDesc) _storagePopUpDesc->setVisible(false);
-	if (_storagePopUp) _storagePopUp->setVisible(false);
-	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(false);
-	if (_storageUsernameDesc) _storageUsernameDesc->setVisible(false);
-	if (_storageUsername) _storageUsername->setVisible(false);
-	if (_storageUsedSpaceDesc) _storageUsedSpaceDesc->setVisible(false);
-	if (_storageUsedSpace) _storageUsedSpace->setVisible(false);
-	if (_storageLastSyncDesc) _storageLastSyncDesc->setVisible(false);
-	if (_storageLastSync) _storageLastSync->setVisible(false);
-	if (_storageConnectButton) _storageConnectButton->setVisible(false);
-	if (_storageRefreshButton) _storageRefreshButton->setVisible(false);
-	if (_storageDownloadButton) _storageDownloadButton->setVisible(false);
+	if (_storagePopUpDesc)
+		_storagePopUpDesc->setVisible(false);
+	if (_storagePopUp)
+		_storagePopUp->setVisible(false);
+	if (_storageUsernameDesc)
+		_storageUsernameDesc->setVisible(false);
+	if (_storageUsernameDesc)
+		_storageUsernameDesc->setVisible(false);
+	if (_storageUsername)
+		_storageUsername->setVisible(false);
+	if (_storageUsedSpaceDesc)
+		_storageUsedSpaceDesc->setVisible(false);
+	if (_storageUsedSpace)
+		_storageUsedSpace->setVisible(false);
+	if (_storageLastSyncDesc)
+		_storageLastSyncDesc->setVisible(false);
+	if (_storageLastSync)
+		_storageLastSync->setVisible(false);
+	if (_storageConnectButton)
+		_storageConnectButton->setVisible(false);
+	if (_storageRefreshButton)
+		_storageRefreshButton->setVisible(false);
+	if (_storageDownloadButton)
+		_storageDownloadButton->setVisible(false);
 
 	serverLabelPosition = (_storagePopUpDesc ? _storagePopUpDesc->getRelY() : 0);
 #endif
@@ -1852,7 +1878,8 @@ void GlobalOptionsDialog::setupCloudTab() {
 
 	bool serverIsRunning = LocalServer.isRunning();
 
-	if (serverLabelPosition < 0) serverLabelPosition = serverInfoY;
+	if (serverLabelPosition < 0)
+		serverLabelPosition = serverInfoY;
 	if (_runServerButton) {
 		_runServerButton->setVisible(true);
 		_runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
@@ -1861,8 +1888,10 @@ void GlobalOptionsDialog::setupCloudTab() {
 	if (_serverInfoLabel) {
 		_serverInfoLabel->setVisible(true);
 		_serverInfoLabel->setPos(_serverInfoLabel->getRelX(), serverLabelPosition);
-		if (serverIsRunning) _serverInfoLabel->setLabel(LocalServer.getAddress());
-		else _serverInfoLabel->setLabel(_("Not running"));
+		if (serverIsRunning)
+			_serverInfoLabel->setLabel(LocalServer.getAddress());
+		else
+			_serverInfoLabel->setLabel(_("Not running"));
 	}
 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
 	if (_serverPortDesc) {
@@ -1881,16 +1910,24 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_serverPortClearButton->setEnabled(!serverIsRunning);
 	}
 #else
-	if (_serverPortDesc) _serverPortDesc->setVisible(false);
-	if (_serverPort) _serverPort->setVisible(false);
-	if (_serverPortClearButton) _serverPortClearButton->setVisible(false);
+	if (_serverPortDesc)
+		_serverPortDesc->setVisible(false);
+	if (_serverPort)
+		_serverPort->setVisible(false);
+	if (_serverPortClearButton)
+		_serverPortClearButton->setVisible(false);
 #endif
 #else
-	if (_runServerButton) _runServerButton->setVisible(false);
-	if (_serverInfoLabel) _serverInfoLabel->setVisible(false);
-	if (_serverPortDesc) _serverPortDesc->setVisible(false);
-	if (_serverPort) _serverPort->setVisible(false);
-	if (_serverPortClearButton) _serverPortClearButton->setVisible(false);
+	if (_runServerButton)
+		_runServerButton->setVisible(false);
+	if (_serverInfoLabel)
+		_serverInfoLabel->setVisible(false);
+	if (_serverPortDesc)
+		_serverPortDesc->setVisible(false);
+	if (_serverPort)
+		_serverPort->setVisible(false);
+	if (_serverPortClearButton)
+		_serverPortClearButton->setVisible(false);
 #endif
 }
 #endif
diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index 49f35ed..b87fc60 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -135,8 +135,10 @@ void RemoteBrowserDialog::handleTickle() {
 void RemoteBrowserDialog::updateListing() {
 	// Update the path display
 	Common::String path = _node.path();
-	if (path.empty()) path = "/"; //root
-	if (_navigationLocked) path = "Loading... " + path;
+	if (path.empty())
+		path = "/"; //root
+	if (_navigationLocked)
+		path = "Loading... " + path;
 	_currentPath->setLabel(path);
 
 	if (!_navigationLocked) {
@@ -168,7 +170,8 @@ void RemoteBrowserDialog::goUp() {
 		_rememberedNodeContents.erase(_node.path());
 
 	Common::String path = _node.path();
-	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar();
+	if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\'))
+		path.deleteLastChar();
 	if (path.empty()) {
 		_rememberedNodeContents.erase("");
 	} else {
@@ -183,7 +186,8 @@ void RemoteBrowserDialog::goUp() {
 }
 
 void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
-	if (_navigationLocked || _workingRequest) return;
+	if (_navigationLocked || _workingRequest)
+		return;
 
 	if (_rememberedNodeContents.contains(node.path())) {
 		_nodeContent = _rememberedNodeContents[node.path()];
@@ -205,7 +209,8 @@ void RemoteBrowserDialog::listDirectory(Cloud::StorageFile node) {
 
 void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	_navigationLocked = false;
 	_nodeContent = response.value;
@@ -215,7 +220,8 @@ void RemoteBrowserDialog::directoryListedCallback(Cloud::Storage::ListDirectoryR
 
 void RemoteBrowserDialog::directoryListedErrorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
+	if (_ignoreCallback)
+		return;
 
 	_navigationLocked = false;
 	_node = _backupNode;
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 0a39f59..0bed5f4 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -111,9 +111,9 @@ SaveLoadChooserType getRequestedSaveLoadDialog(const MetaEngine &metaEngine) {
 	g_gui.checkScreenChange();
 
 	if (g_gui.getWidth() >= 640 && g_gui.getHeight() >= 400
-	    && metaEngine.hasFeature(MetaEngine::kSavesSupportMetaInfo)
-	    && metaEngine.hasFeature(MetaEngine::kSavesSupportThumbnail)
-	    && userConfig.equalsIgnoreCase("grid")) {
+		&& metaEngine.hasFeature(MetaEngine::kSavesSupportMetaInfo)
+		&& metaEngine.hasFeature(MetaEngine::kSavesSupportThumbnail)
+		&& userConfig.equalsIgnoreCase("grid")) {
 		// In case we are 640x400 or higher, this dialog is not in save mode,
 		// the user requested the grid dialog and the engines supports it we
 		// try to set it up.
@@ -232,7 +232,8 @@ void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 			ConnMan.showCloudDisabledIcon();
 		} else {
 			Cloud::SavesSyncRequest *request = CloudMan.syncSaves();
-			if (request) request->setTarget(this);
+			if (request)
+				request->setTarget(this);
 		}
 	}
 }
@@ -296,13 +297,15 @@ void SaveLoadChooserDialog::listSaves() {
 		Common::String pattern = _target + ".###";
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 		for (uint32 i = 0; i < files.size(); ++i) {
-			if (!files[i].matchString(pattern, true)) continue;
+			if (!files[i].matchString(pattern, true))
+				continue;
 
 			//make up some slot number
 			int slotNum = 0;
 			for (uint32 j = (files[i].size() > 3 ? files[i].size() - 3 : 0); j < files[i].size(); ++j) { //3 last chars
 				char c = files[i][j];
-				if (c < '0' || c > '9') continue;
+				if (c < '0' || c > '9')
+					continue;
 				slotNum = slotNum * 10 + (c - '0');
 			}
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 5838dd1..e3bac98 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -80,15 +80,18 @@ void StorageWizardDialog::open() {
 
 		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();
+			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
+			} else {
 				doClose = false;
+			}
 		}
 
 		if (doClose) {
@@ -106,7 +109,8 @@ void StorageWizardDialog::open() {
 
 void StorageWizardDialog::close() {
 	if (Cloud::CloudManager::couldUseLocalServer()) {
-		if (_stopServerOnClose) LocalServer.stopOnIdle();
+		if (_stopServerOnClose)
+			LocalServer.stopOnIdle();
 		LocalServer.indexPageHandler().setTarget(nullptr);
 	}
 	Dialog::close();
@@ -139,8 +143,10 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 
 		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.");
+			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());
 		}
 
@@ -154,8 +160,10 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 				uint32 crc = crc16(code);
 				ok = (crc == gotcrc);
 			}
-			if (ok) message = _("All OK!");
-			else message = _("Invalid code");
+			if (ok)
+				message = _("All OK!");
+			else
+				message = _("Invalid code");
 		}
 		_connectWidget->setEnabled(ok);
 		_messageWidget->setLabel(message);
@@ -172,7 +180,8 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		Common::String code;
 		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
 			Common::String subcode = _codeWidget[i]->getEditString();
-			if (subcode.size() == 0) continue;
+			if (subcode.size() == 0)
+				continue;
 			code += subcode;
 			code.deleteLastChar();
 		}
@@ -203,13 +212,23 @@ void StorageWizardDialog::handleTickle() {
 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;
+	case Cloud::kStorageDropboxId:
+		url += "db";
+		break;
+	case Cloud::kStorageOneDriveId:
+		url += "od";
+		break;
+	case Cloud::kStorageGoogleDriveId:
+		url += "gd";
+		break;
+	case Cloud::kStorageBoxId:
+		url += "bx";
+		break;
 	}
 
-	if (Cloud::CloudManager::couldUseLocalServer()) url += "s";
+	if (Cloud::CloudManager::couldUseLocalServer())
+		url += "s";
+
 	return url;
 }
 
@@ -222,7 +241,8 @@ int StorageWizardDialog::decodeHashchar(char c) {
 }
 
 bool StorageWizardDialog::correctChecksum(Common::String s) {
-	if (s.size() == 0) return false; //no last char
+	if (s.size() == 0)
+		return false; //no last char
 	int providedChecksum = decodeHashchar(s.lastChar());
 	int calculatedChecksum = 0x2A; //any initial value would do, but it must equal to the one used on the page where these checksums were generated
 	for (uint32 i = 0; i < s.size()-1; ++i) {


Commit: f95073f0084b3fae3e2cbf14fd98135c37eabbdf
    https://github.com/scummvm/scummvm/commit/f95073f0084b3fae3e2cbf14fd98135c37eabbdf
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: JANITORIAL: More whitespace fixes

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 776ee71..697ca48 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -43,13 +43,15 @@ BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, Co
 
 BoxListDirectoryByIdRequest::~BoxListDirectoryByIdRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void BoxListDirectoryByIdRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest) _workingRequest->finish();
+	if (_workingRequest)
+		_workingRequest->finish();
 	_files.clear();
 	_ignoreCallback = false;
 
@@ -74,8 +76,10 @@ void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
 
 void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (response.request) _date = response.request->date();
+	if (_ignoreCallback)
+		return;
+	if (response.request)
+		_date = response.request->date();
 
 	Networking::ErrorResponse error(this);
 	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
@@ -125,16 +129,22 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 
 		Common::JSONArray items = responseObject.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest"))
+				continue;
 
 			Common::JSONObject item = items[i]->asObject();
 
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest"))
+				continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest"))
+				continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest"))
+				continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest"))
+				continue;
 			if (!Networking::CurlJsonRequest::jsonContainsString(item, "size", "BoxListDirectoryByIdRequest") &&
-				!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
+					!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "BoxListDirectoryByIdRequest"))
+				continue;
 
 			Common::String id = item.getVal("id")->asString();
 			Common::String name = item.getVal("name")->asString();
@@ -162,16 +172,20 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		received += responseObject.getVal("limit")->asIntegerNumber();
 	bool hasMore = (received < totalCount);
 
-	if (hasMore) makeRequest(received);
-	else finishListing(_files);
+	if (hasMore)
+		makeRequest(received);
+	else
+		finishListing(_files);
 
 	delete json;
 }
 
 void BoxListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback) return;
-	if (error.request) _date = error.request->date();
+	if (_ignoreCallback)
+		return;
+	if (error.request)
+		_date = error.request->date();
 	finishError(error);
 }
 
@@ -183,7 +197,8 @@ Common::String BoxListDirectoryByIdRequest::date() const { return _date; }
 
 void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback)
+		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace Box


Commit: f3959e140106334b33cf74832fc5b20d27407d0a
    https://github.com/scummvm/scummvm/commit/f3959e140106334b33cf74832fc5b20d27407d0a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Upload ListDirectory Requests

Lots of checks to avoid JSON-related segfaults added.

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 697ca48..0171b52 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -43,15 +43,13 @@ BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, Co
 
 BoxListDirectoryByIdRequest::~BoxListDirectoryByIdRequest() {
 	_ignoreCallback = true;
-	if (_workingRequest)
-		_workingRequest->finish();
+	if (_workingRequest) _workingRequest->finish();
 	delete _listDirectoryCallback;
 }
 
 void BoxListDirectoryByIdRequest::start() {
 	_ignoreCallback = true;
-	if (_workingRequest)
-		_workingRequest->finish();
+	if (_workingRequest) _workingRequest->finish();
 	_files.clear();
 	_ignoreCallback = false;
 
@@ -76,8 +74,11 @@ void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
 
 void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback)
+	if (_ignoreCallback) {
+		delete response.value;
 		return;
+	}
+
 	if (response.request)
 		_date = response.request->date();
 
@@ -103,7 +104,7 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 	Common::JSONObject responseObject = json->asObject();
 	//debug(9, "%s", json->stringify(true).c_str());
 
-	//TODO: check that error is returned the right way
+	//TODO: handle error messages passed as JSON
 	/*
 	if (responseObject.contains("error") || responseObject.contains("error_summary")) {
 		warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
@@ -129,22 +130,15 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 
 		Common::JSONArray items = responseObject.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest"))
-				continue;
+			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
 
 			Common::JSONObject item = items[i]->asObject();
 
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest"))
-				continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest"))
-				continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest"))
-				continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest"))
-				continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "size", "BoxListDirectoryByIdRequest") &&
-					!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "BoxListDirectoryByIdRequest"))
-				continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::CurlJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
 
 			Common::String id = item.getVal("id")->asString();
 			Common::String name = item.getVal("name")->asString();
@@ -172,20 +166,16 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
 		received += responseObject.getVal("limit")->asIntegerNumber();
 	bool hasMore = (received < totalCount);
 
-	if (hasMore)
-		makeRequest(received);
-	else
-		finishListing(_files);
+	if (hasMore) makeRequest(received);
+	else finishListing(_files);
 
 	delete json;
 }
 
 void BoxListDirectoryByIdRequest::errorCallback(Networking::ErrorResponse error) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback)
-		return;
-	if (error.request)
-		_date = error.request->date();
+	if (_ignoreCallback) return;
+	if (error.request) _date = error.request->date();
 	finishError(error);
 }
 
@@ -197,8 +187,7 @@ Common::String BoxListDirectoryByIdRequest::date() const { return _date; }
 
 void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
 	Request::finishSuccess();
-	if (_listDirectoryCallback)
-		(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+	if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
 }
 
 } // End of namespace Box
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index def9155..2e1d618 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -71,8 +71,10 @@ void DropboxListDirectoryRequest::start() {
 
 void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback)
+	if (_ignoreCallback) {
+		delete response.value;
 		return;
+	}
 
 	if (response.request)
 		_date = response.request->date();
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index d2e94a7..5597d78 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -72,8 +72,10 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken)
 
 void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
-	if (_ignoreCallback)
+	if (_ignoreCallback) {
+		delete response.value;
 		return;
+	}
 	if (response.request)
 		_date = response.request->date();
 
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 30a89a7..069d6fd 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -104,20 +104,40 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	if (!json) {
-		error.failed = true;
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
 		finishError(error);
 		return;
 	}
 
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
 	Common::JSONObject object = json->asObject();
 
-	//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
+	//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
+	if (!Networking::CurlJsonRequest::jsonContainsArray(object, "value", "OneDriveListDirectoryRequest")) {
+		error.response = "\"value\" not found or that's not an array!";
+		finishError(error);
+		delete json;
+		return;
+	}
 
 	Common::JSONArray items = object.getVal("value")->asArray();
 	for (uint32 i = 0; i < items.size(); ++i) {
+		if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;
+
 		Common::JSONObject item = items[i]->asObject();
 
+		if (!Networking::CurlJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
+		if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::CurlJsonRequest::jsonContainsString(item, "lastModifiedDateTime", "OneDriveListDirectoryRequest")) continue;
+
 		Common::String path = _currentDirectory + item.getVal("name")->asString();
 		bool isDirectory = item.contains("folder");
 		uint32 size = item.getVal("size")->asIntegerNumber();
@@ -132,6 +152,13 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
 
 	bool hasMore = object.contains("@odata.nextLink");
 	if (hasMore) {
+		if (!Networking::CurlJsonRequest::jsonContainsString(object, "@odata.nextLink", "OneDriveListDirectoryRequest")) {
+			error.response = "\"@odata.nextLink\" is not a string!";
+			finishError(error);
+			delete json;
+			return;
+		}
+
 		makeRequest(object.getVal("@odata.nextLink")->asString());
 	} else {
 		listNextDirectory();
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 4d87aa2..7db56cf 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -42,7 +42,7 @@ CurlJsonRequest::~CurlJsonRequest() {
 
 char *CurlJsonRequest::getPreparedContents() {
 	//write one more byte in the end
-	byte zero[1] = { 0 };
+	byte zero[1] = {0};
 	_contentsStream.write(zero, 1);
 
 	//replace all "bad" bytes with '.' character
@@ -114,8 +114,12 @@ bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningP
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix) {
+bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
 		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
 		return false;
 	}
@@ -127,8 +131,12 @@ bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *k
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix) {
+bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
 		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
 		return false;
 	}
@@ -140,4 +148,51 @@ bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const
 	return false;
 }
 
+bool CurlJsonRequest::jsonContainsArray(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	if (item.getVal(key)->isArray()) return true;
+
+	warning("%s: passed item's \"%s\" attribute is not an array!", warningPrefix, key);
+	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+	return false;
+}
+
+bool CurlJsonRequest::jsonContainsStringOrIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	if (item.getVal(key)->isString() || item.getVal(key)->isIntegerNumber()) return true;
+
+	warning("%s: passed item's \"%s\" attribute is neither a string or an integer!", warningPrefix, key);
+	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+	return false;
+}
+
+bool CurlJsonRequest::jsonContainsAttribute(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	return true;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index 1d1409e..cab75bf 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -54,8 +54,11 @@ public:
 	virtual void restart();
 
 	static bool jsonIsObject(Common::JSONValue *item, const char *warningPrefix);
-	static bool jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix);
-	static bool jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix);
+	static bool jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+	static bool jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+	static bool jsonContainsArray(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+	static bool jsonContainsStringOrIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+	static bool jsonContainsAttribute(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
 };
 
 } // End of namespace Networking


Commit: d57e0c89b5b20dac247cb2f43450014d84719ba6
    https://github.com/scummvm/scummvm/commit/d57e0c89b5b20dac247cb2f43450014d84719ba6
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: #define all OAuth2/API-related URLs

Changed paths:
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxinforequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp



diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 0171b52..c013f1e 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -34,6 +34,7 @@ namespace Cloud {
 namespace Box {
 
 #define BOX_LIST_DIRECTORY_LIMIT 1000
+#define BOX_FOLDERS_API_LINK "https://api.box.com/2.0/folders/%s/items?offset=%u&limit=%u&fields=%s"
 
 BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
@@ -58,7 +59,7 @@ void BoxListDirectoryByIdRequest::start() {
 
 void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
 	Common::String url = Common::String::format(
-		"https://api.box.com/2.0/folders/%s/items?offset=%u&limit=%u&fields=%s",
+		BOX_FOLDERS_API_LINK,
 		_requestedId.c_str(),
 		offset,
 		BOX_LIST_DIRECTORY_LIMIT,
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 2b2be70..401ec6a 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -37,6 +37,11 @@
 namespace Cloud {
 namespace Box {
 
+#define BOX_OAUTH2_TOKEN "https://api.box.com/oauth2/token"
+#define BOX_API_FOLDERS "https://api.box.com/2.0/folders"
+#define BOX_API_FILES_CONTENT "https://api.box.com/2.0/files/%s/content"
+#define BOX_API_USERS_ME "https://api.box.com/2.0/users/me"
+
 char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *BoxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
@@ -80,7 +85,7 @@ void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback
 	if (errorCallback == nullptr)
 		errorCallback = getErrorPrintingCallback();
 
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.box.com/oauth2/token");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, BOX_OAUTH2_TOKEN);
 	if (codeFlow) {
 		request->addPostField("grant_type=authorization_code");
 		request->addPostField("code=" + code);
@@ -223,7 +228,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare
 	if (!errorCallback)
 		errorCallback = getErrorPrintingCallback();
 
-	Common::String url = "https://api.box.com/2.0/folders";
+	Common::String url = BOX_API_FOLDERS;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::createDirectoryInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
@@ -263,7 +268,7 @@ bool BoxStorage::uploadStreamSupported() {
 
 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
-		Common::String url = "https://api.box.com/2.0/files/" + id + "/content";
+		Common::String url = Common::String::format(BOX_API_FILES_CONTENT, id.c_str());
 		Common::String header = "Authorization: Bearer " + _token;
 		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
@@ -276,7 +281,7 @@ Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::N
 
 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, "https://api.box.com/2.0/users/me");
+	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, BOX_API_USERS_ME);
 	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index c308ddb..29034c4 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -33,6 +33,8 @@
 namespace Cloud {
 namespace Box {
 
+#define BOX_API_FILES "https://upload.box.com/api/2.0/files"
+
 BoxUploadRequest::BoxUploadRequest(BoxStorage *storage, Common::String path, Common::String localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _localPath(localPath), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -102,7 +104,7 @@ void BoxUploadRequest::upload() {
 		}
 	}
 
-	Common::String url = "https://upload.box.com/api/2.0/files";
+	Common::String url = BOX_API_FILES;
 	if (_resolvedId != "")
 		url += "/" + _resolvedId;
 	url += "/content";
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 968fb45..ad9b0fc 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -30,6 +30,8 @@
 namespace Cloud {
 namespace Dropbox {
 
+#define DROPBOX_API_CREATE_FOLDER "https://api.dropboxapi.com/2/files/create_folder"
+
 DropboxCreateDirectoryRequest::DropboxCreateDirectoryRequest(Common::String token, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _token(token), _path(path), _boolCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -51,7 +53,7 @@ void DropboxCreateDirectoryRequest::start() {
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::JsonResponse>(this, &DropboxCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxCreateDirectoryRequest, Networking::ErrorResponse>(this, &DropboxCreateDirectoryRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/files/create_folder");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_CREATE_FOLDER);
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index 37700ea..207c202 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -31,6 +31,9 @@
 namespace Cloud {
 namespace Dropbox {
 
+#define DROPBOX_API_GET_CURRENT_ACCOUNT "https://api.dropboxapi.com/2/users/get_current_account"
+#define DROPBOX_API_GET_SPACE_USAGE "https://api.dropboxapi.com/2/users/get_space_usage"
+
 DropboxInfoRequest::DropboxInfoRequest(Common::String token, Storage::StorageInfoCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _token(token), _infoCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -52,7 +55,7 @@ void DropboxInfoRequest::start() {
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::userResponseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/users/get_current_account");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 	request->addPostField("null"); //use POST
@@ -90,7 +93,7 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, Networking::JsonResponse>(this, &DropboxInfoRequest::quotaResponseCallback);
 	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxInfoRequest, Networking::ErrorResponse>(this, &DropboxInfoRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/2/users/get_space_usage");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, DROPBOX_API_GET_SPACE_USAGE);
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 	request->addPostField("null"); //use POST
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index 2e1d618..8b00f7c 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -31,6 +31,9 @@
 namespace Cloud {
 namespace Dropbox {
 
+#define DROPBOX_API_LIST_FOLDER "https://api.dropboxapi.com/2/files/list_folder"
+#define DROPBOX_API_LIST_FOLDER_CONTINUE "https://api.dropboxapi.com/2/files/list_folder/continue"
+
 DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
 	Networking::Request(nullptr, ecb), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb),
 	_token(token), _workingRequest(nullptr), _ignoreCallback(false) {
@@ -53,7 +56,7 @@ void DropboxListDirectoryRequest::start() {
 
 	Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, DROPBOX_API_LIST_FOLDER);
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Content-Type: application/json");
 
@@ -71,6 +74,7 @@ void DropboxListDirectoryRequest::start() {
 
 void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse response) {
 	_workingRequest = nullptr;
+
 	if (_ignoreCallback) {
 		delete response.value;
 		return;
@@ -85,58 +89,109 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	Common::JSONValue *json = response.value;
-	if (json) {
-		Common::JSONObject responseObjecct = json->asObject();
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
+
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
+	Common::JSONObject responseObject = json->asObject();
+
+	if (responseObject.contains("error") || responseObject.contains("error_summary")) {
+		if (responseObject.contains("error_summary") && responseObject.getVal("error_summary")->isString()) {
+			warning("Dropbox returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
+		}
+		error.failed = true;
+		error.response = json->stringify();
+		finishError(error);
+		delete json;
+		return;
+	}
 
-		if (responseObjecct.contains("error") || responseObjecct.contains("error_summary")) {
-			warning("Dropbox returned error: %s", responseObjecct.getVal("error_summary")->asString().c_str());
-			error.failed = true;
-			error.response = json->stringify();
+	//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
+	if (responseObject.contains("entries")) {
+		if (!responseObject.getVal("entries")->isArray()) {
+			error.response = Common::String::format(
+				"\"entries\" found, but that's not an array!\n%s",
+				responseObject.getVal("entries")->stringify(true).c_str()
+				);
 			finishError(error);
 			delete json;
 			return;
 		}
 
-		//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
-
-		if (responseObjecct.contains("entries")) {
-			Common::JSONArray items = responseObjecct.getVal("entries")->asArray();
-			for (uint32 i = 0; i < items.size(); ++i) {
-				Common::JSONObject item = items[i]->asObject();
-				Common::String path = item.getVal("path_lower")->asString();
-				bool isDirectory = (item.getVal(".tag")->asString() == "folder");
-				uint32 size = 0, timestamp = 0;
-				if (!isDirectory) {
-					size = item.getVal("size")->asIntegerNumber();
-					timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
-				}
-				_files.push_back(StorageFile(path, size, timestamp, isDirectory));
+		Common::JSONArray items = responseObject.getVal("entries")->asArray();
+		for (uint32 i = 0; i < items.size(); ++i) {
+			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "DropboxListDirectoryRequest"))
+				continue;
+
+			Common::JSONObject item = items[i]->asObject();
+
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, "path_lower", "DropboxListDirectoryRequest"))
+				continue;
+			if (!Networking::CurlJsonRequest::jsonContainsString(item, ".tag", "DropboxListDirectoryRequest"))
+				continue;
+
+			Common::String path = item.getVal("path_lower")->asString();
+			bool isDirectory = (item.getVal(".tag")->asString() == "folder");
+			uint32 size = 0, timestamp = 0;
+			if (!isDirectory) {
+				if (!Networking::CurlJsonRequest::jsonContainsString(item, "server_modified", "DropboxListDirectoryRequest"))
+					continue;
+				if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "DropboxListDirectoryRequest"))
+					continue;
+
+				size = item.getVal("size")->asIntegerNumber();
+				timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
 			}
+			_files.push_back(StorageFile(path, size, timestamp, isDirectory));
 		}
+	}
 
-		bool hasMore = (responseObjecct.contains("has_more") && responseObjecct.getVal("has_more")->asBool());
+	bool hasMore = false;
+	if (responseObject.contains("has_more")) {
+		if (!responseObject.getVal("has_more")->isBool()) {
+			warning("DropboxListDirectoryRequest: \"has_more\" is not a boolean");
+			debug(9, "%s", responseObject.getVal("has_more")->stringify(true).c_str());
+			error.response = "\"has_more\" is not a boolean!";
+			finishError(error);
+			delete json;
+			return;
+		}
 
-		if (hasMore) {
-			Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
-			Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
-			Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder/continue");
-			request->addHeader("Authorization: Bearer " + _token);
-			request->addHeader("Content-Type: application/json");
+		hasMore = responseObject.getVal("has_more")->asBool();
+	}
 
-			Common::JSONObject jsonRequestParameters;
-			jsonRequestParameters.setVal("cursor", new Common::JSONValue(responseObjecct.getVal("cursor")->asString()));
+	if (hasMore) {
+		if (!Networking::CurlJsonRequest::jsonContainsString(responseObject, "cursor", "DropboxListDirectoryRequest")) {
+			error.response = "\"has_more\" found, but \"cursor\" is not (or it's not a string)!";
+			finishError(error);
+			delete json;
+			return;
+		}
 
-			Common::JSONValue value(jsonRequestParameters);
-			request->addPostField(Common::JSON::stringify(&value));
+		Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback);
+		Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback);
+		Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, DROPBOX_API_LIST_FOLDER_CONTINUE);
+		request->addHeader("Authorization: Bearer " + _token);
+		request->addHeader("Content-Type: application/json");
 
-			_workingRequest = ConnMan.addRequest(request);
-		} else {
-			finishListing(_files);
-		}
+		Common::JSONObject jsonRequestParameters;
+		jsonRequestParameters.setVal("cursor", new Common::JSONValue(responseObject.getVal("cursor")->asString()));
+
+		Common::JSONValue value(jsonRequestParameters);
+		request->addPostField(Common::JSON::stringify(&value));
+
+		_workingRequest = ConnMan.addRequest(request);
 	} else {
-		warning("null, not json");
-		error.failed = true;
-		finishError(error);
+		finishListing(_files);
 	}
 
 	delete json;
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index cd1dff8..20f8a68 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -37,6 +37,9 @@
 namespace Cloud {
 namespace Dropbox {
 
+#define DROPBOX_OAUTH2_TOKEN "https://api.dropboxapi.com/oauth2/token"
+#define DROPBOX_API_FILES_DOWNLOAD "https://content.dropboxapi.com/2/files/download"
+
 char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
@@ -65,7 +68,7 @@ void DropboxStorage::getAccessToken(Common::String code) {
 		loadKeyAndSecret();
 	Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);
 	Networking::ErrorCallback errorCallback = new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::codeFlowFailed);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, errorCallback, "https://api.dropboxapi.com/oauth2/token");
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, errorCallback, DROPBOX_OAUTH2_TOKEN);
 	request->addPostField("code=" + code);
 	request->addPostField("grant_type=authorization_code");
 	request->addPostField("client_id=" + Common::String(KEY));
@@ -129,7 +132,7 @@ Networking::Request *DropboxStorage::streamFileById(Common::String path, Network
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
 
-	Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, "https://content.dropboxapi.com/2/files/download"); //TODO: is it right?
+	Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, DROPBOX_API_FILES_DOWNLOAD); //TODO: is it OK to pass no callbacks?
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 03c3fbc..f129eaa 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -32,6 +32,9 @@
 namespace Cloud {
 namespace Dropbox {
 
+#define DROPBOX_API_FILES_UPLOAD "https://content.dropboxapi.com/2/files/upload"
+#define DROPBOX_API_FILES_UPLOAD_SESSION "https://content.dropboxapi.com/2/files/upload_session/"
+
 DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -68,12 +71,12 @@ void DropboxUploadRequest::start() {
 void DropboxUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
-	Common::String url = "https://content.dropboxapi.com/2/files/upload_session/";
+	Common::String url = DROPBOX_API_FILES_UPLOAD_SESSION;
 	Common::JSONObject jsonRequestParameters;
 
 	if (_contentsStream->pos() == 0 || _sessionId == "") {
 		if ((uint32)_contentsStream->size() <= UPLOAD_PER_ONE_REQUEST) {
-			url = "https://content.dropboxapi.com/2/files/upload";
+			url = DROPBOX_API_FILES_UPLOAD;
 			jsonRequestParameters.setVal("path", new Common::JSONValue(_savePath));
 			jsonRequestParameters.setVal("mode", new Common::JSONValue("overwrite"));
 			jsonRequestParameters.setVal("autorename", new Common::JSONValue(false));
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index 5597d78..5261112 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -33,6 +33,9 @@
 namespace Cloud {
 namespace GoogleDrive {
 
+#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken&orderBy=folder,name"
+//files(id,mimeType,modifiedTime,name,size),nextPageToken
+
 GoogleDriveListDirectoryByIdRequest::GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, Common::String id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -57,8 +60,7 @@ void GoogleDriveListDirectoryByIdRequest::start() {
 }
 
 void GoogleDriveListDirectoryByIdRequest::makeRequest(Common::String pageToken) {
-	Common::String url = "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken&orderBy=folder,name";
-	//files(id,mimeType,modifiedTime,name,size),nextPageToken
+	Common::String url = GOOGLEDRIVE_API_FILES;
 	if (pageToken != "")
 		url += "&pageToken=" + pageToken;
 	url += "&q=%27" + _requestedId + "%27+in+parents";
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 2201321..af19019 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -37,6 +37,11 @@
 namespace Cloud {
 namespace GoogleDrive {
 
+#define GOOGLEDRIVE_OAUTH2_TOKEN "https://accounts.google.com/o/oauth2/token"
+#define GOOGLEDRIVE_API_FILES_ALT_MEDIA "https://www.googleapis.com/drive/v3/files/%s?alt=media"
+#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/drive/v3/files"
+#define GOOGLEDRIVE_API_ABOUT "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user"
+
 char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
@@ -79,7 +84,7 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::Error
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
 	if (errorCallback == nullptr)
 		errorCallback = getErrorPrintingCallback();
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://accounts.google.com/o/oauth2/token"); //TODO
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, GOOGLEDRIVE_OAUTH2_TOKEN); //TODO
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
@@ -224,7 +229,7 @@ Networking::Request *GoogleDriveStorage::upload(Common::String path, Common::See
 
 Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
-		Common::String url = "https://www.googleapis.com/drive/v3/files/" + ConnMan.urlEncode(id) + "?alt=media";
+		Common::String url = Common::String::format(GOOGLEDRIVE_API_FILES_ALT_MEDIA, ConnMan.urlEncode(id).c_str());
 		Common::String header = "Authorization: Bearer " + _token;
 		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
@@ -246,7 +251,7 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::Str
 	if (!errorCallback)
 		errorCallback = getErrorPrintingCallback();
 
-	Common::String url = "https://www.googleapis.com/drive/v3/files";
+	Common::String url = GOOGLEDRIVE_API_FILES;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
 	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
@@ -270,7 +275,7 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
 	if (!callback)
 		callback = new Common::Callback<GoogleDriveStorage, StorageInfoResponse>(this, &GoogleDriveStorage::printInfo);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user");
+	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, GOOGLEDRIVE_API_ABOUT);
 	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 3cdee26..bd9d879 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -33,6 +33,8 @@
 namespace Cloud {
 namespace GoogleDrive {
 
+#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/upload/drive/v3/files"
+
 GoogleDriveUploadRequest::GoogleDriveUploadRequest(GoogleDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -111,7 +113,7 @@ void GoogleDriveUploadRequest::startUpload() {
 		}
 	}
 
-	Common::String url = "https://www.googleapis.com/upload/drive/v3/files";
+	Common::String url = GOOGLEDRIVE_API_FILES;
 	if (_resolvedId != "")
 		url += "/" + ConnMan.urlEncode(_resolvedId);
 	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index bd612d6..a31fd02 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -31,6 +31,8 @@
 namespace Cloud {
 namespace OneDrive {
 
+#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
+
 OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -65,7 +67,7 @@ void OneDriveCreateDirectoryRequest::start() {
 		}
 	}
 
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot";
+	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT;
 	if (parent != "")
 		url += ":/" + ConnMan.urlEncode(parent) + ":";
 	url += "/children";
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 069d6fd..a247a9f 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -31,6 +31,8 @@
 namespace Cloud {
 namespace OneDrive {
 
+#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/children"
+
 OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
 	Networking::Request(nullptr, ecb),
 	_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
@@ -74,8 +76,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
 
 	Common::String dir = _currentDirectory;
 	dir.deleteLastChar();
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(dir);
-	url += ":/children";
+	Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, ConnMan.urlEncode(dir).c_str());
 	makeRequest(url);
 }
 
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 5612cbf..af5d9f1 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -38,6 +38,10 @@
 namespace Cloud {
 namespace OneDrive {
 
+#define ONEDRIVE_OAUTH2_TOKEN "https://login.live.com/oauth20_token.srf"
+#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://api.onedrive.com/v1.0/drive/special/approot:/"
+#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
+
 char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
 char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
@@ -81,7 +85,7 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCal
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
 	if (errorCallback == nullptr)
 		errorCallback = getErrorPrintingCallback();
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://login.live.com/oauth20_token.srf"); //TODO
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, ONEDRIVE_OAUTH2_TOKEN);
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
@@ -227,7 +231,7 @@ Networking::Request *OneDriveStorage::upload(Common::String path, Common::Seekab
 }
 
 Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
-	Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(path);
+	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _token);
@@ -242,7 +246,7 @@ Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolC
 
 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, "https://api.onedrive.com/v1.0/drive/special/approot");
+	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
 	request->addHeader("Authorization: bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 331800a..ef1507d 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -33,6 +33,9 @@
 namespace Cloud {
 namespace OneDrive {
 
+#define ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/upload.createSession"
+#define ONEDRIVE_API_SPECIAL_APPROOT_CONTENT "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/content"
+
 OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
 	Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
 	_workingRequest(nullptr), _ignoreCallback(false) {
@@ -70,7 +73,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
 	if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
-		Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(_savePath) + ":/upload.createSession"; //folder must exist
+		Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD, ConnMan.urlEncode(_savePath).c_str()); //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
 		Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
@@ -82,7 +85,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 	Common::String url;
 	if (_uploadUrl == "") {
-		url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + ConnMan.urlEncode(_savePath) + ":/content";
+		url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CONTENT, ConnMan.urlEncode(_savePath).c_str());
 	} else {
 		url = _uploadUrl;
 	}


Commit: 15c6772ff7638e104027f7b7777180e6191841fc
    https://github.com/scummvm/scummvm/commit/15c6772ff7638e104027f7b7777180e6191841fc
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
ALL: Fix debug, warning and error usage

Added prefixes, used debug(9).

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/downloadrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxinforequest.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/id/idresolveidrequest.cpp
    backends/cloud/id/idstorage.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/savessyncrequest.cpp
    backends/cloud/storage.cpp
    backends/networking/curl/connectionmanager.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curlrequest.cpp
    backends/networking/curl/networkreadstream.cpp
    backends/networking/sdl_net/client.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/reader.cpp
    gui/options.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 401ec6a..c2c4c62 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -115,8 +115,8 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 
 	Common::JSONObject result = json->asObject();
 	if (!result.contains("access_token") || !result.contains("refresh_token")) {
-		warning("Bad response, no token passed");
-		debug("%s", json->stringify().c_str());
+		warning("BoxStorage: bad response, no token passed");
+		debug(9, "%s", json->stringify().c_str());
 		if (callback)
 			(*callback)(BoolResponse(nullptr, false));
 	} else {
@@ -141,8 +141,8 @@ void BoxStorage::codeFlowComplete(BoolResponse response) {
 }
 
 void BoxStorage::codeFlowFailed(Networking::ErrorResponse error) {
-	debug("Box's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
+	debug(9, "BoxStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug(9, "%s", error.response.c_str());
 	CloudMan.removeStorage(this);
 }
 
@@ -158,7 +158,7 @@ Common::String BoxStorage::name() const {
 void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("BoxStorage::infoInnerCallback: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
@@ -210,7 +210,7 @@ Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirect
 void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("BoxStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
@@ -292,12 +292,12 @@ BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
-		warning("No access_token found");
+		warning("BoxStorage: no access_token found");
 		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
-		warning("No refresh_token found");
+		warning("BoxStorage: no refresh_token found");
 		return nullptr;
 	}
 
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index 65964f3..c798b97 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -69,7 +69,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		long httpCode = -1;
 		if (_stream) {
 			httpCode = _stream->httpResponseCode();
-			debug(9, "code %ld", httpCode);
+			debug(9, "BoxTokenRefresher: code %ld", httpCode);
 		}
 
 		bool irrecoverable = true;
@@ -77,12 +77,12 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (result.contains("code")) {
 			code = result.getVal("code")->asString();
-			debug(9, "code = %s", code.c_str());
+			debug(9, "BoxTokenRefresher: code = %s", code.c_str());
 		}
 
 		if (result.contains("message")) {
 			message = result.getVal("message")->asString();
-			debug(9, "message = %s", message.c_str());
+			debug(9, "BoxTokenRefresher: message = %s", message.c_str());
 		}
 
 		//TODO: decide when token refreshment will help
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index 29034c4..f68ba6a 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -143,8 +143,9 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 	}
 
 	if (error.httpResponseCode != 200 && error.httpResponseCode != 201)
-		warning("looks like an error");
+		warning("BoxUploadRequest: looks like an error (bad HTTP code)");
 
+	//TODO: add more JSON warnings there
 	Common::JSONValue *json = response.value;
 	if (json) {
 		if (json->isObject()) {
@@ -188,10 +189,10 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 			*/
 		}
 
-		warning("no file info to return");
+		warning("BoxUploadRequest: no file info to return");
 		finishUpload(StorageFile(_savePath, 0, 0, false));
 	} else {
-		warning("null, not json");
+		warning("BoxUploadRequest: null, not json");
 		finishError(error);
 	}
 
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index f706ed6..e28670f 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -99,8 +99,10 @@ void DownloadRequest::handle() {
 
 	if (_remoteFileStream->eos()) {
 		if (_remoteFileStream->httpResponseCode() != 200) {
-			warning("HTTP response code is not 200 OK (it's %ld)", _remoteFileStream->httpResponseCode());
+			warning("DownloadRequest: HTTP response code is not 200 OK (it's %ld)", _remoteFileStream->httpResponseCode());
 			//TODO: do something about it actually
+			// the problem is file's already downloaded, stream is over
+			// so we can't return error message anymore
 		}
 
 		finishDownload(_remoteFileStream->httpResponseCode() == 200);
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index ad9b0fc..6cc6801 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -80,7 +80,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("DropboxCreateDirectoryRequest: NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index 207c202..c5cbb9d 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -77,7 +77,7 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("DropboxInfoRequest: NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
@@ -115,7 +115,7 @@ void DropboxInfoRequest::quotaResponseCallback(Networking::JsonResponse response
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("DropboxInfoRequest: NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 20f8a68..6fd1f9d 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -86,8 +86,8 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 	if (json) {
 		Common::JSONObject result = json->asObject();
 		if (!result.contains("access_token") || !result.contains("uid")) {
-			warning("%s", json->stringify(true).c_str());
-			warning("Bad response, no token/uid passed");
+			warning("DropboxStorage: bad response, no token/uid passed");
+			debug(9, "%s", json->stringify(true).c_str());
 			CloudMan.removeStorage(this);
 		} else {
 			_token = result.getVal("access_token")->asString();
@@ -99,14 +99,14 @@ void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 
 		delete json;
 	} else {
-		debug("DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
+		debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
 		CloudMan.removeStorage(this);
 	}
 }
 
 void DropboxStorage::codeFlowFailed(Networking::ErrorResponse error) {
-	debug("Dropbox's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
+	debug(9, "DropboxStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug(9, "%s", error.response.c_str());
 	CloudMan.removeStorage(this);
 }
 
@@ -161,12 +161,12 @@ DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
-		warning("No access_token found");
+		warning("DropboxStorage: no access_token found");
 		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
-		warning("No user_id found");
+		warning("DropboxStorage: no user_id found");
 		return nullptr;
 	}
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index f129eaa..dd3a381 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -123,7 +123,6 @@ void DropboxUploadRequest::uploadNextPart() {
 }
 
 void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
-	debug(9, "partUploadedCallback");
 	_workingRequest = nullptr;
 	if (_ignoreCallback)
 		return;
@@ -133,6 +132,7 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
+	// TODO: add more JSON-related warnings
 	Common::JSONValue *json = response.value;
 	if (json) {
 		bool needsFinishRequest = false;
@@ -140,7 +140,7 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 		if (json->isObject()) {
 			Common::JSONObject object = json->asObject();
 
-			//debug("%s", json->stringify(true).c_str());
+			//debug(9, "%s", json->stringify(true).c_str());
 
 			if (object.contains("error") || object.contains("error_summary")) {
 				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
@@ -163,19 +163,19 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 				if (object.contains("session_id"))
 					_sessionId = object.getVal("session_id")->asString();
 				else
-					warning("no session_id found in Dropbox's response");
+					warning("DropboxUploadRequest: no session_id found");
 				needsFinishRequest = true;
 			}
 		}
 
 		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {
-			warning("no file info to return");
+			warning("DropboxUploadRequest: no file info to return");
 			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");
+		warning("DropboxUploadRequest: null, not json");
 		finishError(error);
 	}
 
@@ -183,7 +183,6 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 }
 
 void DropboxUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
-	debug("partUploadedErrorCallback");
 	_workingRequest = nullptr;
 	if (_ignoreCallback)
 		return;
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index fe9704c..7eeee0c 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -129,7 +129,7 @@ void FolderDownloadRequest::downloadNextFile() {
 		if (_remoteDirectoryPath != "" && (_remoteDirectoryPath.lastChar() != '/' && _remoteDirectoryPath.lastChar() != '\\'))
 			localPath.erase(0, 1);
 	} else {
-		warning("Can't process the following paths:");
+		warning("FolderDownloadRequest: Can't process the following paths:");
 		warning("remote directory: %s", _remoteDirectoryPath.c_str());
 		warning("remote file under that directory: %s", remotePath.c_str());
 	}
@@ -139,7 +139,7 @@ void FolderDownloadRequest::downloadNextFile() {
 		else
 			localPath = _localDirectoryPath + "/" + localPath;
 	}
-	debug(9, "%s -> %s", remotePath.c_str(), localPath.c_str());
+	debug(9, "FolderDownloadRequest: %s -> %s", remotePath.c_str(), localPath.c_str());
 	_workingRequest = _storage->downloadById(
 		_currentFile.id(), localPath,
 		new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback),
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index af19019..7ae9dde 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -113,14 +113,14 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 
 	Common::JSONObject result = json->asObject();
 	if (!result.contains("access_token")) {
-		warning("Bad response, no token passed");
-		debug("%s", json->stringify().c_str());
+		warning("GoogleDriveStorage: bad response, no token passed");
+		debug(9, "%s", json->stringify().c_str());
 		if (callback)
 			(*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
 		if (!result.contains("refresh_token"))
-			warning("No refresh_token passed");
+			warning("GoogleDriveStorage: no refresh_token passed");
 		else
 			_refreshToken = result.getVal("refresh_token")->asString();
 		CloudMan.save(); //ask CloudManager to save our new refreshToken
@@ -143,8 +143,8 @@ void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
 }
 
 void GoogleDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
-	debug("Google Drive's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
+	debug(9, "GoogleDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug(9, "%s", error.response.c_str());
 	CloudMan.removeStorage(this);
 }
 
@@ -160,7 +160,7 @@ Common::String GoogleDriveStorage::name() const {
 void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("GoogleDriveStorage: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
@@ -201,7 +201,7 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("GoogleDriveStorage: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
@@ -241,7 +241,7 @@ Networking::Request *GoogleDriveStorage::streamFileById(Common::String id, Netwo
 }
 
 void GoogleDriveStorage::printInfo(StorageInfoResponse response) {
-	debug(9, "\nuser info:");
+	debug(9, "\nGoogleDriveStorage: user info:");
 	debug(9, "\tname: %s", response.value.name().c_str());
 	debug(9, "\temail: %s", response.value.email().c_str());
 	debug(9, "\tdisk usage: %lu/%lu", response.value.used(), response.value.available());
@@ -286,12 +286,12 @@ GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix)
 	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
-		warning("No access_token found");
+		warning("GoogleDriveStorage: no access_token found");
 		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
-		warning("No refresh_token found");
+		warning("GoogleDriveStorage: no refresh_token found");
 		return nullptr;
 	}
 
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 1b85850..99661c2 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -69,7 +69,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		//new token needed => request token & then retry original request
 		if (_stream) {
 			httpResponseCode = _stream->httpResponseCode();
-			debug(9, "code %ld", httpResponseCode);
+			debug(9, "GoogleDriveTokenRefresher: code = %ld", httpResponseCode);
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -79,12 +79,12 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String message;
 		if (error.contains("code") && error.getVal("code")->isIntegerNumber()) {
 			code = error.getVal("code")->asIntegerNumber();
-			debug(9, "code = %u", code);
+			debug(9, "GoogleDriveTokenRefresher: code = %u", code);
 		}
 
 		if (error.contains("message")) {
 			message = error.getVal("message")->asString();
-			debug(9, "message = %s", message.c_str());
+			debug(9, "GoogleDriveTokenRefresher: message = %s", message.c_str());
 		}
 
 		if (code == 401 || message == "Invalid Credentials")
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index bd9d879..14f43ee 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -305,13 +305,13 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 		}
 
 		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
-			warning("no file info to return");
+			warning("GoogleDriveUploadRequest: no file info to return");
 			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");
+		warning("GoogleDriveUploadRequest: null, not json");
 		finishError(error);
 	}
 
diff --git a/backends/cloud/id/idresolveidrequest.cpp b/backends/cloud/id/idresolveidrequest.cpp
index 6742a58..e8589fc 100644
--- a/backends/cloud/id/idresolveidrequest.cpp
+++ b/backends/cloud/id/idresolveidrequest.cpp
@@ -70,16 +70,16 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 		return;
 
 	Common::String currentLevelName = _requestedPath;
-	///debug("'%s'", currentLevelName.c_str());
+	///debug(9, "'%s'", currentLevelName.c_str());
 	if (_currentDirectory.size())
 		currentLevelName.erase(0, _currentDirectory.size());
 	if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\'))
 		currentLevelName.erase(0, 1);
-	///debug("'%s'", currentLevelName.c_str());
+	///debug(9, "'%s'", currentLevelName.c_str());
 	for (uint32 i = 0; i < currentLevelName.size(); ++i) {
 		if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
 			currentLevelName.erase(i);
-			///debug("'%s'", currentLevelName.c_str());
+			///debug(9, "'%s'", currentLevelName.c_str());
 			break;
 		}
 	}
@@ -90,7 +90,7 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 	path += currentLevelName;
 	bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
 
-	///debug("so, searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
+	///debug(9, "IdResolveIdRequest: searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
 
 	Common::Array<StorageFile> &files = response.value;
 	bool found = false;
@@ -100,7 +100,7 @@ void IdResolveIdRequest::listedDirectoryCallback(Storage::FileArrayResponse resp
 				_currentDirectory += "/";
 			_currentDirectory += files[i].name();
 			_currentDirectoryId = files[i].id();
-			///debug("found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
+			///debug(9, "IdResolveIdRequest: found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
 			listNextDirectory(files[i]);
 			found = true;
 			break;
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index 857e3fb..36f2df6 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -36,7 +36,7 @@ namespace Id {
 IdStorage::~IdStorage() {}
 
 void IdStorage::printFiles(FileArrayResponse response) {
-	debug(9, "files:");
+	debug(9, "IdStorage: files:");
 	Common::Array<StorageFile> &files = response.value;
 	for (uint32 i = 0; i < files.size(); ++i) {
 		debug(9, "\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
@@ -47,11 +47,11 @@ void IdStorage::printFiles(FileArrayResponse response) {
 }
 
 void IdStorage::printBool(BoolResponse response) {
-	debug(9, "bool: %s", response.value ? "true" : "false");
+	debug(9, "IdStorage: bool: %s", response.value ? "true" : "false");
 }
 
 void IdStorage::printFile(UploadResponse response) {
-	debug(9, "\nuploaded file info:");
+	debug(9, "\nIdStorage: uploaded file info:");
 	debug(9, "\tid: %s", response.value.path().c_str());
 	debug(9, "\tname: %s", response.value.name().c_str());
 	debug(9, "\tsize: %u", response.value.size());
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index a31fd02..4d6d8fc 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -102,7 +102,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("OneDriveCreateDirectoryRequest: NULL passed instead of JSON");
 		finishError(error);
 		return;
 	}
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index af5d9f1..1008cab 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -113,8 +113,8 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 
 	Common::JSONObject result = json->asObject();
 	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
-		warning("Bad response, no token or user_id passed");
-		debug("%s", json->stringify().c_str());
+		warning("OneDriveStorage: bad response, no token or user_id passed");
+		debug(9, "%s", json->stringify().c_str());
 		if (callback)
 			(*callback)(BoolResponse(nullptr, false));
 	} else {
@@ -141,8 +141,8 @@ void OneDriveStorage::codeFlowComplete(BoolResponse response) {
 }
 
 void OneDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
-	debug("OneDrive's code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
+	debug(9, "OneDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug(9, "%s", error.response.c_str());
 	CloudMan.removeStorage(this);
 }
 
@@ -159,7 +159,7 @@ Common::String OneDriveStorage::name() const {
 void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("NULL passed instead of JSON");
+		warning("OneDriveStorage: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
@@ -199,7 +199,7 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 
 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
 	if (!response.value) {
-		warning("fileInfoCallback: NULL");
+		warning("OneDriveStorage::fileInfoCallback: NULL, not JSON");
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 		return;
@@ -214,8 +214,8 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 				new Networking::NetworkReadStream(url, nullptr, "")
 			));
 	} else {
-		warning("downloadUrl not found in passed JSON");
-		debug("%s", response.value->stringify().c_str());
+		warning("OneDriveStorage: downloadUrl not found in passed JSON");
+		debug(9, "%s", response.value->stringify().c_str());
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 	}
@@ -257,17 +257,17 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
 	loadKeyAndSecret();
 
 	if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
-		warning("No access_token found");
+		warning("OneDriveStorage: no access_token found");
 		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
-		warning("No user_id found");
+		warning("OneDriveStorage: no user_id found");
 		return nullptr;
 	}
 
 	if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
-		warning("No refresh_token found");
+		warning("OneDriveStorage: no refresh_token found");
 		return nullptr;
 	}
 
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index f759759..5e3bce9 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -69,7 +69,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		//new token needed => request token & then retry original request
 		if (_stream) {
 			httpResponseCode = _stream->httpResponseCode();
-			debug(9, "code %ld", httpResponseCode);
+			debug(9, "OneDriveTokenRefresher: code = %ld", httpResponseCode);
 		}
 
 		Common::JSONObject error = result.getVal("error")->asObject();
@@ -78,12 +78,12 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		Common::String code, message;
 		if (error.contains("code")) {
 			code = error.getVal("code")->asString();
-			debug(9, "code = %s", code.c_str());
+			debug(9, "OneDriveTokenRefresher: code = %s", code.c_str());
 		}
 
 		if (error.contains("message")) {
 			message = error.getVal("message")->asString();
-			debug(9, "message = %s", message.c_str());
+			debug(9, "OneDriveTokenRefresher: message = %s", message.c_str());
 		}
 
 		//determine whether token refreshing would help in this situation
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index ef1507d..172266c 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -130,7 +130,7 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 			Common::JSONObject object = json->asObject();
 
 			if (object.contains("error")) {
-				warning("OneDrive returned error: %s", json->stringify(true).c_str());
+				warning("OneDriveUploadRequest: error: %s", json->stringify(true).c_str());
 				error.response = json->stringify(true);
 				finishError(error);
 				delete json;
@@ -150,18 +150,18 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 				if (object.contains("uploadUrl"))
 					_uploadUrl = object.getVal("uploadUrl")->asString();
 				else
-					warning("no uploadUrl found in OneDrive's response");
+					warning("OneDriveUploadRequest: no uploadUrl found");
 			}
 		}
 
 		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
-			warning("no file info to return");
+			warning("OneDriveUploadRequest: no file info to return");
 			finishUpload(StorageFile(_savePath, 0, 0, false));
 		} else {
 			uploadNextPart();
 		}
 	} else {
-		warning("null, not json");
+		warning("OneDriveUploadRequest: null, not json");
 		finishError(error);
 	}
 
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 7776804..889c6b9 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -125,11 +125,11 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
 			_filesToUpload.push_back(i->_key);
 	}
 
-	debug(9, "\ndownload files:");
+	debug(9, "\nSavesSyncRequest: download files:");
 	for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
 		debug(9, "%s", _filesToDownload[i].name().c_str());
 	}
-	debug(9, "\nupload files:");
+	debug(9, "\nSavesSyncRequest: upload files:");
 	for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
 		debug(9, "%s", _filesToUpload[i].c_str());
 	}
@@ -190,7 +190,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 	Common::String dir = _storage->savesDirectoryPath();
 	if (dir.lastChar() == '/')
 		dir.deleteLastChar();
-	debug(9, "creating %s", dir.c_str());
+	debug(9, "SavesSyncRequest: creating %s", dir.c_str());
 	_workingRequest = _storage->createDirectory(
 		dir,
 		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
@@ -238,7 +238,7 @@ void SavesSyncRequest::downloadNextFile() {
 
 	sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
 
-	debug(9, "downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
+	debug(9, "SavesSyncRequest: downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
 	_workingRequest = _storage->downloadById(
 		_currentDownloadingFile.id(),
 		DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
@@ -289,7 +289,7 @@ void SavesSyncRequest::uploadNextFile() {
 	_currentUploadingFile = _filesToUpload.back();
 	_filesToUpload.pop_back();
 
-	debug(9, "uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
+	debug(9, "SavesSyncRequest: uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
 	if (_storage->uploadStreamSupported()) {
 		_workingRequest = _storage->upload(
 			_storage->savesDirectoryPath() + _currentUploadingFile,
@@ -370,7 +370,7 @@ Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
 }
 
 void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
-	debug("SavesSync::finishError");
+	debug(9, "SavesSync::finishError");
 	//if we were downloading a file - remember the name
 	//and make the Request close() it, so we can delete it
 	Common::String name = _currentDownloadingFile.name();
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index b085401..910d80d 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -42,8 +42,8 @@ Networking::ErrorCallback Storage::getErrorPrintingCallback() {
 }
 
 void Storage::printErrorResponse(Networking::ErrorResponse error) {
-	debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
-	debug("%s", error.response.c_str());
+	debug(9, "Storage: error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+	debug(9, "%s", error.response.c_str());
 }
 
 Networking::Request *Storage::addRequest(Networking::Request *request) {
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
index 1d5fd0d..f3dc91a 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -194,7 +194,7 @@ void ConnectionManager::processTransfers() {
 		if (curlMsg->msg == CURLMSG_DONE) {
 			debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
 		} else {
-			debug("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
+			warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
 		}
 
 		curl_multi_remove_handle(_multi, easyHandle);
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 7db56cf..aac89a1 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -68,7 +68,7 @@ void CurlJsonRequest::handle() {
 		uint32 readBytes = _stream->read(_buffer, CURL_JSON_REQUEST_BUFFER_SIZE);
 		if (readBytes != 0)
 			if (_contentsStream.write(_buffer, readBytes) != readBytes)
-				warning("MemoryWriteStreamDynamic was unable to write all the bytes");
+				warning("CurlJsonRequest: unable to write all the bytes into MemoryWriteStreamDynamic");
 
 		if (_stream->eos()) {
 			char *contents = getPreparedContents();
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
index 2d5bb1f..64fa347 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/curl/curlrequest.cpp
@@ -52,7 +52,7 @@ void CurlRequest::handle() {
 
 	if (_stream && _stream->eos()) {
 		if (_stream->httpResponseCode() != 200) {
-			warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+			warning("CurlRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 			ErrorResponse error(this, false, true, "", _stream->httpResponseCode());
 			finishError(error);
 			return;
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 5c760d9..21f2689 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -129,7 +129,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 		);
 
 		if (code != CURL_FORMADD_OK)
-			debug("field failed formadd");
+			warning("NetworkReadStream: field curl_formadd('%s') failed", i->_key.c_str());
 	}
 
 	for (Common::HashMap<Common::String, Common::String>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
@@ -142,7 +142,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 		);
 
 		if (code != CURL_FORMADD_OK)
-			debug("file failed formadd");
+			warning("NetworkReadStream: file curl_formadd('%s') failed", i->_key.c_str());
 	}
 
 	curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp
index 0026d0e..dab38ba 100644
--- a/backends/networking/sdl_net/client.cpp
+++ b/backends/networking/sdl_net/client.cpp
@@ -62,7 +62,7 @@ void Client::open(SDLNet_SocketSet set, TCPsocket socket) {
 	if (set) {
 		int numused = SDLNet_TCP_AddSocket(set, socket);
 		if (numused == -1) {
-			error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
+			error("Client: SDLNet_AddSocket: %s\n", SDLNet_GetError());
 		}
 	}
 }
@@ -79,13 +79,13 @@ bool Client::readMoreIfNeeded() {
 
 	int bytes = SDLNet_TCP_Recv(_socket, _buffer, CLIENT_BUFFER_SIZE);
 	if (bytes <= 0) {
-		warning("Client::readHeaders recv fail");
+		warning("Client::readMoreIfNeeded: recv fail");
 		close();
 		return false;
 	}
 
 	if (_stream->write(_buffer, bytes) != bytes) {
-		warning("failed to write() into MemoryReadWriteStream");
+		warning("Client::readMoreIfNeeded: failed to write() into MemoryReadWriteStream");
 		close();
 		return false;
 	}
@@ -146,7 +146,7 @@ void Client::close() {
 		if (_socket) {
 			int numused = SDLNet_TCP_DelSocket(_set, _socket);
 			if (numused == -1)
-				error("SDLNet_DelSocket: %s\n", SDLNet_GetError());
+				error("Client: SDLNet_DelSocket: %s\n", SDLNet_GetError());
 		}
 		_set = nullptr;
 	}
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 75dbea0..8a40aed 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -96,14 +96,14 @@ void LocalWebserver::start() {
 	// Create a listening TCP socket
 	IPaddress ip;
 	if (SDLNet_ResolveHost(&ip, NULL, _serverPort) == -1) {
-		error("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		error("LocalWebserver: SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	}
 
 	resolveAddress(&ip);
 
 	_serverSocket = SDLNet_TCP_Open(&ip);
 	if (!_serverSocket) {
-		warning("SDLNet_TCP_Open: %s", SDLNet_GetError());
+		warning("LocalWebserver: SDLNet_TCP_Open: %s", SDLNet_GetError());
 		stopTimer();
 		g_system->displayMessageOnOSD(_("Failed to start local webserver.\nCheck whether selected port is not used by another application and try again."));
 		_handleMutex.unlock();
@@ -113,12 +113,12 @@ void LocalWebserver::start() {
 	// Create a socket set
 	_set = SDLNet_AllocSocketSet(MAX_CONNECTIONS + 1); //one more for our server socket
 	if (!_set) {
-		error("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
+		error("LocalWebserver: SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
 	}
 
 	int numused = SDLNet_TCP_AddSocket(_set, _serverSocket);
 	if (numused == -1) {
-		error("SDLNet_AddSocket: %s\n", SDLNet_GetError());
+		error("LocalWebserver: SDLNet_AddSocket: %s\n", SDLNet_GetError());
 	}
 	_handleMutex.unlock();
 }
@@ -183,7 +183,7 @@ void LocalWebserver::handle() {
 	_handleMutex.lock();
 	int numready = SDLNet_CheckSockets(_set, 0);
 	if (numready == -1) {
-		error("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
+		error("LocalWebserver: SDLNet_CheckSockets: %s\n", SDLNet_GetError());
 	} else if (numready) {
 		acceptClient();
 	}
@@ -268,11 +268,11 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 	// default way (might work everywhere, surely works on Windows)
 	const char *name = SDLNet_ResolveIP(ip);
 	if (name == NULL) {
-		warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+		warning("LocalWebserver: SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 	} else {
 		IPaddress localIp;
 		if (SDLNet_ResolveHost(&localIp, name, _serverPort) == -1) {
-			warning("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+			warning("LocalWebserver: SDLNet_ResolveHost: %s\n", SDLNet_GetError());
 		} else {
 			_address = Common::String::format(
 				"http://%u.%u.%u.%u:%u/",
@@ -284,7 +284,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 
 	// check that our trick worked
 	if (_address.contains("/127.0.0.1:") || _address.contains("localhost") || _address.contains("/0.0.0.0:"))
-		warning("Failed to resolve IP with the default way");
+		warning("LocalWebserver: Failed to resolve IP with the default way");
 	else
 		return;
 
@@ -307,7 +307,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 			tmpAddrPtr = &((struct sockaddr_in *)i->ifa_addr)->sin_addr;
 			char addressBuffer[INET_ADDRSTRLEN];
 			inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
-			debug("%s IP Address %s", i->ifa_name, addressBuffer);
+			debug(9, "%s IP Address %s", i->ifa_name, addressBuffer);
 			addr = addressBuffer;
 		}
 
@@ -317,7 +317,7 @@ void LocalWebserver::resolveAddress(void *ipAddress) {
 			tmpAddrPtr = &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr;
 			char addressBuffer[INET6_ADDRSTRLEN];
 			inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
-			debug("%s IP Address %s", i->ifa_name, addressBuffer);
+			debug(9, "%s IP Address %s", i->ifa_name, addressBuffer);
 			addr = addressBuffer;
 		}
 		*/
diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 828f583..0e4cc9a 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -138,7 +138,7 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
 
 void Reader::handleFirstHeaders(Common::String headers) {
 	if (!_boundary.empty()) {
-		warning("handleFirstHeaders() called when first headers were already handled");
+		warning("Reader: handleFirstHeaders() called when first headers were already handled");
 		return;
 	}
 
@@ -252,7 +252,7 @@ void Reader::parseQueryParameters() {
 		} else {
 			if (_query[i] == '&') {
 				if (_queryParameters.contains(key))
-					warning("Query parameter \"%s\" is already set!", key.c_str());
+					warning("Reader: query parameter \"%s\" is already set!", key.c_str());
 				else
 					_queryParameters[key] = LocalWebserver::urlDecode(value);
 				readingKey = true;
@@ -265,7 +265,7 @@ void Reader::parseQueryParameters() {
 
 	if (!key.empty()) {
 		if (_queryParameters.contains(key))
-			warning("Query parameter \"%s\" is already set!", key.c_str());
+			warning("Reader: query parameter \"%s\" is already set!", key.c_str());
 		else
 			_queryParameters[key] = LocalWebserver::urlDecode(value);
 	}
@@ -410,9 +410,9 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
 		if (bts == "--")
 			_allContentRead = true;
 		else if (bts != "\r\n")
-			warning("strange bytes: \"%s\"", bts.c_str());
+			warning("Reader: strange bytes: \"%s\"", bts.c_str());
 	} else {
-		warning("strange ending");
+		warning("Reader: strange ending");
 		_allContentRead = true;
 	}
 
diff --git a/gui/options.cpp b/gui/options.cpp
index 603fc23..f2ae946 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1950,8 +1950,8 @@ void GlobalOptionsDialog::storageListDirectoryCallback(Cloud::Storage::ListDirec
 }
 
 void GlobalOptionsDialog::storageErrorCallback(Networking::ErrorResponse response) {
-	debug("error response (%s, %ld):", (response.failed ? "failed" : "interrupted"), response.httpResponseCode);
-	debug("%s", response.response.c_str());
+	debug(9, "GlobalOptionsDialog: error response (%s, %ld):", (response.failed ? "failed" : "interrupted"), response.httpResponseCode);
+	debug(9, "%s", response.response.c_str());
 
 	if (!response.interrupted)
 		g_system->displayMessageOnOSD(_("Request failed.\nCheck your Internet connection."));


Commit: c9b819b577e9e406fd8ac85773c82101b98766c2
    https://github.com/scummvm/scummvm/commit/c9b819b577e9e406fd8ac85773c82101b98766c2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Make Options dialog stop LocalServer on close

Commit also adds a fix for StorageWizardDialog, where LocalServer was
used even if USE_SDL_NET was undefined.

Changed paths:
    gui/options.cpp
    gui/storagewizarddialog.cpp



diff --git a/gui/options.cpp b/gui/options.cpp
index f2ae946..0fe4e9f 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1509,6 +1509,11 @@ void GlobalOptionsDialog::close() {
 #endif
 #endif
 	}
+#ifdef USE_SDL_NET
+	if (LocalServer.isRunning()) {
+		LocalServer.stop();
+	}
+#endif
 	OptionsDialog::close();
 }
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index e3bac98..d4ad3e3 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -100,19 +100,23 @@ void StorageWizardDialog::open() {
 		}
 	}
 
+#ifdef USE_SDL_NET
 	if (Cloud::CloudManager::couldUseLocalServer()) {
 		_stopServerOnClose = !LocalServer.isRunning();
 		LocalServer.start();
 		LocalServer.indexPageHandler().setTarget(this);
 	}
+#endif
 }
 
 void StorageWizardDialog::close() {
+#ifdef USE_SDL_NET
 	if (Cloud::CloudManager::couldUseLocalServer()) {
 		if (_stopServerOnClose)
 			LocalServer.stopOnIdle();
 		LocalServer.indexPageHandler().setTarget(nullptr);
 	}
+#endif
 	Dialog::close();
 }
 


Commit: 86f7b75dd7436322f90b6633739b96f92dec16a2
    https://github.com/scummvm/scummvm/commit/86f7b75dd7436322f90b6633739b96f92dec16a2
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix SDL_Net-related errors

Checked by rebuilding ScummVM without SDL_Net in MinGW.

Also fixes StorageWizardDialog's warning about _stopServerOnClose.

Changed paths:
    gui/options.cpp
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h



diff --git a/gui/options.cpp b/gui/options.cpp
index 0fe4e9f..1e9a8c2 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1319,7 +1319,11 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_runServerButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
 	_serverInfoLabel = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerInfoLabel", _("Not running"));
 
+#ifdef USE_SDL_NET
 	uint32 port = Networking::LocalWebserver::getPort();
+#else
+	uint32 port = 0; // the following widgets are hidden anyway
+#endif
 	_serverPortDesc = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortDesc", _("Server's port:"), _("Which port is used by server\nAuth with server is not available with non-default port"));
 	_serverPort = new EditTextWidget(container, "GlobalOptions_Cloud_Container.ServerPortEditText", Common::String::format("%u", port), 0);
 	_serverPortClearButton = addClearButton(container, "GlobalOptions_Cloud_Container.ServerPortClearButton", kServerPortClearCmd);
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index d4ad3e3..9e522fb 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -41,7 +41,10 @@ enum {
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
-	Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false), _stopServerOnClose(false) {
+	Dialog("GlobalOptions_Cloud_ConnectionWizard"), _storageId(storageId), _close(false) {
+#ifdef USE_SDL_NET
+	_stopServerOnClose = false;
+#endif
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
 	Common::String headline = Common::String::format(_("%s Storage Connection Wizard"), CloudMan.listStorages()[_storageId].c_str());
@@ -195,10 +198,12 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		close();
 		break;
 	}
+#ifdef USE_SDL_NET
 	case kStorageCodePassedCmd:
 		CloudMan.connectStorage(_storageId, LocalServer.indexPageHandler().code());
 		_close = true;
 		break;
+#endif
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index 6b00d60..cc70f86 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -46,7 +46,9 @@ class StorageWizardDialog : public Dialog {
 	StaticTextWidget *_messageWidget;
 	ButtonWidget *_connectWidget;
 	bool _close;
+#ifdef USE_SDL_NET
 	bool _stopServerOnClose;
+#endif
 
 	/** Return short scummvm.org URL for user to navigate to. */
 	Common::String getUrl() const;


Commit: b8fae56c6733cdb5f6e4f64266ca61105eb3155d
    https://github.com/scummvm/scummvm/commit/b8fae56c6733cdb5f6e4f64266ca61105eb3155d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TESTBED: Fix a few Cloud warnings

Changed paths:
    engines/testbed/cloud.cpp



diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index 775ae10..a2d6273 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -118,7 +118,7 @@ void CloudTests::directoryListedCallback(Cloud::Storage::FileArrayResponse respo
 		Testsuite::logPrintf("Info! %u directories listed, first one is '%s'\n", directories, directory.c_str());
 	} else {
 		Testsuite::logPrintf("Info! %u directories and %u files listed\n", directories, files);
-		Testsuite::logPrintf("Info! First directory is '%u' and first file is '%s'\n", directory.c_str(), file.c_str());
+		Testsuite::logPrintf("Info! First directory is '%s' and first file is '%s'\n", directory.c_str(), file.c_str());
 	}
 }
 
@@ -134,7 +134,7 @@ void CloudTests::directoryCreatedCallback(Cloud::Storage::BoolResponse response)
 void CloudTests::fileUploadedCallback(Cloud::Storage::UploadResponse response) {
 	ConfParams.setCloudTestCallbackCalled(true);
 	Testsuite::logPrintf("Info! Uploaded file into '%s'\n", response.value.path().c_str());
-	Testsuite::logPrintf("Info! It's id = '%s' and size = '%lu'\n", response.value.id().c_str(), response.value.size());
+	Testsuite::logPrintf("Info! It's id = '%s' and size = '%u'\n", response.value.id().c_str(), response.value.size());
 }
 
 void CloudTests::fileDownloadedCallback(Cloud::Storage::BoolResponse response) {
@@ -151,7 +151,7 @@ void CloudTests::directoryDownloadedCallback(Cloud::Storage::FileArrayResponse r
 	if (response.value.size() == 0) {
 		Testsuite::logPrintf("Info! Directory is downloaded successfully!\n");
 	} else {
-		Testsuite::logPrintf("Warning! %lu files were not downloaded during folder downloading!\n", response.value.size());
+		Testsuite::logPrintf("Warning! %u files were not downloaded during folder downloading!\n", response.value.size());
 	}
 }
 


Commit: b665fc933d7d1c9e32820e5a3f24d0e3456a0ff7
    https://github.com/scummvm/scummvm/commit/b665fc933d7d1c9e32820e5a3f24d0e3456a0ff7
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
ALL: Make simpleSaveNames() a MetaEngineFeature

Added it into hasFeature() of all engines which returned `true` in
simpleSaveNames() before.

As mentioned in #788, SCI is not always using simple names, so it
doesn't have such feature now.

Changed paths:
    engines/access/detection.cpp
    engines/adl/detection.cpp
    engines/agi/detection.cpp
    engines/agos/detection.cpp
    engines/avalanche/detection.cpp
    engines/bbvs/detection.cpp
    engines/cge/detection.cpp
    engines/cge2/detection.cpp
    engines/cine/detection.cpp
    engines/cruise/detection.cpp
    engines/draci/detection.cpp
    engines/drascula/detection.cpp
    engines/dreamweb/detection.cpp
    engines/fullpipe/detection.cpp
    engines/gnap/detection.cpp
    engines/groovie/detection.cpp
    engines/hopkins/detection.cpp
    engines/hugo/detection.cpp
    engines/kyra/detection.cpp
    engines/lab/detection.cpp
    engines/lure/detection.cpp
    engines/mads/detection.cpp
    engines/metaengine.h
    engines/mohawk/detection.cpp
    engines/mortevielle/detection.cpp
    engines/neverhood/detection.cpp
    engines/parallaction/detection.cpp
    engines/pegasus/detection.cpp
    engines/prince/detection.cpp
    engines/prince/detection.h
    engines/prince/saveload.cpp
    engines/queen/detection.cpp
    engines/saga/detection.cpp
    engines/sci/detection.cpp
    engines/scumm/detection.cpp
    engines/sherlock/detection.cpp
    engines/sky/detection.cpp
    engines/sword1/detection.cpp
    engines/sword2/sword2.cpp
    engines/sword25/detection.cpp
    engines/teenagent/detection.cpp
    engines/tinsel/detection.cpp
    engines/toltecs/detection.cpp
    engines/tony/detection.cpp
    engines/toon/detection.cpp
    engines/touche/detection.cpp
    engines/tsage/detection.cpp
    engines/tucker/detection.cpp
    engines/voyeur/detection.cpp
    engines/wage/detection.cpp
    engines/wintermute/detection.cpp
    engines/zvision/detection.cpp
    gui/saveload-dialog.cpp



diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp
index 435f0d1..3e70de3 100644
--- a/engines/access/detection.cpp
+++ b/engines/access/detection.cpp
@@ -100,7 +100,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -112,7 +111,8 @@ bool AccessMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Access::AccessEngine::hasFeature(EngineFeature f) const {
@@ -172,8 +172,6 @@ SaveStateList AccessMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool AccessMetaEngine::simpleSaveNames() const { return true; }
-
 int AccessMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
index 7031a58..af51f95 100644
--- a/engines/adl/detection.cpp
+++ b/engines/adl/detection.cpp
@@ -175,7 +175,6 @@ public:
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	int getMaximumSaveSlot() const { return 'O' - 'A'; }
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	void removeSaveState(const char *target, int slot) const;
 
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
@@ -190,6 +189,7 @@ bool AdlMetaEngine::hasFeature(MetaEngineFeature f) const {
 	case kSavesSupportThumbnail:
 	case kSavesSupportCreationDate:
 	case kSavesSupportPlayTime:
+	case kSimpleSavesNames:
 		return true;
 	default:
 		return false;
@@ -290,8 +290,6 @@ SaveStateList AdlMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool AdlMetaEngine::simpleSaveNames() const { return true; }
-
 void AdlMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.s%02d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(fileName);
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index dc0dbbd..5cb239f 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -216,7 +216,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -232,7 +231,8 @@ bool AgiMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSavesSupportMetaInfo) ||
 	    (f == kSavesSupportThumbnail) ||
 	    (f == kSavesSupportCreationDate) ||
-	    (f == kSavesSupportPlayTime);
+	    (f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool AgiBase::hasFeature(EngineFeature f) const {
@@ -324,8 +324,6 @@ SaveStateList AgiMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool AgiMetaEngine::simpleSaveNames() const { return true; }
-
 int AgiMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void AgiMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index dc96eb6..dbc4ee9 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -120,13 +120,13 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 };
 
 bool AgosMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
-		(f == kSupportsListSaves);
+		(f == kSupportsListSaves) ||
+		(f == kSimpleSavesNames);
 }
 
 bool AGOS::AGOSEngine::hasFeature(EngineFeature f) const {
@@ -208,8 +208,6 @@ SaveStateList AgosMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool AgosMetaEngine::simpleSaveNames() const { return true; }
-
 int AgosMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 #if PLUGIN_ENABLED_DYNAMIC(AGOS)
diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp
index 392d0a0..def395b 100644
--- a/engines/avalanche/detection.cpp
+++ b/engines/avalanche/detection.cpp
@@ -83,7 +83,6 @@ public:
 
 	int getMaximumSaveSlot() const { return 99; }
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 };
@@ -100,7 +99,8 @@ bool AvalancheMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsDeleteSave) ||
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 SaveStateList AvalancheMetaEngine::listSaves(const char *target) const {
@@ -157,8 +157,6 @@ SaveStateList AvalancheMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool AvalancheMetaEngine::simpleSaveNames() const { return true; }
-
 void AvalancheMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(fileName);
diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp
index 9aca719..fa735c9 100644
--- a/engines/bbvs/detection.cpp
+++ b/engines/bbvs/detection.cpp
@@ -86,7 +86,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -98,7 +97,8 @@ bool BbvsMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsLoadingDuringStartup) ||
 	    (f == kSavesSupportMetaInfo) ||
 	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
+	    (f == kSavesSupportCreationDate) ||
+		(f == kSimpleSavesNames);
 }
 
 void BbvsMetaEngine::removeSaveState(const char *target, int slot) const {
@@ -136,8 +136,6 @@ SaveStateList BbvsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool BbvsMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor BbvsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String filename = Bbvs::BbvsEngine::getSavegameFilename(target, slot);
 	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index eb88b6c..0df1e87 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -131,7 +131,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -181,7 +180,8 @@ bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
 	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
+	    (f == kSavesSupportCreationDate) ||
+		(f == kSimpleSavesNames);
 }
 
 void CGEMetaEngine::removeSaveState(const char *target, int slot) const {
@@ -240,8 +240,6 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool CGEMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp
index d980f82..3701baa 100644
--- a/engines/cge2/detection.cpp
+++ b/engines/cge2/detection.cpp
@@ -127,7 +127,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -186,7 +185,8 @@ bool CGE2MetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
 		(f == kSupportsListSaves) ||
-		(f == kSupportsLoadingDuringStartup);
+		(f == kSupportsLoadingDuringStartup) ||
+		(f == kSimpleSavesNames);
 }
 
 int CGE2MetaEngine::getMaximumSaveSlot() const {
@@ -240,8 +240,6 @@ SaveStateList CGE2MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool CGE2MetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index 2d10b81..ec01e87 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -104,7 +104,6 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -174,8 +173,6 @@ SaveStateList CineMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool CineMetaEngine::simpleSaveNames() const { return false; }
-
 int CineMetaEngine::getMaximumSaveSlot() const { return 9; }
 
 void CineMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index 9e3ebb5..6f5d236 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -211,7 +211,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -255,8 +254,6 @@ SaveStateList CruiseMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool CruiseMetaEngine::simpleSaveNames() const { return false; }
-
 void CruiseMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Cruise::CruiseEngine::getSavegameFile(slot));
 }
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index 8a67981..65427bd 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -98,7 +98,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -148,8 +147,6 @@ SaveStateList DraciMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool DraciMetaEngine::simpleSaveNames() const { return false; }
-
 void DraciMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Draci::DraciEngine::getSavegameFile(slot));
 }
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 863ea98..3bc8069 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -326,7 +326,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -340,7 +339,8 @@ bool DrasculaMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 const ExtraGuiOptions DrasculaMetaEngine::getExtraGuiOptions(const Common::String &target) const {
@@ -383,8 +383,6 @@ SaveStateList DrasculaMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool DrasculaMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 
diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp
index abe1198..8e24c44 100644
--- a/engines/dreamweb/detection.cpp
+++ b/engines/dreamweb/detection.cpp
@@ -86,7 +86,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -152,8 +151,6 @@ SaveStateList DreamWebMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool DreamWebMetaEngine::simpleSaveNames() const { return false; }
-
 int DreamWebMetaEngine::getMaximumSaveSlot() const { return 99; }
 
 void DreamWebMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/fullpipe/detection.cpp b/engines/fullpipe/detection.cpp
index a183be8..6f92f19 100644
--- a/engines/fullpipe/detection.cpp
+++ b/engines/fullpipe/detection.cpp
@@ -90,7 +90,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const { return 8; }
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
@@ -135,8 +134,6 @@ SaveStateList FullpipeMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool FullpipeMetaEngine::simpleSaveNames() const { return false; }
-
 void FullpipeMetaEngine::removeSaveState(const char *target, int slot) const {
 	g_system->getSavefileManager()->removeSavefile(Fullpipe::getSavegameFile(slot));
 }
diff --git a/engines/gnap/detection.cpp b/engines/gnap/detection.cpp
index 523a8b6..d92a037 100644
--- a/engines/gnap/detection.cpp
+++ b/engines/gnap/detection.cpp
@@ -78,7 +78,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -90,7 +89,8 @@ bool GnapMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
-		(f == kSavesSupportCreationDate);
+		(f == kSavesSupportCreationDate) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Gnap::GnapEngine::hasFeature(EngineFeature f) const {
@@ -142,8 +142,6 @@ SaveStateList GnapMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool GnapMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor GnapMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 22ced10..b12e264 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -352,7 +352,6 @@ public:
 
 	bool hasFeature(MetaEngineFeature f) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -377,8 +376,6 @@ SaveStateList GroovieMetaEngine::listSaves(const char *target) const {
 	return SaveLoad::listValidSaves(target);
 }
 
-bool GroovieMetaEngine::simpleSaveNames() const { return false; }
-
 int GroovieMetaEngine::getMaximumSaveSlot() const {
 	return SaveLoad::getMaximumSlot();
 }
diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp
index 84af4fc..041afec 100644
--- a/engines/hopkins/detection.cpp
+++ b/engines/hopkins/detection.cpp
@@ -117,7 +117,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -129,7 +128,8 @@ bool HopkinsMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Hopkins::HopkinsEngine::hasFeature(EngineFeature f) const {
@@ -183,8 +183,6 @@ SaveStateList HopkinsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool HopkinsMetaEngine::simpleSaveNames() const { return true; }
-
 int HopkinsMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index ed67eae..4e4746c 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -149,7 +149,6 @@ public:
 
 	int getMaximumSaveSlot() const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	void removeSaveState(const char *target, int slot) const;
 };
@@ -222,8 +221,6 @@ SaveStateList HugoMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool HugoMetaEngine::simpleSaveNames() const { return false; }
-
 SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 1dcfd08..70c7e7c 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -162,7 +162,6 @@ public:
 	bool hasFeature(MetaEngineFeature f) const;
 	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -174,7 +173,8 @@ bool KyraMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsLoadingDuringStartup) ||
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportThumbnail);
+	    (f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Kyra::KyraEngine_v1::hasFeature(EngineFeature f) const {
@@ -274,8 +274,6 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool KyraMetaEngine::simpleSaveNames() const { return true; }
-
 int KyraMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/lab/detection.cpp b/engines/lab/detection.cpp
index 2b2e57d..bf6d456 100644
--- a/engines/lab/detection.cpp
+++ b/engines/lab/detection.cpp
@@ -138,7 +138,6 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -152,7 +151,8 @@ bool LabMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Lab::LabEngine::hasFeature(EngineFeature f) const {
@@ -192,8 +192,6 @@ SaveStateList LabMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool LabMetaEngine::simpleSaveNames() const { return true; }
-
 int LabMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp
index 808cea4..690a358 100644
--- a/engines/lure/detection.cpp
+++ b/engines/lure/detection.cpp
@@ -212,7 +212,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -267,8 +266,6 @@ SaveStateList LureMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool LureMetaEngine::simpleSaveNames() const { return false; }
-
 int LureMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void LureMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp
index d5354c4..4fb8b82 100644
--- a/engines/mads/detection.cpp
+++ b/engines/mads/detection.cpp
@@ -155,7 +155,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -167,7 +166,8 @@ bool MADSMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool MADS::MADSEngine::hasFeature(EngineFeature f) const {
@@ -218,8 +218,6 @@ SaveStateList MADSMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool MADSMetaEngine::simpleSaveNames() const { return true; }
-
 int MADSMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 6afb122..568b66a 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -116,20 +116,6 @@ public:
 	}
 
 	/**
-	* Return whether engine's saves could be detected with
-	* "<target>.###" pattern and "###" corresponds to slot
-	* number.
-	*
-	* If that's not true or engine is using some unusual way
-	* of detecting saves and slot numbers, this should return
-	* false. In that case Save/Load dialog would be unavailable
-	* during cloud saves sync.
-	*
-	* @return	true, if "<target>.###" is OK for this engine
-	*/
-	virtual bool simpleSaveNames() const { return false; }
-
-	/**
 	 * Return a list of extra GUI options for the specified target.
 	 * If no target is specified, all of the available custom GUI options are
 	 * Returned for the plugin (used to set default values).
@@ -250,7 +236,19 @@ public:
 		 * the game till the save.
 		 * This flag may only be set when 'kSavesSupportMetaInfo' is set.
 		 */
-		kSavesSupportPlayTime
+		kSavesSupportPlayTime,
+
+		/**
+		* Feature is available if engine's saves could be detected
+		* with "<target>.###" pattern and "###" corresponds to slot
+		* number.
+		*
+		* If that's not true or engine is using some unusual way
+		* of detecting saves and slot numbers, this should be
+		* unavailable. In that case Save/Load dialog for engine's
+		* games is locked during cloud saves sync.
+		*/
+		kSimpleSavesNames
 	};
 
 	/**
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index d3c44a8..246d3ec 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -200,7 +200,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
 	SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 999; }
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -273,8 +272,6 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool MohawkMetaEngine::simpleSaveNames() const { return false; }
-
 void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
 
 	// Removing saved games is only supported in Myst/Riven currently.
diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp
index 2f5a790..7bc5339 100644
--- a/engines/mortevielle/detection.cpp
+++ b/engines/mortevielle/detection.cpp
@@ -72,7 +72,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 };
 
@@ -91,6 +90,7 @@ bool MortevielleMetaEngine::hasFeature(MetaEngineFeature f) const {
 	case kSavesSupportMetaInfo:
 	case kSavesSupportThumbnail:
 	case kSavesSupportCreationDate:
+	case kSimpleSavesNames:
 		return true;
 	default:
 		return false;
@@ -103,8 +103,6 @@ SaveStateList MortevielleMetaEngine::listSaves(const char *target) const {
 	return Mortevielle::SavegameManager::listSaves(target);
 }
 
-bool MortevielleMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor MortevielleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String filename = Mortevielle::MortevielleEngine::generateSaveFilename(target, slot);
 	return Mortevielle::SavegameManager::querySaveMetaInfos(filename);
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
index 903c437..0c0347e 100644
--- a/engines/neverhood/detection.cpp
+++ b/engines/neverhood/detection.cpp
@@ -214,7 +214,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -229,7 +228,8 @@ bool NeverhoodMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Neverhood::NeverhoodEngine::hasFeature(EngineFeature f) const {
@@ -284,8 +284,6 @@ SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool NeverhoodMetaEngine::simpleSaveNames() const { return true; }
-
 int NeverhoodMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index 989fc9d..4c52990 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -234,7 +234,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -295,8 +294,6 @@ SaveStateList ParallactionMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ParallactionMetaEngine::simpleSaveNames() const { return false; }
-
 int ParallactionMetaEngine::getMaximumSaveSlot() const { return 99; }
 
 void ParallactionMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
index 54cb4ca..161a133 100644
--- a/engines/pegasus/detection.cpp
+++ b/engines/pegasus/detection.cpp
@@ -148,7 +148,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 999; }
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -179,8 +178,6 @@ SaveStateList PegasusMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool PegasusMetaEngine::simpleSaveNames() const { return false; }
-
 void PegasusMetaEngine::removeSaveState(const char *target, int slot) const {
 	// See listSaves() for info on the pattern
 	Common::StringArray fileNames = Pegasus::PegasusEngine::listSaveFiles();
diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp
index 1c6f63a..ad75982 100644
--- a/engines/prince/detection.cpp
+++ b/engines/prince/detection.cpp
@@ -56,7 +56,8 @@ bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
 		(f == kSupportsListSaves) ||
-		(f == kSupportsLoadingDuringStartup);
+		(f == kSupportsLoadingDuringStartup) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Prince::PrinceEngine::hasFeature(EngineFeature f) const {
diff --git a/engines/prince/detection.h b/engines/prince/detection.h
index 39cfdd9..3076253 100644
--- a/engines/prince/detection.h
+++ b/engines/prince/detection.h
@@ -121,7 +121,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp
index 2855bdc..d3360ba 100644
--- a/engines/prince/saveload.cpp
+++ b/engines/prince/saveload.cpp
@@ -105,8 +105,6 @@ SaveStateList PrinceMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool PrinceMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp
index 3f8b97e..aed8b7d 100644
--- a/engines/queen/detection.cpp
+++ b/engines/queen/detection.cpp
@@ -444,7 +444,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const { return 99; }
 	virtual void removeSaveState(const char *target, int slot) const;
 
@@ -530,8 +529,6 @@ SaveStateList QueenMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool QueenMetaEngine::simpleSaveNames() const { return false; }
-
 void QueenMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String filename = Common::String::format("queen.s%02d", slot);
 
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 7f6e0a2..6fe4277 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -144,7 +144,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -158,7 +157,8 @@ bool SagaMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Saga::SagaEngine::hasFeature(EngineFeature f) const {
@@ -208,8 +208,6 @@ SaveStateList SagaMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool SagaMetaEngine::simpleSaveNames() const { return true; }
-
 int SagaMetaEngine::getMaximumSaveSlot() const { return MAX_SAVES - 1; }
 
 void SagaMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 08794a0..ad2b0f3 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -518,7 +518,6 @@ public:
 	const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -791,8 +790,6 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool SciMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int slotNr) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slotNr);
 	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 67344e8..e6740df 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -960,7 +960,6 @@ public:
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -975,7 +974,8 @@ bool ScummMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool ScummEngine::hasFeature(EngineFeature f) const {
@@ -1300,8 +1300,6 @@ SaveStateList ScummMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ScummMetaEngine::simpleSaveNames() const { return true; }
-
 void ScummMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String filename = ScummEngine::makeSavegameName(target, slot, false);
 	g_system->getSavefileManager()->removeSavefile(filename);
diff --git a/engines/sherlock/detection.cpp b/engines/sherlock/detection.cpp
index f54c6db..c6e632f 100644
--- a/engines/sherlock/detection.cpp
+++ b/engines/sherlock/detection.cpp
@@ -159,8 +159,6 @@ public:
 	 */
 	virtual SaveStateList listSaves(const char *target) const;
 
-	virtual bool simpleSaveNames() const;
-
 	/**
 	 * Returns the maximum number of allowed save slots
 	 */
@@ -201,7 +199,8 @@ bool SherlockMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Sherlock::SherlockEngine::hasFeature(EngineFeature f) const {
@@ -219,8 +218,6 @@ SaveStateList SherlockMetaEngine::listSaves(const char *target) const {
 	return Sherlock::SaveManager::getSavegameList(target);
 }
 
-bool SherlockMetaEngine::simpleSaveNames() const { return true; }
-
 int SherlockMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVEGAME_SLOTS;
 }
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index 802687b..d86689e 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -84,7 +84,6 @@ public:
 	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
 
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -248,8 +247,6 @@ SaveStateList SkyMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool SkyMetaEngine::simpleSaveNames() const { return false; }
-
 int SkyMetaEngine::getMaximumSaveSlot() const { return MAX_SAVE_GAMES; }
 
 void SkyMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index d9cc3cd..0edf856 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -91,7 +91,6 @@ public:
 	virtual GameDescriptor findGame(const char *gameid) const;
 	virtual GameList detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -264,8 +263,6 @@ SaveStateList SwordMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool SwordMetaEngine::simpleSaveNames() const { return false; }
-
 int SwordMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void SwordMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 3213a26..4f3caa4 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -97,7 +97,6 @@ public:
 	virtual GameDescriptor findGame(const char *gameid) const;
 	virtual GameList detectGames(const Common::FSList &fslist) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 
@@ -108,7 +107,8 @@ bool Sword2MetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 		(f == kSupportsListSaves) ||
 		(f == kSupportsLoadingDuringStartup) ||
-		(f == kSupportsDeleteSave);
+		(f == kSupportsDeleteSave) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Sword2::Sword2Engine::hasFeature(EngineFeature f) const {
@@ -257,8 +257,6 @@ SaveStateList Sword2MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool Sword2MetaEngine::simpleSaveNames() const { return true; }
-
 int Sword2MetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void Sword2MetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
index 1c4544c..c5f55b5 100644
--- a/engines/sword25/detection.cpp
+++ b/engines/sword25/detection.cpp
@@ -69,7 +69,6 @@ public:
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); }
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 };
 
 bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
@@ -110,8 +109,6 @@ SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool Sword25MetaEngine::simpleSaveNames() const { return false; }
-
 #if PLUGIN_ENABLED_DYNAMIC(SWORD25)
 	REGISTER_PLUGIN_DYNAMIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
 #else
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index a8d32e8..caa7bdb 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -149,8 +149,6 @@ public:
 		return saveList;
 	}
 
-	virtual bool simpleSaveNames() const { return false; }
-
 	virtual int getMaximumSaveSlot() const {
 		return MAX_SAVES - 1;
 	}
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 7d8b54e..d6bcfe5 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -101,7 +101,6 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -110,7 +109,8 @@ bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 		(f == kSupportsListSaves) ||
 		(f == kSupportsLoadingDuringStartup) ||
-		(f == kSupportsDeleteSave);
+		(f == kSupportsDeleteSave) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Tinsel::TinselEngine::hasFeature(EngineFeature f) const {
@@ -165,8 +165,6 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool TinselMetaEngine::simpleSaveNames() const { return true; }
-
 bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc;
 	if (gd) {
diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp
index 8da9106..cc27341 100644
--- a/engines/toltecs/detection.cpp
+++ b/engines/toltecs/detection.cpp
@@ -221,7 +221,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -235,7 +234,8 @@ bool ToltecsMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime);
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Toltecs::ToltecsEngine::hasFeature(EngineFeature f) const {
@@ -289,8 +289,6 @@ SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ToltecsMetaEngine::simpleSaveNames() const { return true; }
-
 int ToltecsMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp
index 5dda4c7..ec0b3e1 100644
--- a/engines/tony/detection.cpp
+++ b/engines/tony/detection.cpp
@@ -82,7 +82,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -142,8 +141,6 @@ SaveStateList TonyMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool TonyMetaEngine::simpleSaveNames() const { return false; }
-
 int TonyMetaEngine::getMaximumSaveSlot() const {
 	return 99;
 }
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index eac91a5..e93d676 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -148,7 +148,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -160,7 +159,8 @@ bool ToonMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
 	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
+	    (f == kSavesSupportCreationDate) ||
+		(f == kSimpleSavesNames);
 }
 
 void ToonMetaEngine::removeSaveState(const char *target, int slot) const {
@@ -213,8 +213,6 @@ SaveStateList ToonMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ToonMetaEngine::simpleSaveNames() const { return true; }
-
 SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index e2737d4..dcb58ff 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -155,7 +155,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -211,8 +210,6 @@ SaveStateList ToucheMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ToucheMetaEngine::simpleSaveNames() const { return false; }
-
 int ToucheMetaEngine::getMaximumSaveSlot() const {
 	return Touche::kMaxSaveStates - 1;
 }
diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp
index 716ac4a..e476391 100644
--- a/engines/tsage/detection.cpp
+++ b/engines/tsage/detection.cpp
@@ -95,6 +95,7 @@ public:
 		case kSavesSupportThumbnail:
 		case kSavesSupportCreationDate:
 		case kSavesSupportPlayTime:
+		case kSimpleSavesNames:
 			return true;
 		default:
 			return false;
@@ -145,8 +146,6 @@ public:
 		return saveList;
 	}
 
-	virtual bool simpleSaveNames() const { return true; }
-
 	virtual int getMaximumSaveSlot() const {
 		return MAX_SAVES - 1;
 	}
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index 227924c..2447e15 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -187,8 +187,6 @@ public:
 		return saveList;
 	}
 
-	virtual bool simpleSaveNames() const { return false; }
-
 	virtual int getMaximumSaveSlot() const {
 		return Tucker::kLastSaveSlot;
 	}
diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp
index 76668eb..eefe174 100644
--- a/engines/voyeur/detection.cpp
+++ b/engines/voyeur/detection.cpp
@@ -81,7 +81,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -93,7 +92,8 @@ bool VoyeurMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportThumbnail) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Voyeur::VoyeurEngine::hasFeature(EngineFeature f) const {
@@ -144,8 +144,6 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool VoyeurMetaEngine::simpleSaveNames() const { return true; }
-
 int VoyeurMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp
index 1069d74..778cd3c 100644
--- a/engines/wage/detection.cpp
+++ b/engines/wage/detection.cpp
@@ -70,7 +70,6 @@ public:
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 };
@@ -79,7 +78,8 @@ bool WageMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 		(f == kSupportsListSaves) ||
 		(f == kSupportsLoadingDuringStartup) ||
-		(f == kSupportsDeleteSave);
+		(f == kSupportsDeleteSave) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Wage::WageEngine::hasFeature(EngineFeature f) const {
@@ -137,8 +137,6 @@ SaveStateList WageMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool WageMetaEngine::simpleSaveNames() const { return true; }
-
 int WageMetaEngine::getMaximumSaveSlot() const { return 999; }
 
 void WageMetaEngine::removeSaveState(const char *target, int slot) const {
diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp
index f225cd3..4e8eab5 100644
--- a/engines/wintermute/detection.cpp
+++ b/engines/wintermute/detection.cpp
@@ -164,8 +164,6 @@ public:
 		return saves;
 	}
 
-	virtual bool simpleSaveNames() const { return false; }
-
 	int getMaximumSaveSlot() const {
 		return 100;
 	}
diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp
index 05dfb8b..5e535a9 100644
--- a/engines/zvision/detection.cpp
+++ b/engines/zvision/detection.cpp
@@ -75,7 +75,6 @@ public:
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
 	SaveStateList listSaves(const char *target) const;
-	virtual bool simpleSaveNames() const;
 	virtual int getMaximumSaveSlot() const;
 	void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
@@ -88,7 +87,8 @@ bool ZVisionMetaEngine::hasFeature(MetaEngineFeature f) const {
 		(f == kSupportsDeleteSave) ||
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
-		(f == kSavesSupportCreationDate);
+		(f == kSavesSupportCreationDate) ||
+		(f == kSimpleSavesNames);
 		//(f == kSavesSupportPlayTime);
 }
 
@@ -160,8 +160,6 @@ SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-bool ZVisionMetaEngine::simpleSaveNames() const { return true; }
-
 int ZVisionMetaEngine::getMaximumSaveSlot() const {
 	return 999;
 }
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 0bed5f4..ae3612f 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -245,7 +245,7 @@ void SaveLoadChooserDialog::handleTickle() {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
 			{
-				SaveLoadCloudSyncProgressDialog dialog(_metaEngine ? _metaEngine->simpleSaveNames() : false);
+				SaveLoadCloudSyncProgressDialog dialog(_metaEngine ? _metaEngine->hasFeature(MetaEngine::kSimpleSavesNames) : false);
 				CloudMan.setSyncTarget(&dialog);
 				int result = dialog.runModal();
 				if (result == kCancelSyncCmd) {
@@ -293,7 +293,7 @@ void SaveLoadChooserDialog::listSaves() {
 
 #ifdef USE_LIBCURL
 	//if there is Cloud support, add currently synced files as "locked" saves in the list
-	if (_metaEngine->simpleSaveNames()) {
+	if (_metaEngine->hasFeature(MetaEngine::kSimpleSavesNames)) {
 		Common::String pattern = _target + ".###";
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 		for (uint32 i = 0; i < files.size(); ++i) {


Commit: c74ba4652d497ddbdec4342029d94bc77387a705
    https://github.com/scummvm/scummvm/commit/c74ba4652d497ddbdec4342029d94bc77387a705
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add "Paste" button in StorageWizardDialog

It pastes clipboard contents as code into 8 fields of that dialog.
(Clipboard support works with SDL2 only.)

"Open URL" and "Paste" buttons are placed in the left column under the
picture (because there is no room for 4 buttons in the bottom row).

Commit also adds "dropbox.bmp", which is just a square 115x115 picture.
Such pictures are would be used as Storages logos in that dialog.

In lowres there is no left column, so all 4 buttons are in the same row.
None of them are visible, because they are overflowed. Container has to
be added to continue working on them.

Changed paths:
  A gui/themes/scummmodern/dropbox.bmp
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index ac209d6..e400e69 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -61,6 +61,7 @@ const char *const ThemeEngine::kImageStopSmallButton = "stopbtn_small.bmp";
 const char *const ThemeEngine::kImageEditSmallButton = "editbtn_small.bmp";
 const char *const ThemeEngine::kImageSwitchModeSmallButton = "switchbtn_small.bmp";
 const char *const ThemeEngine::kImageFastReplaySmallButton = "fastreplay_small.bmp";
+const char *const ThemeEngine::kImageDropboxLogo = "dropbox.bmp";
 
 struct TextDrawData {
 	const Graphics::Font *_fontPtr;
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 91f82b1..eb9b7da 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -250,6 +250,7 @@ public:
 	static const char *const kImageEditSmallButton; ///< Edit recording metadata in recorder onscreen dialog (for 320xY)
 	static const char *const kImageSwitchModeSmallButton; ///< Switch mode button in recorder onscreen dialog (for 320xY)
 	static const char *const kImageFastReplaySmallButton; ///< Fast playback mode button in recorder onscreen dialog (for 320xY)
+	static const char *const kImageDropboxLogo;      ///< Dropbox logo used in the StorageWizardDialog
 
 	/**
 	 * Graphics mode enumeration.
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 9e522fb..e04ca02 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -20,6 +20,13 @@
  *
  */
 
+#ifdef USE_SDL2
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_clipboard.h>
+#endif
+
 #include "gui/storagewizarddialog.h"
 #include "gui/gui-manager.h"
 #include "gui/message.h"
@@ -37,7 +44,8 @@ namespace GUI {
 enum {
 	kConnectCmd = 'Cnnt',
 	kCodeBoxCmd = 'CdBx',
-	kOpenUrlCmd = 'OpUr'
+	kOpenUrlCmd = 'OpUr',
+	kPasteCodeCmd = 'PsCd'
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
@@ -62,6 +70,7 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 	// Buttons
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
 	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
+	_pasteCodeWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.PasteCodeButton", _("Paste"), _("Pastes clipboard contents into fields"), kPasteCodeCmd);
 	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
 
 	if (Cloud::CloudManager::couldUseLocalServer()) {
@@ -72,7 +81,20 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 			_codeWidget[i]->setVisible(false);
 		_messageWidget->setVisible(false);
 		_connectWidget->setVisible(false);
+		_pasteCodeWidget->setVisible(false);
 	}
+
+#ifndef USE_SDL2
+	_pasteCodeWidget->setVisible(false);
+#endif
+        
+#ifndef DISABLE_FANCY_THEMES
+	if (g_gui.theme()->supportsImages() && g_system->getOverlayWidth() > 320) { // picture only in high-res
+		GraphicsWidget *gfx = new GraphicsWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Picture");
+		gfx->useThemeTransparency(true);
+		gfx->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDropboxLogo));
+	}
+#endif
 }
 
 void StorageWizardDialog::open() {
@@ -162,7 +184,7 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 			//the last 3 chars must be an encoded crc16
 			if (code.size() > 3) {
 				uint32 size = code.size();
-				uint32 gotcrc = decodeHashchar(code[size-3]) | (decodeHashchar(code[size-2]) << 6) | (decodeHashchar(code[size-1]) << 12);
+				uint32 gotcrc = decodeHashchar(code[size - 3]) | (decodeHashchar(code[size - 2]) << 6) | (decodeHashchar(code[size - 1]) << 12);
 				code.erase(size - 3);
 				uint32 crc = crc16(code);
 				ok = (crc == gotcrc);
@@ -183,6 +205,35 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		}
 		break;
 	}
+	case kPasteCodeCmd: {
+#ifdef USE_SDL2
+		if (SDL_HasClipboardText() == SDL_TRUE) {
+			char *text = SDL_GetClipboardText();
+			if (text != nullptr) {
+				Common::String message = text;
+				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;
+						}
+					}
+					_codeWidget[i]->setEditString(subcode);
+				}
+				handleCommand(sender, kCodeBoxCmd, data);
+				draw();
+			}
+		}
+#endif
+		break;
+	}
 	case kConnectCmd: {
 		Common::String code;
 		for (uint32 i = 0; i < CODE_FIELDS; ++i) {
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index cc70f86..b0d0999 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -44,6 +44,7 @@ class StorageWizardDialog : public Dialog {
 	uint32 _storageId;
 	EditTextWidget *_codeWidget[CODE_FIELDS];
 	StaticTextWidget *_messageWidget;
+	ButtonWidget *_pasteCodeWidget;
 	ButtonWidget *_connectWidget;
 	bool _close;
 #ifdef USE_SDL_NET
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 4a36e7d..42c6b10 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 2e37b00..3284f7d 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -637,9 +637,18 @@
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
-				<widget name = 'Picture'
-						type = 'OptionsLabel'
-				/>
+				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
+					<widget name = 'Picture'
+							width = '115'
+							height = '115'
+					/>
+					<widget name = 'OpenUrlButton'
+							type = 'Button'
+					/>
+					<widget name = 'PasteCodeButton'
+							type = 'Button'
+					/>
+				</layout>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Headline'
 							height = 'Globals.Line.Height'
@@ -704,9 +713,7 @@
 				<widget name = 'CancelButton'
 						type = 'Button'
 				/>
-				<widget name = 'OpenUrlButton'
-						type = 'Button'
-				/>
+				<space />
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 06ce6f6..7666aeb 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -709,6 +709,7 @@
 							height = 'Globals.Line.Height'
 					/>
 				<space size = '4' />
+				<widget name = 'Picture' width = '1' height = '1' />
 			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'CancelButton'
@@ -717,6 +718,9 @@
 				<widget name = 'OpenUrlButton'
 						type = 'Button'
 				/>
+				<widget name = 'PasteCodeButton'
+						type = 'Button'
+				/>
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index fcf35d9..b433aaa 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/dropbox.bmp b/gui/themes/scummmodern/dropbox.bmp
new file mode 100644
index 0000000..bfb9e90
Binary files /dev/null and b/gui/themes/scummmodern/dropbox.bmp differ
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index 31b194d..d7b35b1 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -119,6 +119,7 @@
 		<bitmap filename = 'editbtn_small.bmp'/>
 		<bitmap filename = 'switchbtn_small.bmp'/>
 		<bitmap filename = 'fastreplay_small.bmp'/>
+		<bitmap filename = 'dropbox.bmp'/>
 	</bitmaps>
 
 	<fonts>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 49876be..a242229 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -651,9 +651,18 @@
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
-				<widget name = 'Picture'
-						type = 'OptionsLabel'
-				/>
+				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
+					<widget name = 'Picture'
+							width = '115'
+							height = '115'
+					/>
+					<widget name = 'OpenUrlButton'
+							type = 'Button'
+					/>
+					<widget name = 'PasteCodeButton'
+							type = 'Button'
+					/>
+				</layout>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Headline'
 							height = 'Globals.Line.Height'
@@ -718,9 +727,7 @@
 				<widget name = 'CancelButton'
 						type = 'Button'
 				/>
-				<widget name = 'OpenUrlButton'
-						type = 'Button'
-				/>
+				<space />
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index e10ce4a..26f7fab 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -707,6 +707,7 @@
 							height = 'Globals.Line.Height'
 					/>
 				<space size = '4' />
+				<widget name = 'Picture' width = '1' height = '1' />
 			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'CancelButton'
@@ -715,6 +716,9 @@
 				<widget name = 'OpenUrlButton'
 						type = 'Button'
 				/>
+				<widget name = 'PasteCodeButton'
+						type = 'Button'
+				/>
 				<widget name = 'ConnectButton'
 						type = 'Button'
 				/>


Commit: f39b6ed4ac414d381ed0f46043586b26c05092a1
    https://github.com/scummvm/scummvm/commit/f39b6ed4ac414d381ed0f46043586b26c05092a1
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Container in StorageWizardDialog

It now looks fine in both 640x400 and 320x200!

Changed paths:
    gui/storagewizarddialog.cpp
    gui/storagewizarddialog.h
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index e04ca02..f1b1796 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -31,13 +31,14 @@
 #include "gui/gui-manager.h"
 #include "gui/message.h"
 #include "gui/widget.h"
+#include "gui/widgets/edittext.h"
+#include "gui/widgets/scrollcontainer.h"
 #include "backends/cloud/cloudmanager.h"
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif
 #include "backends/networking/browser/openurl.h"
 #include "common/translation.h"
-#include "widgets/edittext.h"
 
 namespace GUI {
 
@@ -45,7 +46,8 @@ enum {
 	kConnectCmd = 'Cnnt',
 	kCodeBoxCmd = 'CdBx',
 	kOpenUrlCmd = 'OpUr',
-	kPasteCodeCmd = 'PsCd'
+	kPasteCodeCmd = 'PsCd',
+	kStorageWizardContainerReflowCmd = 'SWCr',
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):
@@ -55,46 +57,42 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 #endif
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
 
+	ScrollContainerWidget *container = new ScrollContainerWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Container", kStorageWizardContainerReflowCmd);
+	container->setTarget(this);
+
 	Common::String headline = Common::String::format(_("%s Storage Connection Wizard"), CloudMan.listStorages()[_storageId].c_str());
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Headline", headline);
+	_headlineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.Headline", headline);
 
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.NavigateLine", _s("Navigate to the following URL:"));
-	new StaticTextWidget(this, "GlobalOptions_Cloud_ConnectionWizard.URLLine", getUrl());
+	_navigateLineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.NavigateLine", _s("Navigate to the following URL:"));
+	_urlLineWidget = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.URLLine", getUrl());
 
-	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':"));
+	_returnLine1 = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine1", _s("Obtain the code from the storage, enter it"));
+	_returnLine2 = new StaticTextWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.ReturnLine2", _s("in the following field and press 'Connect':"));
 	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", "");
+		_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", "");
 
 	// Buttons
-	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.CancelButton", _("Cancel"), 0, kCloseCmd);
-	new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.OpenUrlButton", _("Open URL"), 0, kOpenUrlCmd);
-	_pasteCodeWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.PasteCodeButton", _("Paste"), _("Pastes clipboard contents into fields"), kPasteCodeCmd);
-	_connectWidget = new ButtonWidget(this, "GlobalOptions_Cloud_ConnectionWizard.ConnectButton", _("Connect"), 0, kConnectCmd);
+	_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);
 
 	if (Cloud::CloudManager::couldUseLocalServer()) {
 		// 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);
-		_pasteCodeWidget->setVisible(false);
+		_returnLine1->setLabel(_s("You would be navigated to ScummVM's page"));
+		_returnLine2->setLabel(_s("when you'd allow it to use your storage."));
 	}
-
-#ifndef USE_SDL2
-	_pasteCodeWidget->setVisible(false);
-#endif
         
+	_picture = new GraphicsWidget(container, "GlobalOptions_Cloud_ConnectionWizard_Container.Picture");
 #ifndef DISABLE_FANCY_THEMES
-	if (g_gui.theme()->supportsImages() && g_system->getOverlayWidth() > 320) { // picture only in high-res
-		GraphicsWidget *gfx = new GraphicsWidget(this, "GlobalOptions_Cloud_ConnectionWizard.Picture");
-		gfx->useThemeTransparency(true);
-		gfx->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDropboxLogo));
+	if (g_gui.theme()->supportsImages()) {
+		_picture->useThemeTransparency(true);
+		_picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDropboxLogo));
 	}
 #endif
+
+	containerWidgetsReflow();
 }
 
 void StorageWizardDialog::open() {
@@ -255,6 +253,9 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		_close = true;
 		break;
 #endif
+	case kStorageWizardContainerReflowCmd:
+		containerWidgetsReflow();
+		break;
 	default:
 		Dialog::handleCommand(sender, cmd, data);
 	}
@@ -269,6 +270,40 @@ void StorageWizardDialog::handleTickle() {
 	Dialog::handleTickle();
 }
 
+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);
+	}
+	if (_openUrlWidget) _openUrlWidget->setVisible(true);
+	if (_pasteCodeWidget) {
+#ifdef USE_SDL2
+		bool visible = showFields;
+#else
+		bool visible = false;
+#endif
+		_pasteCodeWidget->setVisible(visible);
+	}
+
+	// bottom row
+	if (_cancelWidget) _cancelWidget->setVisible(true);
+	if (_connectWidget) {
+		_connectWidget->setVisible(showFields);
+	}
+}
+
 Common::String StorageWizardDialog::getUrl() const {
 	Common::String url = "https://www.scummvm.org/c/";
 	switch (_storageId) {
diff --git a/gui/storagewizarddialog.h b/gui/storagewizarddialog.h
index b0d0999..61bc8ac 100644
--- a/gui/storagewizarddialog.h
+++ b/gui/storagewizarddialog.h
@@ -32,6 +32,7 @@ class CommandSender;
 class EditTextWidget;
 class StaticTextWidget;
 class ButtonWidget;
+class GraphicsWidget;
 
 #ifdef USE_SDL_NET
 enum StorageWizardDialogCommands {
@@ -42,15 +43,30 @@ enum StorageWizardDialogCommands {
 class StorageWizardDialog : public Dialog {
 	static const uint32 CODE_FIELDS = 8;
 	uint32 _storageId;
+
+	StaticTextWidget *_headlineWidget;
+	StaticTextWidget *_navigateLineWidget;
+	StaticTextWidget *_urlLineWidget;
+	StaticTextWidget *_returnLine1;
+	StaticTextWidget *_returnLine2;
 	EditTextWidget *_codeWidget[CODE_FIELDS];
 	StaticTextWidget *_messageWidget;
+
+	GraphicsWidget *_picture;
+	ButtonWidget *_openUrlWidget;
 	ButtonWidget *_pasteCodeWidget;
+
+	ButtonWidget *_cancelWidget;
 	ButtonWidget *_connectWidget;
+
 	bool _close;
 #ifdef USE_SDL_NET
 	bool _stopServerOnClose;
 #endif
 
+	/** Hides/shows widgets for Container to work with them correctly. */
+	void containerWidgetsReflow();
+
 	/** Return short scummvm.org URL for user to navigate to. */
 	Common::String getUrl() const;
 
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 42c6b10..86d5c49 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 3284f7d..0b030ff 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -635,7 +635,13 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
-		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>		
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard_Container' overlays = 'GlobalOptions_Cloud_ConnectionWizard.Container'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '0'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Picture'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 7666aeb..9bdea8f 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -650,6 +650,12 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard_Container' overlays = 'GlobalOptions_Cloud_ConnectionWizard.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '4'>
 				<widget name = 'Headline'
@@ -670,60 +676,63 @@
 						height = 'Globals.Line.Height'
 				/>
 				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
-						<widget name = 'CodeBox1'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox2'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox3'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox4'
-							width = '60'
-							height = '16'
-						/>
-					</layout>
-					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
-						<widget name = 'CodeBox5'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox6'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox7'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox8'
-							width = '60'
-							height = '16'
-						/>
-					</layout>
-					<widget name = 'MessageLine'
-							height = 'Globals.Line.Height'
+					<widget name = 'CodeBox1'
+						width = '60'
+						height = '16'
 					/>
-				<space size = '4' />
-				<widget name = 'Picture' width = '1' height = '1' />
-			</layout>
-			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
-				<widget name = 'CancelButton'
-						type = 'Button'
-				/>
-				<widget name = 'OpenUrlButton'
-						type = 'Button'
-				/>
-				<widget name = 'PasteCodeButton'
-						type = 'Button'
-				/>
-				<widget name = 'ConnectButton'
-						type = 'Button'
+					<widget name = 'CodeBox2'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox3'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox4'
+						width = '60'
+						height = '16'
+					/>
+				</layout>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'CodeBox5'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox6'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox7'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox8'
+						width = '60'
+						height = '16'
+					/>
+				</layout>
+				<widget name = 'MessageLine'
+						height = 'Globals.Line.Height'
 				/>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'OpenUrlButton'
+							type = 'Button'
+					/>
+					<widget name = 'PasteCodeButton'
+							type = 'Button'
+					/>
+				</layout>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'CancelButton'
+							type = 'Button'
+					/>
+					<space />
+					<widget name = 'ConnectButton'
+							type = 'Button'
+					/>
+				</layout>
+				<space size = '6' />
+				<widget name = 'Picture' width = '1' height = '1' />
 			</layout>
 		</layout>
 	</dialog>
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index b433aaa..23662b1 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index a242229..f1fd702 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -649,7 +649,13 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
-		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>		
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard_Container' overlays = 'GlobalOptions_Cloud_ConnectionWizard.Container'>
+		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '0'>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Picture'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 26f7fab..3f7e263 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -648,6 +648,12 @@
 	</dialog>
 
 	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard' overlays = 'Dialog.GlobalOptions'>
+		<layout type = 'vertical' padding = '0, 0, 0, 0'>
+			<widget name = 'Container'/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'GlobalOptions_Cloud_ConnectionWizard_Container' overlays = 'GlobalOptions_Cloud_ConnectionWizard.Container'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '4'>
 				<widget name = 'Headline'
@@ -668,60 +674,63 @@
 						height = 'Globals.Line.Height'
 				/>
 				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
-						<widget name = 'CodeBox1'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox2'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox3'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox4'
-							width = '60'
-							height = '16'
-						/>
-					</layout>
-					<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
-						<widget name = 'CodeBox5'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox6'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox7'
-							width = '60'
-							height = '16'
-						/>
-						<widget name = 'CodeBox8'
-							width = '60'
-							height = '16'
-						/>
-					</layout>
-					<widget name = 'MessageLine'
-							height = 'Globals.Line.Height'
+					<widget name = 'CodeBox1'
+						width = '60'
+						height = '16'
 					/>
-				<space size = '4' />
-				<widget name = 'Picture' width = '1' height = '1' />
-			</layout>
-			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
-				<widget name = 'CancelButton'
-						type = 'Button'
-				/>
-				<widget name = 'OpenUrlButton'
-						type = 'Button'
-				/>
-				<widget name = 'PasteCodeButton'
-						type = 'Button'
-				/>
-				<widget name = 'ConnectButton'
-						type = 'Button'
+					<widget name = 'CodeBox2'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox3'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox4'
+						width = '60'
+						height = '16'
+					/>
+				</layout>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'CodeBox5'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox6'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox7'
+						width = '60'
+						height = '16'
+					/>
+					<widget name = 'CodeBox8'
+						width = '60'
+						height = '16'
+					/>
+				</layout>
+				<widget name = 'MessageLine'
+						height = 'Globals.Line.Height'
 				/>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'OpenUrlButton'
+							type = 'Button'
+					/>
+					<widget name = 'PasteCodeButton'
+							type = 'Button'
+					/>
+				</layout>
+				<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '4' center = 'true'>
+					<widget name = 'CancelButton'
+							type = 'Button'
+					/>
+					<space />
+					<widget name = 'ConnectButton'
+							type = 'Button'
+					/>
+				</layout>
+				<space size = '6' />
+				<widget name = 'Picture' width = '1' height = '1' />
 			</layout>
 		</layout>
 	</dialog>


Commit: 091ff83ed66336fffada6453a34710214ac3576d
    https://github.com/scummvm/scummvm/commit/091ff83ed66336fffada6453a34710214ac3576d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Add Storage providers logos

StorageWizardDialog now shows logo of the Storage being connected (in
modern highres theme).

Changed paths:
  A gui/themes/scummmodern/box.bmp
  A gui/themes/scummmodern/googledrive.bmp
  A gui/themes/scummmodern/onedrive.bmp
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/storagewizarddialog.cpp
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/dropbox.bmp
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx



diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index e400e69..96108bc 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -62,6 +62,9 @@ const char *const ThemeEngine::kImageEditSmallButton = "editbtn_small.bmp";
 const char *const ThemeEngine::kImageSwitchModeSmallButton = "switchbtn_small.bmp";
 const char *const ThemeEngine::kImageFastReplaySmallButton = "fastreplay_small.bmp";
 const char *const ThemeEngine::kImageDropboxLogo = "dropbox.bmp";
+const char *const ThemeEngine::kImageOneDriveLogo = "onedrive.bmp";
+const char *const ThemeEngine::kImageGoogleDriveLogo = "googledrive.bmp";
+const char *const ThemeEngine::kImageBoxLogo = "box.bmp";
 
 struct TextDrawData {
 	const Graphics::Font *_fontPtr;
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index eb9b7da..7506cee 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -251,6 +251,9 @@ public:
 	static const char *const kImageSwitchModeSmallButton; ///< Switch mode button in recorder onscreen dialog (for 320xY)
 	static const char *const kImageFastReplaySmallButton; ///< Fast playback mode button in recorder onscreen dialog (for 320xY)
 	static const char *const kImageDropboxLogo;      ///< Dropbox logo used in the StorageWizardDialog
+	static const char *const kImageOneDriveLogo;      ///< OneDrive logo used in the StorageWizardDialog
+	static const char *const kImageGoogleDriveLogo;      ///< Google Drive logo used in the StorageWizardDialog
+	static const char *const kImageBoxLogo;      ///< Box logo used in the StorageWizardDialog
 
 	/**
 	 * Graphics mode enumeration.
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index f1b1796..ba3ebd8 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -88,7 +88,20 @@ StorageWizardDialog::StorageWizardDialog(uint32 storageId):
 #ifndef DISABLE_FANCY_THEMES
 	if (g_gui.theme()->supportsImages()) {
 		_picture->useThemeTransparency(true);
-		_picture->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDropboxLogo));
+		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;
+		}
 	}
 #endif
 
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 0b030ff..79320b0 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -645,8 +645,8 @@
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Picture'
-							width = '115'
-							height = '115'
+							width = '109'
+							height = '109'
 					/>
 					<widget name = 'OpenUrlButton'
 							type = 'Button'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index 23662b1..3c1fe66 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/box.bmp b/gui/themes/scummmodern/box.bmp
new file mode 100644
index 0000000..21fb650
Binary files /dev/null and b/gui/themes/scummmodern/box.bmp differ
diff --git a/gui/themes/scummmodern/dropbox.bmp b/gui/themes/scummmodern/dropbox.bmp
index bfb9e90..4ed95f0 100644
Binary files a/gui/themes/scummmodern/dropbox.bmp and b/gui/themes/scummmodern/dropbox.bmp differ
diff --git a/gui/themes/scummmodern/googledrive.bmp b/gui/themes/scummmodern/googledrive.bmp
new file mode 100644
index 0000000..30377a5
Binary files /dev/null and b/gui/themes/scummmodern/googledrive.bmp differ
diff --git a/gui/themes/scummmodern/onedrive.bmp b/gui/themes/scummmodern/onedrive.bmp
new file mode 100644
index 0000000..cd26d71
Binary files /dev/null and b/gui/themes/scummmodern/onedrive.bmp differ
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index d7b35b1..e3d2152 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -120,6 +120,9 @@
 		<bitmap filename = 'switchbtn_small.bmp'/>
 		<bitmap filename = 'fastreplay_small.bmp'/>
 		<bitmap filename = 'dropbox.bmp'/>
+		<bitmap filename = 'onedrive.bmp'/>
+		<bitmap filename = 'googledrive.bmp'/>
+		<bitmap filename = 'box.bmp'/>
 	</bitmaps>
 
 	<fonts>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index f1fd702..f4fa200 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -659,8 +659,8 @@
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '6'>
 					<widget name = 'Picture'
-							width = '115'
-							height = '115'
+							width = '109'
+							height = '109'
 					/>
 					<widget name = 'OpenUrlButton'
 							type = 'Button'


Commit: 527ab4cdf6fc314cb260ae329d88794440b875ef
    https://github.com/scummvm/scummvm/commit/527ab4cdf6fc314cb260ae329d88794440b875ef
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Fix StorageWizardDialog warning

Removed extra comma in the enum.

Changed paths:
    gui/storagewizarddialog.cpp



diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index ba3ebd8..6d0c504 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -47,7 +47,7 @@ enum {
 	kCodeBoxCmd = 'CdBx',
 	kOpenUrlCmd = 'OpUr',
 	kPasteCodeCmd = 'PsCd',
-	kStorageWizardContainerReflowCmd = 'SWCr',
+	kStorageWizardContainerReflowCmd = 'SWCr'
 };
 
 StorageWizardDialog::StorageWizardDialog(uint32 storageId):


Commit: b9bba9bd4bec1bf00a61c347f411a8ecf9ea69e8
    https://github.com/scummvm/scummvm/commit/b9bba9bd4bec1bf00a61c347f411a8ecf9ea69e8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
ALL: Move Clipboard support to OSystem

Commit adds kFeatureClipboardSupport. hasTextInClipboard() and
getTextFromClipboard().

OSystem_SDL has this feature if SDL2 is used.

EditableWidget and StorageWizardDialog use g_system to access clipboard
now.

Changed paths:
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h
    common/system.h
    gui/storagewizarddialog.cpp
    gui/widgets/editable.cpp



diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index 9f7b292..6862bb3 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -64,6 +64,11 @@
 #include <SDL/SDL_net.h>
 #endif
 
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_clipboard.h>
+#endif
+
 OSystem_SDL::OSystem_SDL()
 	:
 #ifdef USE_OPENGL
@@ -171,6 +176,13 @@ void OSystem_SDL::init() {
 
 }
 
+bool OSystem_SDL::hasFeature(Feature f) {
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	if (f == kFeatureClipboardSupport) return true;
+#endif
+	return ModularBackend::hasFeature(f);
+}
+
 void OSystem_SDL::initBackend() {
 	// Check if backend has not been initialized
 	assert(!_inited);
@@ -453,6 +465,26 @@ Common::String OSystem_SDL::getSystemLanguage() const {
 #endif // USE_DETECTLANG
 }
 
+bool OSystem_SDL::hasTextInClipboard() {
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	return SDL_HasClipboardText() == SDL_TRUE;
+#else
+	return false;
+#endif
+}
+
+Common::String OSystem_SDL::getTextFromClipboard() {
+	if (!hasTextInClipboard()) return "";
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	char *text = SDL_GetClipboardText();
+	if (text == nullptr) return "";
+	return text;
+#else
+	return "";
+#endif
+}
+
 uint32 OSystem_SDL::getMillis(bool skipRecord) {
 	uint32 millis = SDL_GetTicks();
 
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index f440cd7..17b4e9b 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -55,6 +55,8 @@ public:
 	 */
 	virtual SdlMixerManager *getMixerManager();
 
+	virtual bool hasFeature(Feature f);
+
 	// Override functions from ModularBackend and OSystem
 	virtual void initBackend();
 #if defined(USE_TASKBAR)
@@ -69,6 +71,10 @@ public:
 
 	virtual Common::String getSystemLanguage() const;
 
+	// Clipboard
+	virtual bool hasTextInClipboard();
+	virtual Common::String getTextFromClipboard();
+
 	virtual void setWindowCaption(const char *caption);
 	virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
 	virtual uint32 getMillis(bool skipRecord = false);
diff --git a/common/system.h b/common/system.h
index 3cbeee7..805eba6 100644
--- a/common/system.h
+++ b/common/system.h
@@ -314,7 +314,15 @@ public:
 		 *
 		 * This feature has no associated state.
 		 */
-		kFeatureDisplayLogFile
+		kFeatureDisplayLogFile,
+
+		/**
+		* The presence of this feature indicates whether the hasTextInClipboard()
+		* and getTextFromClipboard() calls are supported.
+		*
+		* This feature has no associated state.
+		*/
+		kFeatureClipboardSupport
 	};
 
 	/**
@@ -1239,6 +1247,28 @@ public:
 	virtual bool displayLogFile() { return false; }
 
 	/**
+	* Returns whether there is text available in the clipboard.
+	*
+	* The kFeatureClipboardSupport feature flag can be used to
+	* test whether this call has been implemented by the active
+	* backend.
+	*
+	* @return true if there is text in the clipboard, false otherwise
+	*/
+	virtual bool hasTextInClipboard() { return false; }
+
+	/**
+	* Returns clipboard contents as a String.
+	*
+	* The kFeatureClipboardSupport feature flag can be used to
+	* test whether this call has been implemented by the active
+	* backend.
+	*
+	* @return clipboard contents ("" if hasTextInClipboard() == false)
+	*/
+	virtual Common::String getTextFromClipboard() { return ""; }
+
+	/**
 	 * Returns the locale of the system.
 	 *
 	 * This returns the currently set up locale of the system, on which
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index 6d0c504..dd1a3aa 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -20,13 +20,6 @@
  *
  */
 
-#ifdef USE_SDL2
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
-#include <SDL2/SDL.h>
-#include <SDL2/SDL_clipboard.h>
-#endif
-
 #include "gui/storagewizarddialog.h"
 #include "gui/gui-manager.h"
 #include "gui/message.h"
@@ -217,32 +210,27 @@ void StorageWizardDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 	case kPasteCodeCmd: {
-#ifdef USE_SDL2
-		if (SDL_HasClipboardText() == SDL_TRUE) {
-			char *text = SDL_GetClipboardText();
-			if (text != nullptr) {
-				Common::String message = text;
-				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;
-						}
+		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;
 					}
-					_codeWidget[i]->setEditString(subcode);
 				}
-				handleCommand(sender, kCodeBoxCmd, data);
-				draw();
+				_codeWidget[i]->setEditString(subcode);
 			}
+			handleCommand(sender, kCodeBoxCmd, data);
+			draw();
 		}
-#endif
 		break;
 	}
 	case kConnectCmd: {
@@ -302,11 +290,7 @@ void StorageWizardDialog::containerWidgetsReflow() {
 	}
 	if (_openUrlWidget) _openUrlWidget->setVisible(true);
 	if (_pasteCodeWidget) {
-#ifdef USE_SDL2
-		bool visible = showFields;
-#else
-		bool visible = false;
-#endif
+		bool visible = showFields && g_system->hasFeature(OSystem::kFeatureClipboardSupport);
 		_pasteCodeWidget->setVisible(visible);
 	}
 
diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp
index 6c63074..02defe9 100644
--- a/gui/widgets/editable.cpp
+++ b/gui/widgets/editable.cpp
@@ -20,13 +20,6 @@
  *
  */
 
-#ifdef USE_SDL2
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
-#include <SDL2/SDL.h>
-#include <SDL2/SDL_clipboard.h>
-#endif
-
 #include "common/rect.h"
 #include "common/system.h"
 #include "gui/widgets/editable.h"
@@ -192,24 +185,20 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 		forcecaret = true;
 		break;
 
-#ifdef USE_SDL2
-		case Common::KEYCODE_v:
-			if (state.flags & Common::KBD_CTRL) {
-				if (SDL_HasClipboardText() == SDL_TRUE) {
-					char *text = SDL_GetClipboardText();
-					if (text != nullptr) {
-						for (char *ptr = text; *ptr; ++ptr) {
-							if (tryInsertChar(*ptr, _caretPos))
-								++_caretPos;
-						}
-						dirty = true;
-					}
+	case Common::KEYCODE_v:
+		if (g_system->hasFeature(OSystem::kFeatureClipboardSupport) && state.flags & Common::KBD_CTRL) {
+			if (g_system->hasTextInClipboard()) {
+				String text = g_system->getTextFromClipboard();
+				for (uint32 i = 0; i < text.size(); ++i) {
+					if (tryInsertChar(text[i], _caretPos))
+						++_caretPos;
 				}
-			} else {
-				defaultKeyDownHandler(state, dirty, forcecaret, handled);
+				dirty = true;
 			}
-			break;
-#endif
+		} else {
+			defaultKeyDownHandler(state, dirty, forcecaret, handled);
+		}
+		break;
 
 #ifdef MACOSX
 	// Let ctrl-a / ctrl-e move the caret to the start / end of the line.


Commit: 6dd10f3a683ab787b751a84958502f7eda1da84c
    https://github.com/scummvm/scummvm/commit/6dd10f3a683ab787b751a84958502f7eda1da84c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add KEY/SECRET override code

The following constants must be defined if ENABLE_RELEASE is:
* RELEASE_DROPBOX_KEY,
* RELEASE_DROPBOX_SECRET,
* RELEASE_ONEDRIVE_KEY,
* RELEASE_ONEDRIVE_SECRET,
* RELEASE_GOOGLE_DRIVE_KEY,
* RELEASE_GOOGLE_DRIVE_SECRET,
* RELEASE_BOX_KEY,
* RELEASE_BOX_SECRET.

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index c2c4c62..58a2541 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -29,10 +29,14 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "common/config-manager.h"
+
+#ifdef ENABLE_RELEASE
+#include "dists/clouds/cloud_keys.h"
+#endif
 
 namespace Cloud {
 namespace Box {
@@ -46,6 +50,10 @@ char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it o
 char *BoxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void BoxStorage::loadKeyAndSecret() {
+#ifdef ENABLE_RELEASE
+	KEY = RELEASE_BOX_KEY;
+	SECRET = RELEASE_BOX_SECRET;
+#else
 	Common::String k = ConfMan.get("BOX_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
@@ -55,6 +63,7 @@ void BoxStorage::loadKeyAndSecret() {
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
+#endif
 }
 
 BoxStorage::BoxStorage(Common::String accessToken, Common::String refreshToken):
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 6fd1f9d..41536fe 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -29,10 +29,14 @@
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "common/config-manager.h"
+
+#ifdef ENABLE_RELEASE
+#include "dists/clouds/cloud_keys.h"
+#endif
 
 namespace Cloud {
 namespace Dropbox {
@@ -44,6 +48,10 @@ char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading
 char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void DropboxStorage::loadKeyAndSecret() {
+#ifdef ENABLE_RELEASE
+	KEY = RELEASE_DROPBOX_KEY;
+	SECRET = RELEASE_DROPBOX_SECRET;
+#else
 	Common::String k = ConfMan.get("DROPBOX_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
@@ -53,6 +61,7 @@ void DropboxStorage::loadKeyAndSecret() {
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
+#endif
 }
 
 DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {}
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 7ae9dde..d876883 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -34,6 +34,10 @@
 #include "common/json.h"
 #include <curl/curl.h>
 
+#ifdef ENABLE_RELEASE
+#include "dists/clouds/cloud_keys.h"
+#endif
+
 namespace Cloud {
 namespace GoogleDrive {
 
@@ -46,6 +50,10 @@ char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, load
 char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void GoogleDriveStorage::loadKeyAndSecret() {
+#ifdef ENABLE_RELEASE
+	KEY = RELEASE_GOOGLE_DRIVE_KEY;
+	SECRET = RELEASE_GOOGLE_DRIVE_SECRET;
+#else
 	Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
@@ -55,6 +63,7 @@ void GoogleDriveStorage::loadKeyAndSecret() {
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
+#endif
 }
 
 GoogleDriveStorage::GoogleDriveStorage(Common::String accessToken, Common::String refreshToken):
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 1008cab..458aaec 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -30,10 +30,14 @@
 #include "backends/networking/curl/connectionmanager.h"
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/json.h"
 #include <curl/curl.h>
-#include "common/config-manager.h"
+
+#ifdef ENABLE_RELEASE
+#include "dists/clouds/cloud_keys.h"
+#endif
 
 namespace Cloud {
 namespace OneDrive {
@@ -46,6 +50,10 @@ char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading
 char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
 
 void OneDriveStorage::loadKeyAndSecret() {
+#ifdef ENABLE_RELEASE
+	KEY = RELEASE_ONEDRIVE_KEY;
+	SECRET = RELEASE_ONEDRIVE_SECRET;
+#else
 	Common::String k = ConfMan.get("ONEDRIVE_KEY", ConfMan.kCloudDomain);
 	KEY = new char[k.size() + 1];
 	memcpy(KEY, k.c_str(), k.size());
@@ -55,6 +63,7 @@ void OneDriveStorage::loadKeyAndSecret() {
 	SECRET = new char[k.size() + 1];
 	memcpy(SECRET, k.c_str(), k.size());
 	SECRET[k.size()] = 0;
+#endif
 }
 
 OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):


Commit: 0b97aff866686b43ea8b95aeb0f29ac3abe46840
    https://github.com/scummvm/scummvm/commit/0b97aff866686b43ea8b95aeb0f29ac3abe46840
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Minor TODO fix

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 58a2541..7d67ba1 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -47,7 +47,7 @@ namespace Box {
 #define BOX_API_USERS_ME "https://api.box.com/2.0/users/me"
 
 char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
-char *BoxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
+char *BoxStorage::SECRET = nullptr;
 
 void BoxStorage::loadKeyAndSecret() {
 #ifdef ENABLE_RELEASE
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 41536fe..789f6b4 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -45,7 +45,7 @@ namespace Dropbox {
 #define DROPBOX_API_FILES_DOWNLOAD "https://content.dropboxapi.com/2/files/download"
 
 char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
-char *DropboxStorage::SECRET = nullptr; //TODO: hide these secrets somehow
+char *DropboxStorage::SECRET = nullptr;
 
 void DropboxStorage::loadKeyAndSecret() {
 #ifdef ENABLE_RELEASE
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index d876883..789dfaf 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -47,7 +47,7 @@ namespace GoogleDrive {
 #define GOOGLEDRIVE_API_ABOUT "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user"
 
 char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
-char *GoogleDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
+char *GoogleDriveStorage::SECRET = nullptr;
 
 void GoogleDriveStorage::loadKeyAndSecret() {
 #ifdef ENABLE_RELEASE
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 458aaec..9fde2ed 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -47,7 +47,7 @@ namespace OneDrive {
 #define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
 
 char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
-char *OneDriveStorage::SECRET = nullptr; //TODO: hide these secrets somehow
+char *OneDriveStorage::SECRET = nullptr;
 
 void OneDriveStorage::loadKeyAndSecret() {
 #ifdef ENABLE_RELEASE


Commit: 46dda5fce0ac38f54eb7e97889bf3dc6816b8766
    https://github.com/scummvm/scummvm/commit/46dda5fce0ac38f54eb7e97889bf3dc6816b8766
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update NetworkReadStream

It now uses both CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION.
The latter is available in new libcurl (>= 7.32.0) only, thus the former
is added for older versions support.

Changed paths:
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 21f2689..9b20a75 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -57,6 +57,11 @@ static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, c
 	return 0;
 }
 
+static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow) {
+	// for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION)
+	return curlProgressCallback(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow);
+}
+
 void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	_eos = _requestComplete = false;
 	_sendingContentsBuffer = nullptr;
@@ -75,8 +80,14 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
+	curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
+#if LIBCURL_VERSION_NUM >= 0x072000
+	// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
+	// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
 	curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
 	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
+#endif
 	if (uploading) {
 		curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
 		curl_easy_setopt(_easy, CURLOPT_READDATA, this);
@@ -112,8 +123,14 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
+	curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
+#if LIBCURL_VERSION_NUM >= 0x072000
+	// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
+	// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
 	curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
 	curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
+#endif
 
 	// set POST multipart upload form fields/files
 	struct curl_httppost *formpost = nullptr;


Commit: 2c34864a06f31d1d909f7abe62d8047409fe9f00
    https://github.com/scummvm/scummvm/commit/2c34864a06f31d1d909f7abe62d8047409fe9f00
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix UploadFileClientHandler

A few possible memory leaks about `_contentStream` there.

Changed paths:
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 048ff39..e2832bc 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -137,6 +137,12 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 		return;
 	}
 
+	// remove previous stream (if there is one)
+	if (_contentStream) {
+		delete _contentStream;
+		_contentStream = nullptr;
+	}
+
 	// create file stream (and necessary subdirectories)
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(originalNode->getPath(), true)) {
@@ -156,6 +162,9 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 		_contentStream->flush();
 		++_uploadedFiles;
 
+		delete _contentStream;
+		_contentStream = nullptr;
+
 		if (client->noMoreContent()) {
 			// success - redirect back to directory listing
 			HandlerUtils::setMessageHandler(


Commit: 9d96d40b3d78d987d414e0dc77f43c09ef4421ba
    https://github.com/scummvm/scummvm/commit/9d96d40b3d78d987d414e0dc77f43c09ef4421ba
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add JSON-related checks in BoxStorage

Changed paths:
    backends/cloud/box/boxstorage.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 7d67ba1..6209de0 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -119,11 +119,21 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 	if (!json) {
 		warning("BoxStorage: got NULL instead of JSON");
 		if (callback) (*callback)(BoolResponse(nullptr, false));
+		delete callback;
+		return;
+	}
+
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage")) {
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
+		delete json;
+		delete callback;
 		return;
 	}
 
 	Common::JSONObject result = json->asObject();
-	if (!result.contains("access_token") || !result.contains("refresh_token")) {
+	if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "BoxStorage") ||
+		!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "BoxStorage")) {
 		warning("BoxStorage: bad response, no token passed");
 		debug(9, "%s", json->stringify().c_str());
 		if (callback)
@@ -136,6 +146,7 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
+	delete callback;
 }
 
 void BoxStorage::codeFlowComplete(BoolResponse response) {
@@ -172,6 +183,12 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 		return;
 	}
 
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
+		delete json;
+		delete outerCallback;
+		return;
+	}
+
 	Common::JSONObject info = json->asObject();
 
 	Common::String uid, name, email;
@@ -180,19 +197,19 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking
 	// can check that "type": "user"
 	// there is also "max_upload_size", "phone" and "avatar_url"
 
-	if (info.contains("id") && info.getVal("id")->isString())
+	if (Networking::CurlJsonRequest::jsonContainsString(info, "id", "BoxStorage::infoInnerCallback"))
 		uid = info.getVal("id")->asString();
 
-	if (info.contains("name") && info.getVal("name")->isString())
+	if (Networking::CurlJsonRequest::jsonContainsString(info, "name", "BoxStorage::infoInnerCallback"))
 		name = info.getVal("name")->asString();
 
-	if (info.contains("login") && info.getVal("login")->isString())
+	if (Networking::CurlJsonRequest::jsonContainsString(info, "login", "BoxStorage::infoInnerCallback"))
 		email = info.getVal("login")->asString();
 
-	if (info.contains("space_amount") && info.getVal("space_amount")->isIntegerNumber())
+	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_amount", "BoxStorage::infoInnerCallback"))
 		quotaAllocated = info.getVal("space_amount")->asIntegerNumber();
 
-	if (info.contains("space_used") && info.getVal("space_used")->isIntegerNumber())
+	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_used", "BoxStorage::infoInnerCallback"))
 		quotaUsed = info.getVal("space_used")->asIntegerNumber();
 
 	Common::String username = email;
@@ -225,8 +242,12 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networ
 	}
 
 	if (outerCallback) {
-		Common::JSONObject info = json->asObject();
-		(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+		if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
+			Common::JSONObject info = json->asObject();
+			(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+		} else {
+			(*outerCallback)(BoolResponse(nullptr, false));
+		}
 		delete outerCallback;
 	}
 


Commit: 166d1121e5973868b24aa3db6156028edb1d6a72
    https://github.com/scummvm/scummvm/commit/166d1121e5973868b24aa3db6156028edb1d6a72
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update TokenRefreshers

Box's, Google Drive's and OneDrive's token refreshing requests have more
JSON checks now.

Changed paths:
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp



diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index c798b97..ca05eef 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -63,41 +63,44 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
 		return;
 	}
 
-	Common::JSONObject result = json->asObject();
-	if (result.contains("type") && result.getVal("type")->isString() && result.getVal("type")->asString() == "error") {
-		//new token needed => request token & then retry original request
-		long httpCode = -1;
-		if (_stream) {
-			httpCode = _stream->httpResponseCode();
-			debug(9, "BoxTokenRefresher: code %ld", httpCode);
-		}
-
-		bool irrecoverable = true;
-
-		Common::String code, message;
-		if (result.contains("code")) {
-			code = result.getVal("code")->asString();
-			debug(9, "BoxTokenRefresher: code = %s", code.c_str());
-		}
-
-		if (result.contains("message")) {
-			message = result.getVal("message")->asString();
-			debug(9, "BoxTokenRefresher: message = %s", message.c_str());
-		}
-
-		//TODO: decide when token refreshment will help
-		//if (code == "unauthenticated") irrecoverable = false;
-
-		if (irrecoverable) {
-			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
+	if (jsonIsObject(json, "BoxTokenRefresher")) {
+		Common::JSONObject result = json->asObject();
+		if (result.contains("type") && result.getVal("type")->isString() && result.getVal("type")->asString() == "error") {
+			//new token needed => request token & then retry original request
+			long httpCode = -1;
+			if (_stream) {
+				httpCode = _stream->httpResponseCode();
+				debug(9, "BoxTokenRefresher: code %ld", httpCode);
+			}
+
+			bool irrecoverable = true;
+
+			Common::String code, message;
+			if (jsonContainsString(result, "code", "BoxTokenRefresher")) {
+				code = result.getVal("code")->asString();
+				debug(9, "BoxTokenRefresher: code = %s", code.c_str());
+			}
+
+			if (jsonContainsString(result, "message", "BoxTokenRefresher")) {
+				message = result.getVal("message")->asString();
+				debug(9, "BoxTokenRefresher: message = %s", message.c_str());
+			}
+
+			//TODO: decide when token refreshment will help
+			//for now refreshment is used only when HTTP 401 is passed in finishError()
+			//if (code == "unauthenticated") irrecoverable = false;
+
+			if (irrecoverable) {
+				finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
+				delete json;
+				return;
+			}
+
+			pause();
 			delete json;
+			_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
 			return;
 		}
-
-		pause();
-		delete json;
-		_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
-		return;
 	}
 
 	//notify user of success
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 99661c2..7d68510 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -63,43 +63,45 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		return;
 	}
 
-	Common::JSONObject result = json->asObject();
-	long httpResponseCode = -1;
-	if (result.contains("error")) {
-		//new token needed => request token & then retry original request
-		if (_stream) {
-			httpResponseCode = _stream->httpResponseCode();
-			debug(9, "GoogleDriveTokenRefresher: code = %ld", httpResponseCode);
-		}
-
-		Common::JSONObject error = result.getVal("error")->asObject();
-		bool irrecoverable = true;
-
-		uint32 code = -1;
-		Common::String message;
-		if (error.contains("code") && error.getVal("code")->isIntegerNumber()) {
-			code = error.getVal("code")->asIntegerNumber();
-			debug(9, "GoogleDriveTokenRefresher: code = %u", code);
-		}
-
-		if (error.contains("message")) {
-			message = error.getVal("message")->asString();
-			debug(9, "GoogleDriveTokenRefresher: message = %s", message.c_str());
-		}
-
-		if (code == 401 || message == "Invalid Credentials")
-			irrecoverable = false;
-
-		if (irrecoverable) {
-			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
+	if (jsonIsObject(json, "GoogleDriveTokenRefresher")) {
+		Common::JSONObject result = json->asObject();
+		long httpResponseCode = -1;
+		if (jsonContainsAttribute(result, "error", "GoogleDriveTokenRefresher") && jsonIsObject(result.getVal("error"), "GoogleDriveTokenRefresher")) {
+			//new token needed => request token & then retry original request
+			if (_stream) {
+				httpResponseCode = _stream->httpResponseCode();
+				debug(9, "GoogleDriveTokenRefresher: code = %ld", httpResponseCode);
+			}
+
+			Common::JSONObject error = result.getVal("error")->asObject();
+			bool irrecoverable = true;
+
+			uint32 code = -1;
+			Common::String message;
+			if (jsonContainsIntegerNumber(error, "code", "GoogleDriveTokenRefresher")) {
+				code = error.getVal("code")->asIntegerNumber();
+				debug(9, "GoogleDriveTokenRefresher: code = %u", code);
+			}
+
+			if (jsonContainsString(error, "message", "GoogleDriveTokenRefresher")) {
+				message = error.getVal("message")->asString();
+				debug(9, "GoogleDriveTokenRefresher: message = %s", message.c_str());
+			}
+
+			if (code == 401 || message == "Invalid Credentials")
+				irrecoverable = false;
+
+			if (irrecoverable) {
+				finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
+				delete json;
+				return;
+			}
+
+			pause();
 			delete json;
+			_parentStorage->getAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
 			return;
 		}
-
-		pause();
-		delete json;
-		_parentStorage->getAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
-		return;
 	}
 
 	//notify user of success
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 5e3bce9..5ee2772 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -63,48 +63,50 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 		return;
 	}
 
-	Common::JSONObject result = json->asObject();
-	long httpResponseCode = -1;
-	if (result.contains("error")) {
-		//new token needed => request token & then retry original request
-		if (_stream) {
-			httpResponseCode = _stream->httpResponseCode();
-			debug(9, "OneDriveTokenRefresher: code = %ld", httpResponseCode);
-		}
-
-		Common::JSONObject error = result.getVal("error")->asObject();
-		bool irrecoverable = true;
-
-		Common::String code, message;
-		if (error.contains("code")) {
-			code = error.getVal("code")->asString();
-			debug(9, "OneDriveTokenRefresher: code = %s", code.c_str());
-		}
-
-		if (error.contains("message")) {
-			message = error.getVal("message")->asString();
-			debug(9, "OneDriveTokenRefresher: message = %s", message.c_str());
-		}
-
-		//determine whether token refreshing would help in this situation
-		if (code == "itemNotFound") {
-			if (message.contains("application ID"))
+	if (jsonIsObject(json, "OneDriveTokenRefresher")) {
+		Common::JSONObject result = json->asObject();
+		long httpResponseCode = -1;
+		if (jsonContainsAttribute(result, "error", "OneDriveTokenRefresher") && jsonIsObject(result.getVal("error"), "OneDriveTokenRefresher")) {
+			//new token needed => request token & then retry original request
+			if (_stream) {
+				httpResponseCode = _stream->httpResponseCode();
+				debug(9, "OneDriveTokenRefresher: code = %ld", httpResponseCode);
+			}
+
+			Common::JSONObject error = result.getVal("error")->asObject();
+			bool irrecoverable = true;
+
+			Common::String code, message;
+			if (jsonContainsString(error, "code", "OneDriveTokenRefresher")) {
+				code = error.getVal("code")->asString();
+				debug(9, "OneDriveTokenRefresher: code = %s", code.c_str());
+			}
+
+			if (jsonContainsString(error, "message", "OneDriveTokenRefresher")) {
+				message = error.getVal("message")->asString();
+				debug(9, "OneDriveTokenRefresher: message = %s", message.c_str());
+			}
+
+			//determine whether token refreshing would help in this situation
+			if (code == "itemNotFound") {
+				if (message.contains("application ID"))
+					irrecoverable = false;
+			}
+
+			if (code == "unauthenticated")
 				irrecoverable = false;
-		}
 
-		if (code == "unauthenticated")
-			irrecoverable = false;
+			if (irrecoverable) {
+				finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
+				delete json;
+				return;
+			}
 
-		if (irrecoverable) {
-			finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
+			pause();
 			delete json;
+			_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
 			return;
 		}
-
-		pause();
-		delete json;
-		_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
-		return;
 	}
 
 	//notify user of success


Commit: a2e019972779575bcc9038949bca73d78f7bf07b
    https://github.com/scummvm/scummvm/commit/a2e019972779575bcc9038949bca73d78f7bf07b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update BoxUploadRequest

More JSON checks there.

Changed paths:
    backends/cloud/box/boxuploadrequest.cpp



diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index f68ba6a..929c9ce 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -145,56 +145,69 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 	if (error.httpResponseCode != 200 && error.httpResponseCode != 201)
 		warning("BoxUploadRequest: looks like an error (bad HTTP code)");
 
-	//TODO: add more JSON warnings there
+	//check JSON and show warnings if it's malformed
 	Common::JSONValue *json = response.value;
-	if (json) {
-		if (json->isObject()) {
-			Common::JSONObject object = json->asObject();
-			if (object.contains("entries") && object.getVal("entries")->isArray()) {
-				Common::JSONArray entries = object.getVal("entries")->asArray();
-				if (entries.size() > 0) {
-					Common::JSONObject entry = entries[0]->asObject();
-
-					//finished
-					Common::String id = entry.getVal("id")->asString();
-					Common::String name = entry.getVal("name")->asString();
-					bool isDirectory = (entry.getVal("type")->asString() == "folder");
-					uint32 size = 0, timestamp = 0;
-					if (entry.contains("size")) {
-						if (entry.getVal("size")->isString())
-							size = entry.getVal("size")->asString().asUint64();
-						else if (entry.getVal("size")->isIntegerNumber())
-							size = entry.getVal("size")->asIntegerNumber();
-						else
-							warning("strange type for field 'size'");
-					}
-					if (entry.contains("modified_at") && entry.getVal("modified_at")->isString())
-						timestamp = ISO8601::convertToTimestamp(entry.getVal("modified_at")->asString());
-
-					//as we list directory by id, we can't determine full path for the file, so we leave it empty
-					finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
-					return;
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
+
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
+	Common::JSONObject object = json->asObject();
+	if (Networking::CurlJsonRequest::jsonContainsArray(object, "entries", "BoxUploadRequest")) {
+		Common::JSONArray entries = object.getVal("entries")->asArray();
+		if (entries.size() == 0) {
+			warning("BoxUploadRequest: 'entries' found, but it's empty");
+		} else if (Networking::CurlJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
+			warning("BoxUploadRequest: 'entries' first item is not an object");
+		} else {
+			Common::JSONObject item = entries[0]->asObject();
+
+			if (Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxUploadRequest") &&
+				Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxUploadRequest") &&
+				Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxUploadRequest") &&
+				Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxUploadRequest") &&
+				Networking::CurlJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxUploadRequest")) {
+
+				//finished
+				Common::String id = item.getVal("id")->asString();
+				Common::String name = item.getVal("name")->asString();
+				bool isDirectory = (item.getVal("type")->asString() == "folder");
+				uint32 size;
+				if (item.getVal("size")->isString()) {
+					size = item.getVal("size")->asString().asUint64();
+				} else {
+					size = item.getVal("size")->asIntegerNumber();
 				}
-			}
+				uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
 
-			//TODO: check errors
-			/*
-			if (object.contains("error")) {
-				warning("Box returned error: %s", json->stringify(true).c_str());
+				finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
 				delete json;
-				error.response = json->stringify(true);
-				finishError(error);
 				return;
 			}
-			*/
 		}
+	}
 
-		warning("BoxUploadRequest: no file info to return");
-		finishUpload(StorageFile(_savePath, 0, 0, false));
-	} else {
-		warning("BoxUploadRequest: null, not json");
+	//TODO: check errors
+	/*
+	if (object.contains("error")) {
+		warning("Box returned error: %s", json->stringify(true).c_str());
+		delete json;
+		error.response = json->stringify(true);
 		finishError(error);
+		return;
 	}
+	*/
+
+	warning("BoxUploadRequest: no file info to return");
+	finishUpload(StorageFile(_savePath, 0, 0, false));
 
 	delete json;
 }


Commit: 6be736b5ed92800e1cd329d0de4ed0bb7ff2ea38
    https://github.com/scummvm/scummvm/commit/6be736b5ed92800e1cd329d0de4ed0bb7ff2ea38
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update Dropbox Requests

Adding more JSON checks there.

Changed paths:
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxinforequest.cpp



diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 6cc6801..97090b4 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -79,19 +79,27 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	if (!json) {
-		warning("DropboxCreateDirectoryRequest: NULL passed instead of JSON");
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
 		finishError(error);
 		return;
 	}
 
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
 	Common::JSONObject info = json->asObject();
 	if (info.contains("id")) {
 		finishCreation(true);
 	} else {
-		if (info.contains("error_summary") && info.getVal("error_summary")->isString()) {
+		if (Networking::CurlJsonRequest::jsonContainsString(info, "error_summary", "DropboxCreateDirectoryRequest")) {
 			Common::String summary = info.getVal("error_summary")->asString();
 			if (summary.contains("path") && summary.contains("conflict") && summary.contains("folder")) {
+				// existing directory - not an error for CreateDirectoryRequest
 				finishCreation(false);
 				delete json;
 				return;
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index c5cbb9d..6cdbe33 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -76,18 +76,34 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	if (!json) {
-		warning("DropboxInfoRequest: NULL passed instead of JSON");
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
 		finishError(error);
 		return;
 	}
 
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
 	//Dropbox documentation states there are no errors for this API method
 	Common::JSONObject info = json->asObject();
-	Common::JSONObject nameInfo = info.getVal("name")->asObject();
-	_uid = info.getVal("account_id")->asString();
-	_name = nameInfo.getVal("display_name")->asString();
-	_email = info.getVal("email")->asString();
+	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "name", "DropboxInfoRequest") &&
+		Networking::CurlJsonRequest::jsonIsObject(info.getVal("name"), "DropboxInfoRequest")) {
+		Common::JSONObject nameInfo = info.getVal("name")->asObject();
+		if (Networking::CurlJsonRequest::jsonContainsString(nameInfo, "display_name", "DropboxInfoRequest")) {
+			_name = nameInfo.getVal("display_name")->asString();
+		}
+	}
+	if (Networking::CurlJsonRequest::jsonContainsString(info, "account_id", "DropboxInfoRequest")) {
+		_uid = info.getVal("account_id")->asString();
+	}
+	if (Networking::CurlJsonRequest::jsonContainsString(info, "email", "DropboxInfoRequest")) {
+		_email = info.getVal("email")->asString();
+	}
 	CloudMan.setStorageUsername(kStorageDropboxId, _email);
 	delete json;
 
@@ -114,17 +130,44 @@ void DropboxInfoRequest::quotaResponseCallback(Networking::JsonResponse response
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	if (!json) {
-		warning("DropboxInfoRequest: NULL passed instead of JSON");
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
 		finishError(error);
 		return;
 	}
 
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
 	//Dropbox documentation states there are no errors for this API method
 	Common::JSONObject info = json->asObject();
-	Common::JSONObject allocation = info.getVal("allocation")->asObject();
-	uint64 used = info.getVal("used")->asIntegerNumber();
-	uint64 allocated = allocation.getVal("allocated")->asIntegerNumber();
+	
+	if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "used", "DropboxInfoRequest")) {
+		error.response = "Passed JSON misses 'used' attribute!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
+	uint64 used = info.getVal("used")->asIntegerNumber(), allocated = 0;
+
+	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "allocation", "DropboxInfoRequest") &&
+		Networking::CurlJsonRequest::jsonIsObject(info.getVal("allocation"), "DropboxInfoRequest")) {
+		Common::JSONObject allocation = info.getVal("allocation")->asObject();
+		if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(allocation, "allocated", "DropboxInfoRequest")) {
+			error.response = "Passed JSON misses 'allocation/allocated' attribute!";
+			finishError(error);
+			delete json;
+			return;
+		}
+		
+		allocated = allocation.getVal("allocated")->asIntegerNumber();		
+	}
+	
 	finishInfo(StorageInfo(_uid, _name, _email, used, allocated));
 	delete json;
 }


Commit: 364c74df930633bf897cacf391f6dc24dbc183b8
    https://github.com/scummvm/scummvm/commit/364c74df930633bf897cacf391f6dc24dbc183b8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update DropboxStorage

JSON checks added.

Changed paths:
    backends/cloud/dropbox/dropboxstorage.cpp



diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 789f6b4..d120703 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -92,25 +92,34 @@ void DropboxStorage::getAccessToken(Common::String code) {
 
 void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
 	Common::JSONValue *json = (Common::JSONValue *)response.value;
-	if (json) {
-		Common::JSONObject result = json->asObject();
-		if (!result.contains("access_token") || !result.contains("uid")) {
-			warning("DropboxStorage: bad response, no token/uid passed");
-			debug(9, "%s", json->stringify(true).c_str());
-			CloudMan.removeStorage(this);
-		} else {
-			_token = result.getVal("access_token")->asString();
-			_uid = result.getVal("uid")->asString();
-			ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
-			CloudMan.replaceStorage(this, kStorageDropboxId);
-			ConfMan.flushToDisk();
-		}
+	if (json == nullptr) {
+		debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
+		CloudMan.removeStorage(this);
+		return;
+	}
 
+	if (!json->isObject()) {
+		debug(9, "DropboxStorage::codeFlowComplete: Passed JSON is not an object!");
+		CloudMan.removeStorage(this);
 		delete json;
-	} else {
-		debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
+		return;
+	}
+
+	Common::JSONObject result = json->asObject();
+	if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "DropboxStorage::codeFlowComplete") ||
+		!Networking::CurlJsonRequest::jsonContainsString(result, "uid", "DropboxStorage::codeFlowComplete")) {
+		warning("DropboxStorage: bad response, no token/uid passed");
+		debug(9, "%s", json->stringify(true).c_str());
 		CloudMan.removeStorage(this);
+	} else {
+		_token = result.getVal("access_token")->asString();
+		_uid = result.getVal("uid")->asString();
+		ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
+		CloudMan.replaceStorage(this, kStorageDropboxId);
+		ConfMan.flushToDisk();
 	}
+
+	delete json;
 }
 
 void DropboxStorage::codeFlowFailed(Networking::ErrorResponse error) {


Commit: b3aa9f663f3ac0ac325225c1eaf8f78fb8bf9009
    https://github.com/scummvm/scummvm/commit/b3aa9f663f3ac0ac325225c1eaf8f78fb8bf9009
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update DropboxUploadRequest

JSON checks.

Changed paths:
    backends/cloud/dropbox/dropboxuploadrequest.cpp



diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index dd3a381..945493b 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -132,51 +132,53 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse respons
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	// TODO: add more JSON-related warnings
 	Common::JSONValue *json = response.value;
-	if (json) {
-		bool needsFinishRequest = false;
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
 
-		if (json->isObject()) {
-			Common::JSONObject object = json->asObject();
+	bool needsFinishRequest = false;
 
-			//debug(9, "%s", json->stringify(true).c_str());
+	if (json->isObject()) {
+		Common::JSONObject object = json->asObject();
 
-			if (object.contains("error") || object.contains("error_summary")) {
-				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
-				error.response = json->stringify(true);
-				finishError(error);
-				delete json;
-				return;
-			}
+		//debug(9, "%s", json->stringify(true).c_str());
 
-			if (object.contains("server_modified")) {
-				//finished
-				Common::String path = object.getVal("path_lower")->asString();
-				uint32 size = object.getVal("size")->asIntegerNumber();
-				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString());
-				finishUpload(StorageFile(path, size, timestamp, false));
-				return;
+		if (object.contains("error") || object.contains("error_summary")) {
+			if (Networking::CurlJsonRequest::jsonContainsString(object, "error_summary", "DropboxUploadRequest")) {
+				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
 			}
+			error.response = json->stringify(true);
+			finishError(error);
+			delete json;
+			return;
+		}
 
-			if (_sessionId == "") {
-				if (object.contains("session_id"))
-					_sessionId = object.getVal("session_id")->asString();
-				else
-					warning("DropboxUploadRequest: no session_id found");
-				needsFinishRequest = true;
-			}
+		if (Networking::CurlJsonRequest::jsonContainsString(object, "path_lower", "DropboxUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsString(object, "server_modified", "DropboxUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "DropboxUploadRequest")) {
+			//finished
+			Common::String path = object.getVal("path_lower")->asString();
+			uint32 size = object.getVal("size")->asIntegerNumber();
+			uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString());
+			finishUpload(StorageFile(path, size, timestamp, false));
+			return;
 		}
 
-		if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {
-			warning("DropboxUploadRequest: no file info to return");
-			finishUpload(StorageFile(_savePath, 0, 0, false));
-		} else {
-			uploadNextPart();
+		if (_sessionId == "") {
+			if (Networking::CurlJsonRequest::jsonContainsString(object, "session_id", "DropboxUploadRequest"))
+				_sessionId = object.getVal("session_id")->asString();
+			needsFinishRequest = true;
 		}
+	}
+
+	if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {
+		warning("DropboxUploadRequest: no file info to return");
+		finishUpload(StorageFile(_savePath, 0, 0, false));
 	} else {
-		warning("DropboxUploadRequest: null, not json");
-		finishError(error);
+		uploadNextPart();
 	}
 
 	delete json;


Commit: a381e06fda82508d16367b9999593de45c907e90
    https://github.com/scummvm/scummvm/commit/a381e06fda82508d16367b9999593de45c907e90
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GoogleDriveStorage

More JSON checks in callbacks.

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/googledrive/googledrivestorage.cpp



diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 6209de0..7086467 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -118,7 +118,8 @@ void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse
 	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("BoxStorage: got NULL instead of JSON");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
 		delete callback;
 		return;
 	}
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 789dfaf..eb04e7e 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -117,18 +117,27 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 		warning("GoogleDriveStorage: got NULL instead of JSON");
 		if (callback)
 			(*callback)(BoolResponse(nullptr, false));
+		delete callback;
+		return;
+	}
+
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage")) {
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
+		delete json;
+		delete callback;
 		return;
 	}
 
 	Common::JSONObject result = json->asObject();
-	if (!result.contains("access_token")) {
+	if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "GoogleDriveStorage")) {
 		warning("GoogleDriveStorage: bad response, no token passed");
 		debug(9, "%s", json->stringify().c_str());
 		if (callback)
 			(*callback)(BoolResponse(nullptr, false));
 	} else {
 		_token = result.getVal("access_token")->asString();
-		if (!result.contains("refresh_token"))
+		if (!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "GoogleDriveStorage"))
 			warning("GoogleDriveStorage: no refresh_token passed");
 		else
 			_refreshToken = result.getVal("refresh_token")->asString();
@@ -137,6 +146,7 @@ void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonR
 			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
+	delete callback;
 }
 
 void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
@@ -169,7 +179,13 @@ Common::String GoogleDriveStorage::name() const {
 void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("GoogleDriveStorage: NULL passed instead of JSON");
+		warning("GoogleDriveStorage::infoInnerCallback: NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::infoInnerCallback")) {
+		delete json;
 		delete outerCallback;
 		return;
 	}
@@ -179,22 +195,33 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 	Common::String uid, name, email;
 	uint64 quotaUsed = 0, quotaAllocated = 0;
 
-	if (info.contains("user") && info.getVal("user")->isObject()) {
+	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "user", "GoogleDriveStorage::infoInnerCallback") &&
+		Networking::CurlJsonRequest::jsonIsObject(info.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
 		//"me":true, "kind":"drive#user","photoLink": "",
 		//"displayName":"Alexander Tkachev","emailAddress":"alexander at tkachov.ru","permissionId":""
 		Common::JSONObject user = info.getVal("user")->asObject();
-		uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
-		name = user.getVal("displayName")->asString();
-		email = user.getVal("emailAddress")->asString();
+		if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
+			uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
+		if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
+			name = user.getVal("displayName")->asString();
+		if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
+			email = user.getVal("emailAddress")->asString();
 	}
 
-	if (info.contains("storageQuota") && info.getVal("storageQuota")->isObject()) {
+	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
+		Networking::CurlJsonRequest::jsonIsObject(info.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
 		//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
 		Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();
-		Common::String usage = storageQuota.getVal("usage")->asString();
-		Common::String limit = storageQuota.getVal("limit")->asString();
-		quotaUsed = usage.asUint64();
-		quotaAllocated = limit.asUint64();
+
+		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
+			Common::String usage = storageQuota.getVal("usage")->asString();
+			quotaUsed = usage.asUint64();
+		}
+
+		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "limit", "GoogleDriveStorage::infoInnerCallback")) {
+			Common::String limit = storageQuota.getVal("limit")->asString();
+			quotaAllocated = limit.asUint64();
+		}
 	}
 
 	CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
@@ -210,14 +237,18 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Ne
 void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("GoogleDriveStorage: NULL passed instead of JSON");
+		warning("GoogleDriveStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
 		delete outerCallback;
 		return;
 	}
 
 	if (outerCallback) {
-		Common::JSONObject info = json->asObject();
-		(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+		if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
+			Common::JSONObject info = json->asObject();
+			(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
+		} else {
+			(*outerCallback)(BoolResponse(nullptr, false));
+		}
 		delete outerCallback;
 	}
 


Commit: d34b9b91add07c8bed2d2cef6421fc1b93b2e09e
    https://github.com/scummvm/scummvm/commit/d34b9b91add07c8bed2d2cef6421fc1b93b2e09e
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update GoogleDriveUploadRequest

JSON checks in callback.

Changed paths:
    backends/cloud/googledrive/googledriveuploadrequest.cpp



diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 14f43ee..5f61dcd 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -275,44 +275,46 @@ void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse res
 	}
 
 	Common::JSONValue *json = response.value;
-	if (json) {
-		if (json->isObject()) {
-			Common::JSONObject object = json->asObject();
-
-			if (object.contains("error")) {
-				warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
-				error.response = json->stringify(true);
-				finishError(error);
-				delete json;
-				return;
-			}
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
 
-			if (object.contains("id") && object.contains("name")) {
-				//finished
-				Common::String id = object.getVal("id")->asString();
-				Common::String name = object.getVal("name")->asString();
-				bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
-				uint32 size = 0, timestamp = 0;
-				if (object.contains("size") && object.getVal("size")->isString())
-					size = object.getVal("size")->asString().asUint64();
-				if (object.contains("modifiedTime") && object.getVal("modifiedTime")->isString())
-					timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
-
-				//as we list directory by id, we can't determine full path for the file, so we leave it empty
-				finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
-				return;
-			}
+	if (json->isObject()) {
+		Common::JSONObject object = json->asObject();
+
+		if (object.contains("error")) {
+			warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
+			error.response = json->stringify(true);
+			finishError(error);
+			delete json;
+			return;
 		}
 
-		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
-			warning("GoogleDriveUploadRequest: no file info to return");
-			finishUpload(StorageFile(_savePath, 0, 0, false));
-		} else {
-			uploadNextPart();
+		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
+			//finished
+			Common::String id = object.getVal("id")->asString();
+			Common::String name = object.getVal("name")->asString();
+			bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
+			uint32 size = 0, timestamp = 0;
+			if (Networking::CurlJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
+				size = object.getVal("size")->asString().asUint64();
+			if (Networking::CurlJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
+				timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
+
+			finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
+			return;
 		}
+	}
+
+	if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
+		warning("GoogleDriveUploadRequest: no file info to return");
+		finishUpload(StorageFile(_savePath, 0, 0, false));
 	} else {
-		warning("GoogleDriveUploadRequest: null, not json");
-		finishError(error);
+		uploadNextPart();
 	}
 
 	delete json;


Commit: fc8e29d5832728a513cab0f10be218c6a632758a
    https://github.com/scummvm/scummvm/commit/fc8e29d5832728a513cab0f10be218c6a632758a
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update OneDrive

Added JSON checks.

New jsonContainsObject() method added to CurlJsonRequest.

Changed paths:
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/networking/curl/curljsonrequest.cpp
    backends/networking/curl/curljsonrequest.h



diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index eb04e7e..1b4b8ba 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -93,7 +93,7 @@ void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::Error
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
 	if (errorCallback == nullptr)
 		errorCallback = getErrorPrintingCallback();
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, GOOGLEDRIVE_OAUTH2_TOKEN); //TODO
+	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, GOOGLEDRIVE_OAUTH2_TOKEN);
 	if (codeFlow) {
 		request->addPostField("code=" + code);
 		request->addPostField("grant_type=authorization_code");
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index 4d6d8fc..fc7e4f5 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -101,12 +101,19 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
-	if (!json) {
-		warning("OneDriveCreateDirectoryRequest: NULL passed instead of JSON");
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
 		finishError(error);
 		return;
 	}
 
+	if (!json->isObject()) {
+		error.response = "Passed JSON is not an object!";
+		finishError(error);
+		delete json;
+		return;
+	}
+
 	Common::JSONObject info = json->asObject();
 	if (info.contains("id")) {
 		finishCreation(true);
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 9fde2ed..4b70bb7 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -116,12 +116,24 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 	Common::JSONValue *json = response.value;
 	if (!json) {
 		warning("OneDriveStorage: got NULL instead of JSON");
-		if (callback) (*callback)(BoolResponse(nullptr, false));
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
+		delete callback;
+		return;
+	}
+
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage")) {
+		if (callback)
+			(*callback)(BoolResponse(nullptr, false));
+		delete json;
+		delete callback;
 		return;
 	}
 
 	Common::JSONObject result = json->asObject();
-	if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) {
+	if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "OneDriveStorage") ||
+		!Networking::CurlJsonRequest::jsonContainsString(result, "user_id", "OneDriveStorage") ||
+		!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "OneDriveStorage")) {
 		warning("OneDriveStorage: bad response, no token or user_id passed");
 		debug(9, "%s", json->stringify().c_str());
 		if (callback)
@@ -135,6 +147,7 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp
 			(*callback)(BoolResponse(nullptr, true));
 	}
 	delete json;
+	delete callback;
 }
 
 void OneDriveStorage::codeFlowComplete(BoolResponse response) {
@@ -168,7 +181,13 @@ Common::String OneDriveStorage::name() const {
 void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
 	Common::JSONValue *json = response.value;
 	if (!json) {
-		warning("OneDriveStorage: NULL passed instead of JSON");
+		warning("OneDriveStorage::infoInnerCallback: NULL passed instead of JSON");
+		delete outerCallback;
+		return;
+	}
+
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
+		delete json;
 		delete outerCallback;
 		return;
 	}
@@ -178,16 +197,18 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 	Common::String uid, name, email;
 	uint64 quotaUsed = 0, quotaAllocated = 26843545600L; // 25 GB, because I actually don't know any way to find out the real one
 
-	if (info.contains("createdBy") && info.getVal("createdBy")->isObject()) {
+	if (Networking::CurlJsonRequest::jsonContainsObject(info, "createdBy", "OneDriveStorage::infoInnerCallback")) {
 		Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
-		if (createdBy.contains("user") && createdBy.getVal("user")->isObject()) {
+		if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
 			Common::JSONObject user = createdBy.getVal("user")->asObject();
-			uid = user.getVal("id")->asString();
-			name = user.getVal("displayName")->asString();
+			if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
+				uid = user.getVal("id")->asString();
+			if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
+				name = user.getVal("displayName")->asString();
 		}
 	}
 
-	if (info.contains("size") && info.getVal("size")->isIntegerNumber()) {
+	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "size", "OneDriveStorage::infoInnerCallback")) {
 		quotaUsed = info.getVal("size")->asIntegerNumber();
 	}
 
@@ -207,28 +228,43 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Netwo
 }
 
 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
-	if (!response.value) {
-		warning("OneDriveStorage::fileInfoCallback: NULL, not JSON");
+	Common::JSONValue *json = response.value;
+	if (!json) {
+		warning("OneDriveStorage::fileInfoCallback: NULL passed instead of JSON");
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
+		delete outerCallback;
 		return;
 	}
 
-	Common::JSONObject result = response.value->asObject();
-	if (result.contains("@content.downloadUrl")) {
-		const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
+	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
 		if (outerCallback)
-			(*outerCallback)(Networking::NetworkReadStreamResponse(
-				response.request,
-				new Networking::NetworkReadStream(url, nullptr, "")
-			));
-	} else {
+			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
+		delete json;
+		delete outerCallback;
+		return;
+	}
+
+	Common::JSONObject result = response.value->asObject();
+	if (!Networking::CurlJsonRequest::jsonContainsString(result, "@content.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
 		warning("OneDriveStorage: downloadUrl not found in passed JSON");
 		debug(9, "%s", response.value->stringify().c_str());
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
+		delete json;
+		delete outerCallback;
+		return;
 	}
-	delete response.value;
+
+	const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
+	if (outerCallback)
+		(*outerCallback)(Networking::NetworkReadStreamResponse(
+			response.request,
+			new Networking::NetworkReadStream(url, nullptr, "")
+		));
+		
+	delete json;
+	delete outerCallback;
 }
 
 Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index aac89a1..6ff35d7 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -114,6 +114,23 @@ bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningP
 	return false;
 }
 
+bool CurlJsonRequest::jsonContainsObject(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+	if (!item.contains(key)) {
+		if (isOptional) {
+			return true;
+		}
+
+		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		return false;
+	}
+
+	if (item.getVal(key)->isObject()) return true;
+
+	warning("%s: passed item's \"%s\" attribute is not an object!", warningPrefix, key);
+	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+	return false;
+}
+
 bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
index cab75bf..edd5230 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/curl/curljsonrequest.h
@@ -54,6 +54,7 @@ public:
 	virtual void restart();
 
 	static bool jsonIsObject(Common::JSONValue *item, const char *warningPrefix);
+	static bool jsonContainsObject(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
 	static bool jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
 	static bool jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
 	static bool jsonContainsArray(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);


Commit: d5aca1f4fae400250b87d35e3517416dd4f26a8f
    https://github.com/scummvm/scummvm/commit/d5aca1f4fae400250b87d35e3517416dd4f26a8f
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update OneDriveUploadRequest

More JSON checks.

Changed paths:
    backends/cloud/onedrive/onedriveuploadrequest.cpp



diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 172266c..41e6e2a 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -125,44 +125,46 @@ void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse respon
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
 	Common::JSONValue *json = response.value;
-	if (json) {
-		if (json->isObject()) {
-			Common::JSONObject object = json->asObject();
-
-			if (object.contains("error")) {
-				warning("OneDriveUploadRequest: error: %s", json->stringify(true).c_str());
-				error.response = json->stringify(true);
-				finishError(error);
-				delete json;
-				return;
-			}
-
-			if (object.contains("id") && object.contains("name")) {
-				//finished
-				Common::String path = _savePath;
-				uint32 size = object.getVal("size")->asIntegerNumber();
-				uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
-				finishUpload(StorageFile(path, size, timestamp, false));
-				return;
-			}
-
-			if (_uploadUrl == "") {
-				if (object.contains("uploadUrl"))
-					_uploadUrl = object.getVal("uploadUrl")->asString();
-				else
-					warning("OneDriveUploadRequest: no uploadUrl found");
-			}
+	if (json == nullptr) {
+		error.response = "Failed to parse JSON, null passed!";
+		finishError(error);
+		return;
+	}
+
+	if (json->isObject()) {
+		Common::JSONObject object = json->asObject();
+
+		if (object.contains("error")) {
+			warning("OneDriveUploadRequest: error: %s", json->stringify(true).c_str());
+			error.response = json->stringify(true);
+			finishError(error);
+			delete json;
+			return;
 		}
 
-		if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
-			warning("OneDriveUploadRequest: no file info to return");
-			finishUpload(StorageFile(_savePath, 0, 0, false));
-		} else {
-			uploadNextPart();
+		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
+			Networking::CurlJsonRequest::jsonContainsString(object, "lastModifiedDateTime", "OneDriveUploadRequest")) {
+			//finished
+			Common::String path = _savePath;
+			uint32 size = object.getVal("size")->asIntegerNumber();
+			uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
+			finishUpload(StorageFile(path, size, timestamp, false));
+			return;
 		}
+
+		if (_uploadUrl == "") {
+			if (Networking::CurlJsonRequest::jsonContainsString(object, "uploadUrl", "OneDriveUploadRequest"))
+				_uploadUrl = object.getVal("uploadUrl")->asString();
+		}
+	}
+
+	if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
+		warning("OneDriveUploadRequest: no file info to return");
+		finishUpload(StorageFile(_savePath, 0, 0, false));
 	} else {
-		warning("OneDriveUploadRequest: null, not json");
-		finishError(error);
+		uploadNextPart();
 	}
 
 	delete json;


Commit: bb529e6fd0bcbd69f804c599c9d685181560a337
    https://github.com/scummvm/scummvm/commit/bb529e6fd0bcbd69f804c599c9d685181560a337
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update SavesSyncRequest

Add JSON checks in the callback.

Changed paths:
    backends/cloud/savessyncrequest.cpp



diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 889c6b9..9c487bb 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -22,6 +22,8 @@
 
 #include "backends/cloud/savessyncrequest.h"
 #include "backends/cloud/cloudmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/saves/default/default-saves.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
@@ -29,7 +31,6 @@
 #include "common/savefile.h"
 #include "common/system.h"
 #include "gui/saveload-dialog.h"
-#include <backends/saves/default/default-saves.h>
 
 namespace Cloud {
 
@@ -152,7 +153,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 				Common::JSONObject object = value->asObject();
 
 				//Dropbox-related error:
-				if (object.contains("error_summary")) {
+				if (Networking::CurlJsonRequest::jsonContainsString(object, "error_summary", "SavesSyncRequest", true)) {
 					Common::String summary = object.getVal("error_summary")->asString();
 					if (summary.contains("not_found")) {
 						irrecoverable = false;
@@ -160,9 +161,9 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 				}
 
 				//OneDrive-related error:
-				if (object.contains("error") && object.getVal("error")->isObject()) {
+				if (Networking::CurlJsonRequest::jsonContainsObject(object, "error", "SavesSyncRequest", true)) {
 					Common::JSONObject errorNode = object.getVal("error")->asObject();
-					if (errorNode.contains("code") && errorNode.contains("message")) {
+					if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
 						Common::String code = errorNode.getVal("code")->asString();
 						if (code == "itemNotFound") {
 							irrecoverable = false;


Commit: 37859a92039fe6df705024f45f84e1fbc4df806d
    https://github.com/scummvm/scummvm/commit/37859a92039fe6df705024f45f84e1fbc4df806d
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix Requests

Remove unnecessary JSON warnings, fix a few places.

Changed paths:
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/savessyncrequest.cpp
    backends/networking/curl/curljsonrequest.cpp



diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index 929c9ce..5084aa5 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -165,7 +165,7 @@ void BoxUploadRequest::uploadedCallback(Networking::JsonResponse response) {
 		Common::JSONArray entries = object.getVal("entries")->asArray();
 		if (entries.size() == 0) {
 			warning("BoxUploadRequest: 'entries' found, but it's empty");
-		} else if (Networking::CurlJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
+		} else if (!Networking::CurlJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
 			warning("BoxUploadRequest: 'entries' first item is not an object");
 		} else {
 			Common::JSONObject item = entries[0]->asObject();
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 7d68510..8cc492d 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -66,7 +66,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (jsonIsObject(json, "GoogleDriveTokenRefresher")) {
 		Common::JSONObject result = json->asObject();
 		long httpResponseCode = -1;
-		if (jsonContainsAttribute(result, "error", "GoogleDriveTokenRefresher") && jsonIsObject(result.getVal("error"), "GoogleDriveTokenRefresher")) {
+		if (result.contains("error") && jsonIsObject(result.getVal("error"), "GoogleDriveTokenRefresher")) {
 			//new token needed => request token & then retry original request
 			if (_stream) {
 				httpResponseCode = _stream->httpResponseCode();
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 5ee2772..ce7895f 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -66,7 +66,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
 	if (jsonIsObject(json, "OneDriveTokenRefresher")) {
 		Common::JSONObject result = json->asObject();
 		long httpResponseCode = -1;
-		if (jsonContainsAttribute(result, "error", "OneDriveTokenRefresher") && jsonIsObject(result.getVal("error"), "OneDriveTokenRefresher")) {
+		if (result.contains("error") && jsonIsObject(result.getVal("error"), "OneDriveTokenRefresher")) {
 			//new token needed => request token & then retry original request
 			if (_stream) {
 				httpResponseCode = _stream->httpResponseCode();
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 9c487bb..fff46c3 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -153,7 +153,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 				Common::JSONObject object = value->asObject();
 
 				//Dropbox-related error:
-				if (Networking::CurlJsonRequest::jsonContainsString(object, "error_summary", "SavesSyncRequest", true)) {
+				if (object.contains("error_summary") && object.getVal("error_summary")->isString()) {
 					Common::String summary = object.getVal("error_summary")->asString();
 					if (summary.contains("not_found")) {
 						irrecoverable = false;
@@ -161,7 +161,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
 				}
 
 				//OneDrive-related error:
-				if (Networking::CurlJsonRequest::jsonContainsObject(object, "error", "SavesSyncRequest", true)) {
+				if (object.contains("error") && object.getVal("error")->isObject()) {
 					Common::JSONObject errorNode = object.getVal("error")->asObject();
 					if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
 						Common::String code = errorNode.getVal("code")->asString();
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
index 6ff35d7..1899cbd 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -109,7 +109,7 @@ bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningP
 
 	if (item->isObject()) return true;
 
-	warning("%s: passed item is not an object!", warningPrefix);
+	warning("%s: passed item is not an object", warningPrefix);
 	debug(9, "%s", item->stringify(true).c_str());
 	return false;
 }
@@ -120,13 +120,13 @@ bool CurlJsonRequest::jsonContainsObject(Common::JSONObject &item, const char *k
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 
 	if (item.getVal(key)->isObject()) return true;
 
-	warning("%s: passed item's \"%s\" attribute is not an object!", warningPrefix, key);
+	warning("%s: passed item's \"%s\" attribute is not an object", warningPrefix, key);
 	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
 	return false;
 }
@@ -137,13 +137,13 @@ bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *k
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 
 	if (item.getVal(key)->isString()) return true;
 
-	warning("%s: passed item's \"%s\" attribute is not a string!", warningPrefix, key);
+	warning("%s: passed item's \"%s\" attribute is not a string", warningPrefix, key);
 	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
 	return false;
 }
@@ -154,13 +154,13 @@ bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 
 	if (item.getVal(key)->isIntegerNumber()) return true;
 
-	warning("%s: passed item's \"%s\" attribute is not an integer!", warningPrefix, key);
+	warning("%s: passed item's \"%s\" attribute is not an integer", warningPrefix, key);
 	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
 	return false;
 }
@@ -171,13 +171,13 @@ bool CurlJsonRequest::jsonContainsArray(Common::JSONObject &item, const char *ke
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 
 	if (item.getVal(key)->isArray()) return true;
 
-	warning("%s: passed item's \"%s\" attribute is not an array!", warningPrefix, key);
+	warning("%s: passed item's \"%s\" attribute is not an array", warningPrefix, key);
 	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
 	return false;
 }
@@ -188,13 +188,13 @@ bool CurlJsonRequest::jsonContainsStringOrIntegerNumber(Common::JSONObject &item
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 
 	if (item.getVal(key)->isString() || item.getVal(key)->isIntegerNumber()) return true;
 
-	warning("%s: passed item's \"%s\" attribute is neither a string or an integer!", warningPrefix, key);
+	warning("%s: passed item's \"%s\" attribute is neither a string or an integer", warningPrefix, key);
 	debug(9, "%s", item.getVal(key)->stringify(true).c_str());
 	return false;
 }
@@ -205,7 +205,7 @@ bool CurlJsonRequest::jsonContainsAttribute(Common::JSONObject &item, const char
 			return true;
 		}
 
-		warning("%s: passed item misses the \"%s\" attribute!", warningPrefix, key);
+		warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
 		return false;
 	}
 


Commit: 9665719b6624904d05202284e5fc4024cb827332
    https://github.com/scummvm/scummvm/commit/9665719b6624904d05202284e5fc4024cb827332
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
GUI: Set tooltip of local webserver button according to server state

Changed paths:
    gui/options.cpp



diff --git a/gui/options.cpp b/gui/options.cpp
index 1e9a8c2..f798f1e 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1893,6 +1893,7 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_runServerButton->setVisible(true);
 		_runServerButton->setPos(_runServerButton->getRelX(), serverLabelPosition + serverButtonY - serverInfoY);
 		_runServerButton->setLabel(_(serverIsRunning ? "Stop server" : "Run server"));
+		_runServerButton->setTooltip(_(serverIsRunning ? "Stop local webserver" : "Run local webserver"));
 	}
 	if (_serverInfoLabel) {
 		_serverInfoLabel->setVisible(true);


Commit: b68bd78b444f9f533796ba37282c66688adb5f52
    https://github.com/scummvm/scummvm/commit/b68bd78b444f9f533796ba37282c66688adb5f52
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove unused removePathHandler(), make addPathHandler() private

Changed paths:
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 8a40aed..6da27a6 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -153,12 +153,6 @@ void LocalWebserver::addPathHandler(Common::String path, ClientHandlerCallback h
 	_pathHandlers[path] = handler;
 }
 
-void LocalWebserver::removePathHandler(Common::String path) {
-	if (!_pathHandlers.contains(path))
-		warning("LocalWebserver::removePathHandler: no handler known for this path");
-	_pathHandlers.erase(path);
-}
-
 Common::String LocalWebserver::getAddress() { return _address;  }
 
 IndexPageHandler &LocalWebserver::indexPageHandler() { return _indexPageHandler; }
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index f4c59c6..ae9445d 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -82,6 +82,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handleClient(uint32 i);
 	void acceptClient();
 	void resolveAddress(void *ipAddress);
+	void addPathHandler(Common::String path, ClientHandlerCallback handler);
 
 public:
 	static const uint32 DEFAULT_SERVER_PORT = 12345;
@@ -92,8 +93,6 @@ public:
 	void start();
 	void stop();
 	void stopOnIdle();
-	void addPathHandler(Common::String path, ClientHandlerCallback handler);
-	void removePathHandler(Common::String path);
 
 	Common::String getAddress();
 	IndexPageHandler &indexPageHandler();


Commit: 64b361b33545d2ffc1620a7bf4eb248afc9797f4
    https://github.com/scummvm/scummvm/commit/64b361b33545d2ffc1620a7bf4eb248afc9797f4
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Move determineMimeType to ResourceHandler

Changed paths:
    backends/networking/sdl_net/handlers/resourcehandler.cpp
    backends/networking/sdl_net/handlers/resourcehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
index d6c419d..410665f 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.cpp
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -43,7 +43,27 @@ void ResourceHandler::handle(Client &client) {
 	if (file == nullptr)
 		return;
 
-	LocalWebserver::setClientGetHandler(client, file, 200, LocalWebserver::determineMimeType(filename));
+	LocalWebserver::setClientGetHandler(client, file, 200, determineMimeType(filename));
+}
+
+const char *ResourceHandler::determineMimeType(Common::String &filename) {
+	// text
+	if (filename.hasSuffix(".html")) return "text/html";
+	if (filename.hasSuffix(".css")) return "text/css";
+	if (filename.hasSuffix(".txt")) return "text/plain";
+	if (filename.hasSuffix(".js")) return "application/javascript";
+
+	// images
+	if (filename.hasSuffix(".jpeg") || filename.hasSuffix(".jpg") || filename.hasSuffix(".jpe")) return "image/jpeg";
+	if (filename.hasSuffix(".gif")) return "image/gif";
+	if (filename.hasSuffix(".png")) return "image/png";
+	if (filename.hasSuffix(".svg")) return "image/svg+xml";
+	if (filename.hasSuffix(".tiff")) return "image/tiff";
+	if (filename.hasSuffix(".ico")) return "image/vnd.microsoft.icon";
+	if (filename.hasSuffix(".wbmp")) return "image/vnd.wap.wbmp";
+
+	if (filename.hasSuffix(".zip")) return "application/zip";
+	return "application/octet-stream";
 }
 
 /// public
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.h b/backends/networking/sdl_net/handlers/resourcehandler.h
index 1b4ceec..813b808 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.h
+++ b/backends/networking/sdl_net/handlers/resourcehandler.h
@@ -29,6 +29,7 @@ namespace Networking {
 
 class ResourceHandler: public BaseHandler {
 	void handle(Client &client);
+	static const char *determineMimeType(Common::String &filename);
 public:
 	ResourceHandler();
 	virtual ~ResourceHandler();
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 6da27a6..afdee55 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -368,26 +368,6 @@ void LocalWebserver::setClientRedirectHandler(Client &client, Common::SeekableRe
 	client.setHandler(handler);
 }
 
-const char *LocalWebserver::determineMimeType(Common::String &filename) {
-	// text
-	if (filename.hasSuffix(".html")) return "text/html";
-	if (filename.hasSuffix(".css")) return "text/css";
-	if (filename.hasSuffix(".txt")) return "text/plain";
-	if (filename.hasSuffix(".js")) return "application/javascript";
-
-	// images
-	if (filename.hasSuffix(".jpeg") || filename.hasSuffix(".jpg") || filename.hasSuffix(".jpe")) return "image/jpeg";
-	if (filename.hasSuffix(".gif")) return "image/gif";
-	if (filename.hasSuffix(".png")) return "image/png";
-	if (filename.hasSuffix(".svg")) return "image/svg+xml";
-	if (filename.hasSuffix(".tiff")) return "image/tiff";
-	if (filename.hasSuffix(".ico")) return "image/vnd.microsoft.icon";
-	if (filename.hasSuffix(".wbmp")) return "image/vnd.wap.wbmp";
-
-	if (filename.hasSuffix(".zip")) return "application/zip";
-	return "application/octet-stream";
-}
-
 namespace {
 int hexDigit(char c) {
 	if ('0' <= c && c <= '9') return c - '0';
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index ae9445d..f65d2a9 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -103,7 +103,6 @@ public:
 	static void setClientGetHandler(Client &client, Common::SeekableReadStream *responseStream, long code = 200, const char *mimeType = nullptr);
 	static void setClientRedirectHandler(Client &client, Common::String response, Common::String location, const char *mimeType = nullptr);
 	static void setClientRedirectHandler(Client &client, Common::SeekableReadStream *responseStream, Common::String location, const char *mimeType = nullptr);
-	static const char *determineMimeType(Common::String &filename);
 	static Common::String urlDecode(Common::String value);
 	static Common::String urlEncodeQueryParameterValue(Common::String value);
 };


Commit: 0558ba423cb9cdd5f9cd29a1c47a0530399b2399
    https://github.com/scummvm/scummvm/commit/0558ba423cb9cdd5f9cd29a1c47a0530399b2399
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix warnings

Changed paths:
    backends/cloud/storagefile.h
    backends/cloud/storageinfo.h



diff --git a/backends/cloud/storagefile.h b/backends/cloud/storagefile.h
index 1324caf..1183524 100644
--- a/backends/cloud/storagefile.h
+++ b/backends/cloud/storagefile.h
@@ -57,7 +57,7 @@ public:
 	uint32 timestamp() const { return _timestamp; }
 	bool isDirectory() const { return _isDirectory; }
 
-	void setPath(Common::String path) { _path = path; }
+	void setPath(Common::String path_) { _path = path_; }
 };
 
 } // End of namespace Cloud
diff --git a/backends/cloud/storageinfo.h b/backends/cloud/storageinfo.h
index 469f040..f1fb540 100644
--- a/backends/cloud/storageinfo.h
+++ b/backends/cloud/storageinfo.h
@@ -37,8 +37,8 @@ class StorageInfo {
 	uint64 _usedBytes, _allocatedBytes;
 
 public:
-	StorageInfo(Common::String uid, Common::String name, Common::String email, uint64 used, uint64 allocated):
-		_uid(uid), _name(name), _email(email), _usedBytes(used), _allocatedBytes(allocated) {}
+	StorageInfo(Common::String uid_, Common::String name_, Common::String email_, uint64 used_, uint64 allocated):
+		_uid(uid_), _name(name_), _email(email_), _usedBytes(used_), _allocatedBytes(allocated) {}
 
 	Common::String uid() const { return _uid; }
 	Common::String name() const { return _name; }


Commit: 4f0c071e53ed1be17bd9bdd0079e585f0a1bf55c
    https://github.com/scummvm/scummvm/commit/4f0c071e53ed1be17bd9bdd0079e585f0a1bf55c
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add custom User-Agent

Full version is used like in Eugene's Google Analytics stub. Plus, on
PS3 that string contains "PlayStation", and that would be cool to know
that ScummVM+libcurl+PS3 work together.

Changed paths:
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 9b20a75..30659e2 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -24,6 +24,7 @@
 
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/networking/curl/connectionmanager.h"
+#include "base/version.h"
 #include "common/debug.h"
 #include <curl/curl.h>
 
@@ -79,6 +80,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
@@ -122,6 +124,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);


Commit: 712410496e4e59fe1ee1968a94eeb73b51223996
    https://github.com/scummvm/scummvm/commit/712410496e4e59fe1ee1968a94eeb73b51223996
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Fix UploadFileClientHandler

It now redirects user on success not only when file was the last field
in the content, but also when it was uploaded already and Handler worked
further to search for more files.

Changed paths:
    backends/networking/sdl_net/uploadfileclienthandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.h



diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index e2832bc..ee02915 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -167,18 +167,7 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 
 		if (client->noMoreContent()) {
 			// success - redirect back to directory listing
-			HandlerUtils::setMessageHandler(
-				*client,
-				Common::String::format(
-					"%s<br/><a href=\"files?path=%s\">%s</a>",
-					_("Uploaded successfully!"),
-					client->queryParameter("path").c_str(),
-					_("Back to parent directory")
-				),
-				(client->queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
-				LocalWebserver::urlEncodeQueryParameterValue(client->queryParameter("path"))
-			);
-			_state = UFH_STOP;
+			setSuccessHandler(*client);
 			return;
 		}
 	}
@@ -189,9 +178,8 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 		if (_uploadedFiles == 0) {
 			setErrorMessageHandler(*client, _("No file was passed!"));
 		} else {
-			_state = UFH_STOP;
+			setSuccessHandler(*client);
 		}
-		return;
 	}
 }
 
@@ -200,4 +188,20 @@ void UploadFileClientHandler::setErrorMessageHandler(Client &client, Common::Str
 	_state = UFH_ERROR;
 }
 
+void UploadFileClientHandler::setSuccessHandler(Client &client) {
+	// success - redirect back to directory listing
+	HandlerUtils::setMessageHandler(
+		client,
+		Common::String::format(
+			"%s<br/><a href=\"files?path=%s\">%s</a>",
+			_("Uploaded successfully!"),
+			client.queryParameter("path").c_str(),
+			_("Back to parent directory")
+			),
+		(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
+		LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
+	);
+	_state = UFH_STOP;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.h b/backends/networking/sdl_net/uploadfileclienthandler.h
index 4aa6929..b6481cf 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.h
+++ b/backends/networking/sdl_net/uploadfileclienthandler.h
@@ -56,6 +56,7 @@ class UploadFileClientHandler: public ClientHandler {
 	void handleBlockHeaders(Client *client);
 	void handleBlockContent(Client *client);
 	void setErrorMessageHandler(Client &client, Common::String message);
+	void setSuccessHandler(Client &client);
 
 public:
 	UploadFileClientHandler(Common::String parentDirectoryPath);


Commit: a1de322c18d9efdf885ea5b760a404237dcdc0a8
    https://github.com/scummvm/scummvm/commit/a1de322c18d9efdf885ea5b760a404237dcdc0a8
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use overriden handle() instead of ClientHandlerCallback in page handlers

Using a dedicated callback object for this was an unnecessary overhead.

Changed paths:
    backends/networking/sdl_net/handlers/basehandler.h
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/createdirectoryhandler.h
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.h
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.h
    backends/networking/sdl_net/handlers/filesbasehandler.h
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.h
    backends/networking/sdl_net/handlers/indexpagehandler.cpp
    backends/networking/sdl_net/handlers/indexpagehandler.h
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.h
    backends/networking/sdl_net/handlers/resourcehandler.cpp
    backends/networking/sdl_net/handlers/resourcehandler.h
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h



diff --git a/backends/networking/sdl_net/handlers/basehandler.h b/backends/networking/sdl_net/handlers/basehandler.h
index fd55b13..bf53268 100644
--- a/backends/networking/sdl_net/handlers/basehandler.h
+++ b/backends/networking/sdl_net/handlers/basehandler.h
@@ -24,18 +24,15 @@
 #define BACKENDS_NETWORKING_SDL_NET_BASEHANDLER_H
 
 #include "backends/networking/sdl_net/client.h"
-#include "common/callback.h"
 
 namespace Networking {
 
-typedef Common::BaseCallback<Client &> *ClientHandlerCallback;
-
 class BaseHandler {
 public:
 	BaseHandler() {}
 	virtual ~BaseHandler() {}
 
-	virtual ClientHandlerCallback getHandler() = 0;
+	virtual void handle(Client &) = 0;
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index 474ac74..53e58b4 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -25,6 +25,7 @@
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
 #include "common/translation.h"
+#include <common/callback.h>
 
 namespace Networking {
 
@@ -32,6 +33,24 @@ CreateDirectoryHandler::CreateDirectoryHandler() {}
 
 CreateDirectoryHandler::~CreateDirectoryHandler() {}
 
+void CreateDirectoryHandler::handleError(Client &client, Common::String message) const {
+	if (client.queryParameter("answer_json") == "true")
+		setJsonResponseHandler(client, "error", message);
+	else
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
+}
+
+void CreateDirectoryHandler::setJsonResponseHandler(Client &client, Common::String type, Common::String message) const {
+	Common::JSONObject response;
+	response.setVal("type", new Common::JSONValue(type));
+	response.setVal("message", new Common::JSONValue(message));
+
+	Common::JSONValue json = response;
+	LocalWebserver::setClientGetHandler(client, json.stringify(true));
+}
+
+/// public
+
 void CreateDirectoryHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
 	Common::String name = client.queryParameter("directory_name");
@@ -97,26 +116,4 @@ void CreateDirectoryHandler::handle(Client &client) {
 	);
 }
 
-void CreateDirectoryHandler::handleError(Client &client, Common::String message) const {
-	if (client.queryParameter("answer_json") == "true")
-		setJsonResponseHandler(client, "error", message);
-	else
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, message);
-}
-
-void CreateDirectoryHandler::setJsonResponseHandler(Client &client, Common::String type, Common::String message) const {
-	Common::JSONObject response;
-	response.setVal("type", new Common::JSONValue(type));
-	response.setVal("message", new Common::JSONValue(message));
-
-	Common::JSONValue json = response;
-	LocalWebserver::setClientGetHandler(client, json.stringify(true));
-}
-
-/// public
-
-ClientHandlerCallback CreateDirectoryHandler::getHandler() {
-	return new Common::Callback<CreateDirectoryHandler, Client &>(this, &CreateDirectoryHandler::handle);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.h b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
index bd0e887..2a18d5c 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.h
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.h
@@ -28,14 +28,13 @@
 namespace Networking {
 
 class CreateDirectoryHandler: public FilesBaseHandler {
-	void handle(Client &client);
 	void handleError(Client &client, Common::String message) const;
 	void setJsonResponseHandler(Client &client, Common::String type, Common::String message) const;
 public:
 	CreateDirectoryHandler();
 	virtual ~CreateDirectoryHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 20577de..295acce 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -33,6 +33,8 @@ DownloadFileHandler::DownloadFileHandler() {}
 
 DownloadFileHandler::~DownloadFileHandler() {}
 
+/// public
+
 void DownloadFileHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
 
@@ -73,10 +75,4 @@ void DownloadFileHandler::handle(Client &client) {
 	client.setHandler(handler);
 }
 
-/// public
-
-ClientHandlerCallback DownloadFileHandler::getHandler() {
-	return new Common::Callback<DownloadFileHandler, Client &>(this, &DownloadFileHandler::handle);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.h b/backends/networking/sdl_net/handlers/downloadfilehandler.h
index afcedec..5fa5e5d 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.h
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.h
@@ -28,12 +28,11 @@
 namespace Networking {
 
 class DownloadFileHandler: public FilesBaseHandler {
-	void handle(Client &client);
 public:
 	DownloadFileHandler();
 	virtual ~DownloadFileHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index e0ecae9..8c5ee29 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -50,6 +50,8 @@ Common::String encodeDoubleQuotesAndSlashes(Common::String s) {
 
 }
 
+/// public
+
 void FilesAjaxPageHandler::handle(Client &client) {
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
@@ -76,10 +78,4 @@ void FilesAjaxPageHandler::handle(Client &client) {
 	LocalWebserver::setClientGetHandler(client, response);
 }
 
-/// public
-
-ClientHandlerCallback FilesAjaxPageHandler::getHandler() {
-	return new Common::Callback<FilesAjaxPageHandler, Client &>(this, &FilesAjaxPageHandler::handle);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.h b/backends/networking/sdl_net/handlers/filesajaxpagehandler.h
index 8e39ac0..1d9b125 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.h
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.h
@@ -28,13 +28,11 @@
 namespace Networking {
 
 class FilesAjaxPageHandler: public FilesBaseHandler {
-	void handle(Client &client);
-
 public:
 	FilesAjaxPageHandler();
 	virtual ~FilesAjaxPageHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.h b/backends/networking/sdl_net/handlers/filesbasehandler.h
index a960181..1c7f4dd 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.h
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.h
@@ -43,6 +43,8 @@ protected:
 public:
 	FilesBaseHandler();
 	virtual ~FilesBaseHandler();
+
+	virtual void handle(Client &client) = 0;
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index abe6f42..d79c9b3 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -74,61 +74,6 @@ Common::String getDisplayPath(Common::String s) {
 }
 }
 
-void FilesPageHandler::handle(Client &client) {
-	Common::String response =
-		"<html>" \
-			"<head><title>ScummVM</title></head>" \
-			"<body>" \
-				"<p>{create_directory_desc}</p>" \
-				"<form action=\"create\">" \
-					"<input type=\"hidden\" name=\"path\" value=\"{path}\"/>" \
-					"<input type=\"text\" name=\"directory_name\" value=\"\"/>" \
-					"<input type=\"submit\" value=\"{create_directory_button}\"/>" \
-				"</form>" \
-				"<hr/>" \
-				"<p>{upload_file_desc}</p>" \
-				"<form action=\"upload?path={path}\" method=\"post\" enctype=\"multipart/form-data\">" \
-					"<input type=\"file\" name=\"upload_file-f\" allowdirs multiple/>" \
-					"<span>{or_upload_directory_desc}</span>" \
-					"<input type=\"file\" name=\"upload_file-d\" directory webkitdirectory multiple/>" \
-					"<input type=\"submit\" value=\"{upload_file_button}\"/>" \
-				"</form>"
-				"<hr/>" \
-				"<h1>{index_of_directory}</h1>" \
-				"<table>{content}</table>" \
-			"</body>" \
-		"</html>";
-	Common::String itemTemplate = "<tr><td><img src=\"icons/{icon}\"/></td><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
-
-	// load stylish response page from the archive
-	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
-	if (stream)
-		response = HandlerUtils::readEverythingFromStream(stream);
-
-	Common::String path = client.queryParameter("path");
-	Common::String content = "";
-
-	// show an error message if failed to list directory
-	if (!listDirectory(path, content, itemTemplate)) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ScummVM couldn't list the directory you specified."));
-		return;
-	}
-
-	//these occur twice:
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
-	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
-	replace(response, "{upload_files_button}", _("Upload files")); //tab
-	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
-	replace(response, "{create_directory_desc}", _("Type new directory name:"));
-	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
-	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
-	replace(response, "{index_of_directory}", Common::String::format(_("Index of %s"), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
-	replace(response, "{content}", content);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
 		addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
@@ -225,8 +170,59 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
 
 /// public
 
-ClientHandlerCallback FilesPageHandler::getHandler() {
-	return new Common::Callback<FilesPageHandler, Client &>(this, &FilesPageHandler::handle);
+void FilesPageHandler::handle(Client &client) {
+	Common::String response =
+		"<html>" \
+		"<head><title>ScummVM</title></head>" \
+		"<body>" \
+		"<p>{create_directory_desc}</p>" \
+		"<form action=\"create\">" \
+		"<input type=\"hidden\" name=\"path\" value=\"{path}\"/>" \
+		"<input type=\"text\" name=\"directory_name\" value=\"\"/>" \
+		"<input type=\"submit\" value=\"{create_directory_button}\"/>" \
+		"</form>" \
+		"<hr/>" \
+		"<p>{upload_file_desc}</p>" \
+		"<form action=\"upload?path={path}\" method=\"post\" enctype=\"multipart/form-data\">" \
+		"<input type=\"file\" name=\"upload_file-f\" allowdirs multiple/>" \
+		"<span>{or_upload_directory_desc}</span>" \
+		"<input type=\"file\" name=\"upload_file-d\" directory webkitdirectory multiple/>" \
+		"<input type=\"submit\" value=\"{upload_file_button}\"/>" \
+		"</form>"
+		"<hr/>" \
+		"<h1>{index_of_directory}</h1>" \
+		"<table>{content}</table>" \
+		"</body>" \
+		"</html>";
+	Common::String itemTemplate = "<tr><td><img src=\"icons/{icon}\"/></td><td><a href=\"{link}\">{name}</a></td><td>{size}</td></tr>\n"; //TODO: load this template too?
+
+																																		  // load stylish response page from the archive
+	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
+	if (stream)
+		response = HandlerUtils::readEverythingFromStream(stream);
+
+	Common::String path = client.queryParameter("path");
+	Common::String content = "";
+
+	// show an error message if failed to list directory
+	if (!listDirectory(path, content, itemTemplate)) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ScummVM couldn't list the directory you specified."));
+		return;
+	}
+
+	//these occur twice:
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
+	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
+	replace(response, "{upload_files_button}", _("Upload files")); //tab
+	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
+	replace(response, "{create_directory_desc}", _("Type new directory name:"));
+	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
+	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
+	replace(response, "{index_of_directory}", Common::String::format(_("Index of %s"), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
+	replace(response, "{content}", content);
+	LocalWebserver::setClientGetHandler(client, response);
 }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.h b/backends/networking/sdl_net/handlers/filespagehandler.h
index c66b79e..e404036 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.h
+++ b/backends/networking/sdl_net/handlers/filespagehandler.h
@@ -37,8 +37,6 @@ class FilesPageHandler: public FilesBaseHandler {
 		IT_UNKNOWN
 	};
 
-	void handle(Client &client);
-
 	/**
 	 * Lists the directory <path>.
 	 *
@@ -56,7 +54,7 @@ public:
 	FilesPageHandler();
 	virtual ~FilesPageHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 116090b..1c14e7d 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -32,6 +32,10 @@ IndexPageHandler::IndexPageHandler(): CommandSender(nullptr) {}
 
 IndexPageHandler::~IndexPageHandler() {}
 
+/// public
+
+Common::String IndexPageHandler::code() const { return _code; }
+
 void IndexPageHandler::handle(Client &client) {
 	Common::String code = client.queryParameter("code");
 
@@ -54,12 +58,4 @@ void IndexPageHandler::handle(Client &client) {
 	HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!"));
 }
 
-/// public
-
-Common::String IndexPageHandler::code() const { return _code; }
-
-ClientHandlerCallback IndexPageHandler::getHandler() {
-	return new Common::Callback<IndexPageHandler, Client &>(this, &IndexPageHandler::handle);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.h b/backends/networking/sdl_net/handlers/indexpagehandler.h
index 0e256b5..ad4102c 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.h
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.h
@@ -31,14 +31,12 @@ class LocalWebserver;
 
 class IndexPageHandler: public BaseHandler, public GUI::CommandSender {
 	Common::String _code;
-
-	void handle(Client &client);
 public:
 	IndexPageHandler();
 	virtual ~IndexPageHandler();
 
 	Common::String code() const;
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index de349bd..f0bf792 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -32,13 +32,6 @@ ListAjaxHandler::ListAjaxHandler() {}
 
 ListAjaxHandler::~ListAjaxHandler() {}
 
-void ListAjaxHandler::handle(Client &client) {
-	Common::String path = client.queryParameter("path");
-	Common::JSONValue jsonResponse = listDirectory(path);
-	Common::String response = jsonResponse.stringify(true);
-	LocalWebserver::setClientGetHandler(client, response);
-}
-
 Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 	Common::JSONArray itemsList;
 	Common::JSONObject errorResult;
@@ -145,8 +138,11 @@ void ListAjaxHandler::addItem(Common::JSONArray &responseItemsList, ItemType ite
 
 /// public
 
-ClientHandlerCallback ListAjaxHandler::getHandler() {
-	return new Common::Callback<ListAjaxHandler, Client &>(this, &ListAjaxHandler::handle);
+void ListAjaxHandler::handle(Client &client) {
+	Common::String path = client.queryParameter("path");
+	Common::JSONValue jsonResponse = listDirectory(path);
+	Common::String response = jsonResponse.stringify(true);
+	LocalWebserver::setClientGetHandler(client, response);
 }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.h b/backends/networking/sdl_net/handlers/listajaxhandler.h
index c157e1b..40840ad 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.h
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.h
@@ -38,8 +38,6 @@ class ListAjaxHandler: public FilesBaseHandler {
 		IT_UNKNOWN
 	};
 
-	void handle(Client &client);
-
 	/**
 	 * Lists the directory <path>.
 	 *
@@ -57,7 +55,7 @@ public:
 	ListAjaxHandler();
 	virtual ~ListAjaxHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
index 410665f..890c2a7 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.cpp
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -30,22 +30,6 @@ ResourceHandler::ResourceHandler() {}
 
 ResourceHandler::~ResourceHandler() {}
 
-void ResourceHandler::handle(Client &client) {
-	Common::String filename = client.path();
-	filename.deleteChar(0);
-
-	// if archive hidden file is requested, ignore
-	if (filename.size() && filename[0] == '.')
-		return;
-
-	// if file not found, don't set handler either
-	Common::SeekableReadStream *file = HandlerUtils::getArchiveFile(filename);
-	if (file == nullptr)
-		return;
-
-	LocalWebserver::setClientGetHandler(client, file, 200, determineMimeType(filename));
-}
-
 const char *ResourceHandler::determineMimeType(Common::String &filename) {
 	// text
 	if (filename.hasSuffix(".html")) return "text/html";
@@ -68,8 +52,20 @@ const char *ResourceHandler::determineMimeType(Common::String &filename) {
 
 /// public
 
-ClientHandlerCallback ResourceHandler::getHandler() {
-	return new Common::Callback<ResourceHandler, Client &>(this, &ResourceHandler::handle);
+void ResourceHandler::handle(Client &client) {
+	Common::String filename = client.path();
+	filename.deleteChar(0);
+
+	// if archive hidden file is requested, ignore
+	if (filename.size() && filename[0] == '.')
+		return;
+
+	// if file not found, don't set handler either
+	Common::SeekableReadStream *file = HandlerUtils::getArchiveFile(filename);
+	if (file == nullptr)
+		return;
+
+	LocalWebserver::setClientGetHandler(client, file, 200, determineMimeType(filename));
 }
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.h b/backends/networking/sdl_net/handlers/resourcehandler.h
index 813b808..8a1a158 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.h
+++ b/backends/networking/sdl_net/handlers/resourcehandler.h
@@ -28,13 +28,12 @@
 namespace Networking {
 
 class ResourceHandler: public BaseHandler {
-	void handle(Client &client);
 	static const char *determineMimeType(Common::String &filename);
 public:
 	ResourceHandler();
 	virtual ~ResourceHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index 5642e3f..02a7465 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -33,6 +33,8 @@ UploadFileHandler::UploadFileHandler() {}
 
 UploadFileHandler::~UploadFileHandler() {}
 
+/// public
+
 void UploadFileHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
 
@@ -64,10 +66,4 @@ void UploadFileHandler::handle(Client &client) {
 	client.setHandler(new UploadFileClientHandler(path));
 }
 
-/// public
-
-ClientHandlerCallback UploadFileHandler::getHandler() {
-	return new Common::Callback<UploadFileHandler, Client &>(this, &UploadFileHandler::handle);
-}
-
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.h b/backends/networking/sdl_net/handlers/uploadfilehandler.h
index 4cd6115..cbff215 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.h
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.h
@@ -28,13 +28,11 @@
 namespace Networking {
 
 class UploadFileHandler: public FilesBaseHandler {
-	void handle(Client &client);
-
 public:
 	UploadFileHandler();
 	virtual ~UploadFileHandler();
 
-	virtual ClientHandlerCallback getHandler();
+	virtual void handle(Client &client);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index afdee55..abf4369 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -50,14 +50,14 @@ namespace Networking {
 
 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
 	_stopOnIdle(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
-	addPathHandler("/", _indexPageHandler.getHandler());
-	addPathHandler("/files", _filesPageHandler.getHandler());
-	addPathHandler("/create", _createDirectoryHandler.getHandler());
-	addPathHandler("/download", _downloadFileHandler.getHandler());
-	addPathHandler("/upload", _uploadFileHandler.getHandler());
-	addPathHandler("/list", _listAjaxHandler.getHandler());
-	addPathHandler("/filesAJAX", _filesAjaxPageHandler.getHandler());
-	_defaultHandler = _resourceHandler.getHandler();
+	addPathHandler("/", &_indexPageHandler);
+	addPathHandler("/files", &_filesPageHandler);
+	addPathHandler("/create", &_createDirectoryHandler);
+	addPathHandler("/download", &_downloadFileHandler);
+	addPathHandler("/upload", &_uploadFileHandler);
+	addPathHandler("/list", &_listAjaxHandler);
+	addPathHandler("/filesAJAX", &_filesAjaxPageHandler);
+	_defaultHandler = &_resourceHandler;
 }
 
 LocalWebserver::~LocalWebserver() {
@@ -147,7 +147,7 @@ void LocalWebserver::stop() {
 
 void LocalWebserver::stopOnIdle() { _stopOnIdle = true; }
 
-void LocalWebserver::addPathHandler(Common::String path, ClientHandlerCallback handler) {
+void LocalWebserver::addPathHandler(Common::String path, BaseHandler *handler) {
 	if (_pathHandlers.contains(path))
 		warning("LocalWebserver::addPathHandler: path already had a handler");
 	_pathHandlers[path] = handler;
@@ -215,9 +215,9 @@ void LocalWebserver::handleClient(uint32 i) {
 		//if GET, check whether we know a handler for such URL
 		//if PUT, check whether we know a handler for that URL
 		if (_pathHandlers.contains(_client[i].path()))
-			(*_pathHandlers[_client[i].path()])(_client[i]);
+			_pathHandlers[_client[i].path()]->handle(_client[i]);
 		else if (_defaultHandler)
-			(*_defaultHandler)(_client[i]); //try default handler
+			_defaultHandler->handle(_client[i]); //try default handler
 
 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID)
 			break;
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index f65d2a9..5f5f7ef 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -61,8 +61,8 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	Client _client[MAX_CONNECTIONS];
 	int _clients;
 	bool _timerStarted, _stopOnIdle;
-	Common::HashMap<Common::String, ClientHandlerCallback> _pathHandlers;
-	ClientHandlerCallback _defaultHandler;
+	Common::HashMap<Common::String, BaseHandler*> _pathHandlers;
+	BaseHandler *_defaultHandler;
 	IndexPageHandler _indexPageHandler;
 	FilesPageHandler _filesPageHandler;
 	CreateDirectoryHandler _createDirectoryHandler;
@@ -82,7 +82,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	void handleClient(uint32 i);
 	void acceptClient();
 	void resolveAddress(void *ipAddress);
-	void addPathHandler(Common::String path, ClientHandlerCallback handler);
+	void addPathHandler(Common::String path, BaseHandler *handler);
 
 public:
 	static const uint32 DEFAULT_SERVER_PORT = 12345;


Commit: 126fe9c8457b95b13b06eb457f7ce445b031e26b
    https://github.com/scummvm/scummvm/commit/126fe9c8457b95b13b06eb457f7ce445b031e26b
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add "minimal mode" in LocalWebserver

StorageWizardDialog now runs LocalWebserver in "minimal mode" for
security reasons. In this mode server uses only those handlers which
state to support it.

There are two handlers which support minimal mode: IndexPageHandler
(which handles `code` requests needed by StorageWizardDialog) and
ResourceHandler (which provides inner resources like `style.css` or
`logo.png` from `wwwroot.zip` archive).

Changed paths:
    backends/networking/sdl_net/handlers/basehandler.h
    backends/networking/sdl_net/handlers/indexpagehandler.cpp
    backends/networking/sdl_net/handlers/indexpagehandler.h
    backends/networking/sdl_net/handlers/resourcehandler.cpp
    backends/networking/sdl_net/handlers/resourcehandler.h
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    gui/storagewizarddialog.cpp



diff --git a/backends/networking/sdl_net/handlers/basehandler.h b/backends/networking/sdl_net/handlers/basehandler.h
index bf53268..dec5e95 100644
--- a/backends/networking/sdl_net/handlers/basehandler.h
+++ b/backends/networking/sdl_net/handlers/basehandler.h
@@ -33,6 +33,7 @@ public:
 	virtual ~BaseHandler() {}
 
 	virtual void handle(Client &) = 0;
+	virtual bool minimalModeSupported() { return false; }
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 1c14e7d..985bd66 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -58,4 +58,8 @@ void IndexPageHandler::handle(Client &client) {
 	HandlerUtils::setMessageHandler(client, _("ScummVM got the code and already connects to your cloud storage!"));
 }
 
+bool IndexPageHandler::minimalModeSupported() {
+	return true;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.h b/backends/networking/sdl_net/handlers/indexpagehandler.h
index ad4102c..8065954 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.h
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.h
@@ -37,6 +37,7 @@ public:
 
 	Common::String code() const;
 	virtual void handle(Client &client);
+	virtual bool minimalModeSupported();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.cpp b/backends/networking/sdl_net/handlers/resourcehandler.cpp
index 890c2a7..631eb63 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.cpp
+++ b/backends/networking/sdl_net/handlers/resourcehandler.cpp
@@ -68,4 +68,8 @@ void ResourceHandler::handle(Client &client) {
 	LocalWebserver::setClientGetHandler(client, file, 200, determineMimeType(filename));
 }
 
+bool ResourceHandler::minimalModeSupported() {
+	return true;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/handlers/resourcehandler.h b/backends/networking/sdl_net/handlers/resourcehandler.h
index 8a1a158..2ec4c5b 100644
--- a/backends/networking/sdl_net/handlers/resourcehandler.h
+++ b/backends/networking/sdl_net/handlers/resourcehandler.h
@@ -34,6 +34,7 @@ public:
 	virtual ~ResourceHandler();
 
 	virtual void handle(Client &client);
+	virtual bool minimalModeSupported();
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index abf4369..6557c7a 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -49,7 +49,7 @@ DECLARE_SINGLETON(Networking::LocalWebserver);
 namespace Networking {
 
 LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerStarted(false),
-	_stopOnIdle(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
+	_stopOnIdle(false), _minimalMode(false), _clients(0), _idlingFrames(0), _serverPort(DEFAULT_SERVER_PORT) {
 	addPathHandler("/", &_indexPageHandler);
 	addPathHandler("/files", &_filesPageHandler);
 	addPathHandler("/create", &_createDirectoryHandler);
@@ -83,7 +83,7 @@ void LocalWebserver::stopTimer() {
 	_timerStarted = false;
 }
 
-void LocalWebserver::start() {
+void LocalWebserver::start(bool useMinimalMode) {
 	_handleMutex.lock();
 	_serverPort = getPort();
 	_stopOnIdle = false;
@@ -91,6 +91,7 @@ void LocalWebserver::start() {
 		_handleMutex.unlock();
 		return;
 	}
+	_minimalMode = useMinimalMode;
 	startTimer();
 
 	// Create a listening TCP socket
@@ -211,18 +212,28 @@ void LocalWebserver::handleClient(uint32 i) {
 	case READING_HEADERS:
 		_client[i].readHeaders();
 		break;
-	case READ_HEADERS: //decide what to do next with that client
-		//if GET, check whether we know a handler for such URL
-		//if PUT, check whether we know a handler for that URL
-		if (_pathHandlers.contains(_client[i].path()))
-			_pathHandlers[_client[i].path()]->handle(_client[i]);
-		else if (_defaultHandler)
-			_defaultHandler->handle(_client[i]); //try default handler
+	case READ_HEADERS: {
+		// decide what to do next with that client
+		// check whether we know a handler for such URL
+		BaseHandler *handler = nullptr;
+		if (_pathHandlers.contains(_client[i].path())) {
+			handler = _pathHandlers[_client[i].path()];
+		} else {
+			// try default handler
+			handler = _defaultHandler;
+		}
+
+		// if server's in "minimal mode", only handlers which support it are used
+		if (handler && (!_minimalMode || handler->minimalModeSupported()))
+			handler->handle(_client[i]);
 
 		if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID)
 			break;
-		//if no handler, answer with default BAD REQUEST
-		//fallthrough
+
+		// if no handler, answer with default BAD REQUEST
+		// fallthrough
+	}
+
 	case BAD_REQUEST:
 		setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
 		break;
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index 5f5f7ef..c6cf848 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -60,7 +60,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	TCPsocket _serverSocket;
 	Client _client[MAX_CONNECTIONS];
 	int _clients;
-	bool _timerStarted, _stopOnIdle;
+	bool _timerStarted, _stopOnIdle, _minimalMode;
 	Common::HashMap<Common::String, BaseHandler*> _pathHandlers;
 	BaseHandler *_defaultHandler;
 	IndexPageHandler _indexPageHandler;
@@ -90,7 +90,7 @@ public:
 	LocalWebserver();
 	virtual ~LocalWebserver();
 
-	void start();
+	void start(bool useMinimalMode = false);
 	void stop();
 	void stopOnIdle();
 
diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp
index dd1a3aa..60f31d1 100644
--- a/gui/storagewizarddialog.cpp
+++ b/gui/storagewizarddialog.cpp
@@ -132,7 +132,7 @@ void StorageWizardDialog::open() {
 #ifdef USE_SDL_NET
 	if (Cloud::CloudManager::couldUseLocalServer()) {
 		_stopServerOnClose = !LocalServer.isRunning();
-		LocalServer.start();
+		LocalServer.start(true); // using "minimal mode" (no "/files", "/download", etc available)
 		LocalServer.indexPageHandler().setTarget(this);
 	}
 #endif


Commit: dd9e5a95dc5bbae20d3da05d638139120f3113f4
    https://github.com/scummvm/scummvm/commit/dd9e5a95dc5bbae20d3da05d638139120f3113f4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Mark places where path handling is needed

Changed paths:
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index 53e58b4..c539525 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -68,6 +68,8 @@ void CreateDirectoryHandler::handle(Client &client) {
 		return;
 	}
 
+	// TODO: handle <path>
+
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 295acce..8f5633c 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -51,6 +51,8 @@ void DownloadFileHandler::handle(Client &client) {
 		return;
 	}
 
+	// TODO: handle <path>
+
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index d79c9b3..56b0551 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -85,9 +85,14 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 	if (!transformPath(path, prefixToRemove, prefixToAdd))
 		return false;
 
+	// TODO: handle <path>
+
 	Common::FSNode node = Common::FSNode(path);
 	if (path == "/")
 		node = node.getParent(); // absolute root
+
+	// TODO: handle <path>
+
 	if (!node.isDirectory())
 		return false;
 
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index f0bf792..ad8907c 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -50,9 +50,14 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 	if (!transformPath(path, prefixToRemove, prefixToAdd))
 		return errorResult;
 
+	// TODO: handle <path>
+
 	Common::FSNode node = Common::FSNode(path);
 	if (path == "/")
 		node = node.getParent(); // absolute root
+
+	// TODO: handle <path>
+
 	if (!node.isDirectory())
 		return errorResult;
 
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index 02a7465..22896a2 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -51,6 +51,8 @@ void UploadFileHandler::handle(Client &client) {
 		return;
 	}
 
+	// TODO: handle <path>
+
 	// check that <path> exists and is directory
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!node->exists()) {
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index ee02915..cbc1b7f 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -127,6 +127,8 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	if (filename.empty())
 		return;
 
+	// TODO: handle <filename>, <path> + <filename>
+
 	// check that <path>/<filename> doesn't exist
 	Common::String path = _parentDirectoryPath;
 	if (path.lastChar() != '/' && path.lastChar() != '\\')


Commit: acfa1d1f1069e4a4bbed8599d0e6b4e9b2ea37fe
    https://github.com/scummvm/scummvm/commit/acfa1d1f1069e4a4bbed8599d0e6b4e9b2ea37fe
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Handle paths in marked places

Paths containing '../' are forbidden to use in Files Manager. There is
also a special inner black list of paths which are not used and a check
that specified path is under "savepath" or "rootpath" (from "cloud"
domain).

Changed paths:
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/handlerutils.h
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index c539525..284bf16 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -61,6 +61,12 @@ void CreateDirectoryHandler::handle(Client &client) {
 		return;
 	}
 
+	// check that <path> contains no '../'
+	if (HandlerUtils::hasForbiddenCombinations(path)) {
+		handleError(client, _("Invalid path!"));
+		return;
+	}
+
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
@@ -68,10 +74,12 @@ void CreateDirectoryHandler::handle(Client &client) {
 		return;
 	}
 
-	// TODO: handle <path>
-
-	// check that <path> exists and is directory
+	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!HandlerUtils::permittedPath(node->getPath())) {
+		handleError(client, _("Invalid path!"));
+		return;
+	}
 	if (!node->exists()) {
 		handleError(client, _("Parent directory doesn't exists!"));
 		return;
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 8f5633c..9e212b1 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -44,6 +44,12 @@ void DownloadFileHandler::handle(Client &client) {
 		return;
 	}
 
+	// check that <path> contains no '../'
+	if (HandlerUtils::hasForbiddenCombinations(path)) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
+	}
+
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
@@ -51,10 +57,12 @@ void DownloadFileHandler::handle(Client &client) {
 		return;
 	}
 
-	// TODO: handle <path>
-
-	// check that <path> exists and is directory
+	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!HandlerUtils::permittedPath(node->getPath())) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
+	}
 	if (!node->exists()) {
 		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!"));
 		return;
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index 68d7919..5e5787b 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -52,12 +52,16 @@ bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefi
 
 	if (path.hasPrefix("/root")) {
 		prefixToAdd = "/root/";
-		prefixToRemove = "";
+		prefixToRemove = (ConfMan.hasKey("rootpath", "cloud") ? ConfMan.get("rootpath", "cloud") : "");
+		if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
+			prefixToRemove += '/';
+		if (prefixToRemove == "/") prefixToRemove = "";
 		path.erase(0, 5);
+		if (path.size() && (path[0] == '/' || path[0] == '\\'))
+			path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
+		path = prefixToRemove + path; 
 		if (path == "")
 			path = "/"; // absolute root is '/'
-		if (path != "/")
-			path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
 		return true;
 	}
 
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 56b0551..0bc9237 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -81,17 +81,19 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 		return true;
 	}
 
+	if (HandlerUtils::hasForbiddenCombinations(path))
+		return false;
+
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd))
 		return false;
 
-	// TODO: handle <path>
-
 	Common::FSNode node = Common::FSNode(path);
 	if (path == "/")
 		node = node.getParent(); // absolute root
 
-	// TODO: handle <path>
+	if (!HandlerUtils::permittedPath(node.getPath()))
+		return false;
 
 	if (!node.isDirectory())
 		return false;
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index ad8907c..57fa04d 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -46,17 +46,19 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 		return successResult;
 	}
 
+	if (HandlerUtils::hasForbiddenCombinations(path))
+		return errorResult;
+
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd))
 		return errorResult;
 
-	// TODO: handle <path>
-
 	Common::FSNode node = Common::FSNode(path);
 	if (path == "/")
 		node = node.getParent(); // absolute root
 
-	// TODO: handle <path>
+	if (!HandlerUtils::permittedPath(node.getPath()))
+		return errorResult;
 
 	if (!node.isDirectory())
 		return errorResult;
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index 22896a2..a0e992c 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -44,6 +44,12 @@ void UploadFileHandler::handle(Client &client) {
 		return;
 	}
 
+	// check that <path> contains no '../'
+	if (HandlerUtils::hasForbiddenCombinations(path)) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
+	}
+
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
@@ -51,10 +57,12 @@ void UploadFileHandler::handle(Client &client) {
 		return;
 	}
 
-	// TODO: handle <path>
-
-	// check that <path> exists and is directory
+	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
+	if (!HandlerUtils::permittedPath(node->getPath())) {
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		return;
+	}
 	if (!node->exists()) {
 		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!"));
 		return;
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index edb1484..f0c62d3 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -22,6 +22,7 @@
 
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/saves/default/default-saves.h"
 #include "common/archive.h"
 #include "common/config-manager.h"
 #include "common/file.h"
@@ -98,6 +99,75 @@ Common::String HandlerUtils::readEverythingFromStream(Common::SeekableReadStream
 	return result;
 }
 
+Common::String HandlerUtils::normalizePath(const Common::String &path) {
+	Common::String normalized;
+	bool slash = false;
+	for (uint32 i = 0; i < path.size(); ++i) {
+		char c = path[i];
+		if (c == '\\' || c == '/') {
+			slash = true;
+			continue;
+		}
+
+		if (slash) {
+			normalized += '/';
+			slash = false;
+		}
+
+		if ('A' <= c && c <= 'Z') {
+			normalized += c - 'A' + 'a';
+		} else {
+			normalized += c;
+		}
+	}
+	if (slash) normalized += '/';
+	return normalized;
+}
+
+bool HandlerUtils::hasForbiddenCombinations(const Common::String &path) {
+	return (path.contains("../") || path.contains("..\\"));
+}
+
+bool HandlerUtils::isBlacklisted(const Common::String &path) {
+	const char *blacklist[] = {
+		"/etc",
+		"/bin",
+		"c:/windows" // just saying: I know guys who install windows on another drives
+	};
+
+	// normalize path
+	Common::String normalized = normalizePath(path);
+
+	uint32 size = sizeof(blacklist) / sizeof(const char *);
+	for (uint32 i = 0; i < size; ++i)
+		if (normalized.hasPrefix(blacklist[i]))
+			return true;
+
+	return false;
+}
+
+bool HandlerUtils::hasPermittedPrefix(const Common::String &path) {
+	// normalize path
+	Common::String normalized = normalizePath(path);
+
+	// prefix for /root/
+	Common::String prefix;
+	if (ConfMan.hasKey("rootpath", "cloud")) {
+		prefix = normalizePath(ConfMan.get("rootpath", "cloud"));
+		if (prefix == "/" || normalized.hasPrefix(prefix))
+			return true;
+	}
+
+	// prefix for /saves/
+	DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
+	prefix = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
+	return (normalized.hasPrefix(normalizePath(prefix)));
+}
+
+bool HandlerUtils::permittedPath(const Common::String path) {
+	return hasPermittedPrefix(path) && !isBlacklisted(path);
+}
+
 void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
 	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
 
diff --git a/backends/networking/sdl_net/handlerutils.h b/backends/networking/sdl_net/handlerutils.h
index c672268..4c2eff4 100644
--- a/backends/networking/sdl_net/handlerutils.h
+++ b/backends/networking/sdl_net/handlerutils.h
@@ -35,6 +35,12 @@ public:
 	static Common::SeekableReadStream *getArchiveFile(Common::String name);
 	static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
 
+	static Common::String normalizePath(const Common::String &path);
+	static bool hasForbiddenCombinations(const Common::String &path);
+	static bool isBlacklisted(const Common::String &path);
+	static bool hasPermittedPrefix(const Common::String &path);
+	static bool permittedPath(const Common::String path);
+
 	static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
 	static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
 };
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index cbc1b7f..bceeedc 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -127,13 +127,18 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	if (filename.empty())
 		return;
 
-	// TODO: handle <filename>, <path> + <filename>
+	if (HandlerUtils::hasForbiddenCombinations(filename))
+		return;
 
 	// check that <path>/<filename> doesn't exist
 	Common::String path = _parentDirectoryPath;
 	if (path.lastChar() != '/' && path.lastChar() != '\\')
 		path += '/';
 	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
+	if (!HandlerUtils::permittedPath(originalNode->getPath())) {
+		setErrorMessageHandler(*client, _("Invalid path!"));
+		return;
+	}
 	if (originalNode->exists()) {
 		setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
 		return;


Commit: faf849012cd431e9d4dbdbe7a3c1ebf514d3f5c8
    https://github.com/scummvm/scummvm/commit/faf849012cd431e9d4dbdbe7a3c1ebf514d3f5c8
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Add GUI for "rootpath" selection

Cloud tab now contains a button to select path, path label and a clear
button.

Changed paths:
    gui/options.cpp
    gui/options.h
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/gui/options.cpp b/gui/options.cpp
index f798f1e..3f2674b 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -103,7 +103,9 @@ enum {
 	kDownloadStorageCmd = 'dlst',
 	kRunServerCmd = 'rnsv',
 	kCloudTabContainerReflowCmd = 'ctcr',
-	kServerPortClearCmd = 'spcl'
+	kServerPortClearCmd = 'spcl',
+	kChooseRootDirCmd = 'chrp',
+	kRootPathClearCmd = 'clrp'
 };
 #endif
 
@@ -1319,6 +1321,15 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_runServerButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RunServerButton", _("Run server"), _("Run local webserver"), kRunServerCmd);
 	_serverInfoLabel = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.ServerInfoLabel", _("Not running"));
 
+	// Root path
+	if (g_system->getOverlayWidth() > 320)
+		_rootPathButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RootPathButton", _("/root/ Path:"), _("Specifies where Files Manager can access to"), kChooseRootDirCmd);
+	else
+		_rootPathButton = new ButtonWidget(container, "GlobalOptions_Cloud_Container.RootPathButton", _c("/root/ Path:", "lowres"), _("Specifies where Files Manager can access to"), kChooseRootDirCmd);
+	_rootPath = new StaticTextWidget(container, "GlobalOptions_Cloud_Container.RootPath", "/foo/bar", _("Specifies where Files Manager can access to"));
+
+	_rootPathClearButton = addClearButton(container, "GlobalOptions_Cloud_Container.RootPathClearButton", kRootPathClearCmd);
+
 #ifdef USE_SDL_NET
 	uint32 port = Networking::LocalWebserver::getPort();
 #else
@@ -1411,6 +1422,15 @@ void GlobalOptionsDialog::open() {
 	if (mode == ThemeEngine::kGfxDisabled)
 		mode = ThemeEngine::_defaultRendererMode;
 	_rendererPopUp->setSelectedTag(mode);
+
+#ifdef USE_CLOUD
+	Common::String rootPath(ConfMan.get("rootpath", "cloud"));
+	if (rootPath.empty() || !ConfMan.hasKey("rootpath", "cloud")) {
+		_rootPath->setLabel(_c("None", "path"));
+	} else {
+		_rootPath->setLabel(rootPath);
+	}
+#endif
 }
 
 void GlobalOptionsDialog::close() {
@@ -1441,6 +1461,14 @@ void GlobalOptionsDialog::close() {
 			ConfMan.removeKey("pluginspath", _domain);
 #endif
 
+#ifdef USE_CLOUD
+		Common::String rootPath(_rootPath->getLabel());
+		if (!rootPath.empty() && (rootPath != _c("None", "path")))
+			ConfMan.set("rootpath", rootPath, "cloud");
+		else
+			ConfMan.removeKey("rootpath", "cloud");
+#endif
+
 		ConfMan.setInt("autosave_period", _autosavePeriodPopUp->getSelectedTag(), _domain);
 
 		GUI::ThemeEngine::GraphicsMode selected = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag();
@@ -1571,6 +1599,21 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 #endif
+#ifdef USE_CLOUD
+	case kChooseRootDirCmd: {
+		BrowserDialog browser(_("Select directory for Files Manager /root/"), true);
+		if (browser.runModal() > 0) {
+			// User made his choice...
+			Common::FSNode dir(browser.getResult());
+			Common::String path = dir.getPath();
+			if (path.empty())
+				path = "/"; // absolute root
+			_rootPath->setLabel(path);
+			draw();
+		}
+		break;
+	}
+#endif
 	case kThemePathClearCmd:
 		_themePath->setLabel(_c("None", "path"));
 		break;
@@ -1580,6 +1623,11 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	case kSavePathClearCmd:
 		_savePath->setLabel(_("Default"));
 		break;
+#ifdef USE_CLOUD
+	case kRootPathClearCmd:
+		_rootPath->setLabel(_c("None", "path"));
+		break;
+#endif
 	case kChooseSoundFontCmd: {
 		BrowserDialog browser(_("Select SoundFont"), false);
 		if (browser.runModal() > 0) {
@@ -1868,13 +1916,26 @@ void GlobalOptionsDialog::setupCloudTab() {
 	//determine original widget's positions
 	int16 x, y;
 	uint16 w, h;
-	int serverButtonY, serverInfoY, serverPortDescY, serverPortY, serverPortClearButtonY;
+	int serverButtonY, serverInfoY;
+	int serverRootButtonY, serverRootY, serverRootClearButtonY;
+	int serverPortDescY, serverPortY, serverPortClearButtonY;
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RunServerButton", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.RunServerButton's position is undefined");
 	serverButtonY = y;
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerInfoLabel", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.ServerInfoLabel's position is undefined");
 	serverInfoY = y;
+
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RootPathButton", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.RootPathButton's position is undefined");
+	serverRootButtonY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RootPath", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.RootPath's position is undefined");
+	serverRootY = y;
+	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.RootPathClearButton", x, y, w, h))
+		warning("GlobalOptions_Cloud_Container.RootPathClearButton's position is undefined");
+	serverRootClearButtonY = y;
+
 	if (!g_gui.xmlEval()->getWidgetData("GlobalOptions_Cloud_Container.ServerPortDesc", x, y, w, h))
 		warning("GlobalOptions_Cloud_Container.ServerPortDesc's position is undefined");
 	serverPortDescY = y;
@@ -1903,6 +1964,18 @@ void GlobalOptionsDialog::setupCloudTab() {
 		else
 			_serverInfoLabel->setLabel(_("Not running"));
 	}
+	if (_rootPathButton) {
+		_rootPathButton->setVisible(true);
+		_rootPathButton->setPos(_rootPathButton->getRelX(), serverLabelPosition + serverRootButtonY - serverInfoY);
+	}
+	if (_rootPath) {
+		_rootPath->setVisible(true);
+		_rootPath->setPos(_rootPath->getRelX(), serverLabelPosition + serverRootY - serverInfoY);
+	}
+	if (_rootPathClearButton) {
+		_rootPathClearButton->setVisible(true);
+		_rootPathClearButton->setPos(_rootPathClearButton->getRelX(), serverLabelPosition + serverRootClearButtonY - serverInfoY);
+	}
 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
 	if (_serverPortDesc) {
 		_serverPortDesc->setVisible(true);
@@ -1932,6 +2005,12 @@ void GlobalOptionsDialog::setupCloudTab() {
 		_runServerButton->setVisible(false);
 	if (_serverInfoLabel)
 		_serverInfoLabel->setVisible(false);
+	if (_rootPathButton)
+		_rootPathButton->setVisible(false);
+	if (_rootPath)
+		_rootPath->setVisible(false);
+	if (_rootPathClearButton)
+		_rootPathClearButton->setVisible(false);
 	if (_serverPortDesc)
 		_serverPortDesc->setVisible(false);
 	if (_serverPort)
diff --git a/gui/options.h b/gui/options.h
index 42e8ffb..03dbdac 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -268,6 +268,9 @@ protected:
 	ButtonWidget	 *_storageDownloadButton;
 	ButtonWidget	 *_runServerButton;
 	StaticTextWidget *_serverInfoLabel;
+	ButtonWidget	 *_rootPathButton;
+	StaticTextWidget *_rootPath;
+	ButtonWidget	 *_rootPathClearButton;
 	StaticTextWidget *_serverPortDesc;
 	EditTextWidget *_serverPort;
 	ButtonWidget	 *_serverPortClearButton;
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 86d5c49..e574fe5 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 79320b0..c67631a 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -584,6 +584,18 @@
 				/>
 			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'RootPathButton'
+						type = 'Button'
+				/>
+				<widget name = 'RootPath'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'RootPathClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<widget name = 'ServerPortDesc'
 						type = 'OptionsLabel'
 				/>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 9bdea8f..e192566 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -596,6 +596,18 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '16' center = 'true'>
+				<widget name = 'RootPathButton'
+						type = 'Button'
+				/>
+				<widget name = 'RootPathPath'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'RootPathClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'ServerPortDesc'
 						width = '80'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index 3c1fe66..78b62d4 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index f4fa200..bb182c9 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -598,6 +598,18 @@
 				/>
 			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
+				<widget name = 'RootPathButton'
+						type = 'Button'
+				/>
+				<widget name = 'RootPath'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'RootPathClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
 				<widget name = 'ServerPortDesc'
 						type = 'OptionsLabel'
 				/>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 3f7e263..3416fbe 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -594,6 +594,18 @@
 						height = 'Globals.Line.Height'
 				/>
 			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '16' center = 'true'>
+				<widget name = 'RootPathButton'
+						type = 'Button'
+				/>
+				<widget name = 'RootPathPath'
+						height = 'Globals.Line.Height'
+				/>
+				<widget name = 'RootPathClearButton'
+						height = 'Globals.Line.Height'
+						width = 'Globals.Line.Height'
+				/>
+			</layout>
 			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'>
 				<widget name = 'ServerPortDesc'
 						width = '80'


Commit: 8a1cca896eedd37e3926cc036fc00e659719e613
    https://github.com/scummvm/scummvm/commit/8a1cca896eedd37e3926cc036fc00e659719e613
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update handlers

Now if there is no "rootpath" specified, it's not even listed by
FilesPageHandler and ListAjaxHandler. And, of course, not available to
use anywhere else.

Changed paths:
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp



diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index 5e5787b..135e0fb 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -50,9 +50,9 @@ bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefi
 	if (isDirectory && path.lastChar() != '/' && path.lastChar() != '\\')
 		path += '/';
 
-	if (path.hasPrefix("/root")) {
+	if (path.hasPrefix("/root") && ConfMan.hasKey("rootpath", "cloud")) {
 		prefixToAdd = "/root/";
-		prefixToRemove = (ConfMan.hasKey("rootpath", "cloud") ? ConfMan.get("rootpath", "cloud") : "");
+		prefixToRemove = ConfMan.get("rootpath", "cloud");
 		if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
 			prefixToRemove += '/';
 		if (prefixToRemove == "/") prefixToRemove = "";
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 0bc9237..e117b49 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -23,6 +23,7 @@
 #include "backends/networking/sdl_net/handlers/filespagehandler.h"
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "common/config-manager.h"
 #include "common/translation.h"
 
 namespace Networking {
@@ -76,7 +77,8 @@ Common::String getDisplayPath(Common::String s) {
 
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
-		addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
+		if (ConfMan.hasKey("rootpath", "cloud")) 
+			addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
 		addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games"));
 		return true;
 	}
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index 57fa04d..f94b674 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -23,8 +23,9 @@
 #include "backends/networking/sdl_net/handlers/listajaxhandler.h"
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
-#include "common/translation.h"
+#include "common/config-manager.h"
 #include "common/json.h"
+#include "common/translation.h"
 
 namespace Networking {
 
@@ -40,7 +41,8 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 	errorResult.setVal("type", new Common::JSONValue("error"));
 
 	if (path == "" || path == "/") {
-		addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root"));
+		if (ConfMan.hasKey("rootpath", "cloud"))
+			addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root"));
 		addItem(itemsList, IT_DIRECTORY, "/saves/", _("Saved games"));
 		successResult.setVal("items", new Common::JSONValue(itemsList));
 		return successResult;


Commit: dc02a789b6b2fc4e7693b2e7e69ff57b2767b889
    https://github.com/scummvm/scummvm/commit/dc02a789b6b2fc4e7693b2e7e69ff57b2767b889
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Use forbidden combinations

I accidentally tried "folder../" instead "folder/../" and understood
that I made "folder../" forbidden too, though it's a valid folder name.

Changed paths:
    backends/networking/sdl_net/handlerutils.cpp



diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index f0c62d3..dc21ab5 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -125,7 +125,7 @@ Common::String HandlerUtils::normalizePath(const Common::String &path) {
 }
 
 bool HandlerUtils::hasForbiddenCombinations(const Common::String &path) {
-	return (path.contains("../") || path.contains("..\\"));
+	return (path.contains("/../") || path.contains("\\..\\") || path.contains("\\../") || path.contains("/..\\"));
 }
 
 bool HandlerUtils::isBlacklisted(const Common::String &path) {


Commit: 97cf2be7ef4ee5041ade5cd6f1b150d014653ed3
    https://github.com/scummvm/scummvm/commit/97cf2be7ef4ee5041ade5cd6f1b150d014653ed3
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Update LocalWebserver

Reader now reads headers into stream, and some checks are added there
and in UploadFileClientHandler, so if headers are too long, they are
treated as bad request.

Changed paths:
    backends/networking/sdl_net/reader.cpp
    backends/networking/sdl_net/reader.h
    backends/networking/sdl_net/uploadfileclienthandler.cpp



diff --git a/backends/networking/sdl_net/reader.cpp b/backends/networking/sdl_net/reader.cpp
index 0e4cc9a..8f3199f 100644
--- a/backends/networking/sdl_net/reader.cpp
+++ b/backends/networking/sdl_net/reader.cpp
@@ -37,7 +37,7 @@ Reader::Reader() {
 	_windowUsed = 0;
 	_windowSize = 0;
 
-	_headers = "";
+	_headersStream = nullptr;
 	_firstBlock = true;
 
 	_contentLength = 0;
@@ -65,6 +65,9 @@ Reader &Reader::operator=(Reader &r) {
 	_windowSize = r._windowSize;
 	r._window = nullptr;
 
+	_headersStream = r._headersStream;
+	r._headersStream = nullptr;
+
 	_headers = r._headers;
 	_method = r._method;
 	_path = r._path;
@@ -84,6 +87,9 @@ Reader &Reader::operator=(Reader &r) {
 void Reader::cleanup() {
 	//_content is not to be freed, it's not owned by Reader
 
+	if (_headersStream != nullptr)
+		delete _headersStream;
+
 	if (_window != nullptr)
 		freeWindow();
 }
@@ -92,14 +98,20 @@ bool Reader::readAndHandleFirstHeaders() {
 	Common::String boundary = "\r\n\r\n";
 	if (_window == nullptr) {
 		makeWindow(boundary.size());
-		_headers = "";
+	}
+	if (_headersStream == nullptr) {
+		_headersStream = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
 	}
 
-	while (readOneByteInString(_headers, boundary)) {
+	while (readOneByteInStream(_headersStream, boundary)) {
+		if (_headersStream->size() > SUSPICIOUS_HEADERS_SIZE) {
+			_isBadRequest = true;
+			return true;
+		}
 		if (!bytesLeft())
 			return false;
 	}
-	handleFirstHeaders(_headers);
+	handleFirstHeaders(_headersStream);
 
 	freeWindow();
 	_state = RS_READING_CONTENT;
@@ -136,22 +148,23 @@ void readFromThatUntilLineEnd(const char *cstr, Common::String needle, Common::S
 }
 }
 
-void Reader::handleFirstHeaders(Common::String headers) {
+void Reader::handleFirstHeaders(Common::MemoryReadWriteStream *headersStream) {
 	if (!_boundary.empty()) {
 		warning("Reader: handleFirstHeaders() called when first headers were already handled");
 		return;
 	}
 
 	//parse method, path, query, fragment
-	parseFirstLine(headers);
+	_headers = readEverythingFromMemoryStream(headersStream);
+	parseFirstLine(_headers);
 
 	//find boundary
 	_boundary = "";
-	readFromThatUntilLineEnd(headers.c_str(), "boundary=", _boundary);
+	readFromThatUntilLineEnd(_headers.c_str(), "boundary=", _boundary);
 
 	//find content length
 	Common::String contentLength = "";
-	readFromThatUntilLineEnd(headers.c_str(), "Content-Length: ", contentLength);
+	readFromThatUntilLineEnd(_headers.c_str(), "Content-Length: ", contentLength);
 	_contentLength = contentLength.asUint64();
 	_availableBytes = _contentLength;
 }
@@ -160,48 +173,43 @@ void Reader::parseFirstLine(const Common::String &headers) {
 	uint32 headersSize = headers.size();
 	bool bad = false;
 
-	const uint32 SUSPICIOUS_HEADERS_SIZE = 128 * 1024;
-	if (headersSize > SUSPICIOUS_HEADERS_SIZE) bad = true;
-
-	if (!bad) {
-		if (headersSize > 0) {
-			const char *cstr = headers.c_str();
-			const char *position = strstr(cstr, "\r\n");
-			if (position) { //we have at least one line - and we want the first one
-				//"<METHOD> <path> HTTP/<VERSION>\r\n"
-				Common::String method, path, http, buf;
-				uint32 length = position - cstr;
-				if (headersSize > length)
-					headersSize = length;
-				for (uint32 i = 0; i < headersSize; ++i) {
-					if (headers[i] != ' ')
-						buf += headers[i];
-					if (headers[i] == ' ' || i == headersSize - 1) {
-						if (method == "") {
-							method = buf;
-						} else if (path == "") {
-							path = buf;
-						} else if (http == "") {
-							http = buf;
-						} else {
-							bad = true;
-							break;
-						}
-						buf = "";
+	if (headersSize > 0) {
+		const char *cstr = headers.c_str();
+		const char *position = strstr(cstr, "\r\n");
+		if (position) { //we have at least one line - and we want the first one
+			//"<METHOD> <path> HTTP/<VERSION>\r\n"
+			Common::String method, path, http, buf;
+			uint32 length = position - cstr;
+			if (headersSize > length)
+				headersSize = length;
+			for (uint32 i = 0; i < headersSize; ++i) {
+				if (headers[i] != ' ')
+					buf += headers[i];
+				if (headers[i] == ' ' || i == headersSize - 1) {
+					if (method == "") {
+						method = buf;
+					} else if (path == "") {
+						path = buf;
+					} else if (http == "") {
+						http = buf;
+					} else {
+						bad = true;
+						break;
 					}
+					buf = "";
 				}
+			}
 
-				//check that method is supported
-				if (method != "GET" && method != "PUT" && method != "POST")
-					bad = true;
+			//check that method is supported
+			if (method != "GET" && method != "PUT" && method != "POST")
+				bad = true;
 
-				//check that HTTP/<VERSION> is OK
-				if (!http.hasPrefix("HTTP/"))
-					bad = true;
+			//check that HTTP/<VERSION> is OK
+			if (!http.hasPrefix("HTTP/"))
+				bad = true;
 
-				_method = method;
-				parsePathQueryAndAnchor(path);
-			}
+			_method = method;
+			parsePathQueryAndAnchor(path);
 		}
 	}
 
@@ -308,37 +316,33 @@ void Reader::freeWindow() {
 	_windowUsed = _windowSize = 0;
 }
 
-bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
-	byte b = readOne();
-	_window[_windowUsed++] = b;
-	if (_windowUsed < _windowSize)
-		return true;
-
-	//when window is filled, check whether that's the boundary
-	if (Common::String((char *)_window, _windowSize) == boundary)
+namespace {
+bool windowEqualsString(const byte *window, uint32 windowSize, const Common::String &boundary) {
+	if (boundary.size() != windowSize)
 		return false;
 
-	//if not, add the first byte of the window to the string
-	if (stream)
-		stream->writeByte(_window[0]);
-	for (uint32 i = 1; i < _windowSize; ++i)
-		_window[i - 1] = _window[i];
-	--_windowUsed;
+	for (uint32 i = 0; i < windowSize; ++i) {
+		if (window[i] != boundary[i])
+			return false;
+	}
+
 	return true;
 }
+}
 
-bool Reader::readOneByteInString(Common::String &buffer, const Common::String &boundary) {
+bool Reader::readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary) {
 	byte b = readOne();
 	_window[_windowUsed++] = b;
 	if (_windowUsed < _windowSize)
 		return true;
 
 	//when window is filled, check whether that's the boundary
-	if (Common::String((char *)_window, _windowSize) == boundary)
+	if (windowEqualsString(_window, _windowSize, boundary))
 		return false;
 
 	//if not, add the first byte of the window to the string
-	buffer += _window[0];
+	if (stream)
+		stream->writeByte(_window[0]);
 	for (uint32 i = 1; i < _windowSize; ++i)
 		_window[i - 1] = _window[i];
 	--_windowUsed;
@@ -419,7 +423,7 @@ bool Reader::readBlockContent(Common::WriteStream *stream) {
 	return true;
 }
 
-uint32 Reader::bytesLeft() { return _bytesLeft; }
+uint32 Reader::bytesLeft() const { return _bytesLeft; }
 
 void Reader::setContent(Common::MemoryReadWriteStream *stream) {
 	_content = stream;
@@ -442,4 +446,17 @@ Common::String Reader::queryParameter(Common::String name) const { return _query
 
 Common::String Reader::anchor() const { return _anchor; }
 
+Common::String Reader::readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) {
+	Common::String result;
+	char buf[1024];
+	uint32 readBytes;
+	while (true) {
+		readBytes = stream->read(buf, 1024);
+		if (readBytes == 0)
+			break;
+		result += Common::String(buf, readBytes);
+	}
+	return result;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/reader.h b/backends/networking/sdl_net/reader.h
index d4a0ba4..16d62a2 100644
--- a/backends/networking/sdl_net/reader.h
+++ b/backends/networking/sdl_net/reader.h
@@ -80,6 +80,8 @@ class Reader {
 	byte *_window;
 	uint32 _windowUsed, _windowSize;
 
+	Common::MemoryReadWriteStream *_headersStream;
+
 	Common::String _headers;
 	Common::String _method, _path, _query, _anchor;
 	Common::HashMap<Common::String, Common::String> _queryParameters;
@@ -96,7 +98,7 @@ class Reader {
 	bool readBlockHeadersIntoStream(Common::WriteStream *stream); //true when ended reading
 	bool readContentIntoStream(Common::WriteStream *stream); //true when ended reading
 
-	void handleFirstHeaders(Common::String headers);
+	void handleFirstHeaders(Common::MemoryReadWriteStream *headers);
 	void parseFirstLine(const Common::String &headers);
 	void parsePathQueryAndAnchor(Common::String path);
 	void parseQueryParameters();
@@ -104,12 +106,13 @@ class Reader {
 	void makeWindow(uint32 size);
 	void freeWindow();
 	bool readOneByteInStream(Common::WriteStream *stream, const Common::String &boundary);
-	bool readOneByteInString(Common::String &buffer, const Common::String &boundary);
 
 	byte readOne();
-	uint32 bytesLeft();
+	uint32 bytesLeft() const;
 
 public:
+	static const uint32 SUSPICIOUS_HEADERS_SIZE = 1024 * 1024; // 1 MB is really a lot
+
 	Reader();
 	~Reader();
 
@@ -131,6 +134,8 @@ public:
 	Common::String query() const;
 	Common::String queryParameter(Common::String name) const;
 	Common::String anchor() const;
+
+	static Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream);
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index bceeedc..ebf3416 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -24,6 +24,7 @@
 #include "backends/fs/fs-factory.h"
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
+#include "backends/networking/sdl_net/reader.h"
 #include "common/file.h"
 #include "common/memstream.h"
 #include "common/translation.h"
@@ -62,6 +63,11 @@ void UploadFileClientHandler::handle(Client *client) {
 				handleBlockHeaders(client);
 				continue;
 			}
+
+			// fail on suspicious headers
+			if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
+				setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+			}
 			break;
 
 		case UFH_READING_BLOCK_CONTENT:
@@ -95,26 +101,18 @@ void readFromThatUntilDoubleQuote(const char *cstr, Common::String needle, Commo
 		}
 	}
 }
-
-Common::String readEverythingFromMemoryStream(Common::MemoryReadWriteStream *stream) {
-	Common::String result;
-	char buf[1024];
-	uint32 readBytes;
-	while (true) {
-		readBytes = stream->read(buf, 1024);
-		if (readBytes == 0)
-			break;
-		result += Common::String(buf, readBytes);
-	}
-	return result;
-}
 }
 
 void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	_state = UFH_READING_BLOCK_CONTENT;
 
+	// fail on suspicious headers
+	if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
+		setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+	}
+
 	// search for "upload_file" field
-	Common::String headers = readEverythingFromMemoryStream(_headersStream);
+	Common::String headers = Reader::readEverythingFromMemoryStream(_headersStream);
 	Common::String fieldName = "";
 	readFromThatUntilDoubleQuote(headers.c_str(), "name=\"", fieldName);
 	if (!fieldName.hasPrefix("upload_file"))


Commit: 02a997e468041ca0d7a2e9ac95e0b7dd6144c6eb
    https://github.com/scummvm/scummvm/commit/02a997e468041ca0d7a2e9ac95e0b7dd6144c6eb
Author: Peter Bozsó (uruk at scummvm.org)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
CLOUD: Remove unused includes

Changed paths:
    backends/cloud/box/boxstorage.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/id/idstorage.cpp
    backends/networking/curl/networkreadstream.cpp



diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 0185623..2dd516d 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -24,7 +24,6 @@
 #define BACKENDS_CLOUD_BOX_BOXSTORAGE_H
 
 #include "backends/cloud/id/idstorage.h"
-#include "common/callback.h"
 #include "backends/networking/curl/curljsonrequest.h"
 
 namespace Cloud {
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index 945493b..2c9dcc4 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -27,7 +27,6 @@
 #include "backends/networking/curl/curljsonrequest.h"
 #include "backends/networking/curl/networkreadstream.h"
 #include "common/json.h"
-#include "common/debug.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index 36f2df6..0ffa8c0 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -28,7 +28,6 @@
 #include "backends/cloud/id/idresolveidrequest.h"
 #include "backends/cloud/id/idstreamfilerequest.h"
 #include "common/debug.h"
-#include "common/json.h"
 
 namespace Cloud {
 namespace Id {
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
index 30659e2..032aef8 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -25,7 +25,6 @@
 #include "backends/networking/curl/networkreadstream.h"
 #include "backends/networking/curl/connectionmanager.h"
 #include "base/version.h"
-#include "common/debug.h"
 #include <curl/curl.h>
 
 namespace Networking {


Commit: ea360ef8f20655c0eedbf52a46096f231c459214
    https://github.com/scummvm/scummvm/commit/ea360ef8f20655c0eedbf52a46096f231c459214
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:07:55+06:00

Commit Message:
TITANIC: Fix OutSaveFile usage

Changed paths:
    engines/titanic/support/simple_file.h



diff --git a/engines/titanic/support/simple_file.h b/engines/titanic/support/simple_file.h
index f5d0bc7..01aaa86 100644
--- a/engines/titanic/support/simple_file.h
+++ b/engines/titanic/support/simple_file.h
@@ -278,7 +278,7 @@ public:
 	 * Set up a stream for write access
 	 */
 	virtual void open(Common::OutSaveFile *stream) {
-		SimpleFile::open(Common::wrapCompressedWriteStream(stream));
+		SimpleFile::open(new Common::OutSaveFile(Common::wrapCompressedWriteStream(stream)));
 	}
 };
 


Commit: 368f664c813075f8b8cb2c572dce65f60128eda4
    https://github.com/scummvm/scummvm/commit/368f664c813075f8b8cb2c572dce65f60128eda4
Author: Alexander Tkachev (alexander at tkachov.ru)
Date: 2016-08-24T16:24:16+06:00

Commit Message:
COMMON: Fix WriteStream::pos() once again

MemoryReadWriteStream now returns int32, not uint32. It actually doesn't
ever return -1 to indicate that an error occured, so uint32 was a better
choice, but that's what is used in WriteStream base class now.

That method is abstract, so that's also why OutSaveFile had to override
it.

Changed paths:
    backends/saves/savefile.cpp
    common/memstream.h
    common/savefile.h



diff --git a/backends/saves/savefile.cpp b/backends/saves/savefile.cpp
index 5e98347..e21ea16 100644
--- a/backends/saves/savefile.cpp
+++ b/backends/saves/savefile.cpp
@@ -50,6 +50,10 @@ uint32 OutSaveFile::write(const void *dataPtr, uint32 dataSize) {
 	return _wrapped->write(dataPtr, dataSize);
 }
 
+int32 OutSaveFile::pos() const {
+	return _wrapped->pos();
+}
+
 bool SaveFileManager::copySavefile(const String &oldFilename, const String &newFilename) {
 	InSaveFile *inFile = 0;
 	OutSaveFile *outFile = 0;
diff --git a/common/memstream.h b/common/memstream.h
index 94b8d19..25fdde9 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -286,7 +286,7 @@ public:
 		return dataSize;
 	}
 
-	uint32 pos() const { return _pos - _length; } //'read' position in the stream
+	int32 pos() const { return _pos - _length; } //'read' position in the stream
 	uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
 
 	byte *getData() { return _data; }
diff --git a/common/savefile.h b/common/savefile.h
index d84c9e2..eb7e6f9 100644
--- a/common/savefile.h
+++ b/common/savefile.h
@@ -58,6 +58,7 @@ public:
 	virtual void finalize();
 	virtual bool flush();
 	virtual uint32 write(const void *dataPtr, uint32 dataSize);
+	virtual int32 pos() const;
 };
 
 /**


Commit: bfbfbd3e1a8397cc3b1c9eaff6c7754abe3ddc3d
    https://github.com/scummvm/scummvm/commit/bfbfbd3e1a8397cc3b1c9eaff6c7754abe3ddc3d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2016-08-30T13:54:12+02:00

Commit Message:
Merge pull request #788 from Tkachov/cloud

ALL: Add Cloud storage support

Changed paths:
  A backends/cloud/box/boxlistdirectorybyidrequest.cpp
  A backends/cloud/box/boxlistdirectorybyidrequest.h
  A backends/cloud/box/boxstorage.cpp
  A backends/cloud/box/boxstorage.h
  A backends/cloud/box/boxtokenrefresher.cpp
  A backends/cloud/box/boxtokenrefresher.h
  A backends/cloud/box/boxuploadrequest.cpp
  A backends/cloud/box/boxuploadrequest.h
  A backends/cloud/cloudmanager.cpp
  A backends/cloud/cloudmanager.h
  A backends/cloud/downloadrequest.cpp
  A backends/cloud/downloadrequest.h
  A backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
  A backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
  A backends/cloud/dropbox/dropboxinforequest.cpp
  A backends/cloud/dropbox/dropboxinforequest.h
  A backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
  A backends/cloud/dropbox/dropboxlistdirectoryrequest.h
  A backends/cloud/dropbox/dropboxstorage.cpp
  A backends/cloud/dropbox/dropboxstorage.h
  A backends/cloud/dropbox/dropboxuploadrequest.cpp
  A backends/cloud/dropbox/dropboxuploadrequest.h
  A backends/cloud/folderdownloadrequest.cpp
  A backends/cloud/folderdownloadrequest.h
  A backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
  A backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
  A backends/cloud/googledrive/googledrivestorage.cpp
  A backends/cloud/googledrive/googledrivestorage.h
  A backends/cloud/googledrive/googledrivetokenrefresher.cpp
  A backends/cloud/googledrive/googledrivetokenrefresher.h
  A backends/cloud/googledrive/googledriveuploadrequest.cpp
  A backends/cloud/googledrive/googledriveuploadrequest.h
  A backends/cloud/id/idcreatedirectoryrequest.cpp
  A backends/cloud/id/idcreatedirectoryrequest.h
  A backends/cloud/id/iddownloadrequest.cpp
  A backends/cloud/id/iddownloadrequest.h
  A backends/cloud/id/idlistdirectoryrequest.cpp
  A backends/cloud/id/idlistdirectoryrequest.h
  A backends/cloud/id/idresolveidrequest.cpp
  A backends/cloud/id/idresolveidrequest.h
  A backends/cloud/id/idstorage.cpp
  A backends/cloud/id/idstorage.h
  A backends/cloud/id/idstreamfilerequest.cpp
  A backends/cloud/id/idstreamfilerequest.h
  A backends/cloud/iso8601.cpp
  A backends/cloud/iso8601.h
  A backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
  A backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
  A backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
  A backends/cloud/onedrive/onedrivelistdirectoryrequest.h
  A backends/cloud/onedrive/onedrivestorage.cpp
  A backends/cloud/onedrive/onedrivestorage.h
  A backends/cloud/onedrive/onedrivetokenrefresher.cpp
  A backends/cloud/onedrive/onedrivetokenrefresher.h
  A backends/cloud/onedrive/onedriveuploadrequest.cpp
  A backends/cloud/onedrive/onedriveuploadrequest.h
  A backends/cloud/savessyncrequest.cpp
  A backends/cloud/savessyncrequest.h
  A backends/cloud/storage.cpp
  A backends/cloud/storage.h
  A backends/cloud/storagefile.cpp
  A backends/cloud/storagefile.h
  A backends/cloud/storageinfo.h
  A backends/networking/browser/openurl-android.cpp
  A backends/networking/browser/openurl-default.cpp
  A backends/networking/browser/openurl-osx.cpp
  A backends/networking/browser/openurl-posix.cpp
  A backends/networking/browser/openurl-windows.cpp
  A backends/networking/browser/openurl.h
  A backends/networking/connection/islimited-android.cpp
  A backends/networking/connection/islimited-default.cpp
  A backends/networking/connection/islimited.h
  A backends/networking/curl/cloudicon.cpp
  A backends/networking/curl/cloudicon.h
  A backends/networking/curl/cloudicon_data.h
  A backends/networking/curl/cloudicon_disabled_data.h
  A backends/networking/curl/connectionmanager.cpp
  A backends/networking/curl/connectionmanager.h
  A backends/networking/curl/curljsonrequest.cpp
  A backends/networking/curl/curljsonrequest.h
  A backends/networking/curl/curlrequest.cpp
  A backends/networking/curl/curlrequest.h
  A backends/networking/curl/networkreadstream.cpp
  A backends/networking/curl/networkreadstream.h
  A backends/networking/curl/request.cpp
  A backends/networking/curl/request.h
  A backends/networking/make_archive.py
  A backends/networking/sdl_net/client.cpp
  A backends/networking/sdl_net/client.h
  A backends/networking/sdl_net/getclienthandler.cpp
  A backends/networking/sdl_net/getclienthandler.h
  A backends/networking/sdl_net/handlers/basehandler.h
  A backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
  A backends/networking/sdl_net/handlers/createdirectoryhandler.h
  A backends/networking/sdl_net/handlers/downloadfilehandler.cpp
  A backends/networking/sdl_net/handlers/downloadfilehandler.h
  A backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
  A backends/networking/sdl_net/handlers/filesajaxpagehandler.h
  A backends/networking/sdl_net/handlers/filesbasehandler.cpp
  A backends/networking/sdl_net/handlers/filesbasehandler.h
  A backends/networking/sdl_net/handlers/filespagehandler.cpp
  A backends/networking/sdl_net/handlers/filespagehandler.h
  A backends/networking/sdl_net/handlers/indexpagehandler.cpp
  A backends/networking/sdl_net/handlers/indexpagehandler.h
  A backends/networking/sdl_net/handlers/listajaxhandler.cpp
  A backends/networking/sdl_net/handlers/listajaxhandler.h
  A backends/networking/sdl_net/handlers/resourcehandler.cpp
  A backends/networking/sdl_net/handlers/resourcehandler.h
  A backends/networking/sdl_net/handlers/uploadfilehandler.cpp
  A backends/networking/sdl_net/handlers/uploadfilehandler.h
  A backends/networking/sdl_net/handlerutils.cpp
  A backends/networking/sdl_net/handlerutils.h
  A backends/networking/sdl_net/localwebserver.cpp
  A backends/networking/sdl_net/localwebserver.h
  A backends/networking/sdl_net/reader.cpp
  A backends/networking/sdl_net/reader.h
  A backends/networking/sdl_net/uploadfileclienthandler.cpp
  A backends/networking/sdl_net/uploadfileclienthandler.h
  A backends/networking/wwwroot.zip
  A backends/networking/wwwroot/.files.html
  A backends/networking/wwwroot/.filesAJAX.html
  A backends/networking/wwwroot/.index.html
  A backends/networking/wwwroot/ajax.js
  A backends/networking/wwwroot/favicon.ico
  A backends/networking/wwwroot/icons/7z.png
  A backends/networking/wwwroot/icons/dir.png
  A backends/networking/wwwroot/icons/txt.png
  A backends/networking/wwwroot/icons/unk.png
  A backends/networking/wwwroot/icons/up.png
  A backends/networking/wwwroot/icons/zip.png
  A backends/networking/wwwroot/logo.png
  A backends/networking/wwwroot/style.css
  A common/callback.h
  A common/json.cpp
  A common/json.h
  A dists/cloudicon.png
  A dists/cloudicon_disabled.png
  A engines/testbed/cloud.cpp
  A engines/testbed/cloud.h
  A engines/testbed/webserver.cpp
  A engines/testbed/webserver.h
  A engines/tsage/stP1kAlM
  A gui/animation/AccelerateInterpolator.h
  A gui/animation/AlphaAnimation.h
  A gui/animation/Animation.cpp
  A gui/animation/Animation.h
  A gui/animation/DeccelerateInterpolator.h
  A gui/animation/Drawable.h
  A gui/animation/Interpolator.h
  A gui/animation/ParallelAnimation.h
  A gui/animation/RepeatAnimationWrapper.cpp
  A gui/animation/RepeatAnimationWrapper.h
  A gui/animation/ScaleAnimation.h
  A gui/animation/SequenceAnimationComposite.cpp
  A gui/animation/SequenceAnimationComposite.h
  A gui/animation/WaitForConditionAnimation.h
  A gui/downloaddialog.cpp
  A gui/downloaddialog.h
  A gui/editgamedialog.cpp
  A gui/editgamedialog.h
  A gui/remotebrowser.cpp
  A gui/remotebrowser.h
  A gui/storagewizarddialog.cpp
  A gui/storagewizarddialog.h
  A gui/themes/scummmodern/box.bmp
  A gui/themes/scummmodern/dropbox.bmp
  A gui/themes/scummmodern/googledrive.bmp
  A gui/themes/scummmodern/onedrive.bmp
    backends/base-backend.cpp
    backends/base-backend.h
    backends/fs/abstract-fs.h
    backends/fs/amigaos4/amigaos4-fs.cpp
    backends/fs/amigaos4/amigaos4-fs.h
    backends/fs/chroot/chroot-fs.cpp
    backends/fs/chroot/chroot-fs.h
    backends/fs/ds/ds-fs.cpp
    backends/fs/ds/ds-fs.h
    backends/fs/n64/n64-fs.cpp
    backends/fs/n64/n64-fs.h
    backends/fs/posix/posix-fs.cpp
    backends/fs/posix/posix-fs.h
    backends/fs/ps2/ps2-fs.cpp
    backends/fs/ps2/ps2-fs.h
    backends/fs/psp/psp-fs.cpp
    backends/fs/psp/psp-fs.h
    backends/fs/symbian/symbian-fs.cpp
    backends/fs/symbian/symbian-fs.h
    backends/fs/wii/wii-fs.cpp
    backends/fs/wii/wii-fs.h
    backends/fs/windows/windows-fs.cpp
    backends/fs/windows/windows-fs.h
    backends/graphics/graphics.h
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/modular-backend.h
    backends/module.mk
    backends/platform/android/jni.cpp
    backends/platform/android/jni.h
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
    backends/platform/ds/arm9/source/gbampsave.cpp
    backends/platform/n64/framfs_save_manager.h
    backends/platform/n64/pakfs_save_manager.h
    backends/platform/ps2/savefilemgr.cpp
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h
    backends/saves/savefile.cpp
    base/main.cpp
    base/version.cpp
    common/config-manager.cpp
    common/config-manager.h
    common/file.cpp
    common/file.h
    common/memstream.h
    common/module.mk
    common/savefile.h
    common/str.cpp
    common/str.h
    common/system.h
    common/xmlparser.cpp
    configure
    devtools/create_project/create_project.cpp
    dists/android/AndroidManifest.xml
    dists/android/AndroidManifest.xml.in
    dists/scummvm.rc
    engines/access/detection.cpp
    engines/adl/detection.cpp
    engines/agi/detection.cpp
    engines/agos/detection.cpp
    engines/avalanche/detection.cpp
    engines/bbvs/detection.cpp
    engines/cge/detection.cpp
    engines/cge2/detection.cpp
    engines/drascula/detection.cpp
    engines/gnap/detection.cpp
    engines/hopkins/detection.cpp
    engines/kyra/detection.cpp
    engines/kyra/kyra_v1.h
    engines/kyra/saveload.cpp
    engines/kyra/saveload_eob.cpp
    engines/lab/detection.cpp
    engines/mads/detection.cpp
    engines/metaengine.h
    engines/mortevielle/detection.cpp
    engines/neverhood/detection.cpp
    engines/prince/detection.cpp
    engines/saga/detection.cpp
    engines/savestate.cpp
    engines/savestate.h
    engines/scumm/detection.cpp
    engines/sherlock/detection.cpp
    engines/sky/detection.cpp
    engines/sword2/sword2.cpp
    engines/testbed/config-params.h
    engines/testbed/misc.cpp
    engines/testbed/misc.h
    engines/testbed/module.mk
    engines/testbed/testbed.cpp
    engines/tinsel/detection.cpp
    engines/titanic/support/simple_file.h
    engines/toltecs/detection.cpp
    engines/toon/detection.cpp
    engines/tsage/detection.cpp
    engines/voyeur/detection.cpp
    engines/wage/detection.cpp
    engines/zvision/detection.cpp
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    graphics/VectorRendererSpec.h
    graphics/transparent_surface.cpp
    graphics/transparent_surface.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/ThemeParser.cpp
    gui/ThemeParser.h
    gui/debugger.cpp
    gui/gui-manager.cpp
    gui/launcher.cpp
    gui/launcher.h
    gui/module.mk
    gui/object.h
    gui/options.cpp
    gui/options.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/saveload.cpp
    gui/themes/default.inc
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_gfx.stx
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx
    gui/themes/scummtheme.py
    gui/widget.cpp
    gui/widget.h
    gui/widgets/editable.cpp
    gui/widgets/scrollcontainer.cpp
    gui/widgets/scrollcontainer.h








More information about the Scummvm-git-logs mailing list