From 8429c40362b1e4d4010f808740b79baae69c97ed Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 01:10:37 +0600 Subject: 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. --- backends/cloud/cloudthread.cpp | 199 ++++++++ backends/cloud/cloudthread.h | 35 ++ base/main.cpp | 6 + common/json.cpp | 1064 ++++++++++++++++++++++++++++++++++++++++ common/json.h | 205 ++++++++ 5 files changed, 1509 insertions(+) create mode 100644 backends/cloud/cloudthread.cpp create mode 100644 backends/cloud/cloudthread.h create mode 100644 common/json.cpp create mode 100644 common/json.h diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp new file mode 100644 index 0000000000..ac405e0fd7 --- /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 keys = main_object->ObjectKeys(); + + Common::Array::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 0000000000..dcab42f6ae --- /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 1667106543..593179d80e 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 0000000000..f84ad70eb8 --- /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 +#include +#include +#include + +#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 - "" +* 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 A vector containing the keys. +*/ +Array JSONValue::ObjectKeys() const { + Array 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 0000000000..9597102012 --- /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 + #include +#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 + #include + + 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 JSONArray; +typedef HashMap 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 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 -- cgit v1.2.3 From 79e6368b4214936dfbc52719259c4930bb7d0eaf Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 01:19:48 +0600 Subject: CLOUD: Add SimpleJSON library (module.mk hotfix) Forgot to edit those. --- backends/module.mk | 6 ++++++ common/module.mk | 1 + 2 files changed, 7 insertions(+) diff --git a/backends/module.mk b/backends/module.mk index 4c1ca42f06..fc2fb99a9d 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 570040c8e1..54aa16f557 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 \ -- cgit v1.2.3 From a7fb8c72ab0ca60161f5acad42774340ee08abab Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 12:31:26 +0600 Subject: CLOUD: SimpleJSON refactor Resharper + manual methods & fields renaming. --- backends/cloud/cloudthread.cpp | 101 +++++------- backends/cloud/cloudthread.h | 5 +- common/json.cpp | 353 ++++++++++++++++++++--------------------- common/json.h | 138 ++++++++-------- 4 files changed, 281 insertions(+), 316 deletions(-) diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp index ac405e0fd7..e375d6fb2b 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 keys = main_object->ObjectKeys(); + Common::Array keys = main_object->objectKeys(); Common::Array::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 dcab42f6ae..ce448b7274 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 f84ad70eb8..a0bab11995 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 A vector containing the keys. */ -Array JSONValue::ObjectKeys() const { +Array JSONValue::objectKeys() const { Array 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 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 9597102012..64a1e03c5e 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 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 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 -- cgit v1.2.3 From 2ac2816d68c11b50796457f7d41896a1ed7d571e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 13:45:10 +0600 Subject: CLOUD: Refactor SimpleJSON --- backends/cloud/cloudthread.cpp | 26 +++- common/json.cpp | 261 ++++++++++++++++++++--------------------- common/json.h | 8 +- 3 files changed, 156 insertions(+), 139 deletions(-) diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp index e375d6fb2b..f8d93d2baa 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 a0bab11995..878c67e1ae 100644 --- a/common/json.cpp +++ b/common/json.cpp @@ -44,12 +44,7 @@ * THE SOFTWARE. */ -#include "JSON.h" - -#include -#include -#include -#include +#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 64a1e03c5e..5114947966 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__) -- cgit v1.2.3 From e4e2ec390d364770d16a3a478796c1b949e8645f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 14:02:32 +0600 Subject: CLOUD: Remove wcsncasecmp() usage from SimpleJSON Replaced with scumm_strnicmp(). --- common/json.cpp | 8 ++++---- common/json.h | 43 ------------------------------------------- 2 files changed, 4 insertions(+), 47 deletions(-) diff --git a/common/json.cpp b/common/json.cpp index 878c67e1ae..83ce0db29d 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 5114947966..c9debf05af 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 - #include -#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 - #include - - 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 JSONArray; typedef HashMap 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; -- cgit v1.2.3 From 52240c68c7301b941f51ea315994ee7e4665707b Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Wed, 11 May 2016 15:52:20 +0200 Subject: CONFIGURE: Added detection for SDL_net --- configure | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/configure b/configure index e77351a99d..b663701562 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 @@ -3676,6 +3677,24 @@ int main(void) { return 0; } 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 # -- cgit v1.2.3 From 7446ffd73bd184610d550a354a8e252b0b7f334d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 15:15:23 +0600 Subject: CLOUD: Integrate CloudThread into OSystem Would be changed soon. --- backends/cloud/cloudthread.cpp | 21 +++++++++++++++++++-- backends/cloud/cloudthread.h | 11 ++++++++--- backends/platform/sdl/sdl.cpp | 7 +++++++ base/main.cpp | 8 ++++---- common/system.h | 29 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp index f8d93d2baa..813354f43f 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 ce448b7274..334a163dde 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 dca6891fef..84aa5c8421 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 593179d80e..ce69117bc6 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 6d185d3075..1af45fb1fc 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. * -- cgit v1.2.3 From ca2eeb221455e54505aaef7039cfc0b01b807179 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 20:24:53 +0600 Subject: 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. --- backends/cloud/cloudthread.cpp | 215 ------------------------------ backends/cloud/cloudthread.h | 41 ------ backends/cloud/dropbox/dropboxstorage.cpp | 205 ++++++++++++++++++++++++++++ backends/cloud/dropbox/dropboxstorage.h | 45 +++++++ backends/cloud/manager.cpp | 41 ++++++ backends/cloud/manager.h | 44 ++++++ backends/cloud/storage.cpp | 49 +++++++ backends/cloud/storage.h | 61 +++++++++ backends/module.mk | 6 +- backends/platform/sdl/sdl.cpp | 4 +- base/main.cpp | 4 +- common/cloudmanager.h | 53 ++++++++ common/module.mk | 4 + common/system.h | 14 +- 14 files changed, 516 insertions(+), 270 deletions(-) delete mode 100644 backends/cloud/cloudthread.cpp delete mode 100644 backends/cloud/cloudthread.h create mode 100644 backends/cloud/dropbox/dropboxstorage.cpp create mode 100644 backends/cloud/dropbox/dropboxstorage.h create mode 100644 backends/cloud/manager.cpp create mode 100644 backends/cloud/manager.h create mode 100644 backends/cloud/storage.cpp create mode 100644 backends/cloud/storage.h create mode 100644 common/cloudmanager.h diff --git a/backends/cloud/cloudthread.cpp b/backends/cloud/cloudthread.cpp deleted file mode 100644 index 813354f43f..0000000000 --- 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 keys = main_object->objectKeys(); - - Common::Array::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 334a163dde..0000000000 --- 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 0000000000..b841575bf6 --- /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 keys = main_object->objectKeys(); + + Common::Array::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 0000000000..43ed9dcbee --- /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 0000000000..05b23771b0 --- /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 0000000000..11cc595da4 --- /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 0000000000..3272ecf760 --- /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 0000000000..4afa36f8d8 --- /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 fc2fb99a9d..a1e9c11a1d 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 84aa5c8421..e743bdf829 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 ce69117bc6..001d864355 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 0000000000..6b5768280a --- /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 54aa16f557..29def4b8aa 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 1af45fb1fc..b1c74cbc5b 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 -- cgit v1.2.3 From 6c83930126a2dcb9af4fa631fc7226db1756f801 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Wed, 11 May 2016 17:40:55 +0200 Subject: CONFIGURE: Added libcurl detection --- configure | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/configure b/configure index b663701562..9c3da0a214 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" @@ -433,6 +437,41 @@ find_freetypeconfig() { fi } +# +# 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 # @@ -3677,24 +3716,6 @@ int main(void) { return 0; } 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 # @@ -4090,6 +4111,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 + 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 # -- cgit v1.2.3 From fade746f374cc801870e68934f1dbb3e9c726198 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 11 May 2016 22:52:14 +0600 Subject: CLOUD: Add USE_CLOUD feature Adds USE_CLOUD in both configure and create_project. --- backends/module.mk | 14 ++++++-------- backends/platform/sdl/sdl.cpp | 5 ++--- base/main.cpp | 7 +++---- base/version.cpp | 21 +++++++++++++++++++++ common/module.mk | 5 ++--- common/system.h | 15 ++++++--------- configure | 22 ++++++++++++++++++++++ devtools/create_project/create_project.cpp | 11 +++++++---- 8 files changed, 69 insertions(+), 31 deletions(-) diff --git a/backends/module.mk b/backends/module.mk index a1e9c11a1d..aa9e976fc2 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 e743bdf829..f07f568828 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 001d864355..ac24376e37 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 299e4ce325..b2a7111a87 100644 --- a/base/version.cpp +++ b/base/version.cpp @@ -153,5 +153,26 @@ 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 29def4b8aa..7118ea36ff 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 b1c74cbc5b..b24bc0a875 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 9c3da0a214..8b6068b44a 100755 --- a/configure +++ b/configure @@ -4190,6 +4190,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 # diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 70781ffb55..91690c2128 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"}, -- cgit v1.2.3 From 14785b12d35882f5de0aa197213ae7eda505a463 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Thu, 12 May 2016 14:29:27 +0200 Subject: CONFIGURE: Fix cloud support detection --- configure | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 8b6068b44a..de01ca7edc 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' -- cgit v1.2.3 From b272bba7519951f38aa1aa34c70197c7be1b63fd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 12 May 2016 18:52:57 +0600 Subject: CLOUD: Do minor fixes --- backends/cloud/dropbox/dropboxstorage.cpp | 6 ++++-- backends/cloud/storage.cpp | 2 +- backends/cloud/storage.h | 5 +++-- base/version.cpp | 2 +- common/module.mk | 3 --- common/system.h | 2 -- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index b841575bf6..a8e3076aae 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 3272ecf760..ca6b9bf70e 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 4afa36f8d8..e71b8b7aa0 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 b2a7111a87..d40dd573f0 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 7118ea36ff..54aa16f557 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 b24bc0a875..a3092e794f 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; -- cgit v1.2.3 From 1b89e25580c186fc2cc1821acee942e23fea3682 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 13 May 2016 17:23:28 +0600 Subject: CLOUD: Add first Request Just fooling around with the example Request, but the idea works well. --- backends/cloud/dropbox/dropboxstorage.cpp | 178 ++----------------------- backends/cloud/dropbox/dropboxstorage.h | 9 +- backends/cloud/dropbox/finalcountdownrequest.h | 52 ++++++++ backends/cloud/request.h | 53 ++++++++ backends/cloud/storage.cpp | 28 +++- backends/cloud/storage.h | 14 +- 6 files changed, 148 insertions(+), 186 deletions(-) create mode 100644 backends/cloud/dropbox/finalcountdownrequest.h create mode 100644 backends/cloud/request.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index a8e3076aae..8a0772dc3f 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 keys = main_object->objectKeys(); - - Common::Array::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 43ed9dcbee..f80854b987 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 0000000000..34e8a807f9 --- /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 0000000000..ae85c234f2 --- /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 ca6b9bf70e..597eb6bb13 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::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 e71b8b7aa0..dcaa6af213 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 _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() {}; /** -- cgit v1.2.3 From 6ad983bb7220eed2f635163a7fcfdac811b80ca0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 13 May 2016 21:26:47 +0600 Subject: CLOUD: Fix GCC static cloudThread compile error It's not static anymore. --- backends/cloud/dropbox/finalcountdownrequest.h | 2 +- backends/cloud/manager.cpp | 4 ++-- backends/cloud/storage.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/cloud/dropbox/finalcountdownrequest.h b/backends/cloud/dropbox/finalcountdownrequest.h index 34e8a807f9..5ef942180c 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 05b23771b0..58bb0ce83f 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 597eb6bb13..23dcd8c379 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(); } -- cgit v1.2.3 From 8b585d631b21441dfa3de69d2c7c848d172c73be Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 15 May 2016 00:31:02 +0600 Subject: 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. --- backends/cloud/dropbox/curlrequest.cpp | 82 ++++++++++++++++++++++++++ backends/cloud/dropbox/curlrequest.h | 48 +++++++++++++++ backends/cloud/dropbox/dropboxstorage.cpp | 21 +++++-- backends/cloud/dropbox/dropboxstorage.h | 3 +- backends/cloud/dropbox/finalcountdownrequest.h | 52 ---------------- backends/cloud/storage.cpp | 5 +- backends/module.mk | 3 +- 7 files changed, 153 insertions(+), 61 deletions(-) create mode 100644 backends/cloud/dropbox/curlrequest.cpp create mode 100644 backends/cloud/dropbox/curlrequest.h delete mode 100644 backends/cloud/dropbox/finalcountdownrequest.h diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp new file mode 100644 index 0000000000..216b56c19e --- /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 + +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 0000000000..8453fbb90a --- /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 8a0772dc3f..142a059457 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 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 f80854b987..a0229438be 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 5ef942180c..0000000000 --- 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 23dcd8c379..0c5f6a39e7 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::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 aa9e976fc2..ccf5151b2c 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 -- cgit v1.2.3 From 01abba4f1dc9febbe99a4af6af19eb2afa3f618a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 15 May 2016 11:22:35 +0600 Subject: 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. --- backends/cloud/curl/connectionmanager.cpp | 72 +++++++++++++++++++++++++++ backends/cloud/curl/connectionmanager.h | 47 +++++++++++++++++ backends/cloud/curl/networkreadstream.cpp | 83 +++++++++++++++++++++++++++++++ backends/cloud/curl/networkreadstream.h | 76 ++++++++++++++++++++++++++++ backends/cloud/dropbox/curlrequest.cpp | 42 +++++----------- backends/cloud/dropbox/curlrequest.h | 13 ++--- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- backends/cloud/request.h | 4 +- backends/cloud/storage.cpp | 7 ++- backends/cloud/storage.h | 2 + backends/module.mk | 4 +- 11 files changed, 311 insertions(+), 41 deletions(-) create mode 100644 backends/cloud/curl/connectionmanager.cpp create mode 100644 backends/cloud/curl/connectionmanager.h create mode 100644 backends/cloud/curl/networkreadstream.cpp create mode 100644 backends/cloud/curl/networkreadstream.h diff --git a/backends/cloud/curl/connectionmanager.cpp b/backends/cloud/curl/connectionmanager.cpp new file mode 100644 index 0000000000..d0a0ad8b5c --- /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 + +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 0000000000..0ea27201f2 --- /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 0000000000..8adac67569 --- /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 + +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 0000000000..e469784e28 --- /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 216b56c19e..1dfdd9e25f 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 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 8453fbb90a..2c8ab6b8f8 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 142a059457..9298cde0a1 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 ae85c234f2..b4f5ccab0b 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 0c5f6a39e7..d7217a57cd 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::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 dcaa6af213..0949ff8505 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 _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 ccf5151b2c..cf100892f5 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 -- cgit v1.2.3 From 9c22b7cc64a3bd074f7cec012bd2f29f210d4ccf Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 16 May 2016 01:05:40 +0600 Subject: 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. --- backends/cloud/curl/connectionmanager.cpp | 72 ---------------------- backends/cloud/curl/connectionmanager.h | 47 --------------- backends/cloud/curl/networkreadstream.cpp | 83 -------------------------- backends/cloud/curl/networkreadstream.h | 76 ----------------------- backends/cloud/dropbox/curlrequest.cpp | 4 +- backends/cloud/dropbox/curlrequest.h | 9 +-- backends/cloud/request.h | 4 +- backends/cloud/storage.h | 4 +- backends/module.mk | 10 +++- backends/networking/curl/connectionmanager.cpp | 71 ++++++++++++++++++++++ backends/networking/curl/connectionmanager.h | 47 +++++++++++++++ backends/networking/curl/networkreadstream.cpp | 70 ++++++++++++++++++++++ backends/networking/curl/networkreadstream.h | 75 +++++++++++++++++++++++ common/memstream.h | 83 ++++++++++++++++++++++++++ 14 files changed, 364 insertions(+), 291 deletions(-) delete mode 100644 backends/cloud/curl/connectionmanager.cpp delete mode 100644 backends/cloud/curl/connectionmanager.h delete mode 100644 backends/cloud/curl/networkreadstream.cpp delete mode 100644 backends/cloud/curl/networkreadstream.h create mode 100644 backends/networking/curl/connectionmanager.cpp create mode 100644 backends/networking/curl/connectionmanager.h create mode 100644 backends/networking/curl/networkreadstream.cpp create mode 100644 backends/networking/curl/networkreadstream.h diff --git a/backends/cloud/curl/connectionmanager.cpp b/backends/cloud/curl/connectionmanager.cpp deleted file mode 100644 index d0a0ad8b5c..0000000000 --- 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 - -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 0ea27201f2..0000000000 --- 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 8adac67569..0000000000 --- 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 - -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 e469784e28..0000000000 --- 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 1dfdd9e25f..ecc868cb51 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 @@ -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 2c8ab6b8f8..3d5d4adb72 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 b4f5ccab0b..d85a68d570 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 0949ff8505..84b6157a22 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 _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 cf100892f5..0be8ef90a5 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 0000000000..b7b7112c7f --- /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 + +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 0000000000..fadcdf1372 --- /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 0000000000..5049ae507b --- /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 + +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 0000000000..9c7d8f83b6 --- /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 59d5f15b1a..963637cb27 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 -- cgit v1.2.3 From 03217cd5c3de3c17739a246f5967dfd4a14eb120 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 17 May 2016 01:19:49 +0600 Subject: 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. --- backends/cloud/dropbox/curlrequest.cpp | 64 -------------------------- backends/cloud/dropbox/curlrequest.h | 50 -------------------- backends/cloud/dropbox/dropboxstorage.cpp | 20 +++++--- backends/module.mk | 6 +-- backends/networking/curl/connectionmanager.cpp | 26 +++++------ backends/networking/curl/curljsonrequest.cpp | 64 ++++++++++++++++++++++++++ backends/networking/curl/curljsonrequest.h | 46 ++++++++++++++++++ backends/platform/sdl/sdl.cpp | 4 +- common/system.h | 4 +- 9 files changed, 144 insertions(+), 140 deletions(-) delete mode 100644 backends/cloud/dropbox/curlrequest.cpp delete mode 100644 backends/cloud/dropbox/curlrequest.h create mode 100644 backends/networking/curl/curljsonrequest.cpp create mode 100644 backends/networking/curl/curljsonrequest.h diff --git a/backends/cloud/dropbox/curlrequest.cpp b/backends/cloud/dropbox/curlrequest.cpp deleted file mode 100644 index ecc868cb51..0000000000 --- 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 - -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 3d5d4adb72..0000000000 --- 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 9298cde0a1..2db915de38 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 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 0be8ef90a5..72183f9563 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 b7b7112c7f..ceff8b62a4 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 0000000000..acd9675aed --- /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 + +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 0000000000..73e0144c64 --- /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 f07f568828..acb4d999c3 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 a3092e794f..815fa9d19d 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 -- cgit v1.2.3 From 5df8c5140292520bafe92efa94935a776d63d108 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 17 May 2016 20:17:41 +0600 Subject: CLOUD: Fix CurlJsonRequest It's using MemoryWriteStreamDynamic instead of String and it prepares raw byte contents of this stream for JSON::parse(). --- backends/cloud/dropbox/dropboxstorage.cpp | 1 + backends/networking/curl/connectionmanager.cpp | 2 +- backends/networking/curl/curljsonrequest.cpp | 36 +++++++++++++++++++++----- backends/networking/curl/curljsonrequest.h | 6 ++++- backends/networking/curl/networkreadstream.cpp | 9 ++++++- backends/networking/curl/networkreadstream.h | 16 +++++++++++- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 2db915de38..729283bcfa 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 ceff8b62a4..8c718e019b 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 acd9675aed..f5a7a7c8d0 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 73e0144c64..e9634393dc 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 5049ae507b..b8cb81aca2 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 9c7d8f83b6..0333e4fb16 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 -- cgit v1.2.3 From e743a65636a674def45e955cb7a5632aead2a033 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 18 May 2016 14:08:05 +0600 Subject: 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). --- backends/cloud/dropbox/dropboxstorage.cpp | 110 +++++++++++++++++++++++-- backends/cloud/dropbox/dropboxstorage.h | 39 +++++++-- backends/cloud/manager.cpp | 15 +++- backends/cloud/manager.h | 2 + backends/cloud/storage.h | 6 ++ backends/networking/curl/connectionmanager.cpp | 4 +- backends/networking/curl/connectionmanager.h | 3 +- backends/networking/curl/curljsonrequest.cpp | 16 +++- backends/networking/curl/curljsonrequest.h | 8 ++ backends/networking/curl/networkreadstream.cpp | 7 +- backends/networking/curl/networkreadstream.h | 3 +- base/main.cpp | 1 + common/cloudmanager.h | 12 ++- common/config-manager.cpp | 26 ++++++ common/config-manager.h | 9 ++ 15 files changed, 239 insertions(+), 22 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 729283bcfa..add6bff54c 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 @@ -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 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 a0229438be..f2281f146f 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 58bb0ce83f..08340e9288 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 11cc595da4..a1f046d71d 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 84b6157a22..a5d048d3af 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 8c718e019b..d34eab23e8 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 fadcdf1372..b83de6191a 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 f5a7a7c8d0..0c6363467a 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 e9634393dc..17df9693f2 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 b8cb81aca2..f2af4fd50c 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 0333e4fb16..bc4b761ba7 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 ac24376e37..f629eb98d8 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 6b5768280a..8a10a1a3c7 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 24c877a4e9..40d8aca219 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 - @@ -66,6 +70,9 @@ void ConfigManager::copyFrom(ConfigManager &source) { _defaultsDomain = source._defaultsDomain; #ifdef ENABLE_KEYMAPPER _keymapperDomain = source._keymapperDomain; +#endif +#ifdef USE_CLOUD + _cloudDomain = source._cloudDomain; #endif _domainSaveOrder = source._domainSaveOrder; _activeDomainName = source._activeDomainName; @@ -120,6 +127,10 @@ void ConfigManager::addDomain(const String &domainName, const ConfigManager::Dom #ifdef ENABLE_KEYMAPPER } 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 @@ -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; @@ -358,6 +376,10 @@ const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) con #ifdef ENABLE_KEYMAPPER if (domName == kKeymapperDomain) return &_keymapperDomain; +#endif +#ifdef USE_CLOUD + if (domName == kCloudDomain) + return &_cloudDomain; #endif if (_gameDomains.contains(domName)) return &_gameDomains[domName]; @@ -378,6 +400,10 @@ ConfigManager::Domain *ConfigManager::getDomain(const String &domName) { #ifdef ENABLE_KEYMAPPER if (domName == kKeymapperDomain) return &_keymapperDomain; +#endif +#ifdef USE_CLOUD + if (domName == kCloudDomain) + return &_cloudDomain; #endif if (_gameDomains.contains(domName)) return &_gameDomains[domName]; diff --git a/common/config-manager.h b/common/config-manager.h index 14f911f69d..669faaaf88 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 _domainSaveOrder; String _activeDomainName; -- cgit v1.2.3 From 0a947618fb1faa023df03d355e9749905424dd58 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 18 May 2016 15:21:09 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 26 ++++----- backends/cloud/dropbox/dropboxstorage.h | 8 ++- backends/cloud/request.h | 55 ------------------- backends/cloud/storage.cpp | 73 -------------------------- backends/cloud/storage.h | 19 +------ backends/module.mk | 3 +- backends/networking/curl/connectionmanager.cpp | 63 +++++++++++++++++++--- backends/networking/curl/connectionmanager.h | 40 ++++++++++++-- backends/networking/curl/curljsonrequest.cpp | 5 +- backends/networking/curl/curljsonrequest.h | 6 +-- backends/networking/curl/networkreadstream.cpp | 2 + backends/networking/curl/networkreadstream.h | 2 - backends/networking/curl/request.h | 54 +++++++++++++++++++ 13 files changed, 170 insertions(+), 186 deletions(-) delete mode 100644 backends/cloud/request.h delete mode 100644 backends/cloud/storage.cpp create mode 100644 backends/networking/curl/request.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index add6bff54c..93f0eebcf6 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 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 f2281f146f..0500db5d49 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 d85a68d570..0000000000 --- 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 d7217a57cd..0000000000 --- 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::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 a5d048d3af..9e23e97761 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 _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 72183f9563..0142531835 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 d34eab23e8..31e99f989c 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 +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::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 b83de6191a..ed726441c4 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 { + friend void connectionsThread(void *); //calls handle() + + CURLM *_multi; + bool _timerStarted; + Common::Array _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 0c6363467a..59bc830692 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 17df9693f2..cbf3f6dd56 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 f2af4fd50c..8fd39d6884 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 @@ -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 bc4b761ba7..6431a01fee 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 0000000000..4f901f7c94 --- /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 -- cgit v1.2.3 From f913675c43ada5c5f9128d904fd913129da35fe8 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 18 May 2016 15:31:22 +0600 Subject: CLOUD: Add ConnectionManager hotfix Tried to compile these two last commits with GCC and found a few minor problems. --- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/module.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index 0500db5d49..afa9447566 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 0142531835..035eac2623 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 -- cgit v1.2.3 From 17eb5f91433f2414dc73f89abfdd316407259b61 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 21 May 2016 00:44:09 +0600 Subject: 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). --- backends/cloud/dropbox/dropboxstorage.cpp | 42 +++++++++++--- backends/cloud/dropbox/dropboxstorage.h | 33 ++++++++++- backends/cloud/manager.cpp | 4 +- backends/cloud/manager.h | 2 +- backends/cloud/storage.h | 85 ++++++++++++++++++++++------ backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/request.h | 12 +++- common/cloudmanager.h | 2 +- 8 files changed, 149 insertions(+), 33 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 93f0eebcf6..f2270f79cd 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 afa9447566..d60dec29de 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. */ + 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 08340e9288..fc271485bf 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 a1f046d71d..47109cc146 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 9e23e97761..4fb06f6a9d 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& 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. */ + 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 59bc830692..929723c671 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 4f901f7c94..d405ec8551 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 8a10a1a3c7..08ba928f34 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 -- cgit v1.2.3 From ca456a7241fb4b46f9c76c86eeecc273ca31d8f6 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 21 May 2016 14:06:50 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 41 ++++++++++++++++++++++-- backends/cloud/dropbox/dropboxstorage.h | 4 +++ backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/curljsonrequest.h | 2 +- backends/networking/curl/request.h | 6 ++-- common/callback.h | 47 ++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 common/callback.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index f2270f79cd..94bdb43450 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(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 *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 d60dec29de..1cf657bc36 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 *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 929723c671..fe6e218269 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 cbf3f6dd56..56e7205512 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 d405ec8551..b9571075cb 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 0000000000..4cfdd53fd9 --- /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 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 -- cgit v1.2.3 From e1109c0c328aaf671e2b03b3b4e6de1ae9061754 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 21 May 2016 21:30:25 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 20 ++++++++++++++------ backends/cloud/dropbox/dropboxstorage.h | 3 ++- backends/networking/curl/curljsonrequest.cpp | 4 ++-- backends/networking/curl/curljsonrequest.h | 6 +++++- backends/networking/curl/request.h | 5 +++-- common/callback.h | 24 ++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 94bdb43450..04e9eeef0b 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 *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(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 1cf657bc36..efd0eea257 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 *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 fe6e218269..c3064681ef 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 56e7205512..1098638609 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 b9571075cb..860784f2a7 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 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 4cfdd53fd9..4cc63ed346 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 Callback: public BaseCallback { typedef void(T::*TMethod)(void *); T *_object; @@ -42,6 +54,18 @@ public: void operator()(void *ptr) { (_object->*_method)(ptr); } }; +template 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 -- cgit v1.2.3 From 9e531e3ce7f5b3a1cc87b43beb6f72911cb41bdd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 21 May 2016 23:21:42 +0600 Subject: CLOUD: Polish Callbacks Cleaned up all example code and old callbacks. New Callback classes are introduced in "common/callback.h" and documented. --- backends/cloud/dropbox/dropboxstorage.cpp | 113 +++++++-------------------- backends/cloud/dropbox/dropboxstorage.h | 31 ++++---- backends/cloud/manager.cpp | 2 +- backends/cloud/manager.h | 2 +- backends/cloud/storage.h | 26 +++--- backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/curljsonrequest.h | 7 +- backends/networking/curl/request.h | 19 +---- common/callback.h | 97 ++++++++++++++++++++--- common/cloudmanager.h | 2 +- 10 files changed, 149 insertions(+), 152 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 04e9eeef0b..964b95bb09 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 *callback) { + //this is not the real syncSaves() implementation + info(new Common::Callback(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(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 *outerCallback) { + Common::BaseCallback<> *innerCallback = new Common::CallbackBridge(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 *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(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 efd0eea257..8cc9312a87 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. */ - virtual void listDirectory(Common::String path, ListDirectoryCallback callback) {} //TODO + /** Returns pointer to Common::Array. */ + virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array > *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 *callback) {} //TODO /** Returns pointer to Common::ReadStream. */ - virtual void download(Common::String path, DownloadCallback callback) {} //TODO + virtual void download(Common::String path, Common::BaseCallback *callback) {} //TODO /** Calls the callback when finished. */ - virtual void remove(Common::String path, OperationCallback callback) {} //TODO + virtual void remove(Common::String path, Common::BaseCallback *callback) {} //TODO /** Calls the callback when finished. */ - virtual void syncSaves(OperationCallback callback); + virtual void syncSaves(Common::BaseCallback *callback); /** Calls the callback when finished. */ - virtual void createDirectory(Common::String path, OperationCallback callback) {} //TODO + virtual void createDirectory(Common::String path, Common::BaseCallback *callback) {} //TODO /** Calls the callback when finished. */ - virtual void touch(Common::String path, OperationCallback callback) {} //TODO + virtual void touch(Common::String path, Common::BaseCallback *callback) {} //TODO + + /** Returns pointer to the StorageInfo struct. */ + virtual void info(Common::BaseCallback *callback); + + /** This is what is called by CurlJsonRequest. */ + void infoInnerCallback(Common::BaseCallback *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 fc271485bf..a9a2b8a232 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 *callback) { Storage* storage = getCurrentStorage(); if (storage) storage->syncSaves(callback); } diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h index 47109cc146..c247132707 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 *callback); }; } //end of namespace Cloud diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 4fb06f6a9d..f2079eb919 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& 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. */ - virtual void listDirectory(Common::String path, ListDirectoryCallback callback) = 0; + /** Returns pointer to Common::Array. */ + virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array > *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 *callback) = 0; /** Returns pointer to Common::ReadStream. */ - virtual void download(Common::String path, DownloadCallback callback) = 0; + virtual void download(Common::String path, Common::BaseCallback *callback) = 0; /** Calls the callback when finished. */ - virtual void remove(Common::String path, OperationCallback callback) = 0; + virtual void remove(Common::String path, Common::BaseCallback *callback) = 0; /** Calls the callback when finished. */ - virtual void syncSaves(OperationCallback callback) = 0; + virtual void syncSaves(Common::BaseCallback *callback) = 0; /** Calls the callback when finished. */ - virtual void createDirectory(Common::String path, OperationCallback callback) = 0; + virtual void createDirectory(Common::String path, Common::BaseCallback *callback) = 0; /** Calls the callback when finished. */ - virtual void touch(Common::String path, OperationCallback callback) = 0; + virtual void touch(Common::String path, Common::BaseCallback *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 *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 c3064681ef..702de22f46 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 1098638609..50fa8183dd 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 860784f2a7..d3efd588dc 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 + +#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 4cc63ed346..2101331bf0 100644 --- a/common/callback.h +++ b/common/callback.h @@ -25,45 +25,116 @@ namespace Common { -class BaseCallback { +/** +* BaseCallback 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, so it can be used with global C-like +* functions too. +* +* 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 object. +*/ + +template 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, you can +* easily pass your C function by passing +* new GlobalFunctionCallback(yourFunction) +*/ + +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); + virtual void operator()(void *data) { + if (_callback) _callback(data); } }; -template class Callback: public BaseCallback { - typedef void(T::*TMethod)(void *); +/** +* Callback implements an object-oriented callback. +* +* stands for a class which method you want to call. +* , again, is the type of an object passed to operator(). +* +* So, if you have void MyClass::myMethod(AnotherClass) method, +* the corresponding callback is Callback. +* You create it similarly to this: +* new Callback( +* pointerToMyClassObject, +* &MyClass::myMethod +* ) +*/ + +template class Callback: public BaseCallback { +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 CallbackBridge: public BaseCallback { - typedef void(T::*TCallbackMethod)(BaseCallback *, void *); +/** +* CallbackBridge helps you to chain callbacks. +* +* CallbackBridge keeps a pointer to BaseCallback. +* When its operator() is called, it passes this pointer +* along with the actual data (of type ) to the method +* of 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 callback +* and you want to call it from your MyClass::myMethod method, +* you should create CallbackBridge, +* where is data type you want to receive in MyClass::myMethod. +* +* You create it similarly to this: +* new Callback( +* pointerToMyClassObject, +* &MyClass::myMethod, +* outerCallback +* ) +* where `outerCallback` is BaseCallback and `myMethod` is: +* void MyClass::myMethod(BaseCallback *, AnotherClass) +*/ + +template class CallbackBridge: public BaseCallback { + typedef void(T::*TCallbackMethod)(BaseCallback *, S); T *_object; TCallbackMethod _method; - BaseCallback *_outerCallback; + BaseCallback *_outerCallback; public: - CallbackBridge(T *object, TCallbackMethod method, BaseCallback *outerCallback): + CallbackBridge(T *object, TCallbackMethod method, BaseCallback *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 08ba928f34..5ed67d3f7d 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 *callback = 0) = 0; }; } //end of namespace Common -- cgit v1.2.3 From b570499164bf94fc4735bad54e7a722498ae56ea Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 22 May 2016 00:04:00 +0600 Subject: CLOUD: Add Callback typedefs And do some minor cleanup work. --- backends/cloud/dropbox/dropboxstorage.cpp | 10 +++-- backends/cloud/dropbox/dropboxstorage.h | 24 +++++------ backends/cloud/manager.cpp | 2 +- backends/cloud/manager.h | 2 +- backends/cloud/storage.h | 66 ++++++++----------------------- backends/cloud/storagefile.cpp | 48 ++++++++++++++++++++++ backends/cloud/storagefile.h | 53 +++++++++++++++++++++++++ backends/cloud/storageinfo.h | 47 ++++++++++++++++++++++ backends/module.mk | 1 + common/cloudmanager.h | 2 +- 10 files changed, 187 insertions(+), 68 deletions(-) create mode 100644 backends/cloud/storagefile.cpp create mode 100644 backends/cloud/storagefile.h create mode 100644 backends/cloud/storageinfo.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 964b95bb09..49355fa845 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 *callback) { +void DropboxStorage::syncSaves(BoolCallback callback) { //this is not the real syncSaves() implementation info(new Common::Callback(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 *outerCallback) { +void DropboxStorage::info(StorageInfoCallback outerCallback) { Common::BaseCallback<> *innerCallback = new Common::CallbackBridge(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 *outerCallback) { //and then calls the outerCallback (which wants to receive StorageInfo, not void *) } -void DropboxStorage::infoInnerCallback(Common::BaseCallback *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 *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 8cc9312a87..3fc38bc1bb 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. */ - virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array > *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 *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 *callback) {} //TODO + virtual void download(Common::String path, ReadStreamCallback callback) {} //TODO /** Calls the callback when finished. */ - virtual void remove(Common::String path, Common::BaseCallback *callback) {} //TODO + virtual void remove(Common::String path, BoolCallback callback) {} //TODO /** Calls the callback when finished. */ - virtual void syncSaves(Common::BaseCallback *callback); + virtual void syncSaves(BoolCallback callback); /** Calls the callback when finished. */ - virtual void createDirectory(Common::String path, Common::BaseCallback *callback) {} //TODO + virtual void createDirectory(Common::String path, BoolCallback callback) {} //TODO /** Calls the callback when finished. */ - virtual void touch(Common::String path, Common::BaseCallback *callback) {} //TODO + virtual void touch(Common::String path, BoolCallback callback) {} //TODO /** Returns pointer to the StorageInfo struct. */ - virtual void info(Common::BaseCallback *callback); - - /** This is what is called by CurlJsonRequest. */ - void infoInnerCallback(Common::BaseCallback *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 a9a2b8a232..7caf241497 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 *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 c247132707..3ad2e0d607 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 *callback); + virtual void syncSaves(Storage::BoolCallback callback); }; } //end of namespace Cloud diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index f2079eb919..eaf0ba1cc8 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 > *FileArrayCallback; + typedef Common::BaseCallback *ReadStreamCallback; + typedef Common::BaseCallback *StorageInfoCallback; + typedef Common::BaseCallback *BoolCallback; + Storage() {} virtual ~Storage() {} - /** Returns pointer to Common::Array. */ - virtual void listDirectory(Common::String path, Common::BaseCallback< Common::Array > *callback) = 0; + /** Returns Common::Array. */ + 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 *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 *callback) = 0; + virtual void download(Common::String path, ReadStreamCallback callback) = 0; /** Calls the callback when finished. */ - virtual void remove(Common::String path, Common::BaseCallback *callback) = 0; + virtual void remove(Common::String path, BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual void syncSaves(Common::BaseCallback *callback) = 0; + virtual void syncSaves(BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual void createDirectory(Common::String path, Common::BaseCallback *callback) = 0; + virtual void createDirectory(Common::String path, BoolCallback callback) = 0; /** Calls the callback when finished. */ - virtual void touch(Common::String path, Common::BaseCallback *callback) = 0; + virtual void touch(Common::String path, BoolCallback callback) = 0; - /** Returns pointer to the StorageInfo struct. */ - virtual void info(Common::BaseCallback *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 0000000000..0d40698823 --- /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 0000000000..8706ba18e9 --- /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 0000000000..510acb3778 --- /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 035eac2623..2cee007264 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 5ed67d3f7d..5919937720 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 *callback = 0) = 0; + virtual void syncSaves(Cloud::Storage::BoolCallback callback = 0) = 0; }; } //end of namespace Common -- cgit v1.2.3 From c4b1fdc72752516a81b83490035d8f71357d045b Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 22 May 2016 00:07:20 +0600 Subject: CLOUD: Fix Request destructor It now contains not a pointer to a function, but an actual pointer, which must be freed. --- backends/networking/curl/request.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h index d3efd588dc..37cb3884ca 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. -- cgit v1.2.3 From 41e65db7d0468b01f626c6ce21a1b8e6791f58fa Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 23 May 2016 11:23:33 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 25 ++++++++----- backends/cloud/dropbox/dropboxstorage.h | 22 ++++++++++-- backends/cloud/manager.cpp | 59 +++++++++++++++++++++++++------ backends/cloud/manager.h | 6 ++-- backends/cloud/storage.h | 16 +++++++++ common/cloudmanager.h | 8 ++++- 6 files changed, 112 insertions(+), 24 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 49355fa845..d32d86567a 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(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 3fc38bc1bb..493fcdd25d 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. */ + /** + * 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. */ 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 7caf241497..1c11efbcef 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 3ad2e0d607..e531854ba9 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 _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 eaf0ba1cc8..a6b6c48fbe 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. */ virtual void listDirectory(Common::String path, FileArrayCallback callback) = 0; diff --git a/common/cloudmanager.h b/common/cloudmanager.h index 5919937720..d1c89454b1 100644 --- a/common/cloudmanager.h +++ b/common/cloudmanager.h @@ -40,6 +40,12 @@ 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. @@ -47,7 +53,7 @@ public: * @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. -- cgit v1.2.3 From a439bd4c33e6f8a47a856d7cb4a4db7c540a85ab Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 23 May 2016 12:00:12 +0600 Subject: CLOUD: Make StorageInfo useful It now contains a few useful methods to get name or quota usage. DropboxStorage returns a finely filled StorageInfo. --- backends/cloud/dropbox/dropboxstorage.cpp | 20 +++++++++++++++----- backends/cloud/storageinfo.h | 16 +++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index d32d86567a..5a881211e3 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 510acb3778..f09563570f 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 -- cgit v1.2.3 From 735db74b900d1e0a0654ca03983cd91cea36f41e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 23 May 2016 12:21:45 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 34 ++++++++++++++++++++++++---- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/storage.h | 2 +- backends/networking/curl/curljsonrequest.cpp | 6 +++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 5a881211e3..28d14c6a2e 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 >(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(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 493fcdd25d..3077b98763 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. */ - 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 a6b6c48fbe..1435be8329 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. */ - 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 702de22f46..805852ea0a 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! } -- cgit v1.2.3 From e53e3d188b9da424af5e44e51bff265f077ce05e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 00:14:24 +0600 Subject: CLOUD: Add DropboxListDirectoryRequest Does multiple CurlJsonRequests while Dropbox returns "has_more" = true. --- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 114 ++++++++++++++++++++ .../cloud/dropbox/dropboxlistdirectoryrequest.h | 51 +++++++++ backends/cloud/dropbox/dropboxstorage.cpp | 32 ++---- backends/cloud/dropbox/dropboxstorage.h | 2 + backends/cloud/iso8601.cpp | 117 +++++++++++++++++++++ backends/cloud/iso8601.h | 37 +++++++ backends/cloud/storagefile.cpp | 2 +- backends/module.mk | 4 +- 8 files changed, 333 insertions(+), 26 deletions(-) create mode 100644 backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp create mode 100644 backends/cloud/dropbox/dropboxlistdirectoryrequest.h create mode 100644 backends/cloud/iso8601.cpp create mode 100644 backends/cloud/iso8601.h diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp new file mode 100644 index 0000000000..e28a445d63 --- /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(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(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 0000000000..03b4fc121a --- /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 _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 28d14c6a2e..6de9424efc 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 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 >(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 >(this, &DropboxStorage::printFiles), true); } void DropboxStorage::info(StorageInfoCallback outerCallback) { diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index 3077b98763..c1c2e03497 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 files); + public: virtual ~DropboxStorage(); diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp new file mode 100644 index 0000000000..d34c98e7b8 --- /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 0000000000..a53b3bb501 --- /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 0d40698823..02c9cae3cf 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 2cee007264..7e83192ee0 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 -- cgit v1.2.3 From 3582f6165ce829e4990c15bf77d1792ee20dca55 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 00:50:35 +0600 Subject: CLOUD: Change ISO8601 to use strchr I'm not sure it works as it should though. --- backends/cloud/iso8601.cpp | 54 ++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/backends/cloud/iso8601.cpp b/backends/cloud/iso8601.cpp index d34c98e7b8..12cbb742b8 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 -- cgit v1.2.3 From 826a2a921cd0b0a72f71dd6f323097a2f449fab0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 11:57:49 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 67 ++++++++++++++++++++ backends/cloud/downloadrequest.h | 45 ++++++++++++++ .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 1 + .../cloud/dropbox/dropboxlistdirectoryrequest.h | 2 +- backends/cloud/dropbox/dropboxstorage.cpp | 21 ++++++- backends/cloud/dropbox/dropboxstorage.h | 9 ++- backends/cloud/storage.h | 10 ++- backends/module.mk | 2 + backends/networking/curl/connectionmanager.cpp | 2 +- backends/networking/curl/curljsonrequest.cpp | 24 ++------ backends/networking/curl/curljsonrequest.h | 15 +---- backends/networking/curl/curlrequest.cpp | 72 ++++++++++++++++++++++ backends/networking/curl/curlrequest.h | 58 +++++++++++++++++ backends/networking/curl/networkreadstream.cpp | 3 +- 14 files changed, 288 insertions(+), 43 deletions(-) create mode 100644 backends/cloud/downloadrequest.cpp create mode 100644 backends/cloud/downloadrequest.h create mode 100644 backends/networking/curl/curlrequest.cpp create mode 100644 backends/networking/curl/curlrequest.h diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp new file mode 100644 index 0000000000..ed84d4f0e5 --- /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 0000000000..a819910b1b --- /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 e28a445d63..5e5957b12c 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 03b4fc121a..0c10512782 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 6de9424efc..1b6dc1b92f 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 >(this, &DropboxStorage::printFiles), true); + //listDirectory("", new Common::Callback >(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 c1c2e03497..92c3746da5 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 1435be8329..92f3bb96b2 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 7e83192ee0..eb5ce070b5 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 31e99f989c..97ae31a446 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::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 805852ea0a..0366e3b403 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 50fa8183dd..cfb82e97e3 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 0000000000..e13adaca59 --- /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 + +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 0000000000..22f50be418 --- /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 8fd39d6884..0cf7118a8a 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 { -- cgit v1.2.3 From caaa4c5a5d0bce7582cc6611d8bde53fbdb1f2d1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 12:31:27 +0600 Subject: CLOUD: Make DownloadRequest write to local file Tested with .jpg file. Transfer complete, CRC-32 is the same. --- backends/cloud/downloadrequest.cpp | 41 ++++++++++++++++++------------- backends/cloud/downloadrequest.h | 8 +++--- backends/cloud/dropbox/dropboxstorage.cpp | 15 ++++++++--- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/storage.h | 2 +- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index ed84d4f0e5..8124fd67d7 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 a819910b1b..b135b15f23 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 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 1b6dc1b92f..38ad12d94b 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 +#include 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 >(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 92c3746da5..415a3fe5fb 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 92f3bb96b2..e38c6bedd9 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; -- cgit v1.2.3 From 5f4bbe6e9e08f5f76eada84497a7530ffb08fbf1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 16:19:22 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 2 +- backends/cloud/manager.cpp | 19 +++- backends/cloud/manager.h | 1 + backends/cloud/onedrive/onedrivestorage.cpp | 151 ++++++++++++++++++++++++++++ backends/cloud/onedrive/onedrivestorage.h | 119 ++++++++++++++++++++++ backends/module.mk | 3 +- common/cloudmanager.h | 10 ++ 7 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 backends/cloud/onedrive/onedrivestorage.cpp create mode 100644 backends/cloud/onedrive/onedrivestorage.h diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 8124fd67d7..e86b6552e9 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 1c11efbcef..a7e92dfe03 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 e531854ba9..08106e0513 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 0000000000..b632c74580 --- /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 +#include +#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 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 0000000000..99f8476bc1 --- /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. */ + 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 eb5ce070b5..c8842ca32b 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 d1c89454b1..a480bdf684 100644 --- a/common/cloudmanager.h +++ b/common/cloudmanager.h @@ -46,6 +46,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. -- cgit v1.2.3 From 5e346ea6fbcd4a028bc7dd63efd09ff42770f8ef Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 23:08:48 +0600 Subject: CLOUD: Add OneDrive refresh_token support It might be not that easy to restart the request after new token received, though. --- backends/cloud/onedrive/onedrivestorage.cpp | 139 +++++++++++++++++-------- backends/cloud/onedrive/onedrivestorage.h | 26 +++-- backends/networking/curl/networkreadstream.cpp | 6 +- 3 files changed, 119 insertions(+), 52 deletions(-) diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index b632c74580..2f10841cc9 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(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(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(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(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 99f8476bc1..28f37aee2c 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 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(); @@ -91,11 +106,6 @@ public: /** 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. diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp index 0cf7118a8a..9a196b92d2 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); } -- cgit v1.2.3 From d014b5bf3826d78de269891ccf6722e58d7a5bcd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 25 May 2016 15:55:06 +0600 Subject: 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. --- common/memstream.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/memstream.h b/common/memstream.h index 963637cb27..76fa07cd24 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; -- cgit v1.2.3 From ae8e7f39f5b3e4f647a29d39d19cce7626528bb1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 25 May 2016 16:32:22 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 6 +++--- backends/fs/abstract-fs.h | 9 +++++++++ backends/fs/posix/posix-fs.cpp | 24 +++++++++++++++++++++++ backends/fs/posix/posix-fs.h | 1 + backends/fs/windows/windows-fs.cpp | 32 +++++++++++++++++++++++++++++++ backends/fs/windows/windows-fs.h | 1 + common/file.cpp | 18 +++++++++++++++-- common/file.h | 2 +- 8 files changed, 87 insertions(+), 6 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 38ad12d94b..e5041466e9 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 >(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 dcfdc08975..24286b649d 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 1a6c4e40a5..d388f306fd 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 0703ac5bf5..4ebce7e9d9 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 49549b83cb..b43686f911 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 d06044603a..7c9f2c1e1a 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 4d9c630076..52b66bd2f4 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 3d174834e9..8ad6249d6d 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(); -- cgit v1.2.3 From 17fc40a94436f970af75d6717c7a63c70eafcb12 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 25 May 2016 16:49:17 +0600 Subject: CLOUD: Add AbstractFSNode::create() backends stubs --- backends/fs/amigaos4/amigaos4-fs.cpp | 5 +++++ backends/fs/amigaos4/amigaos4-fs.h | 1 + backends/fs/chroot/chroot-fs.cpp | 5 +++++ backends/fs/chroot/chroot-fs.h | 1 + backends/fs/ds/ds-fs.cpp | 10 ++++++++++ backends/fs/ds/ds-fs.h | 2 ++ backends/fs/n64/n64-fs.cpp | 5 +++++ backends/fs/n64/n64-fs.h | 1 + backends/fs/ps2/ps2-fs.cpp | 5 +++++ backends/fs/ps2/ps2-fs.h | 1 + backends/fs/psp/psp-fs.cpp | 5 +++++ backends/fs/psp/psp-fs.h | 1 + backends/fs/symbian/symbian-fs.cpp | 6 ++++++ backends/fs/symbian/symbian-fs.h | 1 + backends/fs/wii/wii-fs.cpp | 5 +++++ backends/fs/wii/wii-fs.h | 1 + 16 files changed, 55 insertions(+) diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp index 6d5b099736..24a8fb98ad 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 408354888e..3ed45d3f77 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 2cbb4af9d6..f5f7c84570 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 9ff913be31..e0ecc1c47e 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 3782caf432..83b1295838 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 b9ccfcbe9c..939d1a1555 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 fe37dad467..d568500d30 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 d503a6601e..d520ad5be6 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 9b6e1270f1..8391e063a0 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 63b866ba5b..c9da434535 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 e8aad9fa98..6bd5e93435 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 1bb4543d19..648544650b 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 8fbc3a402a..e4163caa33 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 339e998a28..1327f9f2e9 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 43f4f592b7..46041bf499 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 c77c543dae..affb765884 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 -- cgit v1.2.3 From eda575a660543884b1a4addd21b676a67d2c2a31 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 17:12:40 +0600 Subject: CLOUD: Add Requests id (Upgrading ConnectionManager step by step.) --- backends/cloud/dropbox/dropboxstorage.cpp | 18 +++++++++--------- backends/cloud/dropbox/dropboxstorage.h | 16 ++++++++-------- backends/cloud/onedrive/onedrivestorage.cpp | 4 ++-- backends/cloud/onedrive/onedrivestorage.h | 16 ++++++++-------- backends/cloud/storage.h | 26 ++++++++++++++++---------- backends/networking/curl/connectionmanager.cpp | 18 ++++++++++++------ backends/networking/curl/connectionmanager.h | 9 ++++++--- backends/networking/curl/request.h | 9 +++++++-- 8 files changed, 68 insertions(+), 48 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index e5041466e9..9acbfc0428 100644 --- a/backends/cloud/dropbox/dropboxstorage.cpp +++ b/backends/cloud/dropbox/dropboxstorage.cpp @@ -84,8 +84,8 @@ void DropboxStorage::printFiles(Common::Array 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(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 415a3fe5fb..6abd3d1d94 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. */ - 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 2f10841cc9..b0690be5b0 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(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 28f37aee2c..4141771f65 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. */ - 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 e38c6bedd9..394fc2c22d 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. */ - 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 97ae31a446..8428bd25f0 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::iterator i = _requests.begin(); i != _requests.end();) { - if ((*i)->handle()) { - delete (*i); - _requests.erase(i); + for (Common::HashMap::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 ed726441c4..9651a0166a 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 { CURLM *_multi; bool _timerStarted; - Common::Array _requests; + Common::HashMap _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 37cb3884ca..0265d3e2f3 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 -- cgit v1.2.3 From 62ccf1f902100febfb1be02b67e84a6e4938ebbf Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 17:56:13 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 17 ++++++++- backends/cloud/downloadrequest.h | 1 + .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 34 ++++++++++++++---- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 6 ++++ backends/networking/curl/connectionmanager.cpp | 41 +++++++++++++++++----- backends/networking/curl/connectionmanager.h | 24 +++++++++++-- backends/networking/curl/curljsonrequest.cpp | 2 ++ backends/networking/curl/curlrequest.cpp | 9 ++++- backends/networking/curl/curlrequest.h | 2 +- backends/networking/curl/request.h | 2 ++ 10 files changed, 117 insertions(+), 21 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index e86b6552e9..a96c298fe8 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 b135b15f23..c1564100c2 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 5e5957b12c..3dada884a0 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(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(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 0c10512782..36070a2a32 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 _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 8428bd25f0..b448d8e514 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::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 idsToRemove; + for (Common::HashMap::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 9651a0166a..9ae52b3eeb 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 { friend void connectionsThread(void *); //calls handle() CURLM *_multi; bool _timerStarted; - Common::HashMap _requests; + Common::HashMap _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 0366e3b403..21c0a0f644 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 e13adaca59..e1c8f2b18c 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 22f50be418..ec1a9e33c6 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 0265d3e2f3..136f007920 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; } }; -- cgit v1.2.3 From f4547f44df32ce1f49a6a36df083e7703751adcd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 19:02:55 +0600 Subject: CLOUD: Add RequestIdPair struct Can be used with Callback (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. --- backends/cloud/downloadrequest.cpp | 6 ++-- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 11 ++++---- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 2 +- backends/cloud/dropbox/dropboxstorage.cpp | 26 +++++++++--------- backends/cloud/dropbox/dropboxstorage.h | 4 +-- backends/cloud/onedrive/onedrivestorage.cpp | 32 +++++++++++----------- backends/cloud/onedrive/onedrivestorage.h | 8 +++--- backends/cloud/storage.h | 14 +++++++--- backends/networking/curl/connectionmanager.h | 7 ++--- backends/networking/curl/curljsonrequest.cpp | 4 +-- backends/networking/curl/curljsonrequest.h | 2 +- backends/networking/curl/curlrequest.cpp | 2 +- backends/networking/curl/curlrequest.h | 2 +- backends/networking/curl/request.h | 14 ++++++++-- common/callback.h | 12 ++++---- 15 files changed, 81 insertions(+), 65 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index a96c298fe8..6f777b7cb1 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 3dada884a0..be9304081e 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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::DataCallback innerCallback = new Common::Callback(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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::DataCallback innerCallback = new Common::Callback(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 36070a2a32..58f3dc6113 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h @@ -40,7 +40,7 @@ class DropboxListDirectoryRequest: public Networking::Request { Common::Array _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 9acbfc0428..02b033fced 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(this, &DropboxStorage::infoInnerCallback, outerCallback); + Networking::DataCallback innerCallback = new Common::CallbackBridge(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(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 6abd3d1d94..4fe6109c17 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 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 b0690be5b0..36b3e26f1b 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(this, &OneDriveStorage::codeFlowComplete), code); + getAccessToken(new Common::Callback(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(this, &OneDriveStorage::tokenRefreshed, callback); + Networking::DataCallback innerCallback = new Common::CallbackBridge(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(this, &OneDriveStorage::printJsonTokenReceived)); + getAccessToken(new Common::Callback(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(this, &OneDriveStorage::printJson); + Networking::DataCallback innerCallback = new Common::Callback(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 4141771f65..3c92880750 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 394fc2c22d..325d57d02c 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 namespace Cloud { class Storage { public: - typedef Common::BaseCallback< Common::Array > *FileArrayCallback; - typedef Common::BaseCallback *ReadStreamCallback; - typedef Common::BaseCallback *StorageInfoCallback; - typedef Common::BaseCallback *BoolCallback; + typedef Networking::RequestIdPair&> RequestFileArrayPair; + typedef Networking::RequestIdPair RequestReadStreamPair; + typedef Networking::RequestIdPair RequestStorageInfoPair; + typedef Networking::RequestIdPair RequestBoolPair; + + typedef Common::BaseCallback *FileArrayCallback; + typedef Common::BaseCallback *ReadStreamCallback; + typedef Common::BaseCallback *StorageInfoCallback; + typedef Common::BaseCallback *BoolCallback; Storage() {} virtual ~Storage() {} diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h index 9ae52b3eeb..15327a28b2 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 { diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp index 21c0a0f644..11eeb2904a 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 cfb82e97e3..9b23cd79f4 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 already virtual ~CurlJsonRequest(); virtual bool handle(); diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp index e1c8f2b18c..6f5c612bdf 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 ec1a9e33c6..c7f07fcb18 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 136f007920..f2c2f1f247 100644 --- a/backends/networking/curl/request.h +++ b/backends/networking/curl/request.h @@ -28,6 +28,16 @@ namespace Networking { +template struct RequestIdPair { + int32 id; + T value; + + RequestIdPair(int32 rid, T v) : id(rid), value(v) {} +}; + +typedef RequestIdPair RequestDataPair; +typedef Common::BaseCallback *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 2101331bf0..4356e4b551 100644 --- a/common/callback.h +++ b/common/callback.h @@ -48,21 +48,21 @@ public: }; /** -* GlobalFunctionCallback is a simple wrapper for global C functions. +* GlobalFunctionCallback is a simple wrapper for global C functions. * -* If there is a method, which accepts BaseCallback, you can +* If there is a method, which accepts BaseCallback, you can * easily pass your C function by passing -* new GlobalFunctionCallback(yourFunction) +* new GlobalFunctionCallback(yourFunction) */ -class GlobalFunctionCallback: public BaseCallback { - typedef void(*GlobalFunction)(void *result); +template class GlobalFunctionCallback: public BaseCallback { + 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); } }; -- cgit v1.2.3 From a7b28605a01b59de6f3acc9df4cd1cac707c39e7 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 19:09:06 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 15 ++++++--------- backends/cloud/downloadrequest.h | 2 +- backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp | 6 ++---- backends/cloud/dropbox/dropboxlistdirectoryrequest.h | 2 +- backends/networking/curl/curljsonrequest.cpp | 5 +---- backends/networking/curl/curljsonrequest.h | 2 +- backends/networking/curl/curlrequest.cpp | 7 ++----- backends/networking/curl/curlrequest.h | 2 +- backends/networking/curl/request.h | 4 +--- 9 files changed, 16 insertions(+), 29 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 6f777b7cb1..756a904c74 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 c1564100c2..724cf19d89 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 be9304081e..31f015a1cd 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 58f3dc6113..afa544de69 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 11eeb2904a..fd3d631ba9 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 9b23cd79f4..3d5dd7858b 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 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 6f5c612bdf..e30b7ce018 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 c7f07fcb18..1a644e4369 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 f2c2f1f247..d81fe903b8 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; -- cgit v1.2.3 From b246c17850687e7b15b644b761fbfe835ffc1c32 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 19:22:27 +0600 Subject: CLOUD: Fix CurlJsonRequest to use JsonCallback Type safety first. --- backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp | 8 ++++---- backends/cloud/dropbox/dropboxlistdirectoryrequest.h | 3 ++- backends/cloud/dropbox/dropboxstorage.cpp | 10 +++++----- backends/cloud/dropbox/dropboxstorage.h | 3 ++- backends/cloud/onedrive/onedrivestorage.cpp | 12 ++++++------ backends/cloud/onedrive/onedrivestorage.h | 5 +++-- backends/networking/curl/curljsonrequest.cpp | 8 ++++---- backends/networking/curl/curljsonrequest.h | 9 +++++++-- 8 files changed, 33 insertions(+), 25 deletions(-) diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp index 31f015a1cd..3158149c02 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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback(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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback(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 afa544de69..9a82ef7091 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 _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 02b033fced..47576d7cda 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(this, &DropboxStorage::infoInnerCallback, outerCallback); + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(saveAccessTokenCallback); + Networking::JsonCallback 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 4fe6109c17..4f1e6cd48a 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 files); diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 36b3e26f1b..833ba8e558 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(this, &OneDriveStorage::tokenRefreshed, callback); + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(this, &OneDriveStorage::printJson); + Networking::JsonCallback innerCallback = new Common::Callback(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 3c92880750..1cb7017d64 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 fd3d631ba9..a323b34ed2 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 3d5dd7858b..75d4a6df81 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 RequestJsonPair; +typedef Common::BaseCallback *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 already + CurlJsonRequest(JsonCallback cb, const char *url); virtual ~CurlJsonRequest(); virtual void handle(); -- cgit v1.2.3 From 8f6bcdf55da97db98384c2a8cb9dcdf34232ac35 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 21:40:01 +0600 Subject: 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. --- backends/cloud/onedrive/onedrivestorage.cpp | 16 +-- backends/cloud/onedrive/onedrivestorage.h | 15 ++- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 124 +++++++++++++++++++++ backends/cloud/onedrive/onedrivetokenrefresher.h | 61 ++++++++++ backends/module.mk | 3 +- backends/networking/curl/connectionmanager.cpp | 2 + backends/networking/curl/curljsonrequest.cpp | 9 +- backends/networking/curl/curljsonrequest.h | 3 +- backends/networking/curl/curlrequest.cpp | 7 ++ backends/networking/curl/curlrequest.h | 8 +- 10 files changed, 222 insertions(+), 26 deletions(-) create mode 100644 backends/cloud/onedrive/onedrivetokenrefresher.cpp create mode 100644 backends/cloud/onedrive/onedrivetokenrefresher.h diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 833ba8e558..6f91cadd37 100644 --- a/backends/cloud/onedrive/onedrivestorage.cpp +++ b/backends/cloud/onedrive/onedrivestorage.cpp @@ -31,6 +31,7 @@ #include #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(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(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 1cb7017d64..e120691a08 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 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 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 0000000000..5e72717740 --- /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 + +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(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(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 0000000000..976851282e --- /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 _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 &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 c8842ca32b..5ec34190e9 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 b448d8e514..77a46ec518 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 a323b34ed2..1231f9e7e8 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 75d4a6df81..af19ec3596 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 e30b7ce018..1b42ac5931 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 &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 1a644e4369..c624194142 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 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 &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 -- cgit v1.2.3 From 24007c029b53a5f4502ee1c48c5244b8cf8099ce Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 26 May 2016 23:56:29 +0600 Subject: CLOUD: Add OneDriveStorage::download() Doesn't work when token is invalid, though. --- backends/cloud/onedrive/onedrivestorage.cpp | 43 ++++++++++++++++++++++---- backends/cloud/onedrive/onedrivestorage.h | 6 ++-- backends/networking/curl/connectionmanager.cpp | 2 +- backends/networking/curl/networkreadstream.cpp | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 6f91cadd37..877a1d27d1 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 -#include #include "common/system.h" -#include "common/cloudmanager.h" -#include "onedrivetokenrefresher.h" +#include 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(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(this, &OneDriveStorage::fileDownloaded)); } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index e120691a08..391cabe02a 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 77a46ec518..9d88c59b25 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 9a196b92d2..39316deb7c 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()); -- cgit v1.2.3 From 83b349a033d71e92e292d1f1da0578d557ec6411 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 01:09:10 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 24 +++++++++++--- backends/cloud/downloadrequest.h | 3 +- backends/cloud/dropbox/dropboxstorage.cpp | 8 +++-- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/onedrive/onedrivestorage.cpp | 37 +++++++++++++++++----- backends/cloud/onedrive/onedrivestorage.h | 4 ++- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 2 +- backends/cloud/onedrive/onedrivetokenrefresher.h | 2 +- backends/cloud/storage.h | 4 +-- backends/networking/curl/curljsonrequest.cpp | 4 +-- backends/networking/curl/curljsonrequest.h | 2 +- backends/networking/curl/curlrequest.cpp | 10 +++--- backends/networking/curl/curlrequest.h | 11 ++++--- 13 files changed, 77 insertions(+), 36 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 756a904c74..661d6fd56b 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(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 724cf19d89..181536beba 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 47576d7cda..b5292c83c7 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 4f1e6cd48a..dd082b25f1 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 877a1d27d1..237557df59 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(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 391cabe02a..be2bcdf04c 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 5e72717740..d3ec0cc668 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 976851282e..58b7dcedb4 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.h +++ b/backends/cloud/onedrive/onedrivetokenrefresher.h @@ -52,7 +52,7 @@ public: virtual void setHeaders(Common::Array &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 325d57d02c..0114b46dee 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -37,7 +37,7 @@ namespace Cloud { class Storage { public: typedef Networking::RequestIdPair&> RequestFileArrayPair; - typedef Networking::RequestIdPair RequestReadStreamPair; + typedef Networking::RequestIdPair RequestReadStreamPair; typedef Networking::RequestIdPair RequestStorageInfoPair; typedef Networking::RequestIdPair 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 1231f9e7e8..326d8e27a0 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 af19ec3596..5e78bd1965 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 1b42ac5931..f01a430b87 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 c624194142..18a41a1c06 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 +#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 -- cgit v1.2.3 From 98150beb38f73b56c7bc76f95dcc1d72290e4ac7 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 15:21:06 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 31 +++--- backends/cloud/downloadrequest.h | 7 +- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 32 +++--- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 7 +- backends/cloud/dropbox/dropboxstorage.cpp | 31 +++--- backends/cloud/dropbox/dropboxstorage.h | 22 ++--- backends/cloud/onedrive/onedrivestorage.cpp | 46 ++++----- backends/cloud/onedrive/onedrivestorage.h | 28 +++--- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 43 ++++---- backends/cloud/onedrive/onedrivetokenrefresher.h | 13 ++- backends/cloud/storage.h | 46 +++++---- backends/networking/curl/connectionmanager.cpp | 54 ++++------ backends/networking/curl/connectionmanager.h | 30 ++---- backends/networking/curl/curljsonrequest.cpp | 23 +++-- backends/networking/curl/curljsonrequest.h | 11 ++- backends/networking/curl/curlrequest.cpp | 8 +- backends/networking/curl/curlrequest.h | 16 ++- backends/networking/curl/networkreadstream.h | 2 +- backends/networking/curl/request.h | 110 ++++++++++++++++++--- local/onedrive/2/doom.jpg | Bin 0 -> 720505 bytes 20 files changed, 323 insertions(+), 237 deletions(-) create mode 100644 local/onedrive/2/doom.jpg diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 661d6fd56b..c7f6f75346 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(this, &DownloadRequest::streamCallback)); + storage->streamFile(remoteFile, new Common::Callback(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 181536beba..4ea85760ae 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 +#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 3158149c02..04fbf46ac6 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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback(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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::JsonCallback innerCallback = new Common::Callback(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 files; + finishFiles(files); +} + +void DropboxListDirectoryRequest::finishFiles(Common::Array &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 9a82ef7091..e2a9ebf3a4 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 _files; - int32 _requestId; + Request *_innerRequest; - void responseCallback(Networking::RequestJsonPair pair); + void responseCallback(Networking::JsonResponse pair); void startupWork(); + void finishFiles(Common::Array &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 b5292c83c7..a1a97e00dd 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 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(this, &DropboxStorage::infoInnerCallback, outerCallback); +Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(saveAccessTokenCallback); + Networking::JsonCallback 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 dd082b25f1..f95d0c9812 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 files); @@ -65,34 +65,34 @@ public: /** Public Cloud API comes down there. */ /** Returns Common::Array. */ - 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 237557df59..e181fecabb 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(this, &OneDriveStorage::codeFlowComplete), code); + getAccessToken(new Common::Callback(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(this, &OneDriveStorage::tokenRefreshed, callback); + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(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(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(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(this, &OneDriveStorage::fileDownloaded)); + return download("pic.jpg", "local/onedrive/2/doom.jpg", new Common::Callback(this, &OneDriveStorage::fileDownloaded)); } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index be2bcdf04c..858af6c9bf 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. */ - 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 d3ec0cc668..6943d89297 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(this, &OneDriveTokenRefresher::innerRequestCallback), + new Common::Callback(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(this, &OneDriveTokenRefresher::tokenRefreshed)); + _parentStorage->getAccessToken(new Common::Callback(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 58b7dcedb4..c09879feed 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 _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 &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 0114b46dee..8ae5308a1c 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 namespace Cloud { class Storage { public: - typedef Networking::RequestIdPair&> RequestFileArrayPair; - typedef Networking::RequestIdPair RequestReadStreamPair; - typedef Networking::RequestIdPair RequestStorageInfoPair; - typedef Networking::RequestIdPair RequestBoolPair; + typedef Networking::Response&> FileArrayResponse; + typedef Networking::Response StorageInfoResponse; + typedef Networking::Response BoolResponse; - typedef Common::BaseCallback *FileArrayCallback; - typedef Common::BaseCallback *ReadStreamCallback; - typedef Common::BaseCallback *StorageInfoCallback; - typedef Common::BaseCallback *BoolCallback; + typedef Common::BaseCallback *FileArrayCallback; + typedef Common::BaseCallback *StorageInfoCallback; + typedef Common::BaseCallback *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. */ - 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 9d88c59b25..ef2afc2655 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 idsToRemove; - for (Common::HashMap::iterator i = _requests.begin(); i != _requests.end(); ++i) { - RequestInfo &info = _requests[i->_key]; + debug("handling %d request(s)", _requests.size()); + for (Common::Array::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 15327a28b2..2d37c0595c 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 { friend void connectionsThread(void *); //calls handle() CURLM *_multi; bool _timerStarted; - Common::HashMap _requests; - int32 _nextId; + Common::Array _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 326d8e27a0..3c598d7f18 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 5e78bd1965..0a560f93f4 100644 --- a/backends/networking/curl/curljsonrequest.h +++ b/backends/networking/curl/curljsonrequest.h @@ -29,10 +29,8 @@ namespace Networking { -class NetworkReadStream; - -typedef RequestIdPair RequestJsonPair; -typedef Common::BaseCallback *JsonCallback; +typedef Response JsonResponse; +typedef Common::BaseCallback *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 f01a430b87..a8eb425412 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 18a41a1c06..5677720b1d 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 NetworkReadStreamResponse; +typedef Common::BaseCallback *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 &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 6431a01fee..a0c87460cb 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 d81fe903b8..ff919e02f1 100644 --- a/backends/networking/curl/request.h +++ b/backends/networking/curl/request.h @@ -28,40 +28,124 @@ namespace Networking { -template struct RequestIdPair { - int32 id; +class Request; + +/** +* Response 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 is called DataResponse +* and corresponding callback pointer is DataCallback. +*/ + +template 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 RequestDataPair; -typedef Common::BaseCallback *DataCallback; +typedef Response DataReponse; +typedef Common::BaseCallback *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 0000000000..ef1ca01842 Binary files /dev/null and b/local/onedrive/2/doom.jpg differ -- cgit v1.2.3 From baf37b9330ee96ca04fe742dc923d9fddfcf4c20 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 17:08:16 +0600 Subject: CLOUD: Remove DOOM picture Accidentally commited a test file, so now I'm removing it. --- local/onedrive/2/doom.jpg | Bin 720505 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 local/onedrive/2/doom.jpg diff --git a/local/onedrive/2/doom.jpg b/local/onedrive/2/doom.jpg deleted file mode 100644 index ef1ca01842..0000000000 Binary files a/local/onedrive/2/doom.jpg and /dev/null differ -- cgit v1.2.3 From 1348befe297d32de9bf40f89f37bcbae2c91d81f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 17:50:27 +0600 Subject: 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. --- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 12 ++++++++++++ backends/networking/curl/curlrequest.h | 3 +++ backends/networking/curl/networkreadstream.cpp | 2 +- backends/networking/curl/networkreadstream.h | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp index 6943d89297..6620c23dc6 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 5677720b1d..f77dce92c0 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 39316deb7c..8658fc7d17 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 a0c87460cb..14c00a4baa 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 -- cgit v1.2.3 From dcbaeb57c4cbe58d17fd46b52fd23ae154458398 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 18:56:02 +0600 Subject: CLOUD: Fix OneDriveTokenRefresher Failed to update token in header when "Bearer" was used instead of "bearer". --- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp index 6620c23dc6..4be034b363 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(); } } -- cgit v1.2.3 From 6c01edc57a221e5c376d23f12bb2f090c73c4ffd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 19:32:35 +0600 Subject: CLOUD: Simplify OneDriveTokenRefresher It now just extends CurlJsonRequest, not wraps one. --- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 110 +++++++-------------- backends/cloud/onedrive/onedrivetokenrefresher.h | 22 ++--- backends/networking/curl/curljsonrequest.h | 2 +- 3 files changed, 42 insertions(+), 92 deletions(-) diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp index 4be034b363..94214834b0 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 @@ -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(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(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(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 &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 c09879feed..08212bf6f5 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 _headers; - CurlJsonRequest *_innerRequest; - Networking::JsonCallback _jsonCallback; - Request *_retryRequest; - bool _started; - - void innerRequestCallback(Networking::JsonResponse pair); + Common::Array _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 &headers); - virtual void setHeaders(Common::Array &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 0a560f93f4..9bd12a13de 100644 --- a/backends/networking/curl/curljsonrequest.h +++ b/backends/networking/curl/curljsonrequest.h @@ -33,13 +33,13 @@ typedef Response JsonResponse; typedef Common::BaseCallback *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); -- cgit v1.2.3 From cdf30e9d584f81029311f111f314fabdc1bf6d60 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 27 May 2016 16:29:42 +0200 Subject: CLOUD: Fix posix backend compilation --- backends/fs/posix/posix-fs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp index d388f306fd..b38a07637c 100644 --- a/backends/fs/posix/posix-fs.cpp +++ b/backends/fs/posix/posix-fs.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #ifdef __OS2__ #define INCL_DOS -- cgit v1.2.3 From 0ef1cda1724e973c36c76e07763f681cb14bd597 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 20:59:40 +0600 Subject: CLOUD: Add FolderDownloadRequest Uses Storage's listDirectory() and download() methods to download contents. --- backends/cloud/dropbox/dropboxstorage.cpp | 15 +++- backends/cloud/dropbox/dropboxstorage.h | 5 +- backends/cloud/folderdownloadrequest.cpp | 111 ++++++++++++++++++++++++++++++ backends/cloud/folderdownloadrequest.h | 59 ++++++++++++++++ backends/cloud/storagefile.cpp | 8 +++ backends/cloud/storagefile.h | 1 + backends/module.mk | 1 + 7 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 backends/cloud/folderdownloadrequest.cpp create mode 100644 backends/cloud/folderdownloadrequest.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index a1a97e00dd..379d7bb611 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 files) { +void DropboxStorage::printFiles(FileArrayResponse pair) { debug("files:"); + Common::Array &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(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 f95d0c9812..19f2f9cdd4 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 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 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 0000000000..f60c9f6d69 --- /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(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(this, &FolderDownloadRequest::fileDownloadedCallback) + ); +} + +void FolderDownloadRequest::finish() { + //TODO: somehow indicate that request was interrupted + Common::Array files; + finishFiles(files); +} + +void FolderDownloadRequest::finishFiles(Common::Array &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 0000000000..038dc642ef --- /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 _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 &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 02c9cae3cf..c0f8d3a9bc 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 8706ba18e9..ced99fae30 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 5ec34190e9..54e3817c56 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 \ -- cgit v1.2.3 From bc773835453478c5e56cfc2f653e6d863bc5de95 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 27 May 2016 17:58:36 +0200 Subject: CONFIGURE: Added configure switches for enabling/desabling cloud and net libs --- configure | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/configure b/configure index de01ca7edc..448da8ac16 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 if you have libraries in a nonstandard directory @@ -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 -- cgit v1.2.3 From 1dfa73b8f863972f513d9d04b995cb7316365a8c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 22:15:43 +0600 Subject: CLOUD: Fix ConnectionManager singleton warning --- backends/networking/curl/connectionmanager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index ef2afc2655..337ad42eba 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 -using Common::Singleton; + +namespace Common { DECLARE_SINGLETON(Networking::ConnectionManager); +} + namespace Networking { ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false) { -- cgit v1.2.3 From 8aa87815a62117c5fd29b335c8e33b0cbea0da44 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 22:33:39 +0600 Subject: CLOUD: Fix "global destructor" warning Plain char * is used instead of Common::String in DropboxStorage and OneDriveStorage's KEY and SECRET. --- backends/cloud/dropbox/dropboxstorage.cpp | 28 +++++++++++++++++++--------- backends/cloud/dropbox/dropboxstorage.h | 4 +++- backends/cloud/onedrive/onedrivestorage.cpp | 28 +++++++++++++++++++--------- backends/cloud/onedrive/onedrivestorage.h | 4 +++- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 379d7bb611..3d7eafedeb 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 19f2f9cdd4..c411e25d73 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 e181fecabb..6f3a283fe6 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 858af6c9bf..290abd7926 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; -- cgit v1.2.3 From 14d60e62f8452dd433fcf275cd87fd9b8c3bd6fd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 27 May 2016 22:37:03 +0600 Subject: CLOUD: Fix format string warnings I get 'warning: ISO C++98 does not support the '%lg' ms_printf format' warning though. --- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- common/json.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 3d7eafedeb..ad6702c967 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 83ce0db29d..95c9123b4e 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; -- cgit v1.2.3 From 827c7e43da118f12ae614530340a566a23c42746 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 28 May 2016 01:08:51 +0600 Subject: CLOUD: Add OneDriveListDirectoryRequest Works as charm. --- .../onedrive/onedrivelistdirectoryrequest.cpp | 138 +++++++++++++++++++++ .../cloud/onedrive/onedrivelistdirectoryrequest.h | 64 ++++++++++ backends/cloud/onedrive/onedrivestorage.cpp | 15 ++- backends/cloud/onedrive/onedrivestorage.h | 3 +- backends/module.mk | 3 +- 5 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp create mode 100644 backends/cloud/onedrive/onedrivelistdirectoryrequest.h diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp new file mode 100644 index 0000000000..2c47852fd1 --- /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(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 files; + finishFiles(files); +} + +void OneDriveListDirectoryRequest::finishFiles(Common::Array &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 0000000000..61eebb2669 --- /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 _files; + Common::Array _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 &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 6f3a283fe6..d14379393b 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(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 &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(this, &OneDriveStorage::fileDownloaded)); + return listDirectory("subfolder", new Common::Callback(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 290abd7926..c9a32a802f 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. */ - 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 54e3817c56..f5b1aa4ec8 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 -- cgit v1.2.3 From b74d7a6861dbb5d0fafec0e6587deb7637b0ab12 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 28 May 2016 01:18:37 +0600 Subject: CLOUD: Fix Requests destructors I forgot to delete callbacks! --- backends/cloud/downloadrequest.h | 5 ++++- backends/cloud/folderdownloadrequest.cpp | 7 +++++++ backends/cloud/folderdownloadrequest.h | 2 +- backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/curlrequest.cpp | 4 +--- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h index 4ea85760ae..c9c243be44 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 f60c9f6d69..cbd3501772 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 038dc642ef..949bcad6ee 100644 --- a/backends/cloud/folderdownloadrequest.h +++ b/backends/cloud/folderdownloadrequest.h @@ -47,7 +47,7 @@ class FolderDownloadRequest: public Networking::Request { void finishFiles(Common::Array &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 3c598d7f18..bc5de60f43 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 a8eb425412..61af633f21 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); -- cgit v1.2.3 From 6f35fc42721895be70c6c21f87eb9a75693ff168 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 28 May 2016 20:40:20 +0600 Subject: CLOUD: Add OneDriveStorage::downloadFolder() Just uses FolderDownloadRequest the way DropboxStorage does. --- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/onedrive/onedrivestorage.cpp | 8 +++++++- backends/cloud/onedrive/onedrivestorage.h | 3 +++ backends/cloud/storage.h | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index c411e25d73..59dea9d210 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 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 d14379393b..c924dbf093 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 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(this, &OneDriveStorage::printFiles), true); + return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback(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 c9a32a802f..674d13634d 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 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 8ae5308a1c..8009075d0c 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 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; -- cgit v1.2.3 From f4dfaed19d14c309e44d390b44bff571566779a5 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 28 May 2016 19:52:56 +0200 Subject: Replace 0 constant with nullptr in getCurrentStorage() --- backends/cloud/manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp index a7e92dfe03..9e11f0d4bd 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) { -- cgit v1.2.3 From 81c34adaef269524b1f53adcac722aa6a9730075 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 28 May 2016 20:10:38 +0200 Subject: Fix comment formatting --- backends/cloud/downloadrequest.cpp | 2 +- backends/cloud/downloadrequest.h | 2 +- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 4 +- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 4 +- backends/cloud/dropbox/dropboxstorage.cpp | 4 +- backends/cloud/dropbox/dropboxstorage.h | 35 +++--- backends/cloud/folderdownloadrequest.cpp | 2 +- backends/cloud/folderdownloadrequest.h | 2 +- backends/cloud/iso8601.cpp | 4 +- backends/cloud/iso8601.h | 4 +- backends/cloud/manager.cpp | 2 +- backends/cloud/manager.h | 2 +- .../onedrive/onedrivelistdirectoryrequest.cpp | 4 +- .../cloud/onedrive/onedrivelistdirectoryrequest.h | 4 +- backends/cloud/onedrive/onedrivestorage.cpp | 4 +- backends/cloud/onedrive/onedrivestorage.h | 49 ++++---- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 4 +- backends/cloud/onedrive/onedrivetokenrefresher.h | 4 +- backends/cloud/storage.h | 3 +- backends/cloud/storagefile.cpp | 2 +- backends/cloud/storagefile.h | 11 +- backends/cloud/storageinfo.h | 2 +- backends/networking/curl/connectionmanager.cpp | 2 +- backends/networking/curl/connectionmanager.h | 30 ++--- backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/curljsonrequest.h | 2 +- backends/networking/curl/curlrequest.cpp | 2 +- backends/networking/curl/curlrequest.h | 8 +- backends/networking/curl/networkreadstream.cpp | 2 +- backends/networking/curl/networkreadstream.h | 62 +++++----- backends/networking/curl/request.h | 103 ++++++++--------- common/callback.h | 128 ++++++++++----------- common/cloudmanager.h | 45 ++++---- 33 files changed, 262 insertions(+), 278 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index c7f6f75346..8d5e244e45 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 c9c243be44..0bad5df279 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 04fbf46ac6..6ea90c150d 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp @@ -136,5 +136,5 @@ void DropboxListDirectoryRequest::finishFiles(Common::Array &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 e2a9ebf3a4..8539be1e1e 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 ad6702c967..b1dd865ee2 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 59dea9d210..da508d7844 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 cbd3501772..9c2e8b173a 100644 --- a/backends/cloud/folderdownloadrequest.cpp +++ b/backends/cloud/folderdownloadrequest.cpp @@ -115,4 +115,4 @@ void FolderDownloadRequest::finishFiles(Common::Array &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 949bcad6ee..33fa5992c6 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 12cbb742b8..3bc169ae85 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 a53b3bb501..6e11322cdb 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 9e11f0d4bd..959f395243 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 08106e0513..6eaf17bc91 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 2c47852fd1..452c47ac13 100644 --- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp +++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp @@ -134,5 +134,5 @@ void OneDriveListDirectoryRequest::finishFiles(Common::Array &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 61eebb2669..ce407c041e 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 c924dbf093..c8b4ab1ad3 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 674d13634d..3dc5b7a7ad 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 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 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 94214834b0..d932f9ab17 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp +++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp @@ -93,5 +93,5 @@ void OneDriveTokenRefresher::setHeaders(Common::Array &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 08212bf6f5..90ca9d603a 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 8009075d0c..ac74f90b57 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 c0f8d3a9bc..b37b5e0d53 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 ced99fae30..704a03867f 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 f09563570f..1492898a6c 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 337ad42eba..949dc949f1 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 2d37c0595c..c632fa54ef 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 bc5de60f43..d114d69098 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 9bd12a13de..5e08be24c9 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 61af633f21..a745741bc8 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 f77dce92c0..8623a487d3 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 8658fc7d17..997f0b2fc3 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 14c00a4baa..33edade8d7 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 ff919e02f1..a3c723d218 100644 --- a/backends/networking/curl/request.h +++ b/backends/networking/curl/request.h @@ -31,18 +31,18 @@ namespace Networking { class Request; /** -* Response 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 is called DataResponse -* and corresponding callback pointer is DataCallback. -*/ + * Response 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 is called DataResponse + * and corresponding callback pointer is DataCallback. + */ template struct Response { Request *request; @@ -55,27 +55,26 @@ typedef Response DataReponse; typedef Common::BaseCallback *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 4356e4b551..c6c249a511 100644 --- a/common/callback.h +++ b/common/callback.h @@ -26,20 +26,19 @@ namespace Common { /** -* BaseCallback 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, so it can be used with global C-like -* functions too. -* -* 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 object. -*/ - + * BaseCallback 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, so it can be used with global C-like + * functions too. + * + * 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 object. + */ template class BaseCallback { public: BaseCallback() {} @@ -48,13 +47,12 @@ public: }; /** -* GlobalFunctionCallback is a simple wrapper for global C functions. -* -* If there is a method, which accepts BaseCallback, you can -* easily pass your C function by passing -* new GlobalFunctionCallback(yourFunction) -*/ - + * GlobalFunctionCallback is a simple wrapper for global C functions. + * + * If there is a method, which accepts BaseCallback, you can + * easily pass your C function by passing + * new GlobalFunctionCallback(yourFunction) + */ template class GlobalFunctionCallback: public BaseCallback { typedef void(*GlobalFunction)(T result); GlobalFunction _callback; @@ -68,20 +66,19 @@ public: }; /** -* Callback implements an object-oriented callback. -* -* stands for a class which method you want to call. -* , again, is the type of an object passed to operator(). -* -* So, if you have void MyClass::myMethod(AnotherClass) method, -* the corresponding callback is Callback. -* You create it similarly to this: -* new Callback( -* pointerToMyClassObject, -* &MyClass::myMethod -* ) -*/ - + * Callback implements an object-oriented callback. + * + * stands for a class which method you want to call. + * , again, is the type of an object passed to operator(). + * + * So, if you have void MyClass::myMethod(AnotherClass) method, + * the corresponding callback is Callback. + * You create it similarly to this: + * new Callback( + * pointerToMyClassObject, + * &MyClass::myMethod + * ) + */ template class Callback: public BaseCallback { protected: typedef void(T::*TMethod)(S); @@ -94,37 +91,36 @@ public: }; /** -* CallbackBridge helps you to chain callbacks. -* -* CallbackBridge keeps a pointer to BaseCallback. -* When its operator() is called, it passes this pointer -* along with the actual data (of type ) to the method -* of 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 callback -* and you want to call it from your MyClass::myMethod method, -* you should create CallbackBridge, -* where is data type you want to receive in MyClass::myMethod. -* -* You create it similarly to this: -* new Callback( -* pointerToMyClassObject, -* &MyClass::myMethod, -* outerCallback -* ) -* where `outerCallback` is BaseCallback and `myMethod` is: -* void MyClass::myMethod(BaseCallback *, AnotherClass) -*/ - + * CallbackBridge helps you to chain callbacks. + * + * CallbackBridge keeps a pointer to BaseCallback. + * When its operator() is called, it passes this pointer + * along with the actual data (of type ) to the method + * of 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 callback + * and you want to call it from your MyClass::myMethod method, + * you should create CallbackBridge, + * where is data type you want to receive in MyClass::myMethod. + * + * You create it similarly to this: + * new Callback( + * pointerToMyClassObject, + * &MyClass::myMethod, + * outerCallback + * ) + * where `outerCallback` is BaseCallback and `myMethod` is: + * void MyClass::myMethod(BaseCallback *, AnotherClass) + */ template class CallbackBridge: public BaseCallback { typedef void(T::*TCallbackMethod)(BaseCallback *, S); T *_object; diff --git a/common/cloudmanager.h b/common/cloudmanager.h index a480bdf684..350901e35f 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 -- cgit v1.2.3 From 86d5b1b2e13c09f52d1af21b1f9b43d4b3c712c3 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 28 May 2016 20:38:10 +0200 Subject: Remove some unnecessary blank lines --- backends/cloud/folderdownloadrequest.cpp | 1 - backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp index 9c2e8b173a..00bddb9f09 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 452c47ac13..f1402c43b2 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; -- cgit v1.2.3 From 1747f8ec420edd0517e37a88e5fdd57f1b11b22f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 29 May 2016 20:12:22 +0600 Subject: CLOUD: Add "device_id" into configuration file --- backends/cloud/manager.cpp | 14 ++++++++++++-- backends/cloud/manager.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp index 959f395243..13f23b8e6e 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 6eaf17bc91..f472b80ae3 100644 --- a/backends/cloud/manager.h +++ b/backends/cloud/manager.h @@ -31,6 +31,7 @@ namespace Cloud { class Manager: public Common::CloudManager { Common::Array _storages; uint _currentStorageIndex; + uint _deviceId; public: Manager(); -- cgit v1.2.3 From c235aa29f997b9bd000be5424662b114957f1640 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 29 May 2016 23:25:08 +0600 Subject: 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. --- backends/cloud/savessyncrequest.cpp | 229 ++++++++++++++++++++++++++++++++++++ backends/cloud/savessyncrequest.h | 63 ++++++++++ backends/module.mk | 1 + 3 files changed, 293 insertions(+) create mode 100644 backends/cloud/savessyncrequest.cpp create mode 100644 backends/cloud/savessyncrequest.h 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(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 &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::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(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(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.; + + //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::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 _localFilesTimestamps; + Common::Array _filesToDownload; + Common::Array _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 f5b1aa4ec8..a31704a655 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 \ -- cgit v1.2.3 From 701b07adfb578a6e23cedd7a310fbc1776e0a8a9 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 02:01:48 +0600 Subject: COMMON: Fix JSON to understand integers correctly --- common/json.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- common/json.h | 6 +++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/common/json.cpp b/common/json.cpp index 95c9123b4e..779f99aae0 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); } @@ -554,6 +561,18 @@ JSONValue::JSONValue(double numberValue) { _numberValue = 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 * @@ -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; @@ -694,6 +717,17 @@ bool JSONValue::isNumber() const { return _type == JSONType_Number; } +/** +* 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 * @@ -752,6 +786,18 @@ double JSONValue::asNumber() const { return _numberValue; } +/** +* 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 c9debf05af..35571935ee 100644 --- a/common/json.h +++ b/common/json.h @@ -85,7 +85,7 @@ typedef HashMap 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; -- cgit v1.2.3 From d917592099381402c2681b291d379bda78a1c3f7 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 02:23:29 +0600 Subject: CLOUD: Add DropboxUploadRequest --- backends/cloud/dropbox/dropboxstorage.cpp | 20 ++- backends/cloud/dropbox/dropboxstorage.h | 3 +- backends/cloud/dropbox/dropboxuploadrequest.cpp | 163 ++++++++++++++++++++++++ backends/cloud/dropbox/dropboxuploadrequest.h | 60 +++++++++ backends/cloud/onedrive/onedrivestorage.h | 2 +- backends/cloud/savessyncrequest.h | 1 + backends/cloud/storage.h | 2 +- backends/module.mk | 1 + backends/networking/curl/curljsonrequest.cpp | 2 +- backends/networking/curl/curlrequest.cpp | 35 ++++- backends/networking/curl/curlrequest.h | 7 + backends/networking/curl/networkreadstream.cpp | 14 +- backends/networking/curl/networkreadstream.h | 1 + 13 files changed, 294 insertions(+), 17 deletions(-) create mode 100644 backends/cloud/dropbox/dropboxuploadrequest.cpp create mode 100644 backends/cloud/dropbox/dropboxuploadrequest.h diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index b1dd865ee2..1220a99035 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 -#include 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(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(this, &DropboxStorage::printBool)); } Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index da508d7844..cc515fef41 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 0000000000..d068078502 --- /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(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 0000000000..a7d7a6c581 --- /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 3dc5b7a7ad..41d84f9770 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 a8c54d44ad..dd43cab2a0 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 ac74f90b57..dbd862822b 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 a31704a655..281c6a9060 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 d114d69098..fee0932129 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 a745741bc8..64f6c26fb9 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 &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 8623a487d3..461f153b9d 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 997f0b2fc3..f96a069e16 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 33edade8d7..f1f41264aa 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(); /** -- cgit v1.2.3 From b9e3730ccd9a76101ef0cb8812f41c371a24d0d6 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 13:35:53 +0600 Subject: 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. --- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 4 +- backends/cloud/dropbox/dropboxstorage.cpp | 50 ++++++++++++++++------ backends/cloud/dropbox/dropboxstorage.h | 4 +- backends/cloud/dropbox/dropboxuploadrequest.cpp | 49 +++++++++++++++------ backends/cloud/dropbox/dropboxuploadrequest.h | 6 +-- backends/cloud/onedrive/onedrivestorage.h | 3 +- backends/cloud/savessyncrequest.cpp | 23 ++++++---- backends/cloud/savessyncrequest.h | 2 +- backends/cloud/storage.h | 25 ++++++++++- 9 files changed, 124 insertions(+), 42 deletions(-) diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp index 6ea90c150d..89e92facb8 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 1220a99035..1f983cec67 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(this, &DropboxStorage::printBool)); + return upload("/remote/test4.bmp", "final.bmp", new Common::Callback(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 cc515fef41..ca285802a4 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 d068078502..18e1173eef 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 a7d7a6c581..9b68995969 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 41d84f9770..7028667819 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 d48ec6ba45..be1075cb4d 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::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(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(this, &SavesSyncRequest::fileUploadedCallback) + + _workingRequest = _storage->upload("saves/" + _currentUploadingFile, g_system->getSavefileManager()->openForLoading(_currentUploadingFile), + new Common::Callback(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.; + //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 dd43cab2a0..dca1fb750b 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 dbd862822b..1749881a7d 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&> FileArrayResponse; typedef Networking::Response StorageInfoResponse; typedef Networking::Response BoolResponse; + typedef Networking::Response UploadResponse; typedef Common::BaseCallback *FileArrayCallback; typedef Common::BaseCallback *StorageInfoCallback; typedef Common::BaseCallback *BoolCallback; + typedef Common::BaseCallback *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; -- cgit v1.2.3 From a19fc52c329fdb0611eee7aedfb20fb0d1b9269c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 13:46:14 +0600 Subject: 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). --- backends/cloud/dropbox/dropboxstorage.cpp | 7 ++++--- backends/cloud/dropbox/dropboxuploadrequest.cpp | 12 ++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 1f983cec67..ab3f5d0874 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 18e1173eef..e422793bc4 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"; -- cgit v1.2.3 From aa987e5c52899bfafff4f1f84479a67761569109 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 18:13:31 +0600 Subject: CLOUD: Add ListDirectoryStatus struct It contains flags to indicate whether Request was interrupted or failed, so dependent Requests may see that list is incomplete. --- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 97 ++++++++++++---------- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 18 ++-- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- backends/cloud/dropbox/dropboxstorage.h | 8 +- backends/cloud/dropbox/dropboxuploadrequest.cpp | 1 - backends/cloud/folderdownloadrequest.cpp | 14 +++- backends/cloud/folderdownloadrequest.h | 3 +- .../onedrive/onedrivelistdirectoryrequest.cpp | 52 +++++++----- .../cloud/onedrive/onedrivelistdirectoryrequest.h | 10 +-- backends/cloud/onedrive/onedrivestorage.cpp | 2 +- backends/cloud/onedrive/onedrivestorage.h | 6 +- backends/cloud/savessyncrequest.cpp | 13 ++- backends/cloud/savessyncrequest.h | 2 +- backends/cloud/storage.h | 30 ++++++- 14 files changed, 156 insertions(+), 102 deletions(-) diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp index 89e92facb8..2796a4c19e 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(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(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 files; - finishFiles(files); + ListDirectoryStatus status(files); + status.interrupted = true; + finishStatus(status); } -void DropboxListDirectoryRequest::finishFiles(Common::Array &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 8539be1e1e..3c7c1fd464 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 _files; - Request *_innerRequest; - + Request *_workingRequest; + bool _ignoreCallback; + + void start(); void responseCallback(Networking::JsonResponse pair); - void startupWork(); - - void finishFiles(Common::Array &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 ab3f5d0874..4a17afe6c6 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 ca285802a4..d9967d69f6 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. */ - 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 e422793bc4..e64a8837b8 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 00bddb9f09..db132ffc8a 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(this, &FolderDownloadRequest::directoryListedCallback), + new Common::Callback(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 33fa5992c6..779ea3334f 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 &files); diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp index f1402c43b2..dbd5e44c0b 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 files; - finishFiles(files); + ListDirectoryStatus status(files); + status.interrupted = true; + finishStatus(status); } -void OneDriveListDirectoryRequest::finishFiles(Common::Array &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 ce407c041e..a05dd871dd 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 _files; Common::Array _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 &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 c8b4ab1ad3..19e497258b 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 7028667819..55d039653a 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. */ - 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 be1075cb4d..96386ee62c 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(this, &SavesSyncRequest::directoryListedCallback)); + _workingRequest = _storage->listDirectory("saves", new Common::Callback(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 &remoteFiles = pair.value; + Common::Array &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 dca1fb750b..f2f2aba403 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 1749881a7d..311b3fdc9f 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 &files; + /** Server's original response (empty if not failed) */ + Common::String response; + /** Server's HTTP response code. */ + long httpResponseCode; + + ListDirectoryStatus(Common::Array &f) : + interrupted(false), failed(false), files(f), response(), httpResponseCode(-1) {} + + ListDirectoryStatus(bool interrupt, bool failure, Common::Array &f, Common::String resp, long code) : + interrupted(interrupt), failed(failure), files(f), response(resp), httpResponseCode(code) {} +}; + class Storage { public: typedef Networking::Response&> FileArrayResponse; typedef Networking::Response StorageInfoResponse; typedef Networking::Response BoolResponse; typedef Networking::Response UploadResponse; + typedef Networking::Response ListDirectoryResponse; typedef Common::BaseCallback *FileArrayCallback; typedef Common::BaseCallback *StorageInfoCallback; typedef Common::BaseCallback *BoolCallback; typedef Common::BaseCallback *UploadCallback; + typedef Common::BaseCallback *ListDirectoryCallback; Storage() {} virtual ~Storage() {} @@ -90,10 +112,10 @@ public: * a callback, which is called, when request is complete. */ - /** Returns Common::Array. */ - 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; -- cgit v1.2.3 From cc4512e50b5489ec57adc05b3c2277c132bed767 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 20:28:09 +0600 Subject: COMMON: Add SaveFileManager::openRawFile() It's needed for the cloud saves upload/sync feature. --- backends/saves/default/default-saves.cpp | 16 ++++++++++++++++ backends/saves/default/default-saves.h | 1 + common/savefile.h | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index daec36ae72..75ba50a081 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 bf4ca0229d..166e7004ed 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 9fca07f9d5..d9c5512b7e 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -136,6 +136,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. * -- cgit v1.2.3 From af37ecca3430c871ec8a03bcada90303e6bf9877 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 21:21:31 +0600 Subject: CLOUD: Make SavesSyncRequest work It now actually read the "timestamps" file, loads and saves files as it should, ignores Dropbox's "not_found" error. --- backends/cloud/dropbox/dropboxstorage.cpp | 16 +++- backends/cloud/savessyncrequest.cpp | 132 +++++++++++++++++++++++------- backends/cloud/savessyncrequest.h | 6 +- 3 files changed, 121 insertions(+), 33 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 4a17afe6c6..b33e2b6776 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 +#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(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(this, &DropboxStorage::printUploadStatus)); + //return upload("/remote/slash", "C:/Users/Tkachov/AppData/Roaming/ScummVM/Saved games/sword25.000", new Common::Callback(this, &DropboxStorage::printUploadStatus)); + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &DropboxStorage::printBool))); + //return upload("/remote/test4.bmp", "final.bmp", new Common::Callback(this, &DropboxStorage::printUploadStatus)); } Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index 96386ee62c..e632bfff84 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 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(this, &SavesSyncRequest::directoryListedCallback)); + _workingRequest = _storage->listDirectory("/saves", new Common::Callback(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 &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::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(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(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::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::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 f2f2aba403..da7b27e9b6 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 _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(); -- cgit v1.2.3 From 001b417f33beeb3b2da11f58105b971dc7e6f600 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 30 May 2016 22:18:33 +0600 Subject: CLOUD: Fix OneDriveTokenRefresher It was calling finish(), causing stack overflow. Some minor changes are added also. --- backends/cloud/onedrive/onedrivestorage.cpp | 9 ++++++++- backends/cloud/onedrive/onedrivestorage.h | 1 + backends/cloud/onedrive/onedrivetokenrefresher.cpp | 22 +++++++++++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 19e497258b..b0f4f7be65 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 +#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(this, &OneDriveStorage::printFiles), false); + //return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback(this, &OneDriveStorage::printFiles), false); + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &OneDriveStorage::printBool))); } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index 55d039653a..43675fbfa5 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 d932f9ab17..a0c41ff471 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(this, &OneDriveTokenRefresher::tokenRefreshed)); -- cgit v1.2.3 From eb63b50b7f0841e40365f3fbafa9810e8b190872 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 01:51:32 +0600 Subject: 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. --- backends/cloud/downloadrequest.cpp | 68 ++++++---- backends/cloud/downloadrequest.h | 23 ++-- .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 72 +++++----- .../cloud/dropbox/dropboxlistdirectoryrequest.h | 8 +- backends/cloud/dropbox/dropboxstorage.cpp | 123 +++++++---------- backends/cloud/dropbox/dropboxstorage.h | 33 +++-- backends/cloud/dropbox/dropboxuploadrequest.cpp | 81 ++++++----- backends/cloud/dropbox/dropboxuploadrequest.h | 8 +- backends/cloud/folderdownloadrequest.cpp | 49 ++++--- backends/cloud/folderdownloadrequest.h | 15 +- backends/cloud/manager.cpp | 4 +- backends/cloud/manager.h | 2 +- .../onedrive/onedrivelistdirectoryrequest.cpp | 56 ++++---- .../cloud/onedrive/onedrivelistdirectoryrequest.h | 14 +- backends/cloud/onedrive/onedrivestorage.cpp | 72 +++++----- backends/cloud/onedrive/onedrivestorage.h | 39 +++--- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 42 +++--- backends/cloud/onedrive/onedrivetokenrefresher.h | 12 +- backends/cloud/savessyncrequest.cpp | 151 ++++++++++++++------- backends/cloud/savessyncrequest.h | 17 ++- backends/cloud/storage.h | 66 ++------- backends/module.mk | 3 +- backends/networking/curl/curljsonrequest.cpp | 30 ++-- backends/networking/curl/curljsonrequest.h | 5 +- backends/networking/curl/curlrequest.cpp | 15 +- backends/networking/curl/curlrequest.h | 4 +- backends/networking/curl/request.cpp | 70 ++++++++++ backends/networking/curl/request.h | 75 +++++++--- common/cloudmanager.h | 2 +- 29 files changed, 652 insertions(+), 507 deletions(-) create mode 100644 backends/networking/curl/request.cpp diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 8d5e244e45..7dde74f88d 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(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(this, &DownloadRequest::streamCallback), + new Common::Callback(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 0bad5df279..9e3421d777 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 2796a4c19e..d782f81a69 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(this, &DropboxListDirectoryRequest::responseCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder"); + Networking::JsonCallback callback = new Common::Callback(this, &DropboxListDirectoryRequest::responseCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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(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(this, &DropboxListDirectoryRequest::responseCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 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 &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 3c7c1fd464..3a83af06aa 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 &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 b33e2b6776..d22e0abf60 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 &files = pair.value; + Common::Array &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(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(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(this, &DropboxStorage::printUploadStatus)); - //return upload("/remote/slash", "C:/Users/Tkachov/AppData/Roaming/ScummVM/Saved games/sword25.000", new Common::Callback(this, &DropboxStorage::printUploadStatus)); - return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &DropboxStorage::printBool))); - //return upload("/remote/test4.bmp", "final.bmp", new Common::Callback(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(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(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(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 d9967d69f6..0082e5cebd 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 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 e64a8837b8..50a1b8a612 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(this, &DropboxUploadRequest::partUploadedCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, url); + Networking::JsonCallback callback = new Common::Callback(this, &DropboxUploadRequest::partUploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 9b68995969..a85d7ef883 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 db132ffc8a..19f6c6c9b7 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(this, &FolderDownloadRequest::directoryListedCallback), + new Common::Callback(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(this, &FolderDownloadRequest::fileDownloadedCallback) + new Common::Callback(this, &FolderDownloadRequest::fileDownloadedCallback), + new Common::Callback(this, &FolderDownloadRequest::fileDownloadedErrorCallback) ); } -void FolderDownloadRequest::finish() { - //TODO: somehow indicate that request was interrupted - Common::Array files; - finishFiles(files); -} +void FolderDownloadRequest::handle() {} + +void FolderDownloadRequest::restart() { start(); } -void FolderDownloadRequest::finishFiles(Common::Array &files) { - Request::finish(); +void FolderDownloadRequest::finishSuccess(Common::Array &files) { + Request::finishSuccess(); if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files)); } diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h index 779ea3334f..8fa3b1188b 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 &files); + void finishSuccess(Common::Array &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 13f23b8e6e..2f1533c50d 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 f472b80ae3..013e117046 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 dbd5e44c0b..e362600389 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(this, &OneDriveListDirectoryRequest::listedDirectoryCallback); - Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, url.c_str()); + Networking::ErrorCallback failureCallback = new Common::Callback(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 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 &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 a05dd871dd..b8adfe7ef0 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 &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 b0f4f7be65..8746b7ab33 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(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(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 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 &files = pair.value; + Common::Array &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(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(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(this, &OneDriveStorage::printFiles), false); - return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &OneDriveStorage::printBool))); + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(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 43675fbfa5..f95ea89bc1 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 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 a0c41ff471..bc7bd74dbe 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 &headers) { @@ -108,6 +114,10 @@ void OneDriveTokenRefresher::setHeaders(Common::Array &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 90ca9d603a..04b0bf26b8 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 _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 &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 e632bfff84..b48a99a48c 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(this, &SavesSyncRequest::directoryListedCallback)); + _workingRequest = _storage->listDirectory( + "/saves", + new Common::Callback(this, &SavesSyncRequest::directoryListedCallback), + new Common::Callback(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 localFileNotAvailableInCloud; + for (Common::HashMap::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 &remoteFiles = status.files; + Common::Array &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::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) { + //upload files which are unavailable in cloud + for (Common::HashMap::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 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(this, &SavesSyncRequest::fileDownloadedCallback) + new Common::Callback(this, &SavesSyncRequest::fileDownloadedCallback), + new Common::Callback(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(this, &SavesSyncRequest::fileUploadedCallback) + new Common::Callback(this, &SavesSyncRequest::fileUploadedCallback), + new Common::Callback(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 da7b27e9b6..bf44b70390 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 311b3fdc9f..32c437857c 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 &files; - /** Server's original response (empty if not failed) */ - Common::String response; - /** Server's HTTP response code. */ - long httpResponseCode; - - ListDirectoryStatus(Common::Array &f) : - interrupted(false), failed(false), files(f), response(), httpResponseCode(-1) {} - - ListDirectoryStatus(bool interrupt, bool failure, Common::Array &f, Common::String resp, long code) : - interrupted(interrupt), failed(failure), files(f), response(resp), httpResponseCode(code) {} -}; - class Storage { public: typedef Networking::Response&> FileArrayResponse; typedef Networking::Response StorageInfoResponse; typedef Networking::Response BoolResponse; - typedef Networking::Response UploadResponse; - typedef Networking::Response ListDirectoryResponse; + typedef Networking::Response UploadResponse; + typedef Networking::Response &> ListDirectoryResponse; typedef Common::BaseCallback *FileArrayCallback; typedef Common::BaseCallback *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 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 281c6a9060..c40781d63a 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 fee0932129..df982bc814 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 5e08be24c9..83005a79d5 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 64f6c26fb9..861546b906 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 461f153b9d..660479e34a 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 0000000000..d2f91586a0 --- /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 a3c723d218..de5308fa52 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 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. + * + * flag is set when Request was interrupted, + * i.e. finished by user with finish() call. + * + * flag is set when Request has failed because of + * some error (bad server response, for example). + * + * contains server's original response. + * + * 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 DataReponse; typedef Common::BaseCallback *DataCallback; +typedef Common::BaseCallback *ErrorCallback; /** * RequestState is used to indicate current Request state. @@ -74,6 +106,9 @@ typedef Common::BaseCallback *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, @@ -93,6 +128,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 350901e35f..51c98e7d0c 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 -- cgit v1.2.3 From b39f46788a70a6c72d5ca678c79c0b53ebde9b68 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 14:18:32 +0600 Subject: CLOUD: Add OneDriveUploadRequest Doesn't support server's requested ranges yet. Commit also adds some PUT-related code in NetworkReadStream and CurlRequest. --- backends/cloud/dropbox/dropboxstorage.cpp | 13 -- backends/cloud/dropbox/dropboxstorage.h | 3 +- backends/cloud/onedrive/onedrivestorage.cpp | 19 ++- backends/cloud/onedrive/onedrivestorage.h | 4 +- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 2 + backends/cloud/onedrive/onedriveuploadrequest.cpp | 171 +++++++++++++++++++++ backends/cloud/onedrive/onedriveuploadrequest.h | 61 ++++++++ backends/cloud/storage.cpp | 42 +++++ backends/cloud/storage.h | 2 +- backends/module.mk | 4 +- backends/networking/curl/curlrequest.cpp | 8 +- backends/networking/curl/curlrequest.h | 4 + backends/networking/curl/networkreadstream.cpp | 38 ++++- backends/networking/curl/networkreadstream.h | 16 +- 14 files changed, 354 insertions(+), 33 deletions(-) create mode 100644 backends/cloud/onedrive/onedriveuploadrequest.cpp create mode 100644 backends/cloud/onedrive/onedriveuploadrequest.h create mode 100644 backends/cloud/storage.cpp diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index d22e0abf60..7ba072d578 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 0082e5cebd..a5ef76a3d6 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 8746b7ab33..fe10580616 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 -#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(this, &OneDriveStorage::printFiles), false); - return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO + return Storage::upload( + "uploads/test.jpg", "test.jpg", + new Common::Callback(this, &OneDriveStorage::printFile), getErrorPrintingCallback() + ); + //return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(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 f95ea89bc1..0ced98cd4e 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 bc7bd74dbe..bf849f7964 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 0000000000..752907f333 --- /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(this, &OneDriveUploadRequest::partUploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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(this, &OneDriveUploadRequest::partUploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 0000000000..09419d8a15 --- /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 0000000000..6588b05193 --- /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 32c437857c..1d92189fa4 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 c40781d63a..fd06e52f9d 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 861546b906..a3f997a4ff 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 660479e34a..5737078b2d 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 f96a069e16..283e5e667f 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 f1f41264aa..d48d01b198 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 -- cgit v1.2.3 From 3638c8348d98273da5c4e2c5bd1afa8a985a2d0c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 16:23:25 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 2 ++ backends/cloud/dropbox/dropboxstorage.h | 3 +++ backends/cloud/onedrive/onedrivestorage.cpp | 9 +++------ backends/cloud/onedrive/onedrivestorage.h | 3 +++ backends/cloud/savessyncrequest.cpp | 5 +++-- backends/cloud/storage.h | 3 +++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 7ba072d578..beb510882a 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 a5ef76a3d6..90a1d270e8 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 fe10580616..1e9a641e8a 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(this, &OneDriveStorage::printFiles), false); - return Storage::upload( - "uploads/test.jpg", "test.jpg", - new Common::Callback(this, &OneDriveStorage::printFile), getErrorPrintingCallback() - ); - //return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback(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 0ced98cd4e..45a8dca331 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 b48a99a48c..9727738654 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(this, &SavesSyncRequest::directoryListedCallback), new Common::Callback(this, &SavesSyncRequest::directoryListedErrorCallback) ); @@ -165,6 +165,7 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er //we're lucky - user just lacks his "/cloud/" folder Common::Array 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(this, &SavesSyncRequest::fileUploadedCallback), new Common::Callback(this, &SavesSyncRequest::fileUploadedErrorCallback) ); diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 1d92189fa4..4bd2aa3fb1 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; -- cgit v1.2.3 From 13351a730d79cc2f0d5b964226c69bb04e2c93c1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 19:00:54 +0600 Subject: 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. --- backends/cloud/onedrive/onedrivestorage.cpp | 45 +++++++++++++++++++++++++---- backends/cloud/onedrive/onedrivestorage.h | 5 +++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index 1e9a641e8a..a16a351690 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(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(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO +} + +Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(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 45a8dca331..5741f8e20e 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(); -- cgit v1.2.3 From 0d0033fb6ad00e3081bc2854ce5972746b603105 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 19:18:06 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 14 -------------- backends/cloud/dropbox/dropboxstorage.h | 6 ------ backends/cloud/onedrive/onedrivestorage.cpp | 14 -------------- backends/cloud/onedrive/onedrivestorage.h | 6 ------ backends/cloud/storage.cpp | 21 +++++++++++++++++++++ backends/cloud/storage.h | 11 ++++++++++- 6 files changed, 31 insertions(+), 41 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index beb510882a..1aae73e524 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(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(this, &DropboxStorage::printBool), getErrorPrintingCallback())); //TODO -} - Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) { Networking::JsonCallback innerCallback = new Common::CallbackBridge(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 90a1d270e8..7752dddf9b 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(); @@ -88,9 +85,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 *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 diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index a16a351690..fb2ead3364 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(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(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO -} - Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { Networking::JsonCallback innerCallback = new Common::CallbackBridge(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 5741f8e20e..241b6a8742 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: @@ -98,9 +95,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 *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 diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 6588b05193..2e13376fa8 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(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 4bd2aa3fb1..b4d2680e31 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -48,6 +48,15 @@ public: typedef Common::BaseCallback *UploadCallback; typedef Common::BaseCallback *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; -- cgit v1.2.3 From ca85d4482af0b2e570565d5aa6d562ec86b10100 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 19:32:16 +0600 Subject: 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). --- backends/cloud/dropbox/dropboxstorage.cpp | 8 ++++---- backends/cloud/onedrive/onedrivestorage.cpp | 2 +- backends/cloud/storageinfo.h | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 1aae73e524..263c2c8132 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 fb2ead3364..c64ed6bf72 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 1492898a6c..e0666bc190 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; } }; -- cgit v1.2.3 From 675e7a6ed1d19e64e2f80a4dd1f454e646cb52f5 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 19:41:11 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 16 ---------------- backends/cloud/dropbox/dropboxstorage.h | 6 ------ backends/cloud/onedrive/onedrivestorage.cpp | 17 ----------------- backends/cloud/onedrive/onedrivestorage.h | 6 ------ backends/cloud/storage.cpp | 23 +++++++++++++++++++++++ backends/cloud/storage.h | 4 ++-- 6 files changed, 25 insertions(+), 47 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 263c2c8132..acc96f9476 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(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 7752dddf9b..5db2bb7ee3 100644 --- a/backends/cloud/dropbox/dropboxstorage.h +++ b/backends/cloud/dropbox/dropboxstorage.h @@ -76,12 +76,6 @@ public: /** 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 *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback); - - /** Returns Common::Array 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 diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index c64ed6bf72..e1c6861efd 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 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 241b6a8742..49d63b98a6 100644 --- a/backends/cloud/onedrive/onedrivestorage.h +++ b/backends/cloud/onedrive/onedrivestorage.h @@ -86,12 +86,6 @@ public: /** 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 *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback); - - /** Returns Common::Array 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 diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 2e13376fa8..90e095a146 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 b4d2680e31..4956d5f617 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 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; -- cgit v1.2.3 From 4e7dec550077bc37cf254311aefb621cdeebbdfe Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 20:54:41 +0600 Subject: 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. --- .../dropbox/dropboxcreatedirectoryrequest.cpp | 109 +++++++++++++++++++++ .../cloud/dropbox/dropboxcreatedirectoryrequest.h | 55 +++++++++++ backends/cloud/dropbox/dropboxstorage.cpp | 12 +-- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/manager.cpp | 11 +++ backends/cloud/manager.h | 3 + backends/cloud/onedrive/onedrivestorage.cpp | 5 +- backends/cloud/savessyncrequest.cpp | 4 +- backends/module.mk | 1 + base/main.cpp | 1 + common/cloudmanager.h | 5 + 11 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp create mode 100644 backends/cloud/dropbox/dropboxcreatedirectoryrequest.h diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp new file mode 100644 index 0000000000..c077d8df11 --- /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(this, &DropboxCreateDirectoryRequest::responseCallback); + Networking::ErrorCallback errorCallback = new Common::Callback(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 0000000000..ea3175bb50 --- /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 acc96f9476..f51819ccae 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 -#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(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 5db2bb7ee3..2f57b052b2 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 2f1533c50d..70f2c3a3ef 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(this, &Manager::printBool), nullptr); +} + } // End of namespace Cloud diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h index 013e117046..f68b33517d 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 e1c6861efd..2adf5fffed 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 diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index 9727738654..cf0c427294 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(this, &SavesSyncRequest::directoryListedCallback), new Common::Callback(this, &SavesSyncRequest::directoryListedErrorCallback) ); diff --git a/backends/module.mk b/backends/module.mk index fd06e52f9d..96dfa2294f 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 f629eb98d8..36dd8c6f18 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 51c98e7d0c..936f0e0108 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 -- cgit v1.2.3 From 8cdde307f7b1d3eff71050817921ea0aa8c318fe Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 21:21:35 +0600 Subject: CLOUD: Add OneDriveCreateDirectoryRequest --- backends/cloud/manager.cpp | 2 +- .../onedrive/onedrivecreatedirectoryrequest.cpp | 128 +++++++++++++++++++++ .../onedrive/onedrivecreatedirectoryrequest.h | 57 +++++++++ backends/cloud/onedrive/onedrivestorage.cpp | 6 + backends/cloud/onedrive/onedrivestorage.h | 2 +- backends/module.mk | 1 + 6 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp create mode 100644 backends/cloud/onedrive/onedrivecreatedirectoryrequest.h diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp index 70f2c3a3ef..13424ba8c6 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(this, &Manager::printBool), nullptr); } diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp new file mode 100644 index 0000000000..c48ae1dfbe --- /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(this, &OneDriveCreateDirectoryRequest::responseCallback); + Networking::ErrorCallback errorCallback = new Common::Callback(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 0000000000..4bf0d9ef36 --- /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 2adf5fffed..190a7ab603 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(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 49d63b98a6..8fc7b2ac4d 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 96dfa2294f..4733509f24 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 -- cgit v1.2.3 From 06163cb8b907e30f8463b2b9700d136c73b19a33 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 31 May 2016 21:47:57 +0600 Subject: CLOUD: Fix SavesSyncRequest to create saves folder --- backends/cloud/savessyncrequest.cpp | 33 ++++++++++++++++++++++++++++++--- backends/cloud/savessyncrequest.h | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index cf0c427294..d5cb6f6f76 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(this, &SavesSyncRequest::directoryCreatedCallback), + new Common::Callback(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 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 bf44b70390..0e20159845 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); -- cgit v1.2.3 From 4b3a8be0b9db448971e6095a24501c66714c484f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 1 Jun 2016 12:08:47 +0600 Subject: CLOUD: Shorten Cloud API touch() and isSyncing() are not needed. remove() is not needed too, but it could be used in the future. --- backends/cloud/dropbox/dropboxstorage.h | 6 ------ backends/cloud/onedrive/onedrivestorage.h | 6 ------ backends/cloud/storage.h | 6 ------ 3 files changed, 18 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index 2f57b052b2..2c60097701 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 8fc7b2ac4d..5edd96e59c 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 4956d5f617..1efee85bb3 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; }; -- cgit v1.2.3 From a66322408f95ff7b29cf6967eaecaac06dfe5b31 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 1 Jun 2016 12:39:10 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxstorage.cpp | 8 ++++---- backends/cloud/dropbox/dropboxstorage.h | 3 --- backends/cloud/onedrive/onedrivestorage.cpp | 12 ++++++------ backends/cloud/onedrive/onedrivestorage.h | 3 --- backends/cloud/storage.cpp | 24 +++++++++++++++++++++--- backends/cloud/storage.h | 26 ++++++++++++++++++++++---- backends/networking/curl/connectionmanager.cpp | 11 ++++++----- backends/networking/curl/connectionmanager.h | 13 +++++++++++-- 8 files changed, 70 insertions(+), 30 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index f51819ccae..861a58db4b 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(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 2c60097701..c186d1e5d6 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 190a7ab603..98f0ac5a4d 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(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(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 5edd96e59c..a09d68b6fd 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 90e095a146..0f9a2a168a 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(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(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 1efee85bb3..b1c62ba05b 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -48,7 +48,9 @@ public: typedef Common::BaseCallback *UploadCallback; typedef Common::BaseCallback *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 949dc949f1..ef3c858bec 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::iterator i = _requests.begin(); i != _requests.end();) { - Request *request = *i; + for (Common::Array::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 c632fa54ef..b39a779558 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -39,9 +39,18 @@ class NetworkReadStream; class ConnectionManager : public Common::Singleton { friend void connectionsThread(void *); //calls handle() + typedef Common::BaseCallback *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 _requests; + Common::Array _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. */ -- cgit v1.2.3 From 1f974a7a2a2073074391fbf090d2bf909006e773 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 1 Jun 2016 14:07:50 +0600 Subject: CLOUD: Fix ConnectionManager's destructor It now terminates active Requests. --- backends/networking/curl/connectionmanager.cpp | 18 ++++++++++++++++-- backends/networking/curl/connectionmanager.h | 2 ++ base/main.cpp | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index ef3c858bec..a3cad0d005 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::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 b39a779558..75cff0587b 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 { CURLM *_multi; bool _timerStarted; Common::Array _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 36dd8c6f18..aede61790c 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(); -- cgit v1.2.3 From b3bf5322117d03c003011839ea1e7897c48183fa Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 1 Jun 2016 16:22:42 +0600 Subject: CLOUD: Make CloudManager singleton It's needed to ::destroy() it in main(). --- backends/cloud/cloudmanager.cpp | 122 ++++++++++++++++++++++++++ backends/cloud/cloudmanager.h | 87 +++++++++++++++++++ backends/cloud/manager.cpp | 129 ---------------------------- backends/cloud/manager.h | 53 ------------ backends/cloud/onedrive/onedrivestorage.cpp | 9 +- backends/module.mk | 2 +- backends/platform/sdl/sdl.cpp | 6 -- base/main.cpp | 14 ++- common/cloudmanager.h | 77 ----------------- common/system.h | 22 ----- 10 files changed, 224 insertions(+), 297 deletions(-) create mode 100644 backends/cloud/cloudmanager.cpp create mode 100644 backends/cloud/cloudmanager.h delete mode 100644 backends/cloud/manager.cpp delete mode 100644 backends/cloud/manager.h delete mode 100644 common/cloudmanager.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp new file mode 100644 index 0000000000..d18bb6ff9a --- /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 0000000000..a13eeebb94 --- /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 { + Common::Array _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 13424ba8c6..0000000000 --- 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(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 f68b33517d..0000000000 --- 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 _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 98f0ac5a4d..ca8a2346ad 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 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 4733509f24..40ccd17c78 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 acb4d999c3..dca6891fef 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 aede61790c..4edc7a957a 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 @@ -596,6 +598,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(); diff --git a/common/cloudmanager.h b/common/cloudmanager.h deleted file mode 100644 index 936f0e0108..0000000000 --- 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 815fa9d19d..6d185d3075 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. * -- cgit v1.2.3 From 00d8d232366f418d3a25046786ab9ab5c90a52c2 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 1 Jun 2016 16:33:45 +0600 Subject: CLOUD: Add mutexes in Storage --- backends/cloud/storage.cpp | 9 ++++++++- backends/cloud/storage.h | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 0f9a2a168a..f035c93368 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(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 b1c62ba05b..1276b81827 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(); -- cgit v1.2.3 From 2a9cf65eeb305525c55c4d157ef8b1a104b8f7d7 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 28 Apr 2014 01:05:04 +0300 Subject: GUI: Add mode to skip drawing of button for PicButton --- gui/widget.cpp | 7 ++++--- gui/widget.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gui/widget.cpp b/gui/widget.cpp index f2a29c3100..1d0247883c 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 0f4b300233..28d7a5b8c0 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 */ -- cgit v1.2.3 From 0281b1eba84127bef927d70245739e4e55ad5be2 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 28 Apr 2014 09:08:26 +0300 Subject: GUI: Added support for PNG images --- gui/ThemeEngine.cpp | 58 +++++++++++++++++++++++++++++++++++------------- gui/themes/scummtheme.py | 2 +- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index c850a6a02e..70ce07b211 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 d5fa4dfca7..365787275b 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")): -- cgit v1.2.3 From 762671ccd841c5f54e4cf241a5372c26210b5865 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 2 May 2014 18:09:30 +0300 Subject: GUI: Added possibility to specify several state images for PicButtonWidget --- gui/widget.cpp | 47 +++++++++++++++++++++++++++++++---------------- gui/widget.h | 15 ++++++++++++--- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/gui/widget.cpp b/gui/widget.cpp index 1d0247883c..dfc0a5b50c 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 28d7a5b8c0..d7b4c7b6f2 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; -- cgit v1.2.3 From 53a42ececfff48b93cef414bc2762806786da8d5 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 2 May 2014 23:36:03 +0300 Subject: GUI: Added new alphabitmap image type --- graphics/VectorRenderer.h | 16 ++++++-- graphics/VectorRendererSpec.cpp | 13 +++++-- graphics/VectorRendererSpec.h | 5 ++- graphics/transparent_surface.cpp | 80 +++++++++++++++++++++++++++++++++++++++- graphics/transparent_surface.h | 3 ++ gui/ThemeEngine.cpp | 65 +++++++++++++++++++++++++++++++- gui/ThemeEngine.h | 15 ++++++++ gui/ThemeParser.cpp | 24 ++++++++++++ gui/ThemeParser.h | 5 +++ 9 files changed, 215 insertions(+), 11 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 0352808706..3c042a3c40 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 fc741f6e77..a50bb27c1a 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 -void VectorRendererSpec:: -blitAlphaBitmap(const Graphics::Surface *source, const Common::Rect &r) { +void VectorRendererSpec:: +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 void VectorRendererSpec:: -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; @@ -942,6 +943,12 @@ blitAlphaBitmapClip(const Graphics::Surface *source, const Common::Rect &r, cons } } +template +void VectorRendererSpec:: +blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) { + source->blit(*_activeSurface, r.left, r.top); +} + template void VectorRendererSpec:: applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) { diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h index bee6d4c425..658c70948f 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 c2903d42c3..81b7f3d647 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 c0d3d26e52..461d7a6e4b 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 70ce07b211..1a648b44a0 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 3c259b4f9d..92aafc8975 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 ImagesMap; + typedef Common::HashMap AImagesMap; friend class GUI::Dialog; friend class GUI::GuiObject; @@ -482,6 +484,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 bd5b406ca8..8d917f29e0 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 360e3da009..14305af80d 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); -- cgit v1.2.3 From f0c52096f3e9215cb584b3862f2cd4b9632761d5 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sat, 3 May 2014 02:50:47 +0300 Subject: GUI: Implemented possibility to use alphabitmaps in GraphicsWidget --- graphics/VectorRendererSpec.cpp | 2 +- gui/ThemeEngine.cpp | 47 +++++++++++++++++++++++++++++++++++++++-- gui/ThemeEngine.h | 8 +++++++ gui/widget.cpp | 31 +++++++++++++++++++++++++++ gui/widget.h | 2 ++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index a50bb27c1a..e87397b349 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -888,7 +888,7 @@ template void VectorRendererSpec:: 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 1a648b44a0..26729b916f 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 92aafc8975..ccc5e03f92 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 dfc0a5b50c..62a69d9540 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 d7b4c7b6f2..5e3eea73a0 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; }; -- cgit v1.2.3 From 4474ccf8144563c4bacbb060c943c0f68e317cea Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 5 May 2014 00:52:56 +0300 Subject: GUI: Implemented alphabitmap autoscale --- graphics/VectorRenderer.h | 6 ++++-- graphics/VectorRendererSpec.cpp | 17 +++++++++++------ graphics/VectorRendererSpec.h | 2 +- gui/ThemeEngine.cpp | 2 +- gui/ThemeParser.cpp | 5 +++++ gui/ThemeParser.h | 1 + 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 3c042a3c40..2f609ea6db 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 e87397b349..1fdd0fc4eb 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -884,6 +884,17 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) { } } +template +void VectorRendererSpec:: +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 void VectorRendererSpec:: blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const Common::Rect &clipping) { @@ -943,12 +954,6 @@ blitKeyBitmapClip(const Graphics::Surface *source, const Common::Rect &r, const } } -template -void VectorRendererSpec:: -blitAlphaBitmap(Graphics::TransparentSurface *source, const Common::Rect &r) { - source->blit(*_activeSurface, r.left, r.top); -} - template void VectorRendererSpec:: applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) { diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h index 658c70948f..308fdc5c94 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 26729b916f..545b9b5c56 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 8d917f29e0..ca8b2631a5 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 14305af80d..155731467f 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) -- cgit v1.2.3 From ec7312ac13ce19f64b2b453b43e2c37235dcbe7a Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 5 May 2014 21:36:16 +0300 Subject: GUI: Implemented more modes to autoscale --- graphics/VectorRenderer.h | 10 ++++++++-- graphics/VectorRendererSpec.cpp | 18 +++++++++++++++--- graphics/VectorRendererSpec.h | 2 +- gui/ThemeEngine.cpp | 2 +- gui/ThemeParser.cpp | 11 +++++++---- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 2f609ea6db..fb19fa3156 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 1fdd0fc4eb..68b77d20ee 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -886,13 +886,25 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) { template void VectorRendererSpec:: -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 diff --git a/graphics/VectorRendererSpec.h b/graphics/VectorRendererSpec.h index 308fdc5c94..50ee268d4d 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 545b9b5c56..26729b916f 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 ca8b2631a5..226281122f 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."); -- cgit v1.2.3 From 38114eb760f842eb3145c9d1af62366cf2fab8ca Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Wed, 7 May 2014 13:12:59 +0300 Subject: GUI: Plug NinePatch bitmaps into parser --- graphics/VectorRenderer.h | 3 ++- graphics/VectorRendererSpec.cpp | 4 ++++ gui/ThemeParser.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index fb19fa3156..0a83dc5d49 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 68b77d20ee..f3e496af70 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 226281122f..d4701c49f6 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."); -- cgit v1.2.3 From 75f9b099dc3e198c7a1750a6e26ac4945719492f Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 9 May 2014 19:00:49 +0300 Subject: GUI: Added possibility to specify scale mode for AlphaBitmaps --- graphics/VectorRenderer.h | 11 ++--------- graphics/VectorRendererSpec.cpp | 10 +++++----- graphics/VectorRendererSpec.h | 2 +- gui/ThemeEngine.cpp | 16 ++++++++-------- gui/ThemeEngine.h | 13 ++++++++++--- gui/ThemeParser.cpp | 11 ++++++----- gui/widget.cpp | 16 +++++++++++----- gui/widget.h | 3 ++- 8 files changed, 45 insertions(+), 37 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 0a83dc5d49..b79172931d 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 f3e496af70..0b947e4a0f 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -887,12 +887,12 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) { template void VectorRendererSpec:: -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 50ee268d4d..f8c1e73c6c 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 26729b916f..57c2e5d870 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 ccc5e03f92..625bab7a1b 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 d4701c49f6..72a4d77560 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 62a69d9540..d3de824fc5 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 5e3eea73a0..e2e8bffa7e 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 */ -- cgit v1.2.3 From 94bc75ae464dc37d2a4dee0ac6fb69e75b265413 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sun, 11 May 2014 22:40:35 +0300 Subject: GUI: Implemented centering of dialog background --- graphics/VectorRenderer.h | 7 +++++-- graphics/VectorRendererSpec.cpp | 14 +++++++++++--- graphics/VectorRendererSpec.h | 5 ++++- gui/ThemeParser.cpp | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index b79172931d..c21f780112 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 0b947e4a0f..8af6594fd4 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -887,7 +887,8 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) { template void VectorRendererSpec:: -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 f8c1e73c6c..b681d7e30e 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 72a4d77560..bd0d2c4898 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") { -- cgit v1.2.3 From 1359255ea83f596fbce3332d7d54f9acbc4daf4d Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Thu, 29 May 2014 19:42:34 +0300 Subject: GUI: Added empty dialog background --- gui/ThemeEngine.cpp | 2 ++ gui/ThemeEngine.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 57c2e5d870..ee860ffbf9 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 625bab7a1b..42e5dbfeca 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 -- cgit v1.2.3 From c6e04845cc753fa219ed5c66a330eabccf2839c7 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Thu, 29 May 2014 21:48:14 +0300 Subject: GUI: Switched GUI to draw on TransparentSurface --- graphics/VectorRenderer.h | 4 ++-- gui/ThemeEngine.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index c21f780112..2c32761ed4 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 42e5dbfeca..f4a8b1ed0a 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. */ -- cgit v1.2.3 From 30f7556beed2de07caa338650a89f18b0b71cae5 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Thu, 29 May 2014 17:49:34 +0300 Subject: GUI: Added support for alphabitmaps in picbuttons --- gui/widget.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++------------- gui/widget.h | 4 +++ 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/gui/widget.cpp b/gui/widget.cpp index d3de824fc5..ffdca7337b 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 e2e8bffa7e..e9343f264c 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 */ -- cgit v1.2.3 From 8c7a8116be3f54e03644876c8d41daf1bf2e835a Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sun, 1 Jun 2014 14:01:15 +0300 Subject: GUI: Implemented test point method to GuiObject --- gui/object.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/object.h b/gui/object.h index 219bf77f69..776c941356 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; }; -- cgit v1.2.3 From 6524a8d103c87def348b9560418850cb48d24ce4 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 4 Aug 2014 01:12:49 +0200 Subject: GUI: Added transparency to PicWidgets --- graphics/VectorRenderer.h | 3 ++- graphics/VectorRendererSpec.cpp | 8 ++++---- graphics/VectorRendererSpec.h | 3 ++- gui/ThemeEngine.cpp | 15 ++++++++------- gui/ThemeEngine.h | 4 ++-- gui/widget.cpp | 16 ++++++++-------- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 2c32761ed4..5f7b6e60d3 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 8af6594fd4..9aed3301fa 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -888,11 +888,11 @@ blitKeyBitmap(const Graphics::Surface *source, const Common::Rect &r) { template void VectorRendererSpec:: 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 b681d7e30e..84c802f6df 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 ee860ffbf9..e0563da711 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 f4a8b1ed0a..91f82b1122 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 ffdca7337b..fcfd0f8986 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); } } } -- cgit v1.2.3 From ea80e24481f12c117d6aecf6e5260b08423454ec Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Mon, 13 Apr 2015 01:53:10 +0200 Subject: GUI: Added animation classes --- graphics/transparent_surface.h | 7 ++ gui/animation/AccelerateInterpolator.h | 45 +++++++++++ gui/animation/AlphaAnimation.h | 53 +++++++++++++ gui/animation/Animation.cpp | 98 ++++++++++++++++++++++++ gui/animation/Animation.h | 76 +++++++++++++++++++ gui/animation/DeccelerateInterpolator.h | 41 ++++++++++ gui/animation/Drawable.h | 109 +++++++++++++++++++++++++++ gui/animation/Interpolator.h | 44 +++++++++++ gui/animation/ParallelAnimation.h | 72 ++++++++++++++++++ gui/animation/RepeatAnimationWrapper.cpp | 52 +++++++++++++ gui/animation/RepeatAnimationWrapper.h | 61 +++++++++++++++ gui/animation/ScaleAnimation.h | 69 +++++++++++++++++ gui/animation/SequenceAnimationComposite.cpp | 72 ++++++++++++++++++ gui/animation/SequenceAnimationComposite.h | 51 +++++++++++++ gui/animation/WaitForConditionAnimation.h | 71 +++++++++++++++++ gui/module.mk | 3 + 16 files changed, 924 insertions(+) create mode 100644 gui/animation/AccelerateInterpolator.h create mode 100644 gui/animation/AlphaAnimation.h create mode 100644 gui/animation/Animation.cpp create mode 100644 gui/animation/Animation.h create mode 100644 gui/animation/DeccelerateInterpolator.h create mode 100644 gui/animation/Drawable.h create mode 100644 gui/animation/Interpolator.h create mode 100644 gui/animation/ParallelAnimation.h create mode 100644 gui/animation/RepeatAnimationWrapper.cpp create mode 100644 gui/animation/RepeatAnimationWrapper.h create mode 100644 gui/animation/ScaleAnimation.h create mode 100644 gui/animation/SequenceAnimationComposite.cpp create mode 100644 gui/animation/SequenceAnimationComposite.h create mode 100644 gui/animation/WaitForConditionAnimation.h diff --git a/graphics/transparent_surface.h b/graphics/transparent_surface.h index 461d7a6e4b..8654183548 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 0000000000..31494d369f --- /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 0000000000..82cf3d4ba9 --- /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 0000000000..ee4d900b4d --- /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 0000000000..300720b419 --- /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 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 0000000000..e25ff6a4ba --- /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 0000000000..cf604270aa --- /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 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 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 0000000000..72d7acb8d4 --- /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 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 0000000000..ce1f599fb1 --- /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 _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 0000000000..a7e1413093 --- /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 0000000000..3d766dd1c5 --- /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 0000000000..80a4ae6305 --- /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 0000000000..9ecfeebc8c --- /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 0000000000..4ec0331751 --- /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 _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 0000000000..5a67a37493 --- /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 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 6cbc63d24d..2ffea5a2b5 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 \ -- cgit v1.2.3 From a686049c46387be53010357050bd252845a48d48 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Fri, 22 Jul 2016 11:15:58 +0300 Subject: GUI: Fix widget clipping --- gui/widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/widget.cpp b/gui/widget.cpp index fcfd0f8986..f6e6d09a8a 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; -- cgit v1.2.3 From b02b16ab98330f3471919a57ec0d4e087e2fa924 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 2 Jun 2016 11:37:10 +0600 Subject: CLOUD: Document ConnectionManager's onDeleteCallback --- backends/networking/curl/connectionmanager.cpp | 4 ++-- backends/networking/curl/connectionmanager.h | 28 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index a3cad0d005..44511fdb23 100644 --- a/backends/networking/curl/connectionmanager.cpp +++ b/backends/networking/curl/connectionmanager.cpp @@ -47,7 +47,7 @@ ConnectionManager::~ConnectionManager() { _handleMutex.lock(); for (Common::Array::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 75cff0587b..a91bc01d87 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -42,11 +42,31 @@ class ConnectionManager : public Common::Singleton { typedef Common::BaseCallback *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 -- cgit v1.2.3 From da3b7bd8d9f3d3828b8cea6dff60e5f43e7ad4b1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 15:14:12 +0600 Subject: 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. --- backends/cloud/cloudmanager.cpp | 15 +- backends/cloud/dropbox/dropboxuploadrequest.cpp | 4 +- backends/cloud/googledrive/googledrivestorage.cpp | 331 +++++++++++++++++++++ backends/cloud/googledrive/googledrivestorage.h | 131 ++++++++ .../googledrive/googledrivetokenrefresher.cpp | 121 ++++++++ .../cloud/googledrive/googledrivetokenrefresher.h | 52 ++++ backends/module.mk | 2 + backends/networking/curl/curljsonrequest.cpp | 6 +- common/json.cpp | 6 +- common/json.h | 6 +- 10 files changed, 660 insertions(+), 14 deletions(-) create mode 100644 backends/cloud/googledrive/googledrivestorage.cpp create mode 100644 backends/cloud/googledrive/googledrivestorage.h create mode 100644 backends/cloud/googledrive/googledrivetokenrefresher.cpp create mode 100644 backends/cloud/googledrive/googledrivetokenrefresher.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index d18bb6ff9a..92e45e8811 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 50a1b8a612..bf8e43d7cb 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 0000000000..eef7f1f28d --- /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 + +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(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(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@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(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 &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(this, &GoogleDriveStorage::printInfo); + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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 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 0000000000..8a82a54533 --- /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 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 0000000000..3fae830105 --- /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 + +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(this, &GoogleDriveTokenRefresher::tokenRefreshed)); + return; + } + + //notify user of success + CurlJsonRequest::finishSuccess(json); +} + +void GoogleDriveTokenRefresher::setHeaders(Common::Array &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 0000000000..3dcb56bde6 --- /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 _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 &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 40ccd17c78..8833edc9db 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 df982bc814..46d88657d2 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 779f99aae0..5f63d3fe1f 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 35571935ee..d9bbbdb77c 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; -- cgit v1.2.3 From 1c823b6c1d465bd894f7f37a963165ee009f35ea Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 17:38:35 +0600 Subject: CLOUD: Fix SavesSyncRequest Now it would finish with error if spawned Request is nullptr. --- backends/cloud/savessyncrequest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index d5cb6f6f76..86dc83abf8 100644 --- a/backends/cloud/savessyncrequest.cpp +++ b/backends/cloud/savessyncrequest.cpp @@ -66,6 +66,7 @@ void SavesSyncRequest::start() { new Common::Callback(this, &SavesSyncRequest::directoryListedCallback), new Common::Callback(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(this, &SavesSyncRequest::directoryCreatedCallback), new Common::Callback(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(this, &SavesSyncRequest::fileDownloadedCallback), new Common::Callback(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(this, &SavesSyncRequest::fileUploadedCallback), new Common::Callback(this, &SavesSyncRequest::fileUploadedErrorCallback) ); + if (!_workingRequest) finishError(Networking::ErrorResponse(this)); } void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) { -- cgit v1.2.3 From 7ff1f918084b8d41826ad869d8c9865e1d53e082 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 17:44:26 +0600 Subject: 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. --- backends/base-backend.cpp | 4 ++ backends/base-backend.h | 1 + backends/graphics/graphics.h | 1 + backends/graphics/opengl/opengl-graphics.cpp | 6 +++ backends/graphics/opengl/opengl-graphics.h | 1 + .../graphics/surfacesdl/surfacesdl-graphics.cpp | 48 ++++++++++++++++++++++ backends/graphics/surfacesdl/surfacesdl-graphics.h | 1 + backends/modular-backend.cpp | 4 ++ backends/modular-backend.h | 1 + common/system.h | 27 ++++++++++++ 10 files changed, 94 insertions(+) diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp index 3e95c3e26a..cf6fdfc877 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 598f682b32..7000d3b14a 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 3671b9f0b9..8b0ac19d9e 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 4d6a00a3b3..e9f26bc7bc 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 35435c156e..e0d1664e76 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 5b591e77ff..88fdc09f0a 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 25d6ff041c..a8bafd0a84 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 d8be9ca7ed..08565dcbdd 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 20e8b7357d..cf3babfc47 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 6d185d3075..64e4b927b3 100644 --- a/common/system.h +++ b/common/system.h @@ -1085,6 +1085,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, -- cgit v1.2.3 From 9d186929e16ce023222028143a280aca03605fe9 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 18:04:48 +0600 Subject: 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. --- backends/module.mk | 1 + backends/networking/curl/cloudicon.cpp | 50 ++++++++++++++++++++++++++ backends/networking/curl/cloudicon.h | 40 +++++++++++++++++++++ backends/networking/curl/connectionmanager.cpp | 17 ++++++--- backends/networking/curl/connectionmanager.h | 10 +++++- 5 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 backends/networking/curl/cloudicon.cpp create mode 100644 backends/networking/curl/cloudicon.h diff --git a/backends/module.mk b/backends/module.mk index 8833edc9db..f1ba8b8b1b 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 0000000000..477e864d82 --- /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 0000000000..dc28c8290d --- /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 44511fdb23..c044a1577d 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 a91bc01d87..3a2e974bf4 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 { + 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 *RequestCallback; @@ -73,8 +79,10 @@ class ConnectionManager : public Common::Singleton { bool _timerStarted; Common::Array _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(); -- cgit v1.2.3 From 2a15b8b280af4d4beddb142e468f511a65552e2d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 19:18:01 +0600 Subject: GUI: Add clearOSD() method So one can erase everything from OSD and then blit something on it. --- backends/base-backend.cpp | 5 ++++ backends/base-backend.h | 1 + backends/graphics/graphics.h | 1 + backends/graphics/opengl/opengl-graphics.cpp | 17 ++++++++++++ backends/graphics/opengl/opengl-graphics.h | 1 + .../graphics/surfacesdl/surfacesdl-graphics.cpp | 32 ++++++++++++++++++++++ backends/graphics/surfacesdl/surfacesdl-graphics.h | 1 + backends/modular-backend.cpp | 4 +++ backends/modular-backend.h | 1 + common/system.h | 6 ++++ 10 files changed, 69 insertions(+) diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp index cf6fdfc877..59d674461c 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 7000d3b14a..edee427ca7 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 8b0ac19d9e..1063a10a9c 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 e9f26bc7bc..97788be3ad 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 e0d1664e76..f36d5d17f2 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 88fdc09f0a..4a33890f42 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 a8bafd0a84..01974cf6ab 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 08565dcbdd..6ad80ecbb3 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 cf3babfc47..06c69b55ad 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 64e4b927b3..6071e4582b 100644 --- a/common/system.h +++ b/common/system.h @@ -1112,6 +1112,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, -- cgit v1.2.3 From bcb2a5bd8d8f7fc3141f5e8219c47e0342c4692e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 19:18:35 +0600 Subject: CLOUD: Fix CloudIcon to use clearOSD() Now only icon is shown. --- backends/networking/curl/cloudicon.cpp | 20 ++++++++++++-------- backends/networking/curl/cloudicon.h | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index 477e864d82..e8c92592bf 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 dc28c8290d..24c1fd6f71 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(); -- cgit v1.2.3 From b32c2be78dfa88cdb8bb90174fe6fef8757ae85a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 3 Jun 2016 19:54:20 +0600 Subject: 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(). --- backends/networking/curl/connectionmanager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index c044a1577d..57ea2ce999 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::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; } -- cgit v1.2.3 From 1b9987ddc9ff7a69d0de5bbbbd4aee42fff08b42 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 15:56:15 +0600 Subject: GUI: Add getOSDFormat() and make OSD 32 bpp --- backends/base-backend.cpp | 5 ++++ backends/base-backend.h | 1 + backends/graphics/graphics.h | 2 ++ backends/graphics/opengl/opengl-graphics.cpp | 2 ++ backends/graphics/opengl/opengl-graphics.h | 1 + .../graphics/surfacesdl/surfacesdl-graphics.cpp | 27 ++++++++++++++++++---- backends/graphics/surfacesdl/surfacesdl-graphics.h | 3 +++ backends/modular-backend.cpp | 4 ++++ backends/modular-backend.h | 1 + common/system.h | 6 +++++ 10 files changed, 47 insertions(+), 5 deletions(-) diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp index 59d674461c..dfb9e284ce 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 edee427ca7..2394edaf38 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 1063a10a9c..921dfca61c 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 97788be3ad..c491b03f1f 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 f36d5d17f2..55d2c5c826 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 4a33890f42..85e39839cb 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 01974cf6ab..ff721ea6fc 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 6ad80ecbb3..e1bdf15571 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 06c69b55ad..9cde27915f 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 6071e4582b..3cbeee7d82 100644 --- a/common/system.h +++ b/common/system.h @@ -1118,6 +1118,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, -- cgit v1.2.3 From 4874fafb153a21de5e6ab573c50de7fbf3c46729 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 16:02:20 +0600 Subject: CLOUD: Fix CloudIcon Now it loads the surface once. --- backends/networking/curl/cloudicon.cpp | 49 +++++++++++++++++++++++++++++----- backends/networking/curl/cloudicon.h | 7 ++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index e8c92592bf..ec89c12dc9 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 +#include 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 24c1fd6f71..7cecf3acde 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(); -- cgit v1.2.3 From de84701aead489de944db078e6b61c2584708c53 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 18:51:03 +0600 Subject: GUI: Separate OSD message alpha from OSD surface Now OSD is always drawn. --- .../graphics/surfacesdl/surfacesdl-graphics.cpp | 208 ++++++++++++++------- backends/graphics/surfacesdl/surfacesdl-graphics.h | 15 +- 2 files changed, 147 insertions(+), 76 deletions(-) diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index 85e39839cb..b76b2c7691 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 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 ff721ea6fc..d8f826aca0 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(); -- cgit v1.2.3 From 135f7d09a8ea790df37bff676682163732b1f6ad Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 19:14:54 +0600 Subject: CLOUD: Make CloudIcon pulsate, fade in and fade out That required ConnMan's timer stopping. Would be fixed in the next commit. --- backends/networking/curl/cloudicon.cpp | 75 ++++++++++++++++++++++---- backends/networking/curl/cloudicon.h | 7 ++- backends/networking/curl/connectionmanager.cpp | 1 + 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index ec89c12dc9..d79eef6005 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 7cecf3acde..9419cf04bf 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 57ea2ce999..af8432d260 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); -- cgit v1.2.3 From cec93e2c0319f92c31cbeea2a9e5c5763a71a7dc Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 19:36:10 +0600 Subject: 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. --- backends/networking/curl/cloudicon.cpp | 106 +++++++++++++------------ backends/networking/curl/cloudicon.h | 6 +- backends/networking/curl/connectionmanager.cpp | 14 +++- backends/networking/curl/connectionmanager.h | 3 +- cloudicon.png | Bin 0 -> 1374 bytes 5 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 cloudicon.png diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index d79eef6005..3304c71b98 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 9419cf04bf..0210899777 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 af8432d260..95b38df0c2 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(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 3a2e974bf4..925312b23b 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -79,7 +79,7 @@ class ConnectionManager : public Common::Singleton { bool _timerStarted; Common::Array _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 { 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 0000000000..5f121a855f Binary files /dev/null and b/cloudicon.png differ -- cgit v1.2.3 From 45e83d06c2a42ceb557e1765b15b9200853fcfae Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 20:18:32 +0600 Subject: CLOUD: Fix CloudIcon It's not a Request again, but still it controls ConnMan's timer. --- backends/networking/curl/cloudicon.cpp | 13 ++++--------- backends/networking/curl/cloudicon.h | 8 +++----- backends/networking/curl/connectionmanager.cpp | 18 ++++-------------- backends/networking/curl/connectionmanager.h | 3 +-- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index 3304c71b98..dfb870a2c6 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 0210899777..e981dda611 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 95b38df0c2..f38476088d 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(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 925312b23b..3a2e974bf4 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -79,7 +79,7 @@ class ConnectionManager : public Common::Singleton { bool _timerStarted; Common::Array _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 { void handle(); void interateRequests(); void processTransfers(); - void cloudIconDeleted(Request *icon); public: ConnectionManager(); -- cgit v1.2.3 From 602d17f5fd3f3d89716be9f2975f644512aeb12d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 20:37:47 +0600 Subject: CLOUD: Document CloudIcon::draw() more precisely --- backends/networking/curl/cloudicon.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h index e981dda611..9b30a8640d 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(); }; -- cgit v1.2.3 From 1bcbab7ad23b7dd47392e0d7a1fd2e56b385b367 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 4 Jun 2016 21:40:09 +0600 Subject: CLOUD: Add new cloudicon.png --- backends/networking/curl/cloudicon.cpp | 16 ++++++++++------ cloudicon.png | Bin 1374 -> 1236 bytes 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index dfb870a2c6..d6a6b517ba 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 5f121a855f..3fa0d63b9f 100644 Binary files a/cloudicon.png and b/cloudicon.png differ -- cgit v1.2.3 From e98b0b12adc9ac6440ec4940d595095115f00d23 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 00:06:36 +0600 Subject: CLOUD: Extend Storage & SavesSyncRequest Now one can learn whether SavesSyncRequest is running, its progress and which files are being synced. --- backends/cloud/savessyncrequest.cpp | 22 +++++++++++++++++++-- backends/cloud/savessyncrequest.h | 9 ++++++++- backends/cloud/storage.cpp | 39 +++++++++++++++++++++++++++++++++++-- backends/cloud/storage.h | 12 ++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index 86dc83abf8..c7572c78f9 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(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(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 SavesSyncRequest::getFilesToUpload() { + Common::Array 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 0e20159845..ad656107c9 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 getFilesToUpload(); }; } // End of namespace Cloud diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index f035c93368..95786c2cba 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 Storage::getSyncingFiles() { + Common::Array 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 1276b81827..0f518de5cd 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&> 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 getSyncingFiles(); }; } // End of namespace Cloud -- cgit v1.2.3 From 5a695040d8cbd4e840e7c21e415d2225f9318ea2 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 00:13:33 +0600 Subject: 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. --- backends/cloud/savessyncrequest.cpp | 10 ++++++---- backends/cloud/savessyncrequest.h | 4 ++-- backends/cloud/storage.cpp | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index c7572c78f9..b952681bff 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 SavesSyncRequest::getFilesToUpload() { - Common::Array result = _filesToUpload; - if (_currentUploadingFile != "") - result.push_back(_currentUploadingFile); +Common::Array SavesSyncRequest::getFilesToDownload() { + Common::Array 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 ad656107c9..6e74688493 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 getFilesToUpload(); + /** Returns an array of saves names which are not downloaded yet. */ + Common::Array getFilesToDownload(); }; } // End of namespace Cloud diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 95786c2cba..c1719d97b1 100644 --- a/backends/cloud/storage.cpp +++ b/backends/cloud/storage.cpp @@ -137,7 +137,7 @@ Common::Array Storage::getSyncingFiles() { Common::Array result; _runningRequestsMutex.lock(); if (_savesSyncRequest) - result = _savesSyncRequest->getFilesToUpload(); + result = _savesSyncRequest->getFilesToDownload(); _runningRequestsMutex.unlock(); return result; } -- cgit v1.2.3 From 91f75efa99ffe6d1c4e8e9b9b1fec368af192b7d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 10:56:43 +0600 Subject: GUI: Fix copyRectOnOSD() Now it doesn't require full redraw, but asks to redraw the area which is copied to. --- backends/graphics/surfacesdl/surfacesdl-graphics.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index b76b2c7691..b4b0d33b85 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() { -- cgit v1.2.3 From 69aed03c4f6b23302836315502cd9abbb395415e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 11:35:18 +0600 Subject: CLOUD: Add new CloudManager shortcuts CloudIcon can easily use CloudMan.isWorking() --- backends/cloud/cloudmanager.cpp | 24 ++++++++++++++++++++++++ backends/cloud/cloudmanager.h | 12 ++++++++++++ backends/networking/curl/cloudicon.cpp | 5 ++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 92e45e8811..20d75726af 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 CloudManager::getSyncingFiles() { + Storage *storage = getCurrentStorage(); + if (storage) return storage->getSyncingFiles(); + return Common::Array(); +} + } // End of namespace Common diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h index a13eeebb94..fa3a87de8b 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 getSyncingFiles(); }; /** Shortcut for accessing the connection manager. */ diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index d6a6b517ba..882b0ab34c 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(); -- cgit v1.2.3 From 8d09e1a6b3441315abf6e91b558b7ba820108d3e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 14:50:02 +0600 Subject: CLOUD: Update cloudicon.png The cloud icon is now completely unique and free to use (because I drawn it myself). --- cloudicon.png | Bin 1236 -> 1242 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/cloudicon.png b/cloudicon.png index 3fa0d63b9f..21373aea1a 100644 Binary files a/cloudicon.png and b/cloudicon.png differ -- cgit v1.2.3 From 8de2862eaa6513c43502bea86f53967015298884 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 15:22:26 +0600 Subject: CLOUD: Update syncSaves() to return SavesSyncRequest * So other classes could use that information without casting. --- backends/cloud/cloudmanager.cpp | 5 +++-- backends/cloud/cloudmanager.h | 2 +- backends/cloud/storage.cpp | 6 +++--- backends/cloud/storage.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 20d75726af..b4598d7a97 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 fa3a87de8b..a02dc90008 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 c1719d97b1..19f0845c47 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 0f518de5cd..5941fe4a32 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; -- cgit v1.2.3 From e7763700e2e2016f4573e3feb77b5fab69268683 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 15:44:05 +0600 Subject: CLOUD: Make Save/Load dialog start saves sync It also shows a "sync disabled" icon in case it has a savepath override. --- backends/cloud/savessyncrequest.cpp | 2 +- backends/cloud/savessyncrequest.h | 3 +- backends/networking/curl/cloudicon.cpp | 37 +++++++++++++++++-------- backends/networking/curl/cloudicon.h | 11 ++++++-- backends/networking/curl/connectionmanager.cpp | 5 ++++ backends/networking/curl/connectionmanager.h | 3 ++ cloudicon_disabled.png | Bin 0 -> 1331 bytes gui/saveload-dialog.cpp | 16 +++++++++++ gui/saveload-dialog.h | 2 ++ gui/saveload.cpp | 2 ++ 10 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 cloudicon_disabled.png diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index b952681bff..a99c229925 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 6e74688493..397a8cbcac 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 882b0ab34c..8971d44665 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 9b30a8640d..e007f952bd 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 f38476088d..c675d0d91b 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 3a2e974bf4..8b2153a955 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 0000000000..27c9600b0a Binary files /dev/null and b/cloudicon_disabled.png differ diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 3d4adfff2b..d9713505e6 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 31f28f6452..0e67ba89fe 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 b94d30289b..b3e669899f 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(); -- cgit v1.2.3 From e9721976aa4fc604810cf1daf6d60b206197cd9a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 20:20:22 +0600 Subject: 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! --- backends/cloud/cloudmanager.cpp | 10 +++++ backends/cloud/cloudmanager.h | 12 +++++ backends/cloud/savessyncrequest.cpp | 10 ++++- backends/cloud/storage.cpp | 14 ++++++ backends/cloud/storage.h | 12 +++++ gui/saveload-dialog.cpp | 87 ++++++++++++++++++++++++++++++++++++- gui/saveload-dialog.h | 12 +++++ 7 files changed, 154 insertions(+), 3 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index b4598d7a97..f5d60e025c 100644 --- a/backends/cloud/cloudmanager.cpp +++ b/backends/cloud/cloudmanager.cpp @@ -151,4 +151,14 @@ Common::Array CloudManager::getSyncingFiles() { return Common::Array(); } +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 a02dc90008..c7351dab2e 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 { @@ -89,6 +95,12 @@ public: /** Returns an array of saves names which are not yet synced (thus cannot be used). */ Common::Array 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 a99c229925..e066c53a92 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 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 19f0845c47..c98310d3dd 100644 --- a/backends/cloud/storage.cpp +++ b/backends/cloud/storage.cpp @@ -142,5 +142,19 @@ Common::Array 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 5941fe4a32..40ea14a3a1 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 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 d9713505e6..e5e71a73b8 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 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 0e67ba89fe..ffb8f34458 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; -- cgit v1.2.3 From 3db80154d60c98fe27018dc78c875df52c20cfe9 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 21:07:36 +0600 Subject: CLOUD: Fix SaveLoadCloudSyncProgressDialog crash It's closing itself a bit later now. --- gui/saveload-dialog.cpp | 14 +++++++++++--- gui/saveload-dialog.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index e5e71a73b8..bdae9efea4 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 ffb8f34458..e8f0c37228 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 -- cgit v1.2.3 From 0ce7be17d3fec380f726c1ff16c559344b3e24c1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 21:07:55 +0600 Subject: CLOUD: Make ProgressDialog display downloading progress --- backends/cloud/cloudmanager.cpp | 6 ++++++ backends/cloud/cloudmanager.h | 3 +++ backends/cloud/savessyncrequest.cpp | 16 +++++++++++++++- backends/cloud/savessyncrequest.h | 3 +++ backends/cloud/storage.cpp | 9 +++++++++ backends/cloud/storage.h | 3 +++ gui/saveload-dialog.cpp | 2 +- 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index f5d60e025c..4230c1453b 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 c7351dab2e..dbff0184eb 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 e066c53a92..072685773e 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 397a8cbcac..569feb4e45 100644 --- a/backends/cloud/savessyncrequest.h +++ b/backends/cloud/savessyncrequest.h @@ -69,6 +69,9 @@ public: virtual void handle(); 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(); diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index c98310d3dd..5458276e39 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 40ea14a3a1..058e831e5d 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -147,6 +147,9 @@ public: /** 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 getSyncDownloadingProgress(); + /** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */ virtual double getSyncProgress(); diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index bdae9efea4..6d343c88ed 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) -- cgit v1.2.3 From 9eb4aad7fdea54fd99ad4a9aa4ab78bf64f81794 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 5 Jun 2016 22:26:51 +0600 Subject: 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. --- backends/cloud/savessyncrequest.cpp | 4 ++-- backends/saves/default/default-saves.cpp | 20 +++++++++++++++++--- backends/saves/default/default-saves.h | 7 +++++++ common/savefile.h | 7 +++++++ gui/saveload-dialog.cpp | 23 ++++++++++++++++++----- gui/saveload-dialog.h | 7 +++++-- 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index 072685773e..d983a0fcae 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 75ba50a081..a11d687c16 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 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 166e7004ed..af30cf45e9 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 d9c5512b7e..38b21c9ddd 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 6d343c88ed..2f10182006 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 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 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 e8f0c37228..a767fb8a23 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(); -- cgit v1.2.3 From 6c5a8f34eaf5fe6af0d885cb162b5ebf193030f8 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 10:25:49 +0600 Subject: CLOUD: Add SaveLoadCloudSyncProgress enum It's common for Save/Load dialogs and SavesSyncRequest. --- backends/cloud/savessyncrequest.cpp | 10 +++------- gui/saveload-dialog.cpp | 12 +----------- gui/saveload-dialog.h | 5 +++++ 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index d983a0fcae..f07d1d000f 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 2f10182006..2646194b4b 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 a767fb8a23..435bfc7a5d 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; -- cgit v1.2.3 From f1a56eaf3666a6535ff0d1654e4e014249933452 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 12:22:22 +0600 Subject: GUI: Show "locked" saves during sync --- backends/saves/default/default-saves.cpp | 4 +-- engines/cge/detection.cpp | 5 +++ engines/metaengine.h | 10 ++++++ engines/savestate.cpp | 4 +-- engines/savestate.h | 23 +++++++++++++ gui/saveload-dialog.cpp | 56 ++++++++++++++++++++++++-------- gui/saveload-dialog.h | 19 ++++++++--- 7 files changed, 99 insertions(+), 22 deletions(-) diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index a11d687c16..a669c2ce81 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 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 82d27f8d54..0c79be51d9 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 e7bfebab71..913f61d280 100644 --- a/engines/metaengine.h +++ b/engines/metaengine.h @@ -115,6 +115,16 @@ public: return SaveStateList(); } + /** + * 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 diff --git a/engines/savestate.cpp b/engines/savestate.cpp index 186d7bc5f2..7366aa6a61 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 21ade602fa..3244d61fdb 100644 --- a/engines/savestate.h +++ b/engines/savestate.h @@ -89,6 +89,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 @@ -179,6 +197,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. */ diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 2646194b4b..5360bfe1ac 100644 --- a/gui/saveload-dialog.cpp +++ b/gui/saveload-dialog.cpp @@ -251,6 +251,32 @@ void SaveLoadChooserDialog::reflowLayout() { void SaveLoadChooserDialog::updateSaveList() { Common::Array 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 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 435bfc7a5d..a81e03c881 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; -- cgit v1.2.3 From 67789c3c1621e4b59167bd9f0c0d38eec122dd90 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 13:37:38 +0600 Subject: GUI: Update SaveLoadCloudSyncProgressDialog So now it's centered, includes a progress bar and two labels instead of one. Works fine in 320x200. --- gui/saveload-dialog.cpp | 48 ++++++++++++++++++++++++++++++++++++++---------- gui/saveload-dialog.h | 3 ++- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 5360bfe1ac..af4db178a9 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 a81e03c881..d308535c87 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(); -- cgit v1.2.3 From b614869de80759991c50827f0f1f35c13ef87f0d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 13:37:51 +0600 Subject: CLOUD: Minor fixes --- backends/cloud/dropbox/dropboxuploadrequest.cpp | 5 +++++ backends/cloud/savessyncrequest.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp index bf8e43d7cb..5765892a70 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 f07d1d000f..3c41c005e4 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 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; -- cgit v1.2.3 From f5cb5be393d482130d86794a84358e9b37f37af7 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 15:08:44 +0600 Subject: GUI: Add SaveLoadCloudSyncProgress in ScummModern theme ScummVM would probably crash when using a theme without SaveLoadCloudSyncProgress dialog described. --- gui/gui-manager.cpp | 2 +- gui/saveload-dialog.cpp | 43 +++------------------- gui/themes/scummmodern/scummmodern_layout.stx | 32 ++++++++++++++++ .../scummmodern/scummmodern_layout_lowres.stx | 32 ++++++++++++++++ 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 9acd9434ff..9d68d76c9c 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 af4db178a9..e9248bd082 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 026fa7bc64..f14e447535 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -1056,6 +1056,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 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 d308535c87..3f2c5dfdc3 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(); -- cgit v1.2.3 From bb207ae513ef02bfaf8e76374af40419a20742fe Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 20:04:13 +0600 Subject: 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). --- backends/cloud/cloudmanager.cpp | 9 +- .../googledrivelistdirectorybyidrequest.cpp | 144 +++++++++++++++++++++ .../googledrivelistdirectorybyidrequest.h | 61 +++++++++ .../googledrive/googledriveresolveidrequest.cpp | 128 ++++++++++++++++++ .../googledrive/googledriveresolveidrequest.h | 61 +++++++++ backends/cloud/googledrive/googledrivestorage.cpp | 68 +++++++++- backends/cloud/googledrive/googledrivestorage.h | 16 ++- backends/cloud/storagefile.cpp | 8 ++ backends/cloud/storagefile.h | 3 + backends/module.mk | 2 + 10 files changed, 495 insertions(+), 5 deletions(-) create mode 100644 backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp create mode 100644 backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h create mode 100644 backends/cloud/googledrive/googledriveresolveidrequest.cpp create mode 100644 backends/cloud/googledrive/googledriveresolveidrequest.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 4230c1453b..03cc8f264b 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(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 0000000000..0915b623f3 --- /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(this, &GoogleDriveListDirectoryByIdRequest::responseCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 &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 0000000000..569984520a --- /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 _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 &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 0000000000..bde17b4533 --- /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(this, &GoogleDriveResolveIdRequest::listedDirectoryCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 &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 0000000000..cd6f244a94 --- /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 eef7f1f28d..b0c0d47ecf 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 +#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(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(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 &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(this, &GoogleDriveListDirectoryByIdRequest::responseCallback); + //Networking::ErrorCallback failureCallback = new Common::Callback(this, &GoogleDriveListDirectoryByIdRequest::errorCallback); + Networking::JsonCallback innerCallback = new Common::CallbackBridge(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(this, &GoogleDriveStorage::printInfo); Networking::JsonCallback innerCallback = new Common::CallbackBridge(this, &GoogleDriveStorage::infoInnerCallback, callback); diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h index 8a82a54533..274bc78401 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 - the list of files. */ virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false); + /** Returns Array - 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 b37b5e0d53..452cce3cf3 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 704a03867f..7503653d6b 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 is used to storage (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 f1ba8b8b1b..14755b21eb 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 \ -- cgit v1.2.3 From b4b6ee0186750d6f2e8143313fc059c20512c306 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 6 Jun 2016 21:24:53 +0600 Subject: CLOUD: Add GoogleDriveCreateDirectory Now we can create directories in Google Drive by path, not parent id + directory name! --- backends/cloud/cloudmanager.cpp | 33 +++++- .../googledrivecreatedirectoryrequest.cpp | 114 +++++++++++++++++++++ .../googledrivecreatedirectoryrequest.h | 62 +++++++++++ .../googledrive/googledriveresolveidrequest.cpp | 2 +- backends/cloud/googledrive/googledrivestorage.cpp | 37 ++++--- backends/module.mk | 1 + 6 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp create mode 100644 backends/cloud/googledrive/googledrivecreatedirectoryrequest.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 03cc8f264b..0be52cd886 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(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 0000000000..213f030c26 --- /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(this, &GoogleDriveCreateDirectoryRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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(this, &GoogleDriveCreateDirectoryRequest::createdDirectoryCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 0000000000..0ac178e8a6 --- /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 bde17b4533..5dcb5c6e11 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 b0c0d47ecf..1750dc9646 100644 --- a/backends/cloud/googledrive/googledrivestorage.cpp +++ b/backends/cloud/googledrive/googledrivestorage.cpp @@ -33,6 +33,7 @@ #include #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(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(this, &GoogleDriveListDirectoryByIdRequest::responseCallback); - //Networking::ErrorCallback failureCallback = new Common::Callback(this, &GoogleDriveListDirectoryByIdRequest::errorCallback); + + Common::String url = "https://www.googleapis.com/drive/v3/files"; Networking::JsonCallback innerCallback = new Common::CallbackBridge(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 14755b21eb..9387be5c2b 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 \ -- cgit v1.2.3 From d1d71afb0724c372143d2c303c70291ba43d2d68 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 13:07:46 +0600 Subject: 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. --- backends/cloud/cloudmanager.cpp | 6 +- .../googledrivelistdirectoryrequest.cpp | 117 +++++++++++++++++++++ .../googledrive/googledrivelistdirectoryrequest.h | 65 ++++++++++++ .../googledrive/googledriveresolveidrequest.cpp | 2 +- backends/cloud/googledrive/googledrivestorage.cpp | 6 +- backends/module.mk | 1 + 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp create mode 100644 backends/cloud/googledrive/googledrivelistdirectoryrequest.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 0be52cd886..64fa19ceb9 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 0000000000..803682b684 --- /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(this, &GoogleDriveListDirectoryRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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(this, &GoogleDriveListDirectoryRequest::listedDirectoryCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 &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 0000000000..3eee83fed1 --- /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 _files; + Common::Array _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 &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 5dcb5c6e11..0ef0cd982f 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 1750dc9646..3c85b1f074 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(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 9387be5c2b..c4a8ae40b5 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 \ -- cgit v1.2.3 From 505d3764cb9c873b1dbefcb0d7b2cc7b587c6a42 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 13:49:26 +0600 Subject: CLOUD: Fix GoogleDriveStorage to work with root folder Now it needs another scope and uses "root" instead of "appDataFolder". --- backends/cloud/cloudmanager.cpp | 33 ---------------------- .../googledrivecreatedirectoryrequest.cpp | 2 -- .../googledrivecreatedirectoryrequest.h | 2 -- .../googledrivelistdirectorybyidrequest.cpp | 20 ++++++++++--- .../googledrive/googledriveresolveidrequest.cpp | 2 +- backends/cloud/googledrive/googledrivestorage.cpp | 4 +-- 6 files changed, 19 insertions(+), 44 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 64fa19ceb9..7613b2cbbf 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(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 213f030c26..1554fef200 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 0ac178e8a6..6c5ccddf6c 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 0915b623f3..f7aa7a1c0c 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 0ef0cd982f..a9adb82969 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 3c85b1f074..dce35b5904 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; } -- cgit v1.2.3 From 74a3eba8d67ec5909b950e6b2486ba5e102fb8d0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 16:24:19 +0600 Subject: 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. --- backends/networking/curl/connectionmanager.cpp | 27 +++++++++++++++++++++----- backends/networking/curl/connectionmanager.h | 5 +++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index c675d0d91b..edc7e81229 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::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::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 8b2153a955..66994f0a79 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -77,8 +77,8 @@ class ConnectionManager : public Common::Singleton { CURLM *_multi; bool _timerStarted; - Common::Array _requests; - Common::Mutex _handleMutex; + Common::Array _requests, _addedRequests; + Common::Mutex _handleMutex, _addedRequestsMutex; CloudIcon _icon; uint32 _frame; @@ -87,6 +87,7 @@ class ConnectionManager : public Common::Singleton { void handle(); void interateRequests(); void processTransfers(); + bool hasAddedRequests(); public: ConnectionManager(); -- cgit v1.2.3 From c968f0143c5e36cc9fc429832622eb180732caf8 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 16:25:05 +0600 Subject: CLOUD: Make GoogleDriveResolveIdRequest case-insensitive --- backends/cloud/googledrive/googledriveresolveidrequest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp index a9adb82969..9cd13a78a6 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 &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)); } } -- cgit v1.2.3 From bf71ba9a1c98b39647edb248e913322ee38a0af5 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 16:27:04 +0600 Subject: 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. --- .../googledrivecreatedirectoryrequest.cpp | 30 ++++++++++++++++++++-- .../googledrivecreatedirectoryrequest.h | 3 +++ backends/cloud/googledrive/googledrivestorage.cpp | 6 +---- backends/cloud/savessyncrequest.cpp | 9 +++++++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp b/backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp index 1554fef200..54eff3d6ad 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(this, &GoogleDriveCreateDirectoryRequest::createdBaseDirectoryCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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(this, &GoogleDriveCreateDirectoryRequest::idResolvedCallback); Networking::ErrorCallback innerErrorCallback = new Common::Callback(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 6c5ccddf6c..ede84277de 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 dce35b5904..d7dbd529e5 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 3c41c005e4..1d4ec4c2ee 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) { -- cgit v1.2.3 From ab1d160ec8f99e472667b83aa4bdd7697b702f3a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 19:33:00 +0600 Subject: 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. --- engines/access/detection.cpp | 3 +++ engines/adl/detection.cpp | 3 +++ engines/agi/detection.cpp | 3 +++ engines/agos/detection.cpp | 3 +++ engines/avalanche/detection.cpp | 3 +++ engines/bbvs/detection.cpp | 3 +++ engines/cge/detection.cpp | 6 ++---- engines/cge2/detection.cpp | 3 +++ engines/cine/detection.cpp | 3 +++ engines/cruise/detection.cpp | 3 +++ engines/draci/detection.cpp | 3 +++ engines/drascula/detection.cpp | 3 +++ engines/dreamweb/detection.cpp | 3 +++ engines/fullpipe/detection.cpp | 3 +++ engines/gnap/detection.cpp | 3 +++ engines/groovie/detection.cpp | 3 +++ engines/hopkins/detection.cpp | 3 +++ engines/hugo/detection.cpp | 3 +++ engines/kyra/detection.cpp | 3 +++ engines/lab/detection.cpp | 3 +++ engines/lure/detection.cpp | 3 +++ engines/mads/detection.cpp | 3 +++ engines/metaengine.h | 16 ++++++++++------ engines/mohawk/detection.cpp | 3 +++ engines/mortevielle/detection.cpp | 3 +++ engines/neverhood/detection.cpp | 3 +++ engines/parallaction/detection.cpp | 3 +++ engines/pegasus/detection.cpp | 3 +++ engines/prince/detection.h | 1 + engines/prince/saveload.cpp | 2 ++ engines/queen/detection.cpp | 3 +++ engines/saga/detection.cpp | 3 +++ engines/sci/detection.cpp | 3 +++ engines/scumm/detection.cpp | 3 +++ engines/sherlock/detection.cpp | 4 ++++ engines/sky/detection.cpp | 5 ++++- engines/sword1/detection.cpp | 3 +++ engines/sword2/sword2.cpp | 3 +++ engines/sword25/detection.cpp | 3 +++ engines/teenagent/detection.cpp | 2 ++ engines/tinsel/detection.cpp | 3 +++ engines/toltecs/detection.cpp | 3 +++ engines/tony/detection.cpp | 3 +++ engines/toon/detection.cpp | 3 +++ engines/touche/detection.cpp | 3 +++ engines/tsage/detection.cpp | 2 ++ engines/tucker/detection.cpp | 2 ++ engines/voyeur/detection.cpp | 3 +++ engines/wage/detection.cpp | 3 +++ engines/wintermute/detection.cpp | 2 ++ engines/zvision/detection.cpp | 3 +++ gui/saveload-dialog.cpp | 34 ++++++++++++++++++---------------- 52 files changed, 172 insertions(+), 27 deletions(-) diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp index 368753f117..435f0d1118 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 4bdb722af3..7031a58ec4 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 9f66d78d80..dc0dbbdddf 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 2c89522089..dc96eb6ef6 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 e35c5d2cac..392d0a027f 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 7c0045ee73..9aca719648 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 0c79be51d9..eb88b6cd79 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 2b84d167c7..d980f82b0d 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 ec01e8734d..2d10b81473 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 6f5d236173..9e3ebb5dd5 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 65427bd8cd..8a67981696 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 ffec393a0a..863ea98786 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 8e24c44702..abe1198233 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 6f92f19f24..a183be8a83 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 7e4ab56d1f..523a8b6d1c 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 b12e264a57..22ced10b8a 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 cfdbf8030c..84af4fcdf3 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 4e4746c002..ed67eae416 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 989a45b420..1dcfd08435 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 30890b5acf..2b2e57df22 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 690a358bc3..808cea4422 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 4736503a38..d5354c4883 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 913f61d280..6afb122580 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 + * ".###" 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 ".###" 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 246d3ec3c1..d3c44a8944 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 6791707dd5..2f5a790486 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 0f409a6435..903c4377e0 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 4c52990874..989fc9dfc8 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 161a133c8b..54cb4ca525 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 3076253cf5..39cfdd96e7 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 d3360badd1..2855bdc4bd 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 aed8b7dcb1..3f8b97ed6a 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 0677e84d67..7f6e0a2485 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 ad2b0f31a5..08794a0872 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 4c9d1221aa..67344e8008 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 5a94b3485f..f54c6db952 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 4b91f50a61..802687b461 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 0edf856125..d9cc3cda7f 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 44371bf6cf..3213a26e4c 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 c5f55b5a26..1c4544c76a 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 caa7bdbec9..a8d32e89d4 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 c44f1f4ef3..7d8b54e5db 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 7c707895e6..8da9106931 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 ec0b3e186b..5dda4c7caf 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 5d2e0a9bca..eac91a5595 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 dcb58ffae6..e2737d4f2c 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 584ad87742..716ac4af53 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 2447e15d6b..227924cd28 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 7b9fa6722e..76668eb370 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 a27bfd7fde..1069d740f6 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 4e8eab505f..f225cd3930 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 cc967070d9..05dfb8b8d0 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 1ba1793ef4..0dde1a4060 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 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 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 -- cgit v1.2.3 From 62a640ff440919ae762072a6e6acea415f0957dd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 19:52:36 +0600 Subject: 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. --- gui/saveload-dialog.cpp | 8 +++++--- gui/saveload-dialog.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 0dde1a4060..3ac6074bf8 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 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 3f2c5dfdc3..890169c99a 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); -- cgit v1.2.3 From f6e69b62765482d290b7673e97fb7cf6a03fc943 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 7 Jun 2016 20:40:19 +0600 Subject: 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. --- backends/saves/default/default-saves.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index a669c2ce81..3fcb3cb3b3 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 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; } -- cgit v1.2.3 From 7e6a89c141d621b5caf8c66a58ecbcf60b2d52c1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 12:34:13 +0600 Subject: 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). --- .../googledrive/googledrivelistdirectorybyidrequest.cpp | 6 ++++-- .../googledrive/googledrivelistdirectoryrequest.cpp | 14 ++++++++++---- .../cloud/googledrive/googledrivelistdirectoryrequest.h | 4 ++-- .../cloud/googledrive/googledriveresolveidrequest.cpp | 2 +- backends/cloud/googledrive/googledrivestorage.cpp | 1 + backends/cloud/storagefile.cpp | 7 +++++-- backends/cloud/storagefile.h | 17 +++++++++++++---- 7 files changed, 36 insertions(+), 15 deletions(-) diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp index f7aa7a1c0c..f9af363254 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 803682b684..8811ffc938 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(this, &GoogleDriveListDirectoryRequest::listedDirectoryCallback); Networking::ErrorCallback failureCallback = new Common::Callback(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 3eee83fed1..bea195f70a 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 _files; - Common::Array _directoriesQueue; - Common::String _currentDirectory; + Common::Array _directoriesQueue; + StorageFile _currentDirectory; Request *_workingRequest; bool _ignoreCallback; diff --git a/backends/cloud/googledrive/googledriveresolveidrequest.cpp b/backends/cloud/googledrive/googledriveresolveidrequest.cpp index 9cd13a78a6..e3ab97ea6c 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 d7dbd529e5..6b5c437fd7 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 452cce3cf3..4bee92edce 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 7503653d6b..1324cafc93 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 is used to storage (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 -- cgit v1.2.3 From 60add0df1b63dbd31b88edcabf0769eb675efab0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 12:45:58 +0600 Subject: CLOUD: Update Requests to use StorageFile::id() Only two places to update, as others still require id resolving. --- backends/cloud/folderdownloadrequest.cpp | 4 ++-- backends/cloud/savessyncrequest.cpp | 2 +- backends/cloud/storage.cpp | 5 +++++ backends/cloud/storage.h | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp index 19f6c6c9b7..905b0c79f4 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(this, &FolderDownloadRequest::fileDownloadedCallback), new Common::Callback(this, &FolderDownloadRequest::fileDownloadedErrorCallback) ); diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index 1d4ec4c2ee..cdf1dbac07 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(this, &SavesSyncRequest::fileDownloadedCallback), new Common::Callback(this, &SavesSyncRequest::fileDownloadedErrorCallback) ); diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 5458276e39..f0c1319846 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 058e831e5d..3cf8fb55ff 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 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); -- cgit v1.2.3 From f0d61084daf7292d157e451c7bfc5485757eac43 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 13:02:49 +0600 Subject: CLOUD: Update downloading in Storages Id should be used everywhere. --- backends/cloud/downloadrequest.cpp | 8 ++++---- backends/cloud/downloadrequest.h | 4 ++-- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- backends/cloud/dropbox/dropboxstorage.h | 2 +- backends/cloud/googledrive/googledrivestorage.cpp | 12 ++++++++++++ backends/cloud/googledrive/googledrivestorage.h | 4 ++++ backends/cloud/onedrive/onedrivestorage.cpp | 2 +- backends/cloud/onedrive/onedrivestorage.h | 2 +- backends/cloud/storage.cpp | 17 +++++++++++------ backends/cloud/storage.h | 3 ++- 10 files changed, 39 insertions(+), 17 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 7dde74f88d..307ea00aa2 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(this, &DownloadRequest::streamCallback), new Common::Callback(this, &DownloadRequest::streamErrorCallback) ); diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h index 9e3421d777..def69d47de 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 861a58db4b..038a1683bf 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 c186d1e5d6..9ac0ffb166 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 6b5c437fd7..c6e423e00d 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 274bc78401..a456030369 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 ca8a2346ad..c494f38a6c 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(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 a09d68b6fd..8afdac8b0f 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 f0c1319846..08ab9e9c90 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 3cf8fb55ff..4a5e1236b7 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); -- cgit v1.2.3 From e273e3d6e8dcca6f7e70d3e7c4e6dfc836832378 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 14:17:35 +0600 Subject: 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. --- .../googledrive/googledrivedownloadrequest.cpp | 91 ++++++++++++++++++++++ .../cloud/googledrive/googledrivedownloadrequest.h | 59 ++++++++++++++ .../googledrive/googledriveresolveidrequest.cpp | 12 +-- backends/cloud/googledrive/googledrivestorage.cpp | 53 ++++--------- backends/cloud/googledrive/googledrivestorage.h | 2 - .../googledrive/googledrivestreamfilerequest.cpp | 91 ++++++++++++++++++++++ .../googledrive/googledrivestreamfilerequest.h | 59 ++++++++++++++ backends/module.mk | 2 + 8 files changed, 324 insertions(+), 45 deletions(-) create mode 100644 backends/cloud/googledrive/googledrivedownloadrequest.cpp create mode 100644 backends/cloud/googledrive/googledrivedownloadrequest.h create mode 100644 backends/cloud/googledrive/googledrivestreamfilerequest.cpp create mode 100644 backends/cloud/googledrive/googledrivestreamfilerequest.h diff --git a/backends/cloud/googledrive/googledrivedownloadrequest.cpp b/backends/cloud/googledrive/googledrivedownloadrequest.cpp new file mode 100644 index 0000000000..c885352fae --- /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(this, &GoogleDriveDownloadRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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(this, &GoogleDriveDownloadRequest::downloadCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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 0000000000..89bd313467 --- /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 e3ab97ea6c..6d8da8383d 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 &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 c6e423e00d..d9db4f7dd8 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(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(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 a456030369..2af1edea3c 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 0000000000..424e52c6cb --- /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(this, &GoogleDriveStreamFileRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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(this, &GoogleDriveStreamFileRequest::streamFileCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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 0000000000..aa5596154e --- /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 c4a8ae40b5..817c82970c 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 \ -- cgit v1.2.3 From b29497effe60eaec891ca1b5ce78f8dae69fd599 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 16:46:18 +0600 Subject: CLOUD: Add GoogleDriveUploadRequest Includes NetworkReadStream PATCH method and Headers remembering feature. --- backends/cloud/googledrive/googledrivestorage.cpp | 4 +- .../cloud/googledrive/googledriveuploadrequest.cpp | 341 +++++++++++++++++++++ .../cloud/googledrive/googledriveuploadrequest.h | 70 +++++ backends/module.mk | 1 + backends/networking/curl/curlrequest.cpp | 10 +- backends/networking/curl/curlrequest.h | 4 + backends/networking/curl/networkreadstream.cpp | 35 ++- backends/networking/curl/networkreadstream.h | 27 +- 8 files changed, 481 insertions(+), 11 deletions(-) create mode 100644 backends/cloud/googledrive/googledriveuploadrequest.cpp create mode 100644 backends/cloud/googledrive/googledriveuploadrequest.h diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp index d9db4f7dd8..bb762a4d90 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 0000000000..7ab9f2e3c0 --- /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(this, &GoogleDriveUploadRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(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(this, &GoogleDriveUploadRequest::startUploadCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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(this, &GoogleDriveUploadRequest::partUploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(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 0000000000..e417403542 --- /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 817c82970c..95334fef65 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 a3f997a4ff..6ef0e346af 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 5737078b2d..5c06b58107 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 283e5e667f..ccfb3d5a29 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 d48d01b198..991fdb346d 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(); /** @@ -86,6 +87,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 @@ -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 -- cgit v1.2.3 From 1479d126520a9f3472797c1bb98b534f0b2a6b97 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 16:54:29 +0600 Subject: CLOUD: Minor GoogleDriveUploadRequest fix Just checked that out on cloud sync: Google Drive is officially supported! --- backends/cloud/googledrive/googledriveuploadrequest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp index 7ab9f2e3c0..f5636ef8ba 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(); -- cgit v1.2.3 From 870e96eb9ca6e69bea5f47a215d171fd58ab1265 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 18:51:00 +0600 Subject: CLOUD: Update CloudManager and Storage * Storage::name(); * CloudManager::getStorageName(); * CloudManager::getStorageIndex(); * CloudManager::listStorages(); * CloudManager::switchStorage(). --- backends/cloud/cloudmanager.cpp | 38 ++++++++++++++++++++++- backends/cloud/cloudmanager.h | 31 +++++++++++++++++- backends/cloud/dropbox/dropboxstorage.cpp | 4 +++ backends/cloud/dropbox/dropboxstorage.h | 6 ++++ backends/cloud/googledrive/googledrivestorage.cpp | 4 +++ backends/cloud/googledrive/googledrivestorage.h | 6 ++++ backends/cloud/onedrive/onedrivestorage.cpp | 4 +++ backends/cloud/onedrive/onedrivestorage.h | 6 ++++ backends/cloud/storage.h | 6 ++++ 9 files changed, 103 insertions(+), 2 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 7613b2cbbf..a9baac5d02 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 dbff0184eb..7e8cfd68a3 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 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 038a1683bf..d11d97da39 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 &files = response.value; diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index 9ac0ffb166..60a8075201 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@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 bb762a4d90..30ca1be7e6 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 2af1edea3c..489260db09 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@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 c494f38a6c..c391065396 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 8afdac8b0f..3932d44aae 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@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 4a5e1236b7..11d8f6beb9 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -102,6 +102,12 @@ public: */ virtual void saveConfig(Common::String keyPrefix) = 0; + /** + * Return unique storage name. + * @returns some unique storage name (for example, "Dropbox (user@example.com)") + */ + virtual Common::String name() const = 0; + /** * Public Cloud API comes down there. * -- cgit v1.2.3 From 90ae7b7337ece337cfd6ed2eabcef4f42a3abe7a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 8 Jun 2016 18:56:17 +0600 Subject: GUI: Add Options dialog Cloud tab With StorageBrowser to select a Storage. It actually uses CloudMan to switch active Storage. --- gui/module.mk | 1 + gui/options.cpp | 54 +++++++++++- gui/options.h | 7 ++ gui/storagebrowser.cpp | 96 ++++++++++++++++++++++ gui/storagebrowser.h | 51 ++++++++++++ gui/themes/scummmodern/scummmodern_layout.stx | 13 +++ .../scummmodern/scummmodern_layout_lowres.stx | 13 +++ 7 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 gui/storagebrowser.cpp create mode 100644 gui/storagebrowser.h diff --git a/gui/module.mk b/gui/module.mk index 2ffea5a2b5..ece360b3cc 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 e410971818..df457a89e8 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(""), _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 294b41794b..feb77859e8 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 0000000000..30a0e9e23d --- /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 0000000000..b18c30f8c8 --- /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 f14e447535..693be090e8 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -538,6 +538,19 @@ + + + + + + + + + + + + + + + + + + _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 7e8cfd68a3..9956a92824 100644 --- a/backends/cloud/cloudmanager.h +++ b/backends/cloud/cloudmanager.h @@ -103,6 +103,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. */ diff --git a/gui/options.cpp b/gui/options.cpp index df457a89e8..b0a7968b2d 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", ""); + + 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", ""); + + 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 = _(""); + _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 = _(""); + else sync = _(""); + } + _storageLastSync->setLabel(sync); + } +} +#endif + } // End of namespace GUI diff --git a/gui/options.h b/gui/options.h index feb77859e8..228a415b92 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 693be090e8..093eeba522 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -548,6 +548,30 @@ height = 'Globals.Line.Height' /> + + + + + + + + + + + + -- cgit v1.2.3 From af9930482e17f4b55e46707fc017090e4c24a38e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 9 Jun 2016 13:49:52 +0600 Subject: 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. --- backends/cloud/cloudmanager.cpp | 155 ++++++++++++---------- backends/cloud/cloudmanager.h | 42 ++++-- backends/cloud/dropbox/dropboxstorage.cpp | 11 +- backends/cloud/googledrive/googledrivestorage.cpp | 7 +- backends/cloud/onedrive/onedrivestorage.cpp | 6 +- gui/options.cpp | 63 +++++---- gui/options.h | 4 + 7 files changed, 165 insertions(+), 123 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index cb7e6f7cb3..5c69e30520 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 ""; + 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 9956a92824..5e26ece088 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 { - Common::Array _storages; - uint _currentStorageIndex; + struct StorageConfig { + Common::String name, username; + uint64 usedBytes; + Common::String lastSyncDate; + }; + + Common::Array _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 @@ -75,13 +96,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. * diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index d11d97da39..af73138a4f 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 30ca1be7e6..18ddca5e94 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 c391065396..d73bcdbe34 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 b0a7968b2d..2febf84f9f 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", ""); - 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", ""); 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 = _(""); _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 = _(""); + if (_selectedStorageIndex == CloudMan.getStorageIndex() && CloudMan.isSyncing()) sync = _(""); else sync = _(""); } _storageLastSync->setLabel(sync); + _storageLastSync->setVisible(shown); } } #endif diff --git a/gui/options.h b/gui/options.h index 228a415b92..16ea424b6f 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(); -- cgit v1.2.3 From 9b15ec9989fc67a0537b1e70732d3dba48797165 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 9 Jun 2016 14:23:14 +0600 Subject: CLOUD: Update CloudManager It now has methods to update Storage's information. --- backends/cloud/cloudmanager.cpp | 31 ++++++++++++++++++++++- backends/cloud/cloudmanager.h | 27 ++++++++++++++++++++ backends/cloud/dropbox/dropboxstorage.cpp | 5 ++++ backends/cloud/googledrive/googledrivestorage.cpp | 3 +++ backends/cloud/onedrive/onedrivestorage.cpp | 2 ++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 5c69e30520..a1b1ed1525 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 5e26ece088..7ce7e925da 100644 --- a/backends/cloud/cloudmanager.h +++ b/backends/cloud/cloudmanager.h @@ -143,6 +143,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. */ diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index af73138a4f..e59e19eef9 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 18ddca5e94..143b7ac52c 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 d73bcdbe34..82681756c4 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; } -- cgit v1.2.3 From e1e48968b4e5b7d55594adf038657bf6a8d7bc43 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 9 Jun 2016 15:55:53 +0600 Subject: GUI: Replace Cloud tab's StorageBrowser with PopUp --- gui/module.mk | 1 - gui/options.cpp | 27 +++--- gui/options.h | 3 +- gui/storagebrowser.cpp | 96 ---------------------- gui/storagebrowser.h | 51 ------------ gui/themes/scummmodern/scummmodern_layout.stx | 8 +- .../scummmodern/scummmodern_layout_lowres.stx | 40 ++++++++- 7 files changed, 54 insertions(+), 172 deletions(-) delete mode 100644 gui/storagebrowser.cpp delete mode 100644 gui/storagebrowser.h diff --git a/gui/module.mk b/gui/module.mk index ece360b3cc..2ffea5a2b5 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 2febf84f9f..ec17a7ac09 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", ""); - _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", ""); 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 16ea424b6f..89670fdd64 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 30a0e9e23d..0000000000 --- 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 b18c30f8c8..0000000000 --- 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 093eeba522..a4b4ccda8b 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -541,11 +541,11 @@ - - diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 105c121a2b..4a4479c564 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -529,11 +529,43 @@ - - + + + + + + + + + + + + + - -- cgit v1.2.3 From beb168a3a5bac602a9bf1455e7fe93dda0b13a1c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 9 Jun 2016 17:00:05 +0600 Subject: GUI: Add Cloud tab StorageWizardDialog This is a dialog which guides user through Storage connection procedure. --- gui/module.mk | 1 + gui/options.cpp | 14 ++++- gui/options.h | 1 + gui/storagewizarddialog.cpp | 73 ++++++++++++++++++++++ gui/storagewizarddialog.h | 43 +++++++++++++ gui/themes/scummmodern/scummmodern_layout.stx | 43 +++++++++++++ .../scummmodern/scummmodern_layout_lowres.stx | 38 +++++++++++ 7 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 gui/storagewizarddialog.cpp create mode 100644 gui/storagewizarddialog.h diff --git a/gui/module.mk b/gui/module.mk index 2ffea5a2b5..ef005311cd 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 ec17a7ac09..ac3cc0cc3a 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", ""); + _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 89670fdd64..4addf717b8 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 0000000000..996365da03 --- /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 0000000000..ec5329b8af --- /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 a4b4ccda8b..8406fc9b28 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -572,6 +572,49 @@ height = 'Globals.Line.Height' /> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 4a4479c564..13d854c9ac 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' /> + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3 From e6242b0be8fc9f9abc4daf87f80675cca46df4d9 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 9 Jun 2016 18:49:17 +0600 Subject: GUI: Add Refresh button in Options Cloud tab Commit changes CloudManager and Storages so they would automatically refresh the fields when the could. --- backends/cloud/cloudmanager.cpp | 29 +++++++++++++ backends/cloud/cloudmanager.h | 9 ++++ backends/cloud/dropbox/dropboxstorage.cpp | 27 ++++++------ backends/cloud/googledrive/googledrivestorage.cpp | 49 +++++++++++----------- backends/cloud/onedrive/onedrivestorage.cpp | 41 +++++++++--------- backends/cloud/savessyncrequest.cpp | 5 +++ backends/cloud/storage.h | 10 ++++- gui/options.cpp | 42 ++++++++++++++++++- gui/options.h | 9 ++++ gui/themes/scummmodern/scummmodern_layout.stx | 3 ++ .../scummmodern/scummmodern_layout_lowres.stx | 3 ++ 11 files changed, 166 insertions(+), 61 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index a1b1ed1525..9456dd84a4 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 7ce7e925da..fd130c5ee8 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 e59e19eef9..faff10f1d9 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 143b7ac52c..3196cbe041 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@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@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 82681756c4..178d43c8be 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 cdf1dbac07..e1739f9693 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 &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::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 11d8f6beb9..a76f2169bc 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 . + * Call the 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 ac3cc0cc3a..2ab6b1eb6b 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", ""); _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(this, &GlobalOptionsDialog::storageInfoCallback), nullptr); + Common::String dir = CloudMan.savesDirectoryPath(); + if (dir.lastChar() == '/') dir.deleteLastChar(); + CloudMan.listDirectory(dir, new Common::Callback(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 &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 4addf717b8..1454ddbfc8 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 8406fc9b28..824c6fa790 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -576,6 +576,9 @@ + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 13d854c9ac..3d2c3b49a7 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -573,6 +573,9 @@ + -- cgit v1.2.3 From c99b24c16d1111a701d915832f24ac457aef697d Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 10 Jun 2016 14:06:06 +0600 Subject: COMMON: Add String::asUint64() Instead of all these atoull() I've added everywhere. --- backends/cloud/cloudmanager.cpp | 13 +------------ .../googledrive/googledrivelistdirectorybyidrequest.cpp | 13 +------------ backends/cloud/googledrive/googledrivestorage.cpp | 15 ++------------- backends/cloud/googledrive/googledriveuploadrequest.cpp | 15 ++------------- common/str.cpp | 9 +++++++++ common/str.h | 3 +++ 6 files changed, 18 insertions(+), 50 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 9456dd84a4..fed35a9f38 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 ""; @@ -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 f9af363254..86494e8c13 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 3196cbe041..91d8c819e7 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 f5636ef8ba..8dd8ffcc9a 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 3ff49a90c6..326b4a80d1 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 9ada8aaaa0..89b832d57e 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 -- cgit v1.2.3 From 6a93e8dd09ae2eeab616d14189a58633fd928c07 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 10 Jun 2016 14:28:04 +0600 Subject: CLOUD: Add ConnMan::urlEncode() Tried to use it everywhere I should've use it. --- backends/cloud/googledrive/googledrivestorage.cpp | 2 +- backends/cloud/googledrive/googledriveuploadrequest.cpp | 2 +- backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp | 2 +- backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp | 5 +++-- backends/cloud/onedrive/onedrivestorage.cpp | 2 +- backends/cloud/onedrive/onedriveuploadrequest.cpp | 4 ++-- backends/networking/curl/connectionmanager.cpp | 11 +++++++++++ backends/networking/curl/connectionmanager.h | 3 +++ 8 files changed, 23 insertions(+), 8 deletions(-) diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp index 91d8c819e7..5eab071599 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 8dd8ffcc9a..ce1fae2579 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(this, &GoogleDriveUploadRequest::startUploadCallback); Networking::ErrorCallback failureCallback = new Common::Callback(this, &GoogleDriveUploadRequest::startUploadErrorCallback); diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp index c48ae1dfbe..fe1128bd5b 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(this, &OneDriveCreateDirectoryRequest::responseCallback); Networking::ErrorCallback errorCallback = new Common::Callback(this, &OneDriveCreateDirectoryRequest::errorCallback); diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp index e362600389..040ef153db 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 178d43c8be..d1971f904a 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(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 752907f333..bc54a811a9 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(this, &OneDriveUploadRequest::partUploadedCallback); Networking::ErrorCallback failureCallback = new Common::Callback(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 edc7e81229..5a1e3e97c1 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 66994f0a79..602f8930cd 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. */ -- cgit v1.2.3 From 3e6503743c2f5d90c64bf37e943338c33fc58d2b Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 10 Jun 2016 15:01:56 +0600 Subject: CLOUD: Add Request::date() Used in SavesSyncRequest to update Storage's last sync date. --- .../cloud/dropbox/dropboxcreatedirectoryrequest.cpp | 4 ++++ .../cloud/dropbox/dropboxcreatedirectoryrequest.h | 2 ++ .../cloud/dropbox/dropboxlistdirectoryrequest.cpp | 5 +++++ backends/cloud/dropbox/dropboxlistdirectoryrequest.h | 2 ++ .../googledrive/googledrivecreatedirectoryrequest.cpp | 8 ++++++++ .../googledrive/googledrivecreatedirectoryrequest.h | 2 ++ .../googledrivelistdirectorybyidrequest.cpp | 4 ++++ .../googledrive/googledrivelistdirectorybyidrequest.h | 2 ++ .../googledrive/googledrivelistdirectoryrequest.cpp | 6 ++++++ .../googledrive/googledrivelistdirectoryrequest.h | 2 ++ .../cloud/onedrive/onedrivecreatedirectoryrequest.cpp | 4 ++++ .../cloud/onedrive/onedrivecreatedirectoryrequest.h | 2 ++ .../cloud/onedrive/onedrivelistdirectoryrequest.cpp | 5 +++++ .../cloud/onedrive/onedrivelistdirectoryrequest.h | 2 ++ backends/cloud/savessyncrequest.cpp | 5 +++++ backends/cloud/savessyncrequest.h | 1 + backends/networking/curl/curlrequest.cpp | 19 +++++++++++++++++++ backends/networking/curl/curlrequest.h | 1 + backends/networking/curl/request.cpp | 2 ++ backends/networking/curl/request.h | 14 ++++++++++++++ 20 files changed, 92 insertions(+) diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp index c077d8df11..61cc9ddfcd 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 ea3175bb50..e8599c7df9 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 d782f81a69..933ea2bd8e 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 &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 3a83af06aa..0d96edd1e6 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h @@ -40,6 +40,7 @@ class DropboxListDirectoryRequest: public Networking::Request { Common::Array _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 54eff3d6ad..2b7a805bb9 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 ede84277de..f71afeb888 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 86494e8c13..2530bab557 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 &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 569984520a..ceb533e635 100644 --- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h +++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h @@ -41,6 +41,7 @@ class GoogleDriveListDirectoryByIdRequest: public Networking::Request { Common::Array _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 8811ffc938..3387b43418 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 &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 bea195f70a..b3d8ff6c93 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 fe1128bd5b..2c644c42c5 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 4bf0d9ef36..880e94e8db 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 040ef153db..be6fcb70e7 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 &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 b8adfe7ef0..5e80f4f561 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 e1739f9693..f059e29a8d 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 localFileNotAvailableInCloud; for (Common::HashMap::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 569feb4e45..1a615e80a1 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 6ef0e346af..3a143e5af8 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 &headers) { curl_slist_free_all(_headersList); _headersList = nullptr; diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h index 5c06b58107..68ea3a58d7 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 &headers); diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp index d2f91586a0..4a2704fc76 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 de5308fa52..6a1bc12594 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 -- cgit v1.2.3 From 9ee2eb4e60a34948797620a0f80ae0a80037efc0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 10 Jun 2016 16:35:23 +0600 Subject: GUI: Add EditText in StorageWizardDialog One can enter the code, press 'Connect' button and get a working Storage! --- backends/cloud/cloudmanager.cpp | 10 +++ backends/cloud/cloudmanager.h | 99 ++++++++++++---------- backends/cloud/dropbox/dropboxstorage.cpp | 80 ++++++----------- backends/cloud/dropbox/dropboxstorage.h | 12 ++- backends/cloud/googledrive/googledrivestorage.h | 10 +-- backends/cloud/onedrive/onedrivestorage.h | 8 +- gui/storagewizarddialog.cpp | 20 +++-- gui/storagewizarddialog.h | 2 + gui/themes/scummmodern/scummmodern_layout.stx | 4 + .../scummmodern/scummmodern_layout_lowres.stx | 4 + 10 files changed, 124 insertions(+), 125 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index fed35a9f38..adfebdca88 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 fd130c5ee8..9f4882d772 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 faff10f1d9..180b40c3d5 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(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 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(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 60a8075201..d256e0562b 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 489260db09..8093ef1938 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 3932d44aae..061d0fa172 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 996365da03..d637440fc8 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 ec5329b8af..00a36172fe 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 824c6fa790..cd76856c8b 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -607,6 +607,10 @@ + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 3d2c3b49a7..dd6700ed2e 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -600,6 +600,10 @@ + -- cgit v1.2.3 From a651a983dd75c1b775c1a633b22831812f780b65 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 11 Jun 2016 12:34:44 +0600 Subject: GUI: Add warning message for game's savepath --- gui/launcher.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 9a3300b11f..dee7f17672 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(); -- cgit v1.2.3 From a966de42a97dff932b6292f5021b7eb278310d1d Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 11 Jun 2016 11:18:13 +0200 Subject: CLOUD: Fix compilation error in savesyncrequest.h "savessyncrequest.h:35:35: error: use of undeclared identifier 'UINT_MAX'" --- backends/cloud/savessyncrequest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h index 1a615e80a1..105d7f7f65 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 namespace Cloud { -- cgit v1.2.3 From bad2ec3ef4326593f3eac90af8b847062e2554cd Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 11 Jun 2016 11:34:24 +0200 Subject: CLOUD: Fix initialization of NetworkReadStream "networkreadstream.cpp:51:2: error: delegating constructors are permitted only in C++11" --- backends/networking/curl/networkreadstream.cpp | 16 ++++++++++++---- backends/networking/curl/networkreadstream.h | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp index ccfb3d5a29..d761cbb5aa 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 991fdb346d..7a9d9ff0d1 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); -- cgit v1.2.3 From 2a2beaebc53ff6ef580a12b986d17147df3f15bc Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sat, 11 Jun 2016 21:38:20 +0200 Subject: Fix DropboxStorage::codeFlowComplete() --- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 180b40c3d5..b8851b05ec 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(); -- cgit v1.2.3 From dbafbf25693f13a9a3105379dd2b964095712fce Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 12 Jun 2016 11:47:52 +0600 Subject: CLOUD: Fix getAccessToken() KEY and SECRET should now load before getAccessToken() uses them, so it should work now. --- backends/cloud/dropbox/dropboxstorage.cpp | 7 ++++--- backends/cloud/googledrive/googledrivestorage.cpp | 5 +++-- backends/cloud/onedrive/onedrivestorage.cpp | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index b8851b05ec..bcafcf5264 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(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 5eab071599..76ff1dd7fc 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 d1971f904a..6ae5cb0b16 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 == "") { -- cgit v1.2.3 From c068b74f303484819a8432379890b637d7bba029 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 21:28:16 +0200 Subject: CLOUD: Force handling of all StorageIDs values in CloudManager::getStorageConfigName() --- backends/cloud/cloudmanager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index adfebdca88..0c087be7be 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() { -- cgit v1.2.3 From 1403cf006c5ebfd4a6101b89f4ff1d8760bb0ea4 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 21:32:29 +0200 Subject: CLOUD: Make enum StorageIDs' name singular --- backends/cloud/cloudmanager.cpp | 2 +- backends/cloud/cloudmanager.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 0c087be7be..4a87098f50 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 9f4882d772..48182dcde0 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); -- cgit v1.2.3 From 98788a5e7d0879d9f64e11e3d2308af2b03bc1de Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 21:36:58 +0200 Subject: CLOUD: Remove unnecessary blank lines in switch statement --- backends/cloud/cloudmanager.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 4a87098f50..8d12c0ff3a 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; } -- cgit v1.2.3 From fc3e7dec1a786c5990e37bd963838c235fba098b Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 22:12:15 +0200 Subject: CLOUD: Introduce kStoragePrefix in CloudManager --- backends/cloud/cloudmanager.cpp | 28 +++++++++++++++------------- backends/cloud/cloudmanager.h | 2 ++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 8d12c0ff3a..83b06d9405 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 48182dcde0..8e4c60eb4b 100644 --- a/backends/cloud/cloudmanager.h +++ b/backends/cloud/cloudmanager.h @@ -47,6 +47,8 @@ enum StorageID { }; class CloudManager : public Common::Singleton { + static const char *const kStoragePrefix; + struct StorageConfig { Common::String name, username; uint64 usedBytes; -- cgit v1.2.3 From fd6be018a34f315969371d9183311a56e3a9eaa2 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 22:24:14 +0200 Subject: CLOUD: Fix include in CloudManager --- backends/cloud/cloudmanager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h index 8e4c60eb4b..70b32f0758 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 +#include "common/str-array.h" namespace GUI { -- cgit v1.2.3 From bfc5cab9e88a2f70ac4a60c441c91a8b141ce113 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 22:29:23 +0200 Subject: CLOUD: Fix end of namespace comment in CloudManager --- backends/cloud/cloudmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 83b06d9405..77152d24ec 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 -- cgit v1.2.3 From 219e565c32d81e375677b62f6e82e48fae75f26f Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Sun, 12 Jun 2016 23:02:32 +0200 Subject: CLOUD: Introduce CloudConfigHelper --- backends/cloud/cloudconfighelper.cpp | 58 +++++++++++++++++++++++ backends/cloud/cloudconfighelper.h | 49 +++++++++++++++++++ backends/cloud/cloudmanager.cpp | 28 +++++------ backends/cloud/dropbox/dropboxstorage.cpp | 24 +++++----- backends/cloud/googledrive/googledrivestorage.cpp | 30 ++++++------ backends/cloud/onedrive/onedrivestorage.cpp | 36 +++++++------- 6 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 backends/cloud/cloudconfighelper.cpp create mode 100644 backends/cloud/cloudconfighelper.h diff --git a/backends/cloud/cloudconfighelper.cpp b/backends/cloud/cloudconfighelper.cpp new file mode 100644 index 0000000000..8c31b1c770 --- /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 0000000000..efa8792190 --- /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 { +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 77152d24ec..38b740216b 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 bcafcf5264..b677b56ff9 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 +#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 76ff1dd7fc..2b044ce12a 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 @@ -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 6ae5cb0b16..bc1a4ff130 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 +#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; } -- cgit v1.2.3 From ca0b58513d572419c52ba28ca09f4a086c543893 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 13 Jun 2016 12:27:56 +0600 Subject: CLOUD: Add CloudConfigHelper to module.mk --- backends/module.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/module.mk b/backends/module.mk index 95334fef65..8e9b14f010 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 \ -- cgit v1.2.3 From 0aea8db7e1a59e8cf88436f78019685c9aff6332 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 13 Jun 2016 12:32:33 +0600 Subject: 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). --- backends/cloud/storage.cpp | 10 +++++++++- backends/cloud/storage.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp index 08ab9e9c90..b98f213327 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 a76f2169bc..ace2f30864 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(); -- cgit v1.2.3 From 8a84263d2b7f30bdb87a0b49c1d84454ece38b21 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 14 Jun 2016 21:16:11 +0600 Subject: CLOUD: Do saves sync on Storage connect --- backends/cloud/cloudmanager.cpp | 7 ++++++- backends/cloud/dropbox/dropboxstorage.cpp | 3 +-- backends/cloud/googledrive/googledrivestorage.cpp | 2 -- backends/cloud/onedrive/onedrivestorage.cpp | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 38b740216b..a6f5575b3a 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 b677b56ff9..d77e958da7 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 2b044ce12a..1e31121d6c 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 bc1a4ff130..b91d1cdce1 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) { -- cgit v1.2.3 From a8eebbe8517bf75131af4d7846592ebcc7e79d25 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Tue, 14 Jun 2016 20:47:03 +0200 Subject: CLOUD: Get rid of CloudConfigHelper, use kCloudDomain where approriate --- backends/cloud/cloudconfighelper.cpp | 58 ----------------------- backends/cloud/cloudconfighelper.h | 49 ------------------- backends/cloud/cloudmanager.cpp | 39 +++++++++------ backends/cloud/dropbox/dropboxstorage.cpp | 25 +++++----- backends/cloud/googledrive/googledrivestorage.cpp | 28 +++++------ backends/cloud/onedrive/onedrivestorage.cpp | 36 +++++++------- backends/module.mk | 1 - 7 files changed, 70 insertions(+), 166 deletions(-) delete mode 100644 backends/cloud/cloudconfighelper.cpp delete mode 100644 backends/cloud/cloudconfighelper.h diff --git a/backends/cloud/cloudconfighelper.cpp b/backends/cloud/cloudconfighelper.cpp deleted file mode 100644 index 8c31b1c770..0000000000 --- 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 efa8792190..0000000000 --- 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 { -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 a6f5575b3a..19551213cd 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 d77e958da7..64425812db 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 -#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 1e31121d6c..6d0a152938 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 b91d1cdce1..5c230366a8 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 -#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 8e9b14f010..95334fef65 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 \ -- cgit v1.2.3 From 3db4915b663d989c01a0e8bf7f8d10a6be754432 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 00:47:41 +0600 Subject: 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. --- gui/storagewizarddialog.cpp | 152 ++++++++++++++++++++- gui/storagewizarddialog.h | 11 +- gui/themes/scummmodern/scummmodern_layout.stx | 43 +++++- .../scummmodern/scummmodern_layout_lowres.stx | 43 +++++- 4 files changed, 235 insertions(+), 14 deletions(-) diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index d637440fc8..a4091706c8 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 00a36172fe..e2bab7ea70 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 cd76856c8b..5954196f7e 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -606,10 +606,45 @@ /> - + + + + + + + + + + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index dd6700ed2e..ad9a3b6961 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -600,10 +600,45 @@ - + + + + + + + + + + + + + -- cgit v1.2.3 From c1ffb09fb0e75a96e48299c1df0742f357e5d03d Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Tue, 14 Jun 2016 21:16:18 +0200 Subject: CLOUD: Fix configuration handling in CloudManager --- backends/cloud/cloudmanager.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 19551213cd..8b104dfb20 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) { -- cgit v1.2.3 From a83e91e1ca6666be620d1cd1b0a8e4f267d7eaf2 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 13:31:59 +0600 Subject: 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". --- gui/storagewizarddialog.cpp | 125 ++++++++++++-------------------------------- gui/storagewizarddialog.h | 4 +- 2 files changed, 35 insertions(+), 94 deletions(-) diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index a4091706c8..b419d1127a 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 e2bab7ea70..b75b952222 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); -- cgit v1.2.3 From f571f3dd28a4b82973d26ff724edec60ad6ea845 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 13:40:15 +0600 Subject: CLOUD: Add comments for StorageWizardDialog methods --- gui/storagewizarddialog.cpp | 1 + gui/storagewizarddialog.h | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index b419d1127a..46be812f6d 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 b75b952222..93e368463c 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); -- cgit v1.2.3 From e2b3a9366eaad23549ad8be9316872e48a9ac11b Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 14:49:01 +0600 Subject: CONFIGURE: Add --with-sdlnet-prefix option --- configure | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 448da8ac16..fb24e1ee4f 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" -- cgit v1.2.3 From 9f7bea156aa4c763fd637a9a12ff89e124b51a6c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 16:37:01 +0600 Subject: CLOUD: Init SDL_Net --- backends/platform/sdl/sdl.cpp | 22 ++++++++++++++++++++++ backends/platform/sdl/sdl.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index dca6891fef..9f7b29233d 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 +#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 1fe670c5c3..f440cd77bb 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 -- cgit v1.2.3 From 0af97e59bc63538d18d2241285b68ed287ccd87c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 16:38:37 +0600 Subject: CLOUD: Add LocalWebserver Available as LocalServer singleton. It's being started and stopped by StorageWizardDialog. It doesn't handle clients yet, though. --- backends/cloud/cloudmanager.cpp | 11 +- backends/module.mk | 5 + backends/networking/sdl_net/localwebserver.cpp | 144 +++++++++++++++++++++++++ backends/networking/sdl_net/localwebserver.h | 65 +++++++++++ base/main.cpp | 6 ++ gui/storagewizarddialog.cpp | 20 +++- gui/storagewizarddialog.h | 4 +- 7 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 backends/networking/sdl_net/localwebserver.cpp create mode 100644 backends/networking/sdl_net/localwebserver.h diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 8b104dfb20..96521178ed 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(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 95334fef65..6fb8758650 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 0000000000..681485701b --- /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 + +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 0000000000..a11be0a938 --- /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 { + 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 4edc7a957a..6c91305381 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 46be812f6d..579591ba18 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 93e368463c..fa45f922d9 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 -- cgit v1.2.3 From 6126435b64442ae838953e095191230737391dca Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 21:36:20 +0600 Subject: CLOUD: Add Networking::Client Keeps current client's state --- backends/module.mk | 1 + backends/networking/sdl_net/client.cpp | 99 ++++++++++++++++++++++++++ backends/networking/sdl_net/client.h | 63 ++++++++++++++++ backends/networking/sdl_net/localwebserver.cpp | 73 ++++++++++--------- backends/networking/sdl_net/localwebserver.h | 5 +- 5 files changed, 206 insertions(+), 35 deletions(-) create mode 100644 backends/networking/sdl_net/client.cpp create mode 100644 backends/networking/sdl_net/client.h diff --git a/backends/module.mk b/backends/module.mk index 6fb8758650..74412a792a 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 0000000000..2af940c232 --- /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 + +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 0000000000..74c700e447 --- /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 681485701b..783814de14 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 a11be0a938..8db05f72e1 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 { 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(); -- cgit v1.2.3 From 99c51380fdc866ce393c52eb41803a9ec119a9ad Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 21:59:03 +0600 Subject: CLOUD: Add ClientState::BAD_REQUEST --- backends/networking/sdl_net/client.cpp | 45 ++++++++++++++++++++++++-- backends/networking/sdl_net/client.h | 4 ++- backends/networking/sdl_net/localwebserver.cpp | 4 +++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 2af940c232..8ab9ed385e 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 + //" HTTP/\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/ 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 74c700e447..7f78947223 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 783814de14..ee67f787c3 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; } } -- cgit v1.2.3 From 13c54f66850226a516da0450b633ebafbc26584e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 15 Jun 2016 23:54:53 +0600 Subject: 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. --- backends/module.mk | 1 + backends/networking/sdl_net/client.cpp | 25 ++++++- backends/networking/sdl_net/client.h | 25 ++++++- backends/networking/sdl_net/getclienthandler.cpp | 94 ++++++++++++++++++++++++ backends/networking/sdl_net/getclienthandler.h | 54 ++++++++++++++ backends/networking/sdl_net/localwebserver.cpp | 21 +++++- backends/networking/sdl_net/localwebserver.h | 1 + 7 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 backends/networking/sdl_net/getclienthandler.cpp create mode 100644 backends/networking/sdl_net/getclienthandler.h diff --git a/backends/module.mk b/backends/module.mk index 74412a792a..0f1eea0090 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 8ab9ed385e..22bfb60a2f 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 7f78947223..8b7a7d5295 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 0000000000..48a03e02f9 --- /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::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 0000000000..f434df1a45 --- /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 _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 ee67f787c3..a25dd74121 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 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], "ScummVMHello, World!"); break; case BAD_REQUEST: - //TODO: answer with BAD REQUEST - _client[i].close(); + setClientGetHandler(_client[i], "ScummVM - Bad RequestBAD REQUEST", 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 8db05f72e1..ab453988a8 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -51,6 +51,7 @@ class LocalWebserver : public Common::Singleton { void handle(); void handleClient(uint32 i); void acceptClient(); + void setClientGetHandler(Client &client, Common::String response, long code = 200); public: LocalWebserver(); -- cgit v1.2.3 From 6e1b667dd67b3a99f2a8a2b27cf3dfe032981cbd Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 11:44:07 +0600 Subject: CLOUD: Minor Client fix --- backends/networking/sdl_net/client.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 22bfb60a2f..0e86610e54 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 //" HTTP/\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 -- cgit v1.2.3 From 892c1bf84c4b095d373dfcfb2d84663f55776b46 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 11:44:23 +0600 Subject: CLOUD: Add HTTP response codes in GetClientHandler --- backends/networking/sdl_net/getclienthandler.cpp | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp index 48a03e02f9..f3d596ee93 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"; } -- cgit v1.2.3 From 434b740f4da7f77df902c0be6476cd360688c7ac Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Thu, 16 Jun 2016 07:52:44 +0200 Subject: CLOUD: Remove a couple of unnecessary whitespaces --- gui/storagewizarddialog.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index 579591ba18..f93721fdc1 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; } -- cgit v1.2.3 From ceb86a0dd8421047fc3d067da2a4c7faccc2f782 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 12:37:44 +0600 Subject: CLOUD: Clarify calculatedChecksum's initial value --- gui/storagewizarddialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index f93721fdc1..58ca095148 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]; } -- cgit v1.2.3 From 3946f23d172f55411748171af9822d2be3863701 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 14:19:18 +0600 Subject: 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. --- backends/networking/sdl_net/client.cpp | 79 ++++++++++++++++++++++---- backends/networking/sdl_net/client.h | 11 +++- backends/networking/sdl_net/localwebserver.cpp | 14 ++++- backends/networking/sdl_net/localwebserver.h | 6 ++ 4 files changed, 93 insertions(+), 17 deletions(-) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 0e86610e54..60f6be2adc 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/ 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) { + //[?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 + // yet I believe it to be the right one, because we probably can have "=" 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 8b7a7d5295..eba5dabfba 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 a25dd74121..689bab4cb9 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], "ScummVMHello, World!"); - break; + //fallthrough case BAD_REQUEST: setClientGetHandler(_client[i], "ScummVM - Bad RequestBAD REQUEST", 400); break; diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h index ab453988a8..bda0bfd7c1 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 { static const uint32 SERVER_PORT = 12345; static const uint32 MAX_CONNECTIONS = 10; + typedef Common::BaseCallback *ClientHandler; + friend void localWebserverTimer(void *); //calls handle() SDLNet_SocketSet _set; @@ -45,6 +49,7 @@ class LocalWebserver : public Common::Singleton { Client _client[MAX_CONNECTIONS]; int _clients; bool _timerStarted; + Common::HashMap _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. */ -- cgit v1.2.3 From 733d998e6a005af78b308817ea7326d1f5192069 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 15:13:31 +0600 Subject: CLOUD: Minor Client fix --- backends/networking/sdl_net/client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 60f6be2adc..24b6865052 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 ""; } -- cgit v1.2.3 From 5ac3adbd4fbf40c22fccab6ce8bf8dcab8f98eff Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 15:19:13 +0600 Subject: CLOUD: Add IndexPageHandler This commit also adds LocalWebserver's stopOnIdle(). That means server is not stopped immediately, but only when all clients are served. --- backends/module.mk | 1 + backends/networking/sdl_net/indexpagehandler.cpp | 56 ++++++++++++++++++++++++ backends/networking/sdl_net/indexpagehandler.h | 47 ++++++++++++++++++++ backends/networking/sdl_net/localwebserver.cpp | 23 +++++++++- backends/networking/sdl_net/localwebserver.h | 13 ++++-- gui/storagewizarddialog.cpp | 22 +++++++++- gui/storagewizarddialog.h | 8 ++++ 7 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 backends/networking/sdl_net/indexpagehandler.cpp create mode 100644 backends/networking/sdl_net/indexpagehandler.h diff --git a/backends/module.mk b/backends/module.mk index 0f1eea0090..ecdca2e0a7 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 0000000000..58c4761f59 --- /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, "ScummVMThis is a local webserver index page."); + return; + } + + _code = code; + sendCommand(GUI::kStorageCodePassedCmd, 0); + LocalWebserver::setClientGetHandler(client, "ScummVMScummVM got the code and already connects to your cloud storage!"); +} + +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(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 0000000000..5a1d2d7cbc --- /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 689bab4cb9..971f00af2a 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 bda0bfd7c1..2ad83f7b7a 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 { TCPsocket _serverSocket; Client _client[MAX_CONNECTIONS]; int _clients; - bool _timerStarted; + bool _timerStarted, _stopOnIdle; Common::HashMap _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 58ca095148..c2759f2ae6 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 fa45f922d9..93df56d300 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 -- cgit v1.2.3 From 0def9c50a7d05fc1743b622ffb5fdbba58e697e6 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 19:33:23 +0600 Subject: CLOUD: Fix Client Cleanup in open() --- backends/networking/sdl_net/client.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 24b6865052..67b45b6cbe 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) { -- cgit v1.2.3 From 43071c09723472483af4b69cf454ca75a8cd4613 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 19:34:57 +0600 Subject: CLOUD: Update LocalWebserver * fix handling connections; * fix idling strategy; * add setClientGetHandler() for SeekableReadStream; * add determineMimeType(). --- backends/networking/sdl_net/localwebserver.cpp | 59 ++++++++++++++++++++------ backends/networking/sdl_net/localwebserver.h | 9 +++- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index 971f00af2a..21ffe6786b 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 2ad83f7b7a..f51c33b418 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 { bool _timerStarted, _stopOnIdle; Common::HashMap _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. */ -- cgit v1.2.3 From 5176eaba81f4cda6cd5a14108c3516abd8ba0c84 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 19:47:02 +0600 Subject: 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. --- backends/networking/make_archive.py | 35 ++++++++ backends/networking/sdl_net/indexpagehandler.cpp | 108 ++++++++++++++++++++++- backends/networking/sdl_net/indexpagehandler.h | 7 ++ backends/networking/wwwroot.zip | Bin 0 -> 228117 bytes backends/networking/wwwroot/favicon.ico | Bin 0 -> 94081 bytes backends/networking/wwwroot/index.html | 18 ++++ backends/networking/wwwroot/logo.png | Bin 0 -> 132967 bytes backends/networking/wwwroot/style.css | 21 +++++ dists/scummvm.rc | 3 + 9 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 backends/networking/make_archive.py create mode 100644 backends/networking/wwwroot.zip create mode 100644 backends/networking/wwwroot/favicon.ico create mode 100644 backends/networking/wwwroot/index.html create mode 100644 backends/networking/wwwroot/logo.png create mode 100644 backends/networking/wwwroot/style.css diff --git a/backends/networking/make_archive.py b/backends/networking/make_archive.py new file mode 100644 index 0000000000..72d3b01d80 --- /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 58c4761f59..37fcac6419 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 = "ScummVM{message}"; + + // 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, "ScummVMThis is a local webserver index page."); + 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, "ScummVMScummVM got the code and already connects to your cloud storage!"); + 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(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(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 5a1d2d7cbc..143c300336 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 0000000000..4d98136c77 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 0000000000..0283e8432e 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 0000000000..2a3d9d382d --- /dev/null +++ b/backends/networking/wwwroot/index.html @@ -0,0 +1,18 @@ + + + + ScummVM + + + + +
+
+
+
+
+

