aboutsummaryrefslogtreecommitdiff
path: root/backends/cloud
diff options
context:
space:
mode:
Diffstat (limited to 'backends/cloud')
-rw-r--r--backends/cloud/savessyncrequest.cpp229
-rw-r--r--backends/cloud/savessyncrequest.h63
2 files changed, 292 insertions, 0 deletions
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
new file mode 100644
index 0000000000..d48ec6ba45
--- /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 0000000000..a8c54d44ad
--- /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