diff --git a/sources/Data/JSONArray.uc b/sources/Data/JSONArray.uc
new file mode 100644
index 0000000..d0e9e71
--- /dev/null
+++ b/sources/Data/JSONArray.uc
@@ -0,0 +1,351 @@
+/**
+ * This class implements JSON array storage capabilities.
+ * Array stores ordered JSON values that can be referred by their index.
+ * It can contain any mix of JSON value types and cannot have any gaps,
+ * i.e. in array of length N, there must be a valid value for all indices
+ * from 0 to N-1.
+ * Copyright 2020 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia 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, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia 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 Acedia. If not, see .
+ */
+class JSONArray extends JSONBase;
+
+// Data will simply be stored as an array of JSON values
+var private array data;
+
+// Return type of value stored at a given index.
+// Returns `JSON_Undefined` if and only if given index is out of bounds.
+public final function JSONType GetType(int index)
+{
+ if (index < 0) return JSON_Undefined;
+ if (index >= data.length) return JSON_Undefined;
+
+ return data[index].type;
+}
+
+// Returns current length of this array.
+public final function int GetLength()
+{
+ return data.length;
+}
+
+// Changes length of this array.
+// In case of the increase - fills new indices with `null` values.
+public final function SetLength(int newLength)
+{
+ local int i;
+ local int oldLength;
+ oldLength = data.length;
+ data.length = newLength;
+ if (oldLength >= newLength)
+ {
+ return;
+ }
+ i = oldLength;
+ while (i < newLength)
+ {
+ SetNull(i);
+ i += 1;
+ }
+}
+
+// Following functions are getters for various types of variables.
+// Getter for null value simply checks if it's null
+// and returns true/false as a result.
+// Getters for simple types (number, string, boolean) can have optional
+// default value specified, that will be returned if requested variable
+// doesn't exist or has a different type.
+// Getters for object and array types don't take default values and
+// will simply return `none`.
+public final function float GetNumber(int index, optional float defaultValue)
+{
+ if (index < 0) return defaultValue;
+ if (index >= data.length) return defaultValue;
+ if (data[index].type != JSON_Number) return defaultValue;
+
+ return data[index].numberValue;
+}
+
+public final function string GetString(int index, optional string defaultValue)
+{
+ if (index < 0) return defaultValue;
+ if (index >= data.length) return defaultValue;
+ if (data[index].type != JSON_String) return defaultValue;
+
+ return data[index].stringValue;
+}
+
+public final function bool GetBoolean(int index, optional bool defaultValue)
+{
+ if (index < 0) return defaultValue;
+ if (index >= data.length) return defaultValue;
+ if (data[index].type != JSON_Boolean) return defaultValue;
+
+ return data[index].booleanValue;
+}
+
+public final function bool IsNull(int index)
+{
+ if (index < 0) return false;
+ if (index >= data.length) return false;
+
+ return (data[index].type == JSON_Null);
+}
+
+public final function JSONArray GetArray(int index)
+{
+ if (index < 0) return none;
+ if (index >= data.length) return none;
+ if (data[index].type != JSON_Array) return none;
+
+ return JSONArray(data[index].complexValue);
+}
+
+public final function JSONObject GetObject(int index)
+{
+ if (index < 0) return none;
+ if (index >= data.length) return none;
+ if (data[index].type != JSON_Object) return none;
+
+ return JSONObject(data[index].complexValue);
+}
+
+// Following functions provide simple setters for boolean, string, number
+// and null values.
+// If passed index is negative - does nothing.
+// If index lies beyond array length (`>= GetLength()`), -
+// these functions will expand array in the same way as `GetLength()` function.
+// This can be prevented by setting optional parameter `preventExpansion` to
+// `false` (nothing will be done in this case).
+// They return object itself, allowing user to chain calls like this:
+// `array.SetNumber("num1", 1).SetNumber("num2", 2);`.
+public final function JSONArray SetNumber
+(
+ int index,
+ float value,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_Number;
+ newStorageValue.numberValue = value;
+ data[index] = newStorageValue;
+ return self;
+}
+
+public final function JSONArray SetString
+(
+ int index,
+ string value,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_String;
+ newStorageValue.stringValue = value;
+ data[index] = newStorageValue;
+ return self;
+}
+
+public final function JSONArray SetBoolean
+(
+ int index,
+ bool value,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_Boolean;
+ newStorageValue.booleanValue = value;
+ data[index] = newStorageValue;
+ return self;
+}
+
+public final function JSONArray SetNull
+(
+ int index,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_Null;
+ data[index] = newStorageValue;
+ return self;
+}
+
+// JSON array and object types don't have setters, but instead have
+// functions to create a new, empty array/object under a certain name.
+// If passed index is negative - does nothing.
+// If index lies beyond array length (`>= GetLength()`), -
+// these functions will expand array in the same way as `GetLength()` function.
+// This can be prevented by setting optional parameter `preventExpansion` to
+// `false` (nothing will be done in this case).
+// They return object itself, allowing user to chain calls like this:
+// `array.CreateObject("sub object").CreateArray("sub array");`.
+public final function JSONArray CreateArray
+(
+ int index,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_Array;
+ newStorageValue.complexValue = new class'JSONArray';
+ data[index] = newStorageValue;
+ return self;
+}
+
+public final function JSONArray CreateObject
+(
+ int index,
+ optional bool preventExpansion
+)
+{
+ local JSONStorageValue newStorageValue;
+ if (index < 0) return self;
+
+ if (index >= data.length)
+ {
+ if (preventExpansion)
+ {
+ return self;
+ }
+ else
+ {
+ SetLength(index + 1);
+ }
+ }
+ newStorageValue.type = JSON_Object;
+ newStorageValue.complexValue = new class'JSONObject';
+ data[index] = newStorageValue;
+ return self;
+}
+
+// Wrappers for setter functions that don't take index or
+// `preventExpansion` parameters and add/create value at the end of the array.
+public final function JSONArray AddNumber(float value)
+{
+ return SetNumber(data.length, value);
+}
+
+public final function JSONArray AddString(string value)
+{
+ return SetString(data.length, value);
+}
+
+public final function JSONArray AddBoolean(bool value)
+{
+ return SetBoolean(data.length, value);
+}
+
+public final function JSONArray AddNull()
+{
+ return SetNull(data.length);
+}
+
+public final function JSONArray AddArray()
+{
+ return CreateArray(data.length);
+}
+
+public final function JSONArray AddObject()
+{
+ return CreateObject(data.length);
+}
+
+// Removes up to `amount` of values, starting from a given index.
+// If `index` falls outside array boundaries - nothing will be done.
+// Returns `true` if value was actually removed and `false` if it didn't exist.
+public final function bool RemoveValue(int index, optional int amount)
+{
+ if (index < 0) return false;
+ if (index >= data.length) return false;
+ if (amount < 1) return false;
+
+ amount = Max(amount, 1);
+ amount = Min(amount, data.length - index);
+ data.Remove(index, amount);
+ return true;
+}
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Data/JSONBase.uc b/sources/Data/JSONBase.uc
new file mode 100644
index 0000000..60e6196
--- /dev/null
+++ b/sources/Data/JSONBase.uc
@@ -0,0 +1,70 @@
+/**
+ * JSON is an open standard file format, and data interchange format,
+ * that uses human-readable text to store and transmit data objects
+ * consisting of name–value pairs and array data types.
+ * For more information refer to https://en.wikipedia.org/wiki/JSON
+ * This is a base class for implementation of JSON data storage for Acedia.
+ * It does not implement parsing and printing from/into human-readable
+ * text representation, just provides means to store such information.
+ *
+ * JSON data is stored as an object (represented via `JSONObject`) that
+ * contains a set of name-value pairs, where value can be
+ * a number, string, boolean value, another object or
+ * an array (represented by `JSONArray`).
+ * Copyright 2020 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia 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, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia 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 Acedia. If not, see .
+ */
+class JSONBase extends Object;
+
+// Enumeration for possible types of JSON values.
+enum JSONType
+{
+ // Technical type, used to indicate that requested value is missing.
+ // Undefined values are not part of JSON format.
+ JSON_Undefined,
+ // An empty value, in teste representation defined by a single word "null".
+ JSON_Null,
+ // A number, recorded as a float.
+ // JSON itself doesn't specify whether number is an integer or float.
+ JSON_Number,
+ // A string.
+ JSON_String,
+ // A bool value.
+ JSON_Boolean,
+ // Array of other JSON values, stored without names;
+ // Single array can contain any mix of value types.
+ JSON_Array,
+ // Another JSON object, i.e. associative array of name-value pairs
+ JSON_Object
+};
+
+// Stores a single JSON value
+struct JSONStorageValue
+{
+ // What type is stored exactly?
+ // Depending on that, uses one of the other fields as a storage.
+ var public JSONType type;
+ var protected float numberValue;
+ var protected string stringValue;
+ var protected bool booleanValue;
+ // Used for storing both JSON objects and arrays.
+ var protected JSONBase complexValue;
+};
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Data/JSONObject.uc b/sources/Data/JSONObject.uc
new file mode 100644
index 0000000..6e1667e
--- /dev/null
+++ b/sources/Data/JSONObject.uc
@@ -0,0 +1,272 @@
+/**
+ * This class implements JSON object storage capabilities.
+ * Whenever one wants to store JSON data, they need to define such object.
+ * It stores name-value pairs, where names are strings and values can be:
+ * ~ Boolean, string, null or number (float in this implementation) data;
+ * ~ Other JSON objects;
+ * ~ JSON Arrays (see `JSONArray` class).
+ *
+ * This implementation provides getters and setters for boolean, string,
+ * null or number types that allow to freely set and fetch their values
+ * by name.
+ * JSON objects and arrays can be fetched by getters, but you cannot
+ * add existing object or array to another object. Instead one has to create
+ * a new, empty object with a certain name and then fill it with data.
+ * This allows to avoid loop situations, where object is contained in itself.
+ * Functions to remove existing values are also provided and are applicable
+ * to all variable types.
+ * Setters can also be used to overwrite any value by a different value,
+ * even of a different type.
+ * Copyright 2020 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia 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, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia 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 Acedia. If not, see .
+ */
+class JSONObject extends JSONBase;
+
+// We will store all our data as a simple array of key(name)-value pairs.
+struct JSONKeyValuePair
+{
+ var string key;
+ var JSONStorageValue value;
+};
+var private array data;
+
+// Returns index of key-value pair in `data` with a given key.
+// Returns `-1` if such a pair does not exist in `data`.
+private final function int GetIndex(string key)
+{
+ local int i;
+ for (i = 0; i < data.length; i += 1)
+ {
+ if (key == data[i].key)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Returns `JSONType` of a variable with a given key in our data.
+// This function can be used to check if certain variable exists
+// in this object, since if such variable does not exist -
+// function will return `JSON_Undefined`.
+public final function JSONType GetType(string key)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return JSON_Undefined;
+
+ return data[index].value.type;
+}
+
+// Following functions are getters for various types of variables.
+// Getter for null value simply checks if it's null
+// and returns true/false as a result.
+// Getters for simple types (number, string, boolean) can have optional
+// default value specified, that will be returned if requested variable
+// doesn't exist or has a different type.
+// Getters for object and array types don't take default values and
+// will simply return `none`.
+public final function float GetNumber(string key, optional float defaultValue)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return defaultValue;
+ if (data[index].value.type != JSON_Number) return defaultValue;
+
+ return data[index].value.numberValue;
+}
+
+public final function string GetString(string key, optional string defaultValue)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return defaultValue;
+ if (data[index].value.type != JSON_String) return defaultValue;
+
+ return data[index].value.stringValue;
+}
+
+public final function bool GetBoolean(string key, optional bool defaultValue)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return defaultValue;
+ if (data[index].value.type != JSON_Boolean) return defaultValue;
+
+ return data[index].value.booleanValue;
+}
+
+public final function bool IsNull(string key)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return false;
+ if (data[index].value.type != JSON_Null) return false;
+
+ return (data[index].value.type == JSON_Null);
+}
+
+public final function JSONArray GetArray(string key)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return none;
+ if (data[index].value.type != JSON_Array) return none;
+
+ return JSONArray(data[index].value.complexValue);
+}
+
+public final function JSONObject GetObject(string key)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return none;
+ if (data[index].value.type != JSON_Object) return none;
+
+ return JSONObject(data[index].value.complexValue);
+}
+
+// Following functions provide simple setters for boolean, string, number
+// and null values.
+// They return object itself, allowing user to chain calls like this:
+// `object.SetNumber("num1", 1).SetNumber("num2", 2);`.
+public final function JSONObject SetNumber(string key, float value)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_Number;
+ newStorageValue.numberValue = value;
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+public final function JSONObject SetString(string key, string value)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_String;
+ newStorageValue.stringValue = value;
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+public final function JSONObject SetBoolean(string key, bool value)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_Boolean;
+ newStorageValue.booleanValue = value;
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+public final function JSONObject SetNull(string key)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_Null;
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+// JSON array and object types don't have setters, but instead have
+// functions to create a new, empty array/object under a certain name.
+// They return object itself, allowing user to chain calls like this:
+// `object.CreateObject("folded object").CreateArray("names list");`.
+public final function JSONObject CreateArray(string key)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_Array;
+ newStorageValue.complexValue = new class'JSONArray';
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+public final function JSONObject CreateObject(string key)
+{
+ local int index;
+ local JSONKeyValuePair newKeyValuePair;
+ local JSONStorageValue newStorageValue;
+ index = GetIndex(key);
+ if (index < 0)
+ {
+ index = data.length;
+ }
+ newStorageValue.type = JSON_Object;
+ newStorageValue.complexValue = new class'JSONObject';
+ newKeyValuePair.key = key;
+ newKeyValuePair.value = newStorageValue;
+ data[index] = newKeyValuePair;
+ return self;
+}
+
+// Removes values with a given name.
+// Returns `true` if value was actually removed and `false` if it didn't exist.
+public final function bool RemoveValue(string key)
+{
+ local int index;
+ index = GetIndex(key);
+ if (index < 0) return false;
+
+ data.Remove(index, 1);
+ return true;
+}
+
+defaultproperties
+{
+}
\ No newline at end of file