diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/callback.h | 138 | ||||
-rw-r--r-- | common/config-manager.cpp | 26 | ||||
-rw-r--r-- | common/config-manager.h | 9 | ||||
-rw-r--r-- | common/file.cpp | 17 | ||||
-rw-r--r-- | common/file.h | 2 | ||||
-rw-r--r-- | common/json.cpp | 1099 | ||||
-rw-r--r-- | common/json.h | 166 | ||||
-rw-r--r-- | common/memstream.h | 83 | ||||
-rw-r--r-- | common/module.mk | 1 | ||||
-rw-r--r-- | common/platform.cpp | 1 | ||||
-rw-r--r-- | common/platform.h | 1 | ||||
-rw-r--r-- | common/savefile.h | 32 | ||||
-rw-r--r-- | common/str.cpp | 18 | ||||
-rw-r--r-- | common/str.h | 20 | ||||
-rw-r--r-- | common/system.h | 93 | ||||
-rw-r--r-- | common/xmlparser.cpp | 2 |
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"; |