aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/callback.h138
-rw-r--r--common/config-manager.cpp26
-rw-r--r--common/config-manager.h9
-rw-r--r--common/file.cpp17
-rw-r--r--common/file.h2
-rw-r--r--common/json.cpp1099
-rw-r--r--common/json.h166
-rw-r--r--common/memstream.h83
-rw-r--r--common/module.mk1
-rw-r--r--common/platform.cpp1
-rw-r--r--common/platform.h1
-rw-r--r--common/savefile.h32
-rw-r--r--common/str.cpp18
-rw-r--r--common/str.h20
-rw-r--r--common/system.h93
-rw-r--r--common/xmlparser.cpp2
16 files changed, 1699 insertions, 9 deletions
diff --git a/common/callback.h b/common/callback.h
new file mode 100644
index 0000000000..c6c249a511
--- /dev/null
+++ b/common/callback.h
@@ -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.
+ *
+ */
+
+#ifndef COMMON_CALLBACK_H
+#define COMMON_CALLBACK_H
+
+namespace Common {
+
+/**
+ * BaseCallback<S> is a simple base class for object-oriented callbacks.
+ *
+ * Object-oriented callbacks are such callbacks that know exact instance
+ * which method must be called.
+ *
+ * For backwards compatibility purposes, there is a GlobalFunctionCallback,
+ * which is BaseCallback<void *>, so it can be used with global C-like
+ * functions too.
+ *
+ * <S> is the type, which is passed to operator() of this callback.
+ * This allows you to specify that you accept a callback, which wants
+ * to receive an <S> object.
+ */
+template<typename S = void *> class BaseCallback {
+public:
+ BaseCallback() {}
+ virtual ~BaseCallback() {}
+ virtual void operator()(S data) = 0;
+};
+
+/**
+ * GlobalFunctionCallback<T> is a simple wrapper for global C functions.
+ *
+ * If there is a method, which accepts BaseCallback<T>, you can
+ * easily pass your C function by passing
+ * new GlobalFunctionCallback<T>(yourFunction)
+ */
+template<typename T> class GlobalFunctionCallback: public BaseCallback<T> {
+ typedef void(*GlobalFunction)(T result);
+ GlobalFunction _callback;
+
+public:
+ GlobalFunctionCallback(GlobalFunction cb): _callback(cb) {}
+ virtual ~GlobalFunctionCallback() {}
+ virtual void operator()(T data) {
+ if (_callback) _callback(data);
+ }
+};
+
+/**
+ * Callback<T, S> implements an object-oriented callback.
+ *
+ * <T> stands for a class which method you want to call.
+ * <S>, again, is the type of an object passed to operator().
+ *
+ * So, if you have void MyClass::myMethod(AnotherClass) method,
+ * the corresponding callback is Callback<MyClass, AnotherClass>.
+ * You create it similarly to this:
+ * new Callback<MyClass, AnotherClass>(
+ * pointerToMyClassObject,
+ * &MyClass::myMethod
+ * )
+ */
+template<class T, typename S = void *> class Callback: public BaseCallback<S> {
+protected:
+ typedef void(T::*TMethod)(S);
+ T *_object;
+ TMethod _method;
+public:
+ Callback(T *object, TMethod method): _object(object), _method(method) {}
+ virtual ~Callback() {}
+ void operator()(S data) { (_object->*_method)(data); }
+};
+
+/**
+ * CallbackBridge<T, OS, S> helps you to chain callbacks.
+ *
+ * CallbackBridge keeps a pointer to BaseCallback<OS>.
+ * When its operator() is called, it passes this pointer
+ * along with the actual data (of type <S>) to the method
+ * of <T> class.
+ *
+ * This is needed when you have to call a callback only
+ * when your own callback is called. So, your callback
+ * is "inner" and the other one is "outer".
+ *
+ * CallbackBridge implements the "inner" one and calls
+ * the method you wanted. It passes the "outer", so you
+ * can call it from your method. You can ignore it, but
+ * probably there is no point in using CallbackBridge then.
+ *
+ * So, if you receive a BaseCallback<SomeClass> callback
+ * and you want to call it from your MyClass::myMethod method,
+ * you should create CallbackBridge<MyClass, SomeClass, S>,
+ * where <S> is data type you want to receive in MyClass::myMethod.
+ *
+ * You create it similarly to this:
+ * new Callback<MyClass, SomeClass, AnotherClass>(
+ * pointerToMyClassObject,
+ * &MyClass::myMethod,
+ * outerCallback
+ * )
+ * where `outerCallback` is BaseCallback<SomeClass> and `myMethod` is:
+ * void MyClass::myMethod(BaseCallback<SomeClass> *, AnotherClass)
+ */
+template<class T, typename OS, typename S = void *> class CallbackBridge: public BaseCallback<S> {
+ typedef void(T::*TCallbackMethod)(BaseCallback<OS> *, S);
+ T *_object;
+ TCallbackMethod _method;
+ BaseCallback<OS> *_outerCallback;
+public:
+ CallbackBridge(T *object, TCallbackMethod method, BaseCallback<OS> *outerCallback):
+ _object(object), _method(method), _outerCallback(outerCallback) {}
+ virtual ~CallbackBridge() {}
+ void operator()(S data) { (_object->*_method)(_outerCallback, data); }
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/common/config-manager.cpp b/common/config-manager.cpp
index 24c877a4e9..fdd0c6f033 100644
--- a/common/config-manager.cpp
+++ b/common/config-manager.cpp
@@ -45,6 +45,10 @@ char const *const ConfigManager::kTransientDomain = "__TRANSIENT";
char const *const ConfigManager::kKeymapperDomain = "keymapper";
#endif
+#ifdef USE_CLOUD
+char const *const ConfigManager::kCloudDomain = "cloud";
+#endif
+
#pragma mark -
@@ -67,6 +71,9 @@ void ConfigManager::copyFrom(ConfigManager &source) {
#ifdef ENABLE_KEYMAPPER
_keymapperDomain = source._keymapperDomain;
#endif
+#ifdef USE_CLOUD
+ _cloudDomain = source._cloudDomain;
+#endif
_domainSaveOrder = source._domainSaveOrder;
_activeDomainName = source._activeDomainName;
_activeDomain = &_gameDomains[_activeDomainName];
@@ -121,6 +128,10 @@ void ConfigManager::addDomain(const String &domainName, const ConfigManager::Dom
} else if (domainName == kKeymapperDomain) {
_keymapperDomain = domain;
#endif
+#ifdef USE_CLOUD
+ } else if (domainName == kCloudDomain) {
+ _cloudDomain = domain;
+#endif
} else if (domain.contains("gameid")) {
// If the domain contains "gameid" we assume it's a game domain
if (_gameDomains.contains(domainName))
@@ -160,6 +171,9 @@ void ConfigManager::loadFromStream(SeekableReadStream &stream) {
#ifdef ENABLE_KEYMAPPER
_keymapperDomain.clear();
#endif
+#ifdef USE_CLOUD
+ _cloudDomain.clear();
+#endif
// TODO: Detect if a domain occurs multiple times (or likewise, if
// a key occurs multiple times inside one domain).
@@ -272,6 +286,10 @@ void ConfigManager::flushToDisk() {
// Write the keymapper domain
writeDomain(*stream, kKeymapperDomain, _keymapperDomain);
#endif
+#ifdef USE_CLOUD
+ // Write the cloud domain
+ writeDomain(*stream, kCloudDomain, _cloudDomain);
+#endif
DomainMap::const_iterator d;
@@ -359,6 +377,10 @@ const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) con
if (domName == kKeymapperDomain)
return &_keymapperDomain;
#endif
+#ifdef USE_CLOUD
+ if (domName == kCloudDomain)
+ return &_cloudDomain;
+#endif
if (_gameDomains.contains(domName))
return &_gameDomains[domName];
if (_miscDomains.contains(domName))
@@ -379,6 +401,10 @@ ConfigManager::Domain *ConfigManager::getDomain(const String &domName) {
if (domName == kKeymapperDomain)
return &_keymapperDomain;
#endif
+#ifdef USE_CLOUD
+ if (domName == kCloudDomain)
+ return &_cloudDomain;
+#endif
if (_gameDomains.contains(domName))
return &_gameDomains[domName];
if (_miscDomains.contains(domName))
diff --git a/common/config-manager.h b/common/config-manager.h
index 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<String> _domainSaveOrder;
String _activeDomainName;
diff --git a/common/file.cpp b/common/file.cpp
index 4d9c630076..9797bcaa69 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,10 +151,23 @@ DumpFile::~DumpFile() {
close();
}
-bool DumpFile::open(const String &filename) {
+bool DumpFile::open(const String &filename, bool createPath) {
assert(!filename.empty());
assert(!_handle);
+ if (createPath) {
+ for (uint32 i = 0; i < filename.size(); ++i) {
+ 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");
+ }
+ }
+ }
+
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();
diff --git a/common/json.cpp b/common/json.cpp
new file mode 100644
index 0000000000..f7a54d23b2
--- /dev/null
+++ b/common/json.cpp
@@ -0,0 +1,1099 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 - https://github.com/MJPA/SimpleJSON
+ *
+ * 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 "common/json.h"
+
+#ifdef __MINGW32__
+#define wcsncasecmp wcsnicmp
+#endif
+
+// Macros to free an array/object
+#define FREE_ARRAY(x) { JSONArray::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete *iter; } }
+#define FREE_OBJECT(x) { JSONObject::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete (*iter)._value; } }
+
+namespace Common {
+
+/**
+* Blocks off the public constructor
+*
+* @access private
+*
+*/
+JSON::JSON() {}
+
+/**
+* Parses a complete JSON encoded string (UNICODE input version)
+*
+* @access public
+*
+* @param char* data The JSON text
+*
+* @return JSONValue* Returns a JSON Value representing the root, or NULL on error
+*/
+JSONValue *JSON::parse(const char *data) {
+ // Skip any preceding whitespace, end of data = no JSON = fail
+ if (!skipWhitespace(&data))
+ return NULL;
+
+ // We need the start of a value here now...
+ JSONValue *value = JSONValue::parse(&data);
+ if (value == NULL)
+ return NULL;
+
+ // Can be white space now and should be at the end of the string then...
+ if (skipWhitespace(&data)) {
+ delete value;
+ return NULL;
+ }
+
+ // We're now at the end of the string
+ return value;
+}
+
+/**
+* Turns the passed in JSONValue into a JSON encode string
+*
+* @access public
+*
+* @param JSONValue* value The root value
+*
+* @return String Returns a JSON encoded string representation of the given value
+*/
+String JSON::stringify(const JSONValue *value) {
+ if (value != NULL)
+ return value->stringify();
+ else
+ return "";
+}
+
+/**
+* Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return bool Returns true if there is more data, or false if the end of the text was reached
+*/
+bool JSON::skipWhitespace(const char **data) {
+ while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
+ (*data)++;
+
+ return **data != 0;
+}
+
+/**
+* Extracts a JSON String as defined by the spec - "<some chars>"
+* Any escaped characters are swapped out for their unescaped values
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+* @param String& str Reference to a String to receive the extracted string
+*
+* @return bool Returns true on success, false on failure
+*/
+bool JSON::extractString(const char **data, String &str) {
+ str = "";
+
+ while (**data != 0) {
+ // Save the char so we can change it if need be
+ char next_char = **data;
+
+ // Escaping something?
+ if (next_char == '\\') {
+ // Move over the escape char
+ (*data)++;
+
+ // Deal with the escaped char
+ switch (**data) {
+ case '"': next_char = '"';
+ break;
+ case '\\': next_char = '\\';
+ break;
+ case '/': next_char = '/';
+ break;
+ case 'b': next_char = '\b';
+ break;
+ case 'f': next_char = '\f';
+ break;
+ case 'n': next_char = '\n';
+ break;
+ case 'r': next_char = '\r';
+ break;
+ case 't': next_char = '\t';
+ break;
+ case 'u': {
+ // We need 5 chars (4 hex + the 'u') or its not valid
+ if (!simplejson_wcsnlen(*data, 5))
+ return false;
+
+ // Deal with the chars
+ next_char = 0;
+ for (int i = 0; i < 4; i++) {
+ // Do it first to move off the 'u' and leave us on the
+ // final hex digit as we move on by one later on
+ (*data)++;
+
+ next_char <<= 4;
+
+ // Parse the hex digit
+ if (**data >= '0' && **data <= '9')
+ next_char |= (**data - '0');
+ else if (**data >= 'A' && **data <= 'F')
+ next_char |= (10 + (**data - 'A'));
+ else if (**data >= 'a' && **data <= 'f')
+ next_char |= (10 + (**data - 'a'));
+ else {
+ // Invalid hex digit = invalid JSON
+ return false;
+ }
+ }
+ break;
+ }
+
+ // By the spec, only the above cases are allowed
+ default:
+ return false;
+ }
+ }
+
+ // End of the string?
+ else if (next_char == '"') {
+ (*data)++;
+ //str.reserve(); // Remove unused capacity //TODO
+ return true;
+ }
+
+ // Disallowed char?
+ else if (next_char < ' ' && next_char != '\t') {
+ // SPEC Violation: Allow tabs due to real world cases
+ return false;
+ }
+
+ // Add the next char
+ str += next_char;
+
+ // Move on
+ (*data)++;
+ }
+
+ // If we're here, the string ended incorrectly
+ return false;
+}
+
+/**
+* Parses some text as though it is an integer
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return double Returns the double value of the number found
+*/
+double JSON::parseInt(const char **data) {
+ double integer = 0;
+ while (**data != 0 && **data >= '0' && **data <= '9')
+ integer = integer * 10 + (*(*data)++ - '0');
+
+ return integer;
+}
+
+/**
+* Parses some text as though it is a decimal
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+*
+* @return double Returns the double value of the decimal found
+*/
+double JSON::parseDecimal(const char **data) {
+ double decimal = 0.0;
+ double factor = 0.1;
+ while (**data != 0 && **data >= '0' && **data <= '9') {
+ int digit = (*(*data)++ - '0');
+ decimal = decimal + digit * factor;
+ factor *= 0.1;
+ }
+ return decimal;
+}
+
+/**
+* Parses a JSON encoded value to a JSONValue object
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the data
+*
+* @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error
+*/
+JSONValue *JSONValue::parse(const char **data) {
+ // Is it a string?
+ if (**data == '"') {
+ String str;
+ if (!JSON::extractString(&(++(*data)), str))
+ return NULL;
+ else
+ return new JSONValue(str);
+ }
+
+ // Is it a boolean?
+ else if ((simplejson_wcsnlen(*data, 4) && 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) && scumm_strnicmp(*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)++;
+
+ long long 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 = integer = 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;
+ onlyInteger = false;
+ }
+
+ // 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);
+ onlyInteger = false;
+ }
+
+ // Was it neg?
+ if (neg) number *= -1;
+
+ if (onlyInteger)
+ return new JSONValue(neg ? -integer : integer);
+
+ 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 *charValue) {
+ _type = JSONType_String;
+ _stringValue = new String(String(charValue));
+}
+
+/**
+* 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 &stringValue) {
+ _type = JSONType_String;
+ _stringValue = new String(stringValue);
+}
+
+/**
+* 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 boolValue) {
+ _type = JSONType_Bool;
+ _boolValue = boolValue;
+}
+
+/**
+* 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 numberValue) {
+ _type = JSONType_Number;
+ _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(long long int numberValue) {
+ _type = JSONType_IntegerNumber;
+ _integerValue = numberValue;
+}
+
+/**
+* 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 &arrayValue) {
+ _type = JSONType_Array;
+ _arrayValue = new JSONArray(arrayValue);
+}
+
+/**
+* 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 &objectValue) {
+ _type = JSONType_Object;
+ _objectValue = new JSONObject(objectValue);
+}
+
+/**
+* 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 &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_IntegerNumber:
+ _integerValue = source._integerValue;
+ 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;
+ }
+}
+
+/**
+* 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 = _arrayValue->begin(); iter != _arrayValue->end(); iter++)
+ delete *iter;
+ delete _arrayValue;
+ } else if (_type == JSONType_Object) {
+ JSONObject::iterator iter;
+ for (iter = _objectValue->begin(); iter != _objectValue->end(); iter++) {
+ delete (*iter)._value;
+ }
+ delete _objectValue;
+ } else if (_type == JSONType_String) {
+ delete _stringValue;
+ }
+}
+
+/**
+* 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 Integer
+*
+* @access public
+*
+* @return bool Returns true if it is an Integer value, false otherwise
+*/
+bool JSONValue::isIntegerNumber() const {
+ return _type == JSONType_IntegerNumber;
+}
+
+/**
+* Checks if the value is an Array
+*
+* @access public
+*
+* @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 (*_stringValue);
+}
+
+/**
+* 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 _boolValue;
+}
+
+/**
+* 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 _numberValue;
+}
+
+/**
+* Retrieves the Integer value of this JSONValue
+* Use isIntegerNumber() before using this method.
+*
+* @access public
+*
+* @return int Returns the number value
+*/
+long long int JSONValue::asIntegerNumber() const {
+ return _integerValue;
+}
+
+/**
+* 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 (*_arrayValue);
+}
+
+/**
+* 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 (*_objectValue);
+}
+
+/**
+* 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 _arrayValue->size();
+ case JSONType_Object:
+ return _objectValue->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 < _arrayValue->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 < _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.
+*
+* @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 _objectValue->find(name) != _objectValue->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 = _objectValue->find(name);
+ if (it != _objectValue->end()) {
+ return it->_value;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+* Retrieves the keys of the JSON Object or an empty vector
+* if this value is not an object.
+*
+* @access public
+*
+* @return std::vector<String> A vector containing the keys.
+*/
+Array<String> JSONValue::objectKeys() const {
+ Array<String> keys;
+
+ if (_type == JSONType_Object) {
+ JSONObject::const_iterator iter = _objectValue->begin();
+ while (iter != _objectValue->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(*_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, "%lg", _numberValue);
+ ret_string = str;
+ }
+ break;
+ }
+
+ case JSONType_IntegerNumber: {
+ char str[80];
+ sprintf(str, "%lld", _integerValue);
+ 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);
+
+ // 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;
+ }
+ }
+
+ 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 (size_t 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..3b219993e7
--- /dev/null
+++ b/common/json.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 - https://github.com/MJPA/SimpleJSON
+ *
+ * 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 "common/array.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/str.h"
+
+// Win32 incompatibilities
+#if defined(WIN32) && !defined(__GNUC__)
+static inline bool isnan(double x) {
+ return x != x;
+}
+
+static inline bool isinf(double x) {
+ return !isnan(x) && isnan(x - x);
+}
+#endif
+
+// Simple function to check a string 's' has at least 'n' characters
+static inline bool simplejson_wcsnlen(const char *s, size_t n) {
+ if (s == 0)
+ return false;
+
+ const char *save = s;
+ while (n-- > 0) {
+ if (*(save++) == 0) return false;
+ }
+
+ return true;
+}
+
+namespace Common {
+
+// Custom types
+class JSONValue;
+typedef Array<JSONValue*> JSONArray;
+typedef HashMap<String, JSONValue*> JSONObject;
+
+class JSON;
+
+enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_IntegerNumber, JSONType_Array, JSONType_Object };
+
+class JSONValue {
+ friend class JSON;
+
+public:
+ JSONValue(/*NULL*/);
+ JSONValue(const char *charValue);
+ JSONValue(const String &stringValue);
+ JSONValue(bool boolValue);
+ JSONValue(double numberValue);
+ JSONValue(long long int 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 isIntegerNumber() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ const String &asString() const;
+ bool asBool() const;
+ double asNumber() const;
+ long long int asIntegerNumber() const;
+ const JSONArray &asArray() const;
+ const JSONObject &asObject() const;
+
+ size_t countChildren() const;
+ bool hasChild(size_t index) const;
+ JSONValue *child(size_t index);
+ bool hasChild(const char *name) const;
+ JSONValue *child(const char *name);
+ Array<String> objectKeys() const;
+
+ String stringify(bool const prettyprint = false) const;
+protected:
+ static JSONValue *parse(const char **data);
+
+private:
+ static String stringifyString(const String &str);
+ String stringifyImpl(size_t const indentDepth) const;
+ static String indent(size_t depth);
+
+ JSONType _type;
+
+ union {
+ bool _boolValue;
+ double _numberValue;
+ long long int _integerValue;
+ String *_stringValue;
+ JSONArray *_arrayValue;
+ JSONObject *_objectValue;
+ };
+
+};
+
+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
diff --git a/common/memstream.h b/common/memstream.h
index 59d5f15b1a..25fdde91c7 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 = _length;
+ _readPos = 0;
+ } else {
+ memcpy(_data, old_data + _readPos, oldCapacity - _readPos);
+ memcpy(_data + oldCapacity - _readPos, old_data, _writePos);
+ _writePos = _length;
+ _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;
+ }
+
+ virtual 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;
+ }
+
+ int32 pos() const { return _pos - _length; } //'read' position in the stream
+ uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+
+ byte *getData() { return _data; }
+};
+
} // End of namespace Common
#endif
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 \
diff --git a/common/platform.cpp b/common/platform.cpp
index 280185d862..6898993b33 100644
--- a/common/platform.cpp
+++ b/common/platform.cpp
@@ -31,6 +31,7 @@ const PlatformDescription g_platforms[] = {
{ "3do", "3do", "3do", "3DO", kPlatform3DO },
{ "acorn", "acorn", "acorn", "Acorn", kPlatformAcorn },
{ "amiga", "ami", "amiga", "Amiga", kPlatformAmiga },
+ { "atari8", "atari8", "atari8", "Atari 8-bit", kPlatformAtari8Bit },
{ "atari", "atari-st", "st", "Atari ST", kPlatformAtariST },
{ "c64", "c64", "c64", "Commodore 64", kPlatformC64 },
{ "pc", "dos", "ibm", "DOS", kPlatformDOS },
diff --git a/common/platform.h b/common/platform.h
index 15bcddb62e..d6284be713 100644
--- a/common/platform.h
+++ b/common/platform.h
@@ -38,6 +38,7 @@ class String;
enum Platform {
kPlatformDOS,
kPlatformAmiga,
+ kPlatformAtari8Bit,
kPlatformAtariST,
kPlatformMacintosh,
kPlatformFMTowns,
diff --git a/common/savefile.h b/common/savefile.h
index 9fca07f9d5..eb7e6f946e 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,21 @@ typedef SeekableReadStream InSaveFile;
* That typically means "save games", but also includes things like the
* IQ points in Indy3.
*/
-typedef WriteStream OutSaveFile;
+class OutSaveFile: public WriteStream {
+protected:
+ ScopedPtr<WriteStream> _wrapped;
+public:
+ OutSaveFile(WriteStream *w);
+ virtual ~OutSaveFile();
+
+ virtual bool err() const;
+ virtual void clearErr();
+ virtual void finalize();
+ virtual bool flush();
+ virtual uint32 write(const void *dataPtr, uint32 dataSize);
+ virtual int32 pos() const;
+};
/**
* The SaveFileManager is serving as a factory for InSaveFile
@@ -137,6 +151,15 @@ public:
virtual InSaveFile *openForLoading(const String &name) = 0;
/**
+ * Open the file with the specified name in the given directory for loading.
+ * In contrast to openForLoading(), it returns raw file instead of unpacked.
+ *
+ * @param name The name of the savefile.
+ * @return Pointer to an InSaveFile, or NULL if an error occurred.
+ */
+ virtual InSaveFile *openRawFile(const String &name) = 0;
+
+ /**
* Removes the given savefile from the system.
*
* @param name The name of the savefile to be removed.
@@ -174,6 +197,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/common/str.cpp b/common/str.cpp
index 3ff49a90c6..90bd539790 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);
}
@@ -829,6 +838,15 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMod
}
}
+void replace(Common::String &source, const Common::String &what, const Common::String &with) {
+ const char *cstr = source.c_str();
+ const char *position = strstr(cstr, what.c_str());
+ if (position) {
+ uint32 index = position - cstr;
+ source.replace(index, what.size(), with);
+ }
+}
+
String tag2string(uint32 tag) {
char str[5];
str[0] = (char)(tag >> 24);
diff --git a/common/str.h b/common/str.h
index 9ada8aaaa0..d55ba072a9 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
@@ -233,12 +236,12 @@ public:
void trim();
uint hash() const;
-
+
/**@{
* Functions to replace some amount of chars with chars from some other string.
*
* @note The implementation follows that of the STL's std::string:
- * http://www.cplusplus.com/reference/string/string/replace/
+ * http://www.cplusplus.com/reference/string/string/replace/
*
* @param pos Starting position for the replace in the original string.
* @param count Number of chars to replace from the original string.
@@ -247,7 +250,7 @@ public:
* @param countOri Same as count
* @param posDest Initial position to read str from.
* @param countDest Number of chars to read from str. npos by default.
- */
+ */
// Replace 'count' bytes, starting from 'pos' with str.
void replace(uint32 pos, uint32 count, const String &str);
// The same as above, but accepts a C-like array of characters.
@@ -264,7 +267,7 @@ public:
// str[posDest, posDest + countDest)
void replace(uint32 posOri, uint32 countOri, const char *str,
uint32 posDest, uint32 countDest);
- /**@}*/
+ /**@}*/
/**
* Print formatted data into a String object. Similar to sprintf,
@@ -387,6 +390,15 @@ String normalizePath(const String &path, const char sep);
*/
bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
+/**
+ * Function which replaces substring with the other. It happens in place.
+ * If there is no substring found, original string is not changed.
+ *
+ * @param source String to search and replace substring in.
+ * @param what Substring to replace.
+ * @param with String to replace with.
+ */
+void replace(Common::String &source, const Common::String &what, const Common::String &with);
/**
* Take a 32 bit value and turn it into a four character string, where each of
diff --git a/common/system.h b/common/system.h
index 6d185d3075..eb9a39bbdf 100644
--- a/common/system.h
+++ b/common/system.h
@@ -314,7 +314,23 @@ public:
*
* This feature has no associated state.
*/
- kFeatureDisplayLogFile
+ kFeatureDisplayLogFile,
+
+ /**
+ * The presence of this feature indicates whether the hasTextInClipboard()
+ * and getTextFromClipboard() calls are supported.
+ *
+ * This feature has no associated state.
+ */
+ kFeatureClipboardSupport,
+
+ /**
+ * The presence of this feature indicates whether the openUrl()
+ * call is supported.
+ *
+ * This feature has no associated state.
+ */
+ kFeatureOpenUrl
};
/**
@@ -1086,6 +1102,45 @@ 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;
+
+ /**
+ * Clears 'on screen display' from everything drawn on it.
+ */
+
+ virtual void clearOSD() = 0;
+
+ /**
+ * Returns 'on screen display' pixel format.
+ */
+
+ virtual Graphics::PixelFormat getOSDFormat() = 0;
+
+ /**
* Return the SaveFileManager, used to store and load savestates
* and other modifiable persistent game data. For more information,
* refer to the SaveFileManager documentation.
@@ -1200,6 +1255,42 @@ public:
virtual bool displayLogFile() { return false; }
/**
+ * Returns whether there is text available in the clipboard.
+ *
+ * The kFeatureClipboardSupport feature flag can be used to
+ * test whether this call has been implemented by the active
+ * backend.
+ *
+ * @return true if there is text in the clipboard, false otherwise
+ */
+ virtual bool hasTextInClipboard() { return false; }
+
+ /**
+ * Returns clipboard contents as a String.
+ *
+ * The kFeatureClipboardSupport feature flag can be used to
+ * test whether this call has been implemented by the active
+ * backend.
+ *
+ * @return clipboard contents ("" if hasTextInClipboard() == false)
+ */
+ virtual Common::String getTextFromClipboard() { return ""; }
+
+ /**
+ * Open the given Url in the default browser (if available on the target
+ * system).
+ *
+ * @return true on success, false otherwise.
+ *
+ * @note It is up to the backend to ensure that the system is in a state
+ * that allows the user to actually see the web page. This might for
+ * example require leaving fullscreen mode.
+ *
+ * @parem url the URL to open
+ */
+ virtual bool openUrl(const Common::String &url) {return false; }
+
+ /**
* Returns the locale of the system.
*
* This returns the currently set up locale of the system, on which
diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp
index da4f577e3c..4470180710 100644
--- a/common/xmlparser.cpp
+++ b/common/xmlparser.cpp
@@ -128,7 +128,7 @@ bool XMLParser::parserError(const String &errStr) {
while (currentPosition--)
errorMessage += (char)_stream->readByte();
}
-
+
errorMessage += "\n\nParser error: ";
errorMessage += errStr;
errorMessage += "\n\n";