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