diff options
author | Alexander Tkachev | 2016-05-11 01:10:37 +0600 |
---|---|---|
committer | Alexander Tkachev | 2016-08-24 16:05:07 +0600 |
commit | 8429c40362b1e4d4010f808740b79baae69c97ed (patch) | |
tree | 8916f123e0dfadd807cfccae0446e4627102d1ed | |
parent | 380902eb071fbe960e615d0c29fc1f5099aa9af8 (diff) | |
download | scummvm-rg350-8429c40362b1e4d4010f808740b79baae69c97ed.tar.gz scummvm-rg350-8429c40362b1e4d4010f808740b79baae69c97ed.tar.bz2 scummvm-rg350-8429c40362b1e4d4010f808740b79baae69c97ed.zip |
CLOUD: Add SimpleJSON library as Common::JSON
This commit also adds CloudThread class, which work() method is called
every second by TimerManager.
Right now it prints JSON examples on the console, so that's why it's
introduced with SimpleJSON library.
-rw-r--r-- | backends/cloud/cloudthread.cpp | 199 | ||||
-rw-r--r-- | backends/cloud/cloudthread.h | 35 | ||||
-rw-r--r-- | base/main.cpp | 6 | ||||
-rw-r--r-- | common/json.cpp | 1064 | ||||
-rw-r--r-- | common/json.h | 205 |
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 |