{message}

+
+
+ + \ 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 0000000000..9fdd2d0d1e 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 0000000000..f092b058a8 --- /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 873feaa419..00d71ef717 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" -- cgit v1.2.3 From c2c2ba908ff916e0ba680596bb1b565069255a67 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Thu, 16 Jun 2016 21:04:10 +0600 Subject: GUI: Hide StorageWizardDialog fields if server present --- backends/networking/sdl_net/localwebserver.cpp | 1 + gui/storagewizarddialog.cpp | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index 21ffe6786b..d6033d1db1 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 c2759f2ae6..88826c2fb8 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() { -- cgit v1.2.3 From a56c7a3bd608d310cf0a3c3b534a20e826ecf8f0 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Fri, 17 Jun 2016 13:05:45 +0600 Subject: CLOUD: Update IndexPageHandler to search wwwroot.zip Now it also searches for that in themepath, not with SearchMan only. --- backends/networking/sdl_net/indexpagehandler.cpp | 55 ++++++++++++++---------- backends/networking/sdl_net/indexpagehandler.h | 1 + 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp index 37fcac6419..a1a426d048 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 143c300336..df509a42b5 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); -- cgit v1.2.3 From 5e70f64e108a3b155fc14f5087a7747e5c89f581 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 13:17:36 +0600 Subject: CLOUD: Embed cloud icons as byte arrays --- backends/networking/curl/cloudicon.cpp | 53 +++++----- backends/networking/curl/cloudicon.h | 2 +- backends/networking/curl/cloudicon_data.h | 111 +++++++++++++++++++ backends/networking/curl/cloudicon_disabled_data.h | 117 +++++++++++++++++++++ cloudicon.png | Bin 1242 -> 0 bytes cloudicon_disabled.png | Bin 1331 -> 0 bytes dists/cloudicon.png | Bin 0 -> 1242 bytes dists/cloudicon_disabled.png | Bin 0 -> 1331 bytes 8 files changed, 254 insertions(+), 29 deletions(-) create mode 100644 backends/networking/curl/cloudicon_data.h create mode 100644 backends/networking/curl/cloudicon_disabled_data.h delete mode 100644 cloudicon.png delete mode 100644 cloudicon_disabled.png create mode 100644 dists/cloudicon.png create mode 100644 dists/cloudicon_disabled.png diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp index 8971d44665..554fc79a3f 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 -#include 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 e007f952bd..7e4d7387a3 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 0000000000..a924dc84d2 --- /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 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 0000000000..fc1638cf97 --- /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 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 21373aea1a..0000000000 Binary files a/cloudicon.png and /dev/null differ diff --git a/cloudicon_disabled.png b/cloudicon_disabled.png deleted file mode 100644 index 27c9600b0a..0000000000 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 0000000000..21373aea1a 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 0000000000..27c9600b0a Binary files /dev/null and b/dists/cloudicon_disabled.png differ -- cgit v1.2.3 From 6ac69729d5c93899a3b1f5d82b6c5b008f030a7c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:15:25 +0600 Subject: CLOUD: Add some mutexes in LocalWebserver --- backends/networking/sdl_net/localwebserver.cpp | 13 ++++++++++++- backends/networking/sdl_net/localwebserver.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index d6033d1db1..1cfbbb8819 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 f51c33b418..2bd8daff5d 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 { Common::HashMap _pathHandlers; IndexPageHandler _indexPageHandler; uint32 _idlingFrames; + Common::Mutex _handleMutex; void startTimer(int interval = TIMER_INTERVAL); void stopTimer(); -- cgit v1.2.3 From 1addefad7e29ff674828c6dad5330c87a4203f29 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:25:55 +0600 Subject: 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). --- backends/cloud/dropbox/dropboxstorage.cpp | 4 ++++ backends/cloud/googledrive/googledrivestorage.cpp | 6 +++++- backends/cloud/onedrive/onedrivestorage.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 64425812db..3cf14d5e0b 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 6d0a152938..df29a87eec 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 5c230366a8..0d2f91c3a8 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); } -- cgit v1.2.3 From b908b286b9767ff7a0207ce9414279ce5291762c Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:34:43 +0600 Subject: 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. --- backends/cloud/dropbox/dropboxuploadrequest.cpp | 4 ++-- backends/cloud/onedrive/onedriveuploadrequest.cpp | 2 +- gui/storagewizarddialog.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp index 5765892a70..f1fb818d36 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 bc54a811a9..08f45935d2 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(this, &OneDriveUploadRequest::partUploadedCallback); Networking::ErrorCallback failureCallback = new Common::Callback(this, &OneDriveUploadRequest::partUploadedErrorCallback); diff --git a/gui/storagewizarddialog.cpp b/gui/storagewizarddialog.cpp index 88826c2fb8..6734d1c97e 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) { -- cgit v1.2.3 From 04cef8928ce0c8664bd3bb923eed3ceaa9ac17c5 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:38:13 +0600 Subject: CLOUD: Fix "type qualifiers ignored" warning --- backends/networking/sdl_net/indexpagehandler.cpp | 2 +- backends/networking/sdl_net/indexpagehandler.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp index a1a426d048..e459b3a47d 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 df509a42b5..cbcc6dab6e 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: -- cgit v1.2.3 From 39eb76f8c2ea62520c29e76ee10d97c2a13f0f42 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:39:34 +0600 Subject: CLOUD: Fix "zero-length format string" warning --- backends/cloud/googledrive/googledrivestorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp index df29a87eec..327c9ee8eb 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(" "); } } -- cgit v1.2.3 From caa53d9f6c191eecf87dc646dac33be25cb98f16 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:41:31 +0600 Subject: CLOUD: Fix "-Wconversion-null" That `false` came from TranslationManager's function, which was returning bool, not a pointer. Somehow missed that line. --- backends/networking/sdl_net/indexpagehandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp index e459b3a47d..2d1f2c2cd7 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()) { -- cgit v1.2.3 From 8484273f36cba8de90e4cfd40a18b8788e50f64b Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 14:51:05 +0600 Subject: 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. --- backends/networking/curl/networkreadstream.cpp | 6 +++--- backends/networking/curl/networkreadstream.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp index d761cbb5aa..5c3730645b 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 7a9d9ff0d1..6f521cbc4b 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(); /** -- cgit v1.2.3 From 81106b04440d76238da0fa0166eb3032b6db591e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 15:54:55 +0600 Subject: CLOUD: Update local server's style.css --- backends/networking/wwwroot.zip | Bin 228117 -> 228232 bytes backends/networking/wwwroot/style.css | 9 ++++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backends/networking/wwwroot.zip b/backends/networking/wwwroot.zip index 4d98136c77..403385ba2e 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 f092b058a8..75c8378229 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; } -- cgit v1.2.3 From aee713141b3a401f08e63cd9ccf5ce3dfe1cb06e Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 18:49:46 +0600 Subject: CLOUD: Make OutSaveFile start saves sync It had to become a proxy class in order to do that. finalize() starts the saves sync. --- backends/platform/ds/arm9/source/gbampsave.cpp | 2 +- backends/platform/n64/framfs_save_manager.h | 2 +- backends/platform/n64/pakfs_save_manager.h | 2 +- backends/platform/ps2/savefilemgr.cpp | 2 +- backends/saves/default/default-saves.cpp | 2 +- backends/saves/savefile.cpp | 24 ++++++++++++++++++++++++ common/savefile.h | 15 ++++++++++++++- engines/kyra/saveload_eob.cpp | 4 ++-- engines/kyra/saveload_hof.cpp | 2 +- engines/kyra/saveload_lok.cpp | 2 +- engines/kyra/saveload_lol.cpp | 2 +- engines/kyra/saveload_mr.cpp | 2 +- engines/tsage/stP1kAlM | Bin 0 -> 24604948 bytes 13 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 engines/tsage/stP1kAlM diff --git a/backends/platform/ds/arm9/source/gbampsave.cpp b/backends/platform/ds/arm9/source/gbampsave.cpp index ef6091e2a2..236ec55801 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 9bd4ee579e..aa26942bdc 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 0c08f0c506..31aa01444c 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 4fd2b1c72b..569d0e13e3 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 3fcb3cb3b3..e20ce31d18 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 b04c53d832..1f007ca713 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 38b21c9ddd..d84c9e275a 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 _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 cca8f3a0a4..6fdff0fc80 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 e8e76143bd..60ceebd28d 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 1d729d0103..cb2124a537 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 e02b8fb22c..a5ecd3b248 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 a938003a07..3c225e6d50 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 0000000000..dfbb3b9786 Binary files /dev/null and b/engines/tsage/stP1kAlM differ -- cgit v1.2.3 From 65e87c6c70fc1e5af8f0c3fb762ca13e6aa6a8e4 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sat, 18 Jun 2016 19:35:57 +0600 Subject: CLOUD: Update save's timestamp on rewrite This commit moves save/load timestamps static methods into DefaultSaveFileManager and fixes a few related bugs. --- backends/cloud/savessyncrequest.cpp | 133 +++++-------------------------- backends/cloud/savessyncrequest.h | 8 +- backends/saves/default/default-saves.cpp | 109 +++++++++++++++++++++++++ backends/saves/default/default-saves.h | 14 +++- 4 files changed, 142 insertions(+), 122 deletions(-) diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index f059e29a8d..4d18647911 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 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::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(this, &SavesSyncRequest::fileDownloadedCallback), new Common::Callback(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 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 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::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 105d7f7f65..19e67d9dd8 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 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 _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 e20ce31d18..54dc1c2966 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 // 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 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 DefaultSaveFileManager::loadTimestamps() { + Common::HashMap timestamps; + + //refresh the files list + Common::Array 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 ×tamps) { + 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::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 af30cf45e9..e9edfb1f36 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 /** * 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 loadTimestamps(); + static void saveTimestamps(Common::HashMap ×tamps); + static Common::String concatWithSavesPath(Common::String name); + +#endif + protected: /** * Get the path to the savegame directory. -- cgit v1.2.3 From 2d3cfffa84f80578dfc0bd3c72b83587f9b8dae5 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 19 Jun 2016 12:58:02 +0600 Subject: KYRA: Fix openSaveForWriting() to return OutSaveFile --- engines/kyra/kyra_v1.h | 3 ++- engines/kyra/saveload.cpp | 4 ++-- engines/kyra/saveload_eob.cpp | 2 +- engines/kyra/saveload_hof.cpp | 2 +- engines/kyra/saveload_lok.cpp | 2 +- engines/kyra/saveload_lol.cpp | 2 +- engines/kyra/saveload_mr.cpp | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index c801829855..4de7494510 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 2ae6420bd7..81ea796fe9 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 6fdff0fc80..9329d14255 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 60ceebd28d..e8e76143bd 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 cb2124a537..1d729d0103 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 a5ecd3b248..e02b8fb22c 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 3c225e6d50..a938003a07 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(); -- cgit v1.2.3 From 4c381dafa3aff9feeb80e1be3272eba8ca73d442 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 19 Jun 2016 13:50:11 +0600 Subject: CLOUD: Delete the incomplete file (when downloading) --- backends/cloud/downloadrequest.cpp | 5 +++++ backends/cloud/downloadrequest.h | 2 ++ backends/cloud/savessyncrequest.cpp | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 307ea00aa2..497509b4a9 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 def69d47de..ff7820e3ee 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 4d18647911..c43625315d 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 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); } -- cgit v1.2.3 From cff183536b62c84b6de8c83c3f249466ae3e75d6 Mon Sep 17 00:00:00 2001 From: Peter Bozsó Date: Mon, 20 Jun 2016 20:21:21 +0200 Subject: CLOUD: Fix crash on exiting ScummVM while ConnMan is active --- backends/networking/curl/connectionmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index 5a1e3e97c1..f7c8e2c92d 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::iterator i = _requests.begin(); i != _requests.end(); ++i) { -- cgit v1.2.3 From fa3ea83165d6c378348f61f8daec85f805626dbe Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 21 Jun 2016 16:58:09 +0600 Subject: CLOUD: Fix some warnings Mostly on format string --- backends/cloud/cloudmanager.cpp | 4 ++-- backends/cloud/dropbox/dropboxstorage.cpp | 2 +- backends/cloud/googledrive/googledriveuploadrequest.cpp | 2 +- backends/networking/sdl_net/getclienthandler.cpp | 2 +- common/json.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 96521178ed..b9de6d48ab 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 3cf14d5e0b..6d73e52c43 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 ce1fae2579..b24e02edbf 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 f3d596ee93..0e73a54912 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::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 5f63d3fe1f..f95a44f8fe 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; } -- cgit v1.2.3 From f3a392359be2f6d05bac107a5f7bd168c178e428 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 21 Jun 2016 17:07:23 +0600 Subject: CLOUD: Fix finishSuccess() warning --- backends/cloud/downloadrequest.cpp | 4 ++-- backends/cloud/downloadrequest.h | 2 +- backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp | 4 ++-- backends/cloud/dropbox/dropboxcreatedirectoryrequest.h | 2 +- backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp | 4 ++-- backends/cloud/dropbox/dropboxlistdirectoryrequest.h | 2 +- backends/cloud/dropbox/dropboxuploadrequest.cpp | 6 +++--- backends/cloud/dropbox/dropboxuploadrequest.h | 2 +- backends/cloud/folderdownloadrequest.cpp | 4 ++-- backends/cloud/folderdownloadrequest.h | 2 +- backends/cloud/googledrive/googledrivecreatedirectoryrequest.cpp | 6 +++--- backends/cloud/googledrive/googledrivecreatedirectoryrequest.h | 2 +- backends/cloud/googledrive/googledrivedownloadrequest.cpp | 4 ++-- backends/cloud/googledrive/googledrivedownloadrequest.h | 2 +- backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp | 4 ++-- backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h | 2 +- backends/cloud/googledrive/googledrivelistdirectoryrequest.cpp | 4 ++-- backends/cloud/googledrive/googledrivelistdirectoryrequest.h | 2 +- backends/cloud/googledrive/googledrivetokenrefresher.cpp | 6 +++--- backends/cloud/googledrive/googledrivetokenrefresher.h | 2 +- backends/cloud/googledrive/googledriveuploadrequest.cpp | 6 +++--- backends/cloud/googledrive/googledriveuploadrequest.h | 2 +- backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp | 4 ++-- backends/cloud/onedrive/onedrivecreatedirectoryrequest.h | 2 +- backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp | 4 ++-- backends/cloud/onedrive/onedrivelistdirectoryrequest.h | 2 +- backends/cloud/onedrive/onedrivetokenrefresher.cpp | 6 +++--- backends/cloud/onedrive/onedrivetokenrefresher.h | 2 +- backends/cloud/onedrive/onedriveuploadrequest.cpp | 6 +++--- backends/cloud/onedrive/onedriveuploadrequest.h | 2 +- backends/cloud/savessyncrequest.cpp | 4 ++-- backends/cloud/savessyncrequest.h | 2 +- backends/networking/curl/curljsonrequest.cpp | 6 +++--- backends/networking/curl/curljsonrequest.h | 2 +- 34 files changed, 58 insertions(+), 58 deletions(-) diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 497509b4a9..c95b8b81af 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 ff7820e3ee..d8e18f9171 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 61cc9ddfcd..a52d41e896 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 e8599c7df9..4bdc6dbee6 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 933ea2bd8e..dcbca3131e 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 &files) { +void DropboxListDirectoryRequest::finishListing(Common::Array &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 0d96edd1e6..62dde71f76 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 &files); + void finishListing(Common::Array &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 f1fb818d36..e530502413 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 a85d7ef883..8d9a3e2650 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 905b0c79f4..83296c3d15 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 &files) { +void FolderDownloadRequest::finishDownload(Common::Array &files) { Request::finishSuccess(); if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files)); } diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h index 8fa3b1188b..bf55567b2d 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 &files); + void finishDownload(Common::Array &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 2b7a805bb9..9e339fd999 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 f71afeb888..7a6ffaca1b 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 c885352fae..df28c8b27f 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 89bd313467..202a393d7a 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 2530bab557..582f67c2cf 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 &files) { +void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array &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 ceb533e635..ecde880323 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 &files); + void finishListing(Common::Array &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 3387b43418..f645041eb3 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 &files) { +void GoogleDriveListDirectoryRequest::finishListing(Common::Array &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 b3d8ff6c93..d76338b7fc 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 &files); + void finishListing(Common::Array &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 3fae830105..3dfb8436e9 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 &headers) { diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h index 3dcb56bde6..3bc5fbd784 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 b24e02edbf..d9ba2815e0 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 e417403542..8cc40795e9 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 2c644c42c5..617346529b 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 880e94e8db..83c72c4e0a 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 be6fcb70e7..baccdf418e 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 &files) { +void OneDriveListDirectoryRequest::finishListing(Common::Array &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 5e80f4f561..eb510ab257 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 &files); + void finishListing(Common::Array &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 bf849f7964..04e155c084 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 &headers) { diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h index 04b0bf26b8..4be1fa726b 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 08f45935d2..fb32443a27 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 09419d8a15..f613c415e1 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 c43625315d..e12f5af7cc 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 19e67d9dd8..d1a9d4a44c 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 46d88657d2..3bfc823d25 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 83005a79d5..bd6f135786 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); -- cgit v1.2.3 From acce1c89ab2b9b362fa9f2fc32ae813c62038705 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 3 Jul 2016 12:11:45 +0600 Subject: CLOUD: Fix saves sync Tested that on actual unix system and found out a few minor bugs related to paths. --- backends/saves/default/default-saves.cpp | 7 ++++--- common/file.cpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index 54dc1c2966..06d4047a4e 100644 --- a/backends/saves/default/default-saves.cpp +++ b/backends/saves/default/default-saves.cpp @@ -359,7 +359,8 @@ void DefaultSaveFileManager::saveTimestamps(Common::HashMap(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 52b66bd2f4..5f3402e9ed 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"); -- cgit v1.2.3 From 97c0bbd2388ac049970fc3c99ebdc072c75724f1 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Sun, 3 Jul 2016 20:09:51 +0600 Subject: GUI: Add DownloadDialog sketch --- gui/downloaddialog.cpp | 88 ++++++++++++++++++++++ gui/downloaddialog.h | 56 ++++++++++++++ gui/module.mk | 1 + gui/options.cpp | 12 ++- gui/options.h | 1 + gui/themes/scummmodern/scummmodern_layout.stx | 21 ++++++ .../scummmodern/scummmodern_layout_lowres.stx | 21 ++++++ 7 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 gui/downloaddialog.cpp create mode 100644 gui/downloaddialog.h diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp new file mode 100644 index 0000000000..b449be5ca8 --- /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 0000000000..333ce0e03b --- /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 ef005311cd..2d71c45106 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 2ab6b1eb6b..1ea64cab3d 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(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 1454ddbfc8..fa7a1d28b9 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 5954196f7e..f9ec30b81b 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -579,6 +579,27 @@ + +
+ + + + + + + + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index ad9a3b6961..fe15dc2410 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -576,6 +576,27 @@ + + + + + + + + + + + + + -- cgit v1.2.3 From 72b82bd2aa66223f6c740e7bf6dce316b2145b15 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 11:52:40 +0600 Subject: GUI: Add RemoteBrowserDialog WIP. Tested with Dropbox. --- gui/downloaddialog.cpp | 40 +++++++++ gui/downloaddialog.h | 8 +- gui/module.mk | 1 + gui/remotebrowser.cpp | 220 +++++++++++++++++++++++++++++++++++++++++++++++++ gui/remotebrowser.h | 71 ++++++++++++++++ 5 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 gui/remotebrowser.cpp create mode 100644 gui/remotebrowser.h diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index b449be5ca8..e94b19634b 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 333ce0e03b..ca702ef927 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 2d71c45106..1fdd0d459a 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 0000000000..37a51d5341 --- /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 +#include +#include + +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::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(this, &RemoteBrowserDialog::directoryListedCallback), + new Common::Callback(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 0000000000..55be1198b5 --- /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 +#include +#include + +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 _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 -- cgit v1.2.3 From e388accda3d4af43c4ae5f060719c4f31bc5cce5 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 12:05:10 +0600 Subject: GUI: Fix "Go up" OneDrive and Google Drive paths do not start with '/', so one was unable to go up to root. --- gui/remotebrowser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index 37a51d5341..a1b0e3f098 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; } -- cgit v1.2.3 From 4aa8e23ea24e275f6b68fe6ed773ee2129d4de26 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 13:00:13 +0600 Subject: GUI: Make RemoteBrowser show "Loading..." --- gui/remotebrowser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index a1b0e3f098..ffa24ecf7a 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) { -- cgit v1.2.3 From 73bb2e20afdd625998a1438adf29e5d9dbaa2929 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 13:00:26 +0600 Subject: GUI: Clean up in RemoteBrowser --- gui/downloaddialog.cpp | 2 +- gui/remotebrowser.cpp | 82 ++++++++++++++------------------------------------ gui/remotebrowser.h | 5 ++- 3 files changed, 26 insertions(+), 63 deletions(-) diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index e94b19634b..6e9410dd51 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 ffa24ecf7a..fac0b02e35 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 55be1198b5..ca1f9379cd 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 _nodeContent; Cloud::StorageFile _choice; - bool _isDirRemoteBrowser; bool _navigationLocked; bool _updateList; -- cgit v1.2.3 From 51a7232c73b1f6c04103716cae88b3781fd8ab33 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 13:05:15 +0600 Subject: GUI: Fix RemoteBrowser Request handling Init with NULL, ignore callbacks, and such. --- gui/remotebrowser.cpp | 26 ++++++++++++++++++++++++-- gui/remotebrowser.h | 4 +++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index fac0b02e35..995e6a654c 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 ca1f9379cd..e13ee9db3d 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(); -- cgit v1.2.3 From 6faf2c26173e0a1305e4cea07a04c2857586bf6f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 13:26:33 +0600 Subject: 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). --- gui/remotebrowser.cpp | 42 ++++++++++++++++++++++++++---------------- gui/remotebrowser.h | 1 + 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index 995e6a654c..efeabb69a6 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(this, &RemoteBrowserDialog::directoryListedCallback), - new Common::Callback(this, &RemoteBrowserDialog::directoryListedErrorCallback), - false - ); + + if (_rememberedNodeContents.contains(node.path())) { + _nodeContent = _rememberedNodeContents[node.path()]; + } else { + _navigationLocked = true; + + _workingRequest = CloudMan.listDirectory( + node.path(), + new Common::Callback(this, &RemoteBrowserDialog::directoryListedCallback), + new Common::Callback(this, &RemoteBrowserDialog::directoryListedErrorCallback), + false + ); + } _backupNode = _node; _node = node; diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h index e13ee9db3d..0137c32dc0 100644 --- a/gui/remotebrowser.h +++ b/gui/remotebrowser.h @@ -53,6 +53,7 @@ protected: StaticTextWidget *_currentPath; Cloud::StorageFile _node, _backupNode; Common::Array _nodeContent; + Common::HashMap > _rememberedNodeContents; Cloud::StorageFile _choice; bool _navigationLocked; bool _updateList; -- cgit v1.2.3 From a37c63998671ad8a341e1b88aead758f408e5fc2 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 13:40:32 +0600 Subject: 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. --- backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp index 582f67c2cf..36bc390da0 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"; -- cgit v1.2.3 From 8972f28bc17b6d2f6fee69adcb025c9a561e851f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 14:06:34 +0600 Subject: GUI: Add RemoteBrowser file list sorting Because Dropbox has no means to specify files order. --- gui/remotebrowser.cpp | 1 + gui/remotebrowser.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index efeabb69a6..e8921b905a 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 0137c32dc0..be416154a1 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 { + 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 -- cgit v1.2.3 From c32d6fa047d03ca7be0e060aadcf33e5ee7d6d4a Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 14:17:35 +0600 Subject: GUI: Add error message in RemoteBrowser For the error callback case. --- gui/remotebrowser.cpp | 14 +++++++++++--- gui/remotebrowser.h | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp index e8921b905a..d176d29b02 100644 --- a/gui/remotebrowser.cpp +++ b/gui/remotebrowser.cpp @@ -31,6 +31,7 @@ #include #include #include +#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 be416154a1..190d8c6895 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; -- cgit v1.2.3 From d776b5397198835e1538071fd4fe53ab491c5da4 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 14:49:33 +0600 Subject: 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. --- gui/downloaddialog.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index 6e9410dd51..34eddbe395 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 } -- cgit v1.2.3 From dc0a95617227c2a2489150aec69ec0c464cd30de Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 15:11:07 +0600 Subject: CLOUD: Add CloudManager::downloadFolder() --- backends/cloud/cloudmanager.cpp | 11 +++++++++++ backends/cloud/cloudmanager.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index b9de6d48ab..0a5006ccef 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 70b32f0758..574c51a439 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 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); -- cgit v1.2.3 From 10250af2516c8f7d41cc79e3c55b59f2eeecfa92 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 15:23:13 +0600 Subject: CLOUD: Fix CloudManager's methods Were not returning created Request. --- backends/cloud/cloudmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 0a5006ccef..68c7e9aa8a 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; -- cgit v1.2.3 From 71a326493b351b845ea800ae88495238b1a61066 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 15:34:02 +0600 Subject: GUI: Initiate download in DownloadDialog --- gui/downloaddialog.cpp | 71 +++++++++++++++++++++++++++++++++++++++++--------- gui/downloaddialog.h | 9 ++++++- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index 34eddbe395..d4c44e53ff 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(this, &DownloadDialog::directoryDownloadedCallback), + new Common::Callback(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 ca702ef927..6836b38bb5 100644 --- a/gui/downloaddialog.h +++ b/gui/downloaddialog.h @@ -25,6 +25,7 @@ #include "gui/dialog.h" #include "common/str.h" +#include 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(); -- cgit v1.2.3 From b8ee9d4e7d32d0cc0dd832cbd0ffec5c5d08db34 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 16:14:30 +0600 Subject: 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. --- backends/cloud/cloudmanager.cpp | 32 ++++++++++++++ backends/cloud/cloudmanager.h | 19 +++++++++ backends/cloud/folderdownloadrequest.cpp | 4 +- backends/cloud/folderdownloadrequest.h | 8 +++- backends/cloud/storage.cpp | 71 +++++++++++++++++++++++++++++++- backends/cloud/storage.h | 28 +++++++++++++ gui/downloaddialog.cpp | 50 +++------------------- gui/downloaddialog.h | 7 ---- 8 files changed, 164 insertions(+), 55 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index 68c7e9aa8a..a1756ed5a6 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 574c51a439..2617a9c62e 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 83296c3d15..e00f85121f 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 &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 bf55567b2d..83d3432746 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 b98f213327..a08fe11a70 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(this, &Storage::directoryDownloadedCallback), + new Common::Callback(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 ace2f30864..28a20720b7 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 d4c44e53ff..ac3b3d3c72 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(this, &DownloadDialog::directoryDownloadedCallback), - new Common::Callback(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 6836b38bb5..508e91adcb 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(); -- cgit v1.2.3 From ddb1a6ccb6238aaed599b271506a94a7c0f18844 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 17:11:58 +0600 Subject: 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. --- backends/cloud/cloudmanager.cpp | 12 +++++ backends/cloud/cloudmanager.h | 6 +++ backends/cloud/folderdownloadrequest.cpp | 13 +++++- backends/cloud/folderdownloadrequest.h | 7 +++ backends/cloud/storage.cpp | 33 ++++++++++++-- backends/cloud/storage.h | 6 +++ gui/downloaddialog.cpp | 53 +++++++++++++++++++--- gui/downloaddialog.h | 14 +++++- gui/themes/scummmodern/scummmodern_layout.stx | 14 ++++++ .../scummmodern/scummmodern_layout_lowres.stx | 14 ++++++ 10 files changed, 159 insertions(+), 13 deletions(-) diff --git a/backends/cloud/cloudmanager.cpp b/backends/cloud/cloudmanager.cpp index a1756ed5a6..dfe65e7f44 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 2617a9c62e..1cdbbccdb9 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 e00f85121f..d57da6bc7f 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 &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 83d3432746..41eacc2afe 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 a08fe11a70..4e3dc435a6 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 namespace Cloud { @@ -198,7 +199,7 @@ bool Storage::startDownload(Common::String remotePath, Common::String localPath) } _downloadFolderRequest = (FolderDownloadRequest *)downloadFolder( remotePath, localPath, - new Common::Callback(this, &Storage::directoryDownloadedCallback), + new Common::Callback(this, &Storage::directoryDownloadedCallback), new Common::Callback(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 28a20720b7..62b42697e6 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 ac3b3d3c72..28a29fcb64 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 508e91adcb..429e20af98 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 f9ec30b81b..e6493ba160 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -594,6 +594,20 @@ + + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index fe15dc2410..ab78d3aee1 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -591,6 +591,20 @@ + + + + + -- cgit v1.2.3 From 458bfcec79e76b927414ed1b2151a4d59503ff86 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 17:26:16 +0600 Subject: GUI: Fix DownloadDialog path creation Was adding a path separator even when none is required. --- gui/downloaddialog.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index 28a29fcb64..05d87c21d8 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); -- cgit v1.2.3 From 052d8bf0aec65f8fa2feb19e9780574e79196203 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 17:42:38 +0600 Subject: GUI: Forbid using download directory in "Add Game" --- gui/launcher.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gui/launcher.cpp b/gui/launcher.cpp index dee7f17672..1ea3169f5e 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!")); -- cgit v1.2.3 From 659dcd9702a82b3fdf5b6c6c87f11267fe313744 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 17:45:50 +0600 Subject: GUI: Fix SaveLoadDialog It was SavesSyncRequest's target even when closed. --- gui/saveload-dialog.cpp | 5 +++++ gui/saveload-dialog.h | 1 + 2 files changed, 6 insertions(+) diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 3ac6074bf8..eff4a5a0a3 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 890169c99a..9c601fd2cb 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(); -- cgit v1.2.3 From 1cfdb9661678a5cabb77c2cf60d2fa357121d584 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 17:55:09 +0600 Subject: 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. --- backends/cloud/folderdownloadrequest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp index d57da6bc7f..6cf55b28cf 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)); } -- cgit v1.2.3 From a5765a339e150952dca27035357bbfb1f88a4718 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Mon, 4 Jul 2016 18:30:23 +0600 Subject: 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. --- gui/downloaddialog.cpp | 81 +++++++++++----------- gui/downloaddialog.h | 12 ++-- gui/themes/scummmodern/scummmodern_layout.stx | 9 +-- .../scummmodern/scummmodern_layout_lowres.stx | 9 +-- 4 files changed, 51 insertions(+), 60 deletions(-) diff --git a/gui/downloaddialog.cpp b/gui/downloaddialog.cpp index 05d87c21d8..9c48111153 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 429e20af98..74dd1e2e39 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 e6493ba160..d2e60d7456 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -588,12 +588,6 @@ - - @@ -610,6 +604,9 @@ /> + - - @@ -607,6 +601,9 @@ /> + 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(); -- cgit v1.2.3 From ad069f442ce1a042361620f30d5b313668df23c4 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 13:11:29 +0600 Subject: GUI: Add "Run server" button in Cloud tab --- gui/object.h | 2 + gui/options.cpp | 72 ++++++++++++++++++++-- gui/options.h | 6 +- gui/themes/scummmodern/scummmodern_layout.stx | 8 +++ .../scummmodern/scummmodern_layout_lowres.stx | 8 +++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/gui/object.h b/gui/object.h index 776c941356..1541c35aa8 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 1ea64cab3d..433278460c 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(_(""), 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 fa7a1d28b9..77470aef57 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 d2e60d7456..9e79ac29b7 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -583,6 +583,14 @@ type = 'Button' /> + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 1ff2e9f524..9f00d2ee95 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -580,6 +580,14 @@ type = 'Button' /> + + + + -- cgit v1.2.3 From 81c85b6651fcb4ba2f1dc0ba2955e34cb541fc35 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 13:30:24 +0600 Subject: CLOUD: Fix SaveLoadDialogs to check USE_CLOUD Linking was failing when disabling curl support. --- gui/saveload-dialog.cpp | 22 +++++++++++++++++++++- gui/saveload-dialog.h | 4 ++++ gui/saveload.cpp | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index eff4a5a0a3..faf6fa9dfe 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 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 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 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 9c601fd2cb..ba2c2bea0e 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 b3e669899f..b08ab7fd1a 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 -- cgit v1.2.3 From 211d9ed5f67712f5e0a1b23e7b3fb492b5256f89 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 14:12:35 +0600 Subject: GUI: Fix Options' Cloud tab reflowing --- gui/options.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/options.cpp b/gui/options.cpp index 433278460c..bf692f8fad 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 -- cgit v1.2.3 From 3da38ca60b65d3f1bd67b049f3c4b2a90a4d6a19 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 15:05:30 +0600 Subject: 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. --- backends/module.mk | 8 ++------ backends/saves/default/default-saves.cpp | 12 ++++++------ backends/saves/default/default-saves.h | 2 +- backends/saves/savefile.cpp | 4 ++-- base/main.cpp | 8 ++------ gui/launcher.cpp | 6 +++--- gui/options.cpp | 22 ++++++++++++---------- gui/options.h | 6 +++--- gui/saveload-dialog.cpp | 18 +++++++++--------- gui/saveload-dialog.h | 4 ++-- gui/saveload.cpp | 2 +- 11 files changed, 43 insertions(+), 49 deletions(-) diff --git a/backends/module.mk b/backends/module.mk index ecdca2e0a7..ece3128d74 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 06d4047a4e..7f96868934 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 // 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 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 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 DefaultSaveFileManager::loadTimestamps() { Common::HashMap 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 e9edfb1f36..f2453810bf 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 1f007ca713..5e983472da 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 6c91305381..dd2c3f54e6 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 1ea3169f5e..309c7479a3 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 bf692f8fad..9d9833496f 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 77470aef57..c4c70aa369 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 faf6fa9dfe..222f637872 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 files = CloudMan.getSyncingFiles(); if (!files.empty()) { @@ -279,7 +279,7 @@ void SaveLoadChooserDialog::reflowLayout() { } void SaveLoadChooserDialog::updateSaveList() { -#ifdef USE_CLOUD +#ifdef USE_LIBCURL Common::Array 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 ba2c2bea0e..4e375bfc2e 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 b08ab7fd1a..3072aa6082 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 -- cgit v1.2.3 From e601c39802adbb64dce4b449c617e1786ef584ed Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 16:31:52 +0600 Subject: CLOUD: Make "Run server" button active It should show the real server's IP over there, but that doesn't work yet. --- backends/networking/sdl_net/localwebserver.cpp | 20 +++++++++++++++- backends/networking/sdl_net/localwebserver.h | 3 +++ gui/options.cpp | 33 +++++++++++++++++++++----- gui/options.h | 3 +++ gui/storagewizarddialog.cpp | 5 ++-- gui/storagewizarddialog.h | 1 + 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index 1cfbbb8819..134ccd8508 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 2bd8daff5d..f73d1b2850 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -59,6 +59,7 @@ class LocalWebserver : public Common::Singleton { 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 9d9833496f..bb9bf4d123 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 c4c70aa369..1d7f9ffd49 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 6734d1c97e..7871017f5d 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 93df56d300..ade3c57b47 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. -- cgit v1.2.3 From eae57728d17beb74e4631c488ad8d1b4aaea7aea Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 17:52:29 +0600 Subject: CLOUD: Resolve local machine's IP --- backends/networking/sdl_net/localwebserver.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index 134ccd8508..d1dda16f8c 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()); -- cgit v1.2.3 From c409d29f66057a1a2c0e405e60cc8aa5623bc52f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 5 Jul 2016 20:42:57 +0600 Subject: 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. --- backends/networking/sdl_net/indexpagehandler.cpp | 28 +++++++++- backends/networking/sdl_net/indexpagehandler.h | 1 + backends/networking/wwwroot.zip | Bin 228232 -> 230835 bytes backends/networking/wwwroot/files.html | 50 ++++++++++++++++++ backends/networking/wwwroot/style.css | 64 +++++++++++++++++++++++ 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 backends/networking/wwwroot/files.html diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp index 2d1f2c2cd7..c2ea470802 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 = "ScummVM{content}"; //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(this, &IndexPageHandler::handle)); + server.addPathHandler("/files", new Common::Callback(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(this, &IndexPageHandler::handleResource)); } } diff --git a/backends/networking/sdl_net/indexpagehandler.h b/backends/networking/sdl_net/indexpagehandler.h index cbcc6dab6e..e70ffd5236 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 403385ba2e..1a301f5db5 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 0000000000..8a2788905a --- /dev/null +++ b/backends/networking/wwwroot/files.html @@ -0,0 +1,50 @@ + + + + ScummVM + + + + +
+
+
+
+
+ + + +
{create_directory_button}{upload_files_button}
+ + +
+
+ {content} +
+
+ + + \ No newline at end of file diff --git a/backends/networking/wwwroot/style.css b/backends/networking/wwwroot/style.css index 75c8378229..59d96671db 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 -- cgit v1.2.3 From e47b6c04b30565b1db238c2784e562f3f4472d70 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 6 Jul 2016 12:41:33 +0600 Subject: CLOUD: Make "/files" list directories Including both virtual "/root" and "/saves" ones. --- backends/networking/sdl_net/indexpagehandler.cpp | 134 ++++++++++++++++++++++- backends/networking/sdl_net/indexpagehandler.h | 24 ++++ backends/networking/wwwroot.zip | Bin 230835 -> 230882 bytes backends/networking/wwwroot/files.html | 4 +- 4 files changed, 159 insertions(+), 3 deletions(-) diff --git a/backends/networking/sdl_net/indexpagehandler.cpp b/backends/networking/sdl_net/indexpagehandler.cpp index c2ea470802..c44c3d4a5a 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 = "ScummVM{content}"; //TODO + Common::String response = "ScummVM{content}
"; //TODO: add controls + Common::String itemTemplate = "{name}{size}\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
%s", + _("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 = "ScummVM{message}"; + + // 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) { + // 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(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 e70ffd5236..0ccbf32480 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 . + * + * Returns true on success. + */ + bool listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate); + + /** + * Transforms virtual 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 1a301f5db5..da031c8ca3 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 8a2788905a..2baf4a02dc 100644 --- a/backends/networking/wwwroot/files.html +++ b/backends/networking/wwwroot/files.html @@ -31,7 +31,9 @@
- {content} + + {content} +
+ + \ 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 0000000000..2a3d9d382d --- /dev/null +++ b/backends/networking/wwwroot/.index.html @@ -0,0 +1,18 @@ + + + + ScummVM + + + + +
+
+
+
+
+

{message}

+
+
+ + \ 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 2baf4a02dc..0000000000 --- a/backends/networking/wwwroot/files.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - ScummVM - - - - -
-
-
-
-
- - - -
{create_directory_button}{upload_files_button}
- - -
-
- - {content} -
-
-
- - - \ 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 2a3d9d382d..0000000000 --- a/backends/networking/wwwroot/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - ScummVM - - - - -
-
-
-
-
-

{message}

-
-
- - \ No newline at end of file -- cgit v1.2.3 From ab4361a76b40b56348ec46311121a0552f0c9d6b Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 6 Jul 2016 18:03:56 +0600 Subject: CLOUD: Make "/create" work One can now create directories through browser. --- backends/networking/sdl_net/client.cpp | 5 +- .../sdl_net/handlers/filespagehandler.cpp | 73 +++++++++++++++++++++ .../networking/sdl_net/handlers/filespagehandler.h | 11 ++++ backends/networking/sdl_net/localwebserver.cpp | 29 ++++++++ backends/networking/sdl_net/localwebserver.h | 1 + backends/networking/wwwroot.zip | Bin 230886 -> 231000 bytes backends/networking/wwwroot/.files.html | 2 + 7 files changed, 119 insertions(+), 2 deletions(-) diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 67b45b6cbe..f567891dd8 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 @@ -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 360fe201b8..327071fc11 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 = "ScummVM{content}
"; //TODO: add controls Common::String itemTemplate = "{name}{size}\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
%s", + errorMessage.c_str(), + _("Back to the files manager") + ) + ); + return; + } + + handleFiles(client); +} + void FilesPageHandler::handleErrorMessage(Client &client, Common::String message) { Common::String response = "ScummVM{message}"; @@ -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 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 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 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 in + 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 6205dcd52c..e5a32c98b0 100644 --- a/backends/networking/sdl_net/handlers/filespagehandler.h +++ b/backends/networking/sdl_net/handlers/filespagehandler.h @@ -29,8 +29,19 @@ 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 in . + * + * Fills on failure. + * + * Returns true on success. + */ + bool createDirectory(Common::String path, Common::String name, Common::String &errorMessage); + /** * Lists the directory . * diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index c3df9f8819..c8f322dad6 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 1d397e6dcc..2e56209ab0 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 4d1be97524..de683a05d4 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 2baf4a02dc..de29ac3fa3 100644 --- a/backends/networking/wwwroot/.files.html +++ b/backends/networking/wwwroot/.files.html @@ -18,6 +18,7 @@
+
{loading}
+
{error}
@@ -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 c1bc14d652..ba31587c4d 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; +} -- cgit v1.2.3 From da229dd84c5761f80b16c25edba7bfb738fc835f Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 19 Jul 2016 13:41:25 +0600 Subject: CLOUD: Add "ajax" parameter for "/create" and "/upload" If it's set, these redirect to "/filesAJAX" instead of "/files". --- .../sdl_net/handlers/createdirectoryhandler.cpp | 4 +++- .../sdl_net/handlers/filesajaxpagehandler.cpp | 14 ++++++++++++++ .../networking/sdl_net/uploadfileclienthandler.cpp | 8 +++++--- backends/networking/wwwroot.zip | Bin 241872 -> 241963 bytes backends/networking/wwwroot/.filesAJAX.html | 7 ++++--- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp index 97d75fcf14..fc02b643c1 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 17be7faad2..df4482166a 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 7cbb11874c..7e93573ca5 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 7e53969589..6f821d9090 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 16a4b5417d..d648466d2c 100644 --- a/backends/networking/wwwroot/.filesAJAX.html +++ b/backends/networking/wwwroot/.filesAJAX.html @@ -19,13 +19,14 @@

{create_directory_desc}

+