diff --git a/AMBuildScript b/AMBuildScript index d8206dae..6425fb02 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -412,6 +412,7 @@ builder.RunBuildScripts( 'modules/fun/AMBuilder', 'modules/geoip/AMBuilder', 'modules/hamsandwich/AMBuilder', + 'modules/json/AMBuilder', 'modules/mysqlx/AMBuilder', 'modules/ns/AMBuilder', 'modules/nvault/AMBuilder', diff --git a/amxmodx/msvc12/amxmodx_mm.sln b/amxmodx/msvc12/amxmodx_mm.sln index 818db48a..30dda64a 100644 --- a/amxmodx/msvc12/amxmodx_mm.sln +++ b/amxmodx/msvc12/amxmodx_mm.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "amxmodx_mm", "amxmodx_mm.vcxproj", "{2BF64D1A-AC89-41B0-9D02-FB8CB610F850}" EndProject @@ -51,6 +51,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "amxxpc", "..\..\compiler\am EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpc300", "..\..\compiler\libpc300\libpc300.vcxproj", "{19B72687-080B-437A-917A-12AEB0031635}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "json", "..\..\modules\json\msvc12\json.vcxproj", "{4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -326,6 +328,18 @@ Global {19B72687-080B-437A-917A-12AEB0031635}.JITReleaseBinLog|Win32.Build.0 = Release|Win32 {19B72687-080B-437A-917A-12AEB0031635}.Release|Win32.ActiveCfg = Release|Win32 {19B72687-080B-437A-917A-12AEB0031635}.Release|Win32.Build.0 = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Debug|Win32.ActiveCfg = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Debug|Win32.Build.0 = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITDebug|Win32.ActiveCfg = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITDebug|Win32.Build.0 = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITDebugBinLog|Win32.ActiveCfg = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITDebugBinLog|Win32.Build.0 = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITRelease|Win32.ActiveCfg = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITRelease|Win32.Build.0 = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITReleaseBinLog|Win32.ActiveCfg = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.JITReleaseBinLog|Win32.Build.0 = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Release|Win32.ActiveCfg = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -352,5 +366,9 @@ Global {7F18E00C-6271-4CAB-B18A-746BE3EC68E7} = {1A75873D-E05D-4F07-A4E2-28DC1BB03226} {39412290-D01C-472F-A439-AB5592A04C08} = {0BB61E37-4EA5-4B18-A164-6CB4810E50F4} {19B72687-080B-437A-917A-12AEB0031635} = {0BB61E37-4EA5-4B18-A164-6CB4810E50F4} + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE} = {1A75873D-E05D-4F07-A4E2-28DC1BB03226} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E7D29DAA-4E6E-43AA-A2FC-7BD7758ABFD9} EndGlobalSection EndGlobal diff --git a/modules/json/AMBuilder b/modules/json/AMBuilder new file mode 100644 index 00000000..309f4a5f --- /dev/null +++ b/modules/json/AMBuilder @@ -0,0 +1,24 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os.path + +binary = AMXX.MetaModule(builder, 'json') + +binary.compiler.defines += [ + 'HAVE_STDINT_H', +] + +binary.compiler.cxxincludes += [ + os.path.join(builder.currentSourcePath, '..', '..', 'third_party', 'parson') +] + +binary.sources = [ + '../../public/sdk/amxxmodule.cpp', + '../../third_party/parson/parson.c', + 'JsonMngr.cpp', + 'JsonNatives.cpp', +] + +if builder.target_platform == 'windows': + binary.sources += ['version.rc'] + +AMXX.modules += [builder.Add(binary)] diff --git a/modules/json/IJsonMngr.h b/modules/json/IJsonMngr.h new file mode 100644 index 00000000..6928c209 --- /dev/null +++ b/modules/json/IJsonMngr.h @@ -0,0 +1,708 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// JSON Interface +// + +//#include + +namespace AMXX +{ + /** + * @brief Lists of possible handle types. + */ + enum JSONHandleType + { + Handle_Value = 0, + Handle_Array, + Handle_Object + }; + + /** + * @brief Lists of possible JSON types. + */ + enum JSONType + { + JSONTypeError = -1, + JSONTypeNull = 1, + JSONTypeString = 2, + JSONTypeNumber = 3, + JSONTypeObject = 4, + JSONTypeArray = 5, + JSONTypeBoolean = 6 + }; + + /** + * @brief Represents a Handle ID. + */ + typedef size_t JS_Handle; + + /** + * @brief Provides functions for managing JSON. + */ + class IJSONMngr/* : public AMXXInterface*/ + { + public: + + //virtual unsigned int GetInterfaceVersion() override final { return 1; } + //virtual const char *GetInterfaceName() override final { return "IJsonMngr"; } + + virtual ~IJSONMngr() {}; + + /** + * @brief Checks if handle with specified type is valid. + * + * @param id JSON handle + * @param type Handle's type + * + * @return True if handle is valid, false otherwise + */ + virtual bool IsValidHandle(JS_Handle id, JSONHandleType type = Handle_Value) = 0; + + /** + * @brief Frees handle. + * + * @param id JSON handle + * + * @noreturn + */ + virtual void Free(JS_Handle id) = 0; + + /** + * @brief Gets JSON type of passed handle. + * + * @param value JSON handle + * + * @return JSON type or JSONTypeError if error occurred + */ + virtual JSONType GetHandleJSONType(JS_Handle value) = 0; + + /** + * @brief Parses JSON string or a file that contains JSON. + * + * @note Handle needs to be freed using Free(). + * + * @param string String to parse + * @param handle Address to variable where value's handle will be stored + * @param is_file True to treat string param as filename, false otherwise + * @param with_comments True if parsing JSON includes comments (it will ignore them), false otherwise + * + * @return True if succeed, false otherwise + */ + virtual bool Parse(const char *string, JS_Handle *handle, bool is_file = false, bool with_comments = false) = 0; + + /** + * @brief Checks if the first value is the same as the second one. + * + * @param value1 JSON handle + * @param value2 JSON handle + * + * @return True if they are the same, false otherwise + */ + virtual bool AreValuesEquals(JS_Handle value1, JS_Handle value2) = 0; + + /** + * @brief Validates json by checking if object have identically named + * fields with matching types. + * + * @note Schema {"name":"", "age":0} will validate + * {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + * but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + * + * @note In case of arrays, only first value in schema is checked against + * all values in tested array. + * + * @note Empty objects ({}) validate all objects, + * empty arrays ([]) validate all arrays, + * null validates values of every type. + * + * @param schema JSON handle + * @param value JSON handle + * + * @return True if passed value is valid, false otherwise + */ + virtual bool IsValueValid(JS_Handle schema, JS_Handle value) = 0; + + /** + * @brief Checks if value has parent and assigns it to variable (if provided). + * + * @note Parent's handle needs to be freed using Free(). + * + * @param value JSON handle + * @param parent Address to variable where parent's handle will be stored + * + * @return True if value has parent, false otherwise + */ + virtual bool GetValueParent(JS_Handle value, JS_Handle *parent = nullptr) = 0; + + /** + * @brief Inits an empty object. + * + * @note Handle needs to be freed using Free(). + * + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitObject(JS_Handle *handle) = 0; + + /** + * @brief Inits an empty array. + * + * @note Handle needs to be freed using Free(). + * + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitArray(JS_Handle *handle) = 0; + + /** + * @brief Inits string data. + * + * @note Handle needs to be freed using Free(). + * + * @param string String that the handle will be initialized with + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitString(const char *string, JS_Handle *handle) = 0; + + /** + * @brief Inits a number. + * + * @note Handle needs to be freed using Free(). + * + * @param number Number that the handle will be initialized with + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitNum(double number, JS_Handle *handle) = 0; + + /** + * @brief Inits a boolean value. + * + * @note Handle needs to be freed using Free(). + * + * @param boolean Boolean value that the handle will be initialized with + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitBool(bool boolean, JS_Handle *handle) = 0; + + /** + * @brief Inits a null. + * + * @note Handle needs to be freed using Free(). + * + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool InitNull(JS_Handle *handle) = 0; + + /** + * @brief Creates deep copy of passed value. + * + * @note Handle needs to be freed using Free(). + * + * @param value JSON handle to be copied + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool DeepCopyValue(JS_Handle value, JS_Handle *handle) = 0; + + /** + * @brief Gets a string data. + * + * @param value JSON handle + * + * @return String data + */ + virtual const char *ValueToString(JS_Handle value) = 0; + + /** + * @brief Gets a number. + * + * @param value JSON handle + * + * @return Number + */ + virtual double ValueToNum(JS_Handle value) = 0; + + /** + * @brief Gets a boolean value. + * + * @param value JSON handle + * + * @return Boolean value + */ + virtual bool ValueToBool(JS_Handle value) = 0; + + //JSON Array API + + //Get functions + + /** + * @brief Gets a value from the array. + * + * @note Handle needs to be freed using Free(). + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayGetValue(JS_Handle array, size_t index, JS_Handle *handle) = 0; + + /** + * @brief Gets string data from the array. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * + * @return String data + */ + virtual const char *ArrayGetString(JS_Handle array, size_t index) = 0; + + /** + * @brief Gets a number from the array. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * + * @return Number + */ + virtual double ArrayGetNum(JS_Handle array, size_t index) = 0; + + /** + * @brief Gets a boolean value from the array. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * + * @return Boolean value + */ + virtual bool ArrayGetBool(JS_Handle array, size_t index) = 0; + + /** + * @brief Gets count of the elements in the array. + * + * @param array JSON handle + * + * @return Number of elements in the array + */ + virtual size_t ArrayGetCount(JS_Handle array) = 0; + + //Set functions + + /** + * @brief Replaces an element in the array with value. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * @param value JSON handle to set + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayReplaceValue(JS_Handle array, size_t index, JS_Handle value) = 0; + + /** + * @brief Replaces an element in the array with string. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * @param string String to copy + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayReplaceString(JS_Handle array, size_t index, const char *string) = 0; + + /** + * @brief Replaces an element in the array with number. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * @param number Number to set + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayReplaceNum(JS_Handle array, size_t index, double number) = 0; + + /** + * @brief Replaces an element in the array with boolean value. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * @param boolean Boolean value to set + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayReplaceBool(JS_Handle array, size_t index, bool boolean) = 0; + + /** + * @brief Replaces an element in the array with null. + * + * @param array JSON handle + * @param index Position in the array (starting from 0) + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayReplaceNull(JS_Handle array, size_t index) = 0; + + /** + * @brief Appends a value in the array. + * + * @param array JSON handle + * @param value JSON handle + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayAppendValue(JS_Handle array, JS_Handle value) = 0; + + /** + * @brief Appends string data in the array. + * + * @param array JSON handle + * @param string String to copy + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayAppendString(JS_Handle array, const char *string) = 0; + + /** + * @brief Appends a number in the array. + * + * @param array JSON handle + * @param string Number to set + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayAppendNum(JS_Handle array, double number) = 0; + + /** + * @brief Appends a boolean value in the array. + * + * @param array JSON handle + * @param boolean Boolean value to set + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayAppendBool(JS_Handle array, bool boolean) = 0; + + /** + * @brief Appends a null in the array. + * + * @param array JSON handle + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayAppendNull(JS_Handle array) = 0; + + //Remove functions + + /** + * @brief Removes an element from the array. + * + * @note Order of values in array may change during execution. + * + * @param array JSON handle + * @param position Position in the array (starting from 0) + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayRemove(JS_Handle array, size_t index) = 0; + + /** + * @brief Removes all elements from the array. + * + * @param array JSON handle + * + * @return True if succeed, false otherwise + */ + virtual bool ArrayClear(JS_Handle array) = 0; + + //Wrappers for Object API + + //Get functions + + /** + * @brief Gets a value from the object. + * + * @note Handle needs to be freed using Free(). + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object JSON handle + * @param name Key name + * @param handle Address to variable where value's handle will be stored + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectGetValue(JS_Handle object, const char *name, JS_Handle *handle, bool dotfunc = false) = 0; + + /** + * @brief Gets string data from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object JSON handle + * @param name Key name + * @param dotfunc True to use dot notation, false to not + * + * @return String data + */ + virtual const char *ObjectGetString(JS_Handle object, const char *name, bool dotfunc = false) = 0; + + /** + * @brief Gets a number from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object JSON handle + * @param name Key name + * @param dotfunc True to use dot notation, false to not + * + * @return Number + */ + virtual double ObjectGetNum(JS_Handle object, const char *name, bool dotfunc = false) = 0; + + /** + * @brief Gets a boolean value from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object JSON handle + * @param name Key name + * @param dotfunc True to use dot notation, false to not + * + * @return Boolean value + */ + virtual bool ObjectGetBool(JS_Handle object, const char *name, bool dotfunc = false) = 0; + + /** + * @brief Gets count of the keys in the object. + * + * @param object JSON handle + * + * @return Keys count + */ + virtual size_t ObjectGetCount(JS_Handle object) = 0; + + /** + * @brief Gets name of the object's key. + * + * @param object JSON handle + * @param index Position from which get key name + * + * @return Key name + */ + virtual const char *ObjectGetName(JS_Handle object, size_t index) = 0; + + /** + * @brief Gets a value at the specified position from the object. + * + * @note Handle needs to be freed using Free(). + * + * @param object JSON handle + * @param index Position from which get key name + * @param handle Address to variable where value's handle will be stored + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectGetValueAt(JS_Handle object, size_t index, JS_Handle *handle) = 0; + + /** + * @brief Checks if object has a value with a specific name and type. + * + * @param object JSON handle + * @param name Key name + * @param type Type of value, if JSONTypeError type will not be checked + * @param dotfunc True to use dot notation, false to not + * + * @return True if has, false if not + */ + virtual bool ObjectHasValue(JS_Handle object, const char *name, JSONType type = JSONTypeError, bool dotfunc = false) = 0; + + //Set functions + + /** + * @brief Sets a value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object JSON handle + * @param name Key name + * @param value JSON handle + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectSetValue(JS_Handle object, const char *name, JS_Handle value, bool dotfunc = false) = 0; + + /** + * @brief Sets string data in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object JSON handle + * @param name Key name + * @param string String to copy + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectSetString(JS_Handle object, const char *name, const char *string, bool dotfunc = false) = 0; + + /** + * @brief Sets a number in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object JSON handle + * @param name Key name + * @param number Number to set + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectSetNum(JS_Handle object, const char *name, double number, bool dotfunc = false) = 0; + + /** + * @brief Sets a boolean value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object JSON handle + * @param name Key name + * @param boolean Boolean value to set + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectSetBool(JS_Handle object, const char *name, bool boolean, bool dotfunc = false) = 0; + + /** + * @brief Sets a null in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object JSON handle + * @param name Key name + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectSetNull(JS_Handle object, const char *name, bool dotfunc = false) = 0; + + //Remove functions + + /** + * @brief Removes a key and its value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object JSON handle + * @param name Key name + * @param dotfunc True to use dot notation, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectRemove(JS_Handle object, const char *name, bool dotfunc = false) = 0; + + /** + * @brief Removes all keys and their values in the object. + * + * @param object JSON handle + * + * @return True if succeed, false otherwise + */ + virtual bool ObjectClear(JS_Handle object) = 0; + + //Serialization API + + /** + * @brief Gets size of serialization. + * + * @param value JSON handle + * @param pretty True to count size for pretty format, false to not + * + * @return Size of serialized string + */ + virtual size_t SerialSize(JS_Handle value, bool pretty) = 0; + + /** + * @brief Copies serialized string to the buffer. + * + * @note The buffer must be large enough or function will + * fail. + * + * @param value JSON handle + * @param buffer Buffer to copy string to + * @param size Size of the buffer + * @param pretty True to format pretty JSON string, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool SerialToBuffer(JS_Handle value, char *buffer, size_t size, bool pretty) = 0; + + /** + * @brief Copies serialized string to the file. + * + * @param value JSON handle + * @param filepath Path to the file + * @param pretty True to format pretty JSON string, false to not + * + * @return True if succeed, false otherwise + */ + virtual bool SerialToFile(JS_Handle value, const char *filepath, bool pretty) = 0; + + /** + * @brief Returns serialized string. + * + * @note Must be freed using FreeString(). + * + * @param value JSON handle + * @param pretty True to format pretty JSON string, false to not + * + * @return Serialized string, nullptr if failed + */ + virtual char *SerialToString(JS_Handle value, bool pretty) = 0; + + /** + * @brief Frees serialized string. + * + * @param string Pointer to serialized string + * + * @noreturn + */ + virtual void FreeString(char *string) = 0; + }; +} diff --git a/modules/json/JsonMngr.cpp b/modules/json/JsonMngr.cpp new file mode 100644 index 00000000..c48dcfae --- /dev/null +++ b/modules/json/JsonMngr.cpp @@ -0,0 +1,467 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// JSON Class +// + +#include "JsonMngr.h" + +JSONMngr::~JSONMngr() +{ + for (auto &i : m_Handles) + { + if (i) + { + _FreeHandle(i); + } + } +} + +JS_Handle JSONMngr::_MakeHandle(void *value, JSONHandleType type, bool must_be_freed) +{ + JS_Handle id; + + if (!m_OldHandles.empty()) + { + id = m_OldHandles.popFrontCopy(); + m_Handles[id] = ke::AutoPtr(new JSONHandle); + } + else + { + m_Handles.append(ke::AutoPtr(new JSONHandle)); + id = m_Handles.length() - 1; + } + + switch (type) + { + case Handle_Value: + { + auto getHandleType = [this](JSON_Value *jsvalue, JS_Handle id) + { + if (!(m_Handles[id]->m_pArray = json_value_get_array(jsvalue))) + { + m_Handles[id]->m_pObject = json_value_get_object(jsvalue); + } + }; + + auto JSValue = m_Handles[id]->m_pValue = static_cast(value); + getHandleType(JSValue, id); + break; + } + case Handle_Array: + { + auto JSArray = m_Handles[id]->m_pArray = static_cast(value); + m_Handles[id]->m_pValue = json_array_get_wrapping_value(JSArray); + break; + } + case Handle_Object: + { + auto JSObject = m_Handles[id]->m_pObject = static_cast(value); + m_Handles[id]->m_pValue = json_object_get_wrapping_value(JSObject); + break; + } + } + + m_Handles[id]->m_bMustBeFreed = must_be_freed; + + return id; +} + +void JSONMngr::_FreeHandle(ke::AutoPtr &ptr) +{ + if (ptr->m_bMustBeFreed && ptr->m_pValue) + { + json_value_free(ptr->m_pValue); + } +} + +void JSONMngr::Free(JS_Handle id) +{ + auto handle = ke::Move(m_Handles[id]); + + if (!handle) + { + return; + } + + _FreeHandle(handle); + m_OldHandles.append(id); +} + +bool JSONMngr::IsValidHandle(JS_Handle handle, JSONHandleType type) +{ + if (handle >= m_Handles.length() || !m_Handles[handle]) + { + return false; + } + + switch (type) + { + case Handle_Array: return m_Handles[handle]->m_pArray != nullptr; + case Handle_Object: return m_Handles[handle]->m_pObject != nullptr; + default: return true; + } +} + +bool JSONMngr::GetValueParent(JS_Handle value, JS_Handle *parent) +{ + auto JSParent = json_value_get_parent(m_Handles[value]->m_pValue); + if (!JSParent) + { + return false; + } + + if (parent) + { + *parent = _MakeHandle(JSParent, Handle_Value); + } + + return true; +} + +bool JSONMngr::InitObject(JS_Handle *handle) +{ + auto JSObject = json_value_get_object(json_value_init_object()); + if (!JSObject) + { + return false; + } + + *handle = _MakeHandle(JSObject, Handle_Object, true); + return true; +} + +bool JSONMngr::InitArray(JS_Handle *handle) +{ + auto JSArray = json_value_get_array(json_value_init_array()); + if (!JSArray) + { + return false; + } + + *handle = _MakeHandle(JSArray, Handle_Array, true); + return true; +} + +bool JSONMngr::InitString(const char *string, JS_Handle *handle) +{ + auto JSValue = json_value_init_string(string); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +bool JSONMngr::InitNum(double number, JS_Handle *handle) +{ + auto JSValue = json_value_init_number(number); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +bool JSONMngr::InitBool(bool boolean, JS_Handle *handle) +{ + auto JSValue = json_value_init_boolean(boolean); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +bool JSONMngr::InitNull(JS_Handle *handle) +{ + auto JSValue = json_value_init_null(); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +bool JSONMngr::Parse(const char *string, JS_Handle *handle, bool is_file, bool with_comments) +{ + auto jsonFunc = (!with_comments) ? json_parse_string : json_parse_string_with_comments; + if (is_file) + { + jsonFunc = (!with_comments) ? json_parse_file : json_parse_file_with_comments; + } + + auto JSValue = jsonFunc(string); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +bool JSONMngr::DeepCopyValue(JS_Handle value, JS_Handle *handle) +{ + auto JSValue = json_value_deep_copy(m_Handles[value]->m_pValue); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value, true); + return true; +} + +const char *JSONMngr::ValueToString(JS_Handle value) +{ + auto string = json_value_get_string(m_Handles[value]->m_pValue); + return (string) ? string : ""; +} + +bool JSONMngr::ArrayGetValue(JS_Handle array, size_t index, JS_Handle *handle) +{ + auto JSValue = json_array_get_value(m_Handles[array]->m_pArray, index); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value); + return true; +} + +const char *JSONMngr::ArrayGetString(JS_Handle array, size_t index) +{ + auto string = json_array_get_string(m_Handles[array]->m_pArray, index); + return (string) ? string : ""; +} + +bool JSONMngr::ArrayReplaceValue(JS_Handle array, size_t index, JS_Handle value) +{ + auto JSValue = m_Handles[value]->m_pValue; + + //We cannot assign the same value to the different arrays or objects + //So if value is already assigned somewhere else let's create a copy of it + if (json_value_get_parent(JSValue)) + { + JSValue = json_value_deep_copy(JSValue); + } + else + { + //Parson will take care of freeing child values + m_Handles[value]->m_bMustBeFreed = false; + } + return json_array_replace_value(m_Handles[array]->m_pArray, index, JSValue) == JSONSuccess; +} + +bool JSONMngr::ArrayAppendValue(JS_Handle array, JS_Handle value) +{ + auto JSValue = m_Handles[value]->m_pValue; + + //We cannot assign the same value to the different arrays or objects + //So if value is already assigned somewhere else let's create a copy of it + if (json_value_get_parent(JSValue)) + { + JSValue = json_value_deep_copy(JSValue); + } + else + { + //Parson will take care of freeing child values + m_Handles[value]->m_bMustBeFreed = false; + } + return json_array_append_value(m_Handles[array]->m_pArray, JSValue) == JSONSuccess; +} + +bool JSONMngr::ObjectGetValue(JS_Handle object, const char *name, JS_Handle *handle, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSValue = (!dotfunc) ? json_object_get_value(JSObject, name) : + json_object_dotget_value(JSObject, name); + + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value); + return true; +} + +const char *JSONMngr::ObjectGetString(JS_Handle object, const char *name, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto string = (!dotfunc) ? json_object_get_string(JSObject, name) : + json_object_dotget_string(JSObject, name); + + return (string) ? string : ""; +} + +double JSONMngr::ObjectGetNum(JS_Handle object, const char *name, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + return (!dotfunc) ? json_object_get_number(JSObject, name) : + json_object_dotget_number(JSObject, name); +} + +bool JSONMngr::ObjectGetBool(JS_Handle object, const char *name, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto result = (!dotfunc) ? json_object_get_boolean(JSObject, name) : + json_object_dotget_boolean(JSObject, name); + + return result == 1; +} + +const char *JSONMngr::ObjectGetName(JS_Handle object, size_t index) +{ + auto string = json_object_get_name(m_Handles[object]->m_pObject, index); + return (string) ? string : ""; +} + +bool JSONMngr::ObjectGetValueAt(JS_Handle object, size_t index, JS_Handle *handle) +{ + auto JSValue = json_object_get_value_at(m_Handles[object]->m_pObject, index); + if (!JSValue) + { + return false; + } + + *handle = _MakeHandle(JSValue, Handle_Value); + return true; +} + +bool JSONMngr::ObjectHasValue(JS_Handle object, const char *name, JSONType type, bool dotfunc) +{ + int result; + auto JSObject = m_Handles[object]->m_pObject; + + if (type == JSONTypeError) + { + result = (!dotfunc) ? json_object_has_value(JSObject, name) : + json_object_dothas_value(JSObject, name); + } + else + { + result = (!dotfunc) ? json_object_has_value_of_type(JSObject, name, type) : + json_object_dothas_value_of_type(JSObject, name, type); + } + return result == 1; +} + +bool JSONMngr::ObjectSetValue(JS_Handle object, const char *name, JS_Handle value, bool dotfunc) +{ + auto JSValue = m_Handles[value]->m_pValue; + + //We cannot assign the same value to the different arrays or objects + //So if value is already assigned somewhere else let's create a copy of it + if (json_value_get_parent(JSValue)) + { + JSValue = json_value_deep_copy(JSValue); + } + else + { + //Parson will take care of freeing child values + m_Handles[value]->m_bMustBeFreed = false; + } + + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_set_value(JSObject, name, JSValue) : + json_object_dotset_value(JSObject, name, JSValue); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::ObjectSetString(JS_Handle object, const char *name, const char *string, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_set_string(JSObject, name, string) : + json_object_dotset_string(JSObject, name, string); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::ObjectSetNum(JS_Handle object, const char *name, double number, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_set_number(JSObject, name, number) : + json_object_dotset_number(JSObject, name, number); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::ObjectSetBool(JS_Handle object, const char *name, bool boolean, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_set_boolean(JSObject, name, boolean) : + json_object_dotset_boolean(JSObject, name, boolean); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::ObjectSetNull(JS_Handle object, const char *name, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_set_null(JSObject, name) : + json_object_dotset_null(JSObject, name); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::ObjectRemove(JS_Handle object, const char *name, bool dotfunc) +{ + auto JSObject = m_Handles[object]->m_pObject; + auto JSResult = (!dotfunc) ? json_object_remove(JSObject, name) : + json_object_dotremove(JSObject, name); + + return JSResult == JSONSuccess; +} + +size_t JSONMngr::SerialSize(JS_Handle value, bool pretty) +{ + auto JSValue = m_Handles[value]->m_pValue; + return (!pretty) ? json_serialization_size(JSValue) : + json_serialization_size_pretty(JSValue); +} + +bool JSONMngr::SerialToBuffer(JS_Handle value, char *buffer, size_t size, bool pretty) +{ + auto JSValue = m_Handles[value]->m_pValue; + auto JSResult = (!pretty) ? json_serialize_to_buffer(JSValue, buffer, size) : + json_serialize_to_buffer_pretty(JSValue, buffer, size); + + return JSResult == JSONSuccess; +} + +bool JSONMngr::SerialToFile(JS_Handle value, const char *filepath, bool pretty) +{ + auto JSValue = m_Handles[value]->m_pValue; + auto JSResult = (!pretty) ? json_serialize_to_file(JSValue, filepath) : + json_serialize_to_file_pretty(JSValue, filepath); + + return JSResult == JSONSuccess; +} + +char *JSONMngr::SerialToString(JS_Handle value, bool pretty) +{ + auto JSValue = m_Handles[value]->m_pValue; + auto result = (!pretty) ? json_serialize_to_string(JSValue) : + json_serialize_to_string_pretty(JSValue); + + return (result) ? result : nullptr; +} diff --git a/modules/json/JsonMngr.h b/modules/json/JsonMngr.h new file mode 100644 index 00000000..eb115558 --- /dev/null +++ b/modules/json/JsonMngr.h @@ -0,0 +1,196 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// JSON Class +// + +#include +#include +#include +#include +#include +#include + +#include "IJsonMngr.h" + +using namespace AMXX; + +class JSONMngr : public IJSONMngr +{ + public: + + JSONMngr() = default; + virtual ~JSONMngr(); + + // Handles + bool IsValidHandle(JS_Handle id, JSONHandleType type = Handle_Value) override; + void Free(JS_Handle id) override; + inline JSONType GetHandleJSONType(JS_Handle value) override + { + return static_cast(json_value_get_type(m_Handles[value]->m_pValue)); + } + + // Parsing + bool Parse(const char *string, JS_Handle *handle, bool is_file, bool with_comments) override; + + // Comapring + inline bool AreValuesEquals(JS_Handle value1, JS_Handle value2) override + { + // to avoid ms compiler warning + return json_value_equals(m_Handles[value1]->m_pValue, m_Handles[value2]->m_pValue) == 1; + } + + // Validating + inline bool IsValueValid(JS_Handle schema, JS_Handle value) override + { + return json_validate(m_Handles[schema]->m_pValue, m_Handles[value]->m_pValue) == JSONSuccess; + } + + // Accessing parent value + bool GetValueParent(JS_Handle value, JS_Handle *parent) override; + + // Init functions + bool InitObject(JS_Handle *handle) override; + bool InitArray(JS_Handle *handle) override; + bool InitString(const char *string, JS_Handle *handle) override; + bool InitNum(double number, JS_Handle *handle) override; + bool InitBool(bool boolean, JS_Handle *handle) override; + bool InitNull(JS_Handle *handle) override; + + // Copying + bool DeepCopyValue(JS_Handle value, JS_Handle *handle) override; + + // Convert functions + const char *ValueToString(JS_Handle value) override; + inline double ValueToNum(JS_Handle value) override + { + return json_value_get_number(m_Handles[value]->m_pValue); + } + inline bool ValueToBool(JS_Handle value) override + { + return json_value_get_boolean(m_Handles[value]->m_pValue) == 1; + } + + // Wrappers for Array API + bool ArrayGetValue(JS_Handle array, size_t index, JS_Handle *handle) override; + const char *ArrayGetString(JS_Handle array, size_t index) override; + inline bool ArrayGetBool(JS_Handle array, size_t index) override + { + return json_array_get_boolean(m_Handles[array]->m_pArray, index) == 1; + } + bool ArrayReplaceValue(JS_Handle array, size_t index, JS_Handle value) override; + bool ArrayAppendValue(JS_Handle array, JS_Handle value) override; + + inline double ArrayGetNum(JS_Handle array, size_t index) override + { + return json_array_get_number(m_Handles[array]->m_pArray, index); + } + inline size_t ArrayGetCount(JS_Handle array) override + { + return json_array_get_count(m_Handles[array]->m_pArray); + } + inline bool ArrayReplaceString(JS_Handle array, size_t index, const char *string) override + { + return json_array_replace_string(m_Handles[array]->m_pArray, index, string) == JSONSuccess; + } + inline bool ArrayReplaceNum(JS_Handle array, size_t index, double number) override + { + return json_array_replace_number(m_Handles[array]->m_pArray, index, number) == JSONSuccess; + } + inline bool ArrayReplaceBool(JS_Handle array, size_t index, bool boolean) override + { + return json_array_replace_boolean(m_Handles[array]->m_pArray, index, boolean) == JSONSuccess; + } + inline bool ArrayReplaceNull(JS_Handle array, size_t index) override + { + return json_array_replace_null(m_Handles[array]->m_pArray, index) == JSONSuccess; + } + inline bool ArrayAppendString(JS_Handle array, const char *string) override + { + return json_array_append_string(m_Handles[array]->m_pArray, string) == JSONSuccess; + } + inline bool ArrayAppendNum(JS_Handle array, double number) override + { + return json_array_append_number(m_Handles[array]->m_pArray, number) == JSONSuccess; + } + inline bool ArrayAppendBool(JS_Handle array, bool boolean) override + { + return json_array_append_boolean(m_Handles[array]->m_pArray, boolean) == JSONSuccess; + } + inline bool ArrayAppendNull(JS_Handle array) override + { + return json_array_append_null(m_Handles[array]->m_pArray) == JSONSuccess; + } + inline bool ArrayRemove(JS_Handle array, size_t index) override + { + return json_array_remove(m_Handles[array]->m_pArray, index) == JSONSuccess; + } + inline bool ArrayClear(JS_Handle array) override + { + return json_array_clear(m_Handles[array]->m_pArray) == JSONSuccess; + } + + // Wrappers for Object API + + // Get functions + bool ObjectGetValue(JS_Handle object, const char *name, JS_Handle *handle, bool dotfunc) override; + const char *ObjectGetString(JS_Handle object, const char *name, bool dotfunc) override; + double ObjectGetNum(JS_Handle object, const char *name, bool dotfunc) override; + bool ObjectGetBool(JS_Handle object, const char *name, bool dotfunc) override; + inline size_t ObjectGetCount(JS_Handle object) override + { + return json_object_get_count(m_Handles[object]->m_pObject); + } + const char *ObjectGetName(JS_Handle object, size_t index) override; + bool ObjectGetValueAt(JS_Handle object, size_t index, JS_Handle *handle) override; + bool ObjectHasValue(JS_Handle object, const char *name, JSONType type, bool dotfunc) override; + + // Set functions + bool ObjectSetValue(JS_Handle object, const char *name, JS_Handle value, bool dotfunc) override; + bool ObjectSetString(JS_Handle object, const char *name, const char *string, bool dotfunc) override; + bool ObjectSetNum(JS_Handle object, const char *name, double number, bool dotfunc) override; + bool ObjectSetBool(JS_Handle object, const char *name, bool boolean, bool dotfunc) override; + bool ObjectSetNull(JS_Handle object, const char *name, bool dotfunc) override; + + // Remove functions + bool ObjectRemove(JS_Handle object, const char *name, bool dotfunc) override; + inline bool ObjectClear(JS_Handle object) override + { + return json_object_clear(m_Handles[object]->m_pObject) == JSONSuccess; + } + + // Serialization API + size_t SerialSize(JS_Handle value, bool pretty) override; + bool SerialToBuffer(JS_Handle value, char *buffer, size_t size, bool pretty) override; + bool SerialToFile(JS_Handle value, const char *filepath, bool pretty) override; + char *SerialToString(JS_Handle value, bool pretty) override; + inline void FreeString(char *string) override + { + json_free_serialized_string(string); + } + + private: + + struct JSONHandle + { + JSON_Value *m_pValue; //Store an pointer to a value + JSON_Array *m_pArray; //Store an pointer to an array + JSON_Object *m_pObject; //Store an pointer to an object + bool m_bMustBeFreed; //Must be freed using json_value_free()? + }; + + JS_Handle _MakeHandle(void *value, JSONHandleType type, bool must_be_freed = false); + void _FreeHandle(ke::AutoPtr &ptr); + + ke::Vector> m_Handles; + ke::Deque m_OldHandles; +}; + +extern ke::UniquePtr JsonMngr; diff --git a/modules/json/JsonNatives.cpp b/modules/json/JsonNatives.cpp new file mode 100644 index 00000000..358f2760 --- /dev/null +++ b/modules/json/JsonNatives.cpp @@ -0,0 +1,937 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// JSON Natives +// + +#include "JsonMngr.h" + +ke::UniquePtr JsonMngr; + +//native JSON:json_parse(const string[], bool:is_file = false, bool:with_comments = false); +static cell AMX_NATIVE_CALL amxx_json_parse(AMX *amx, cell *params) +{ + int len; + auto string = MF_GetAmxString(amx, params[1], 0, &len); + auto is_file = params[2] != 0; + + if (is_file) + { + char path[256]; + string = MF_BuildPathnameR(path, sizeof(path), "%s", string); + } + + JS_Handle handle; + auto result = JsonMngr->Parse(string, &handle, is_file, params[3] != 0); + + return (result) ? handle : -1; +} + +//native bool:json_equals(const JSON:value1, const JSON:value2); +static cell AMX_NATIVE_CALL amxx_json_equals(AMX *amx, cell *params) +{ + auto value1 = params[1], value2 = params[2]; + //For check against Invalid_JSON + if (value1 == -1 || value2 == -1) + { + return value1 == value2; + } + + if (!JsonMngr->IsValidHandle(value1)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value1); + return 0; + } + + if (!JsonMngr->IsValidHandle(value2)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value2); + return 0; + } + + return JsonMngr->AreValuesEquals(value1, value2); +} + +//native bool:json_validate(const JSON:schema, const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_validate(AMX *amx, cell *params) +{ + auto schema = params[1], value = params[2]; + if (!JsonMngr->IsValidHandle(schema)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON schema! %d", schema); + return 0; + } + + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + return JsonMngr->IsValueValid(schema, value); +} + +//native JSON:json_get_parent(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_get_parent(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return -1; + } + + JS_Handle parent; + auto result = JsonMngr->GetValueParent(value, &parent); + + return (result) ? parent : -1; +} + +//native JSONType:json_get_type(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_get_type(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return static_cast(JSONTypeError); + } + + return JsonMngr->GetHandleJSONType(value); +} + +//native JSON:json_init_object(); +static cell AMX_NATIVE_CALL amxx_json_init_object(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitObject(&handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_array(); +static cell AMX_NATIVE_CALL amxx_json_init_array(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitArray(&handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_string(const value[]); +static cell AMX_NATIVE_CALL amxx_json_init_string(AMX *amx, cell *params) +{ + int len; + JS_Handle handle; + auto result = JsonMngr->InitString(MF_GetAmxString(amx, params[1], 0, &len), &handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_number(value); +static cell AMX_NATIVE_CALL amxx_json_init_number(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitNum(params[1], &handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_real(Float:value); +static cell AMX_NATIVE_CALL amxx_json_init_real(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitNum(amx_ctof(params[1]), &handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_bool(bool:value); +static cell AMX_NATIVE_CALL amxx_json_init_bool(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitBool(params[1] != 0, &handle); + + return (result) ? handle : -1; +} + +//native JSON:json_init_null(); +static cell AMX_NATIVE_CALL amxx_json_init_null(AMX *amx, cell *params) +{ + JS_Handle handle; + auto result = JsonMngr->InitNull(&handle); + + return (result) ? handle : -1; +} + +//native JSON:json_deep_copy(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_deep_copy(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return -1; + } + + JS_Handle clonedHandle; + auto result = JsonMngr->DeepCopyValue(value, &clonedHandle); + + return (result) ? clonedHandle : -1; +} + +//native bool:json_free(&JSON:value); +static cell AMX_NATIVE_CALL amxx_json_free(AMX *amx, cell *params) +{ + auto value = MF_GetAmxAddr(amx, params[1]); + if (!JsonMngr->IsValidHandle(*value)) + { + return 0; + } + + JsonMngr->Free(*value); + + *value = -1; + + return 1; +} + +//native json_get_string(const JSON:value, buffer[], maxlen); +static cell AMX_NATIVE_CALL amxx_json_get_string(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + auto string = JsonMngr->ValueToString(value); + + return MF_SetAmxStringUTF8Char(amx, params[2], string, strlen(string), params[3]); +} + +//native json_get_number(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_get_number(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + return static_cast(JsonMngr->ValueToNum(value)); +} + +//native Float:json_get_real(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_get_real(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + auto result = static_cast(JsonMngr->ValueToNum(value)); + + return amx_ftoc(result); +} + +//native bool:json_get_bool(const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_get_bool(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + return JsonMngr->ValueToBool(value); +} + +//native JSON:json_array_get_value(const JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_get_value(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return -1; + } + + JS_Handle handle; + auto result = JsonMngr->ArrayGetValue(array, params[2], &handle); + + return (result) ? handle : -1; +} + +//native json_array_get_string(const JSON:array, index, buffer[], maxlen); +static cell AMX_NATIVE_CALL amxx_json_array_get_string(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + auto string = JsonMngr->ArrayGetString(array, params[2]); + + return MF_SetAmxStringUTF8Char(amx, params[3], string, strlen(string), params[4]); +} + +//native json_array_get_number(const JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_get_number(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return static_cast(JsonMngr->ArrayGetNum(array, params[2])); +} + +//native Float:json_array_get_real(const JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_get_real(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + auto result = static_cast(JsonMngr->ArrayGetNum(array, params[2])); + + return amx_ftoc(result); +} + +//native bool:json_array_get_bool(const JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_get_bool(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayGetBool(array, params[2]); +} + +//native json_array_get_count(const JSON:array); +static cell AMX_NATIVE_CALL amxx_json_array_get_count(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayGetCount(array); +} + +//native bool:json_array_replace_value(JSON:array, index, const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_array_replace_value(AMX *amx, cell *params) +{ + auto array = params[1], value = params[3]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + return JsonMngr->ArrayReplaceValue(array, params[2], value); +} + +//native bool:json_array_replace_string(JSON:array, index, const string[]); +static cell AMX_NATIVE_CALL amxx_json_array_replace_string(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + int len; + auto string = MF_GetAmxString(amx, params[3], 0, &len); + + return JsonMngr->ArrayReplaceString(array, params[2], string); +} + +//native bool:json_array_replace_number(JSON:array, index, number); +static cell AMX_NATIVE_CALL amxx_json_array_replace_number(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayReplaceNum(array, params[2], params[3]); +} + +//native bool:json_array_replace_real(JSON:array, index, Float:number); +static cell AMX_NATIVE_CALL amxx_json_array_replace_real(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayReplaceNum(array, params[2], amx_ctof(params[3])); +} + +//native bool:json_array_replace_bool(JSON:array, index, bool:boolean); +static cell AMX_NATIVE_CALL amxx_json_array_replace_bool(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayReplaceBool(array, params[2], params[3] != 0); +} + +//native bool:json_array_replace_null(JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_replace_null(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayReplaceNull(array, params[2]); +} + +//native bool:json_array_append_value(JSON:array, const JSON:value); +static cell AMX_NATIVE_CALL amxx_json_array_append_value(AMX *amx, cell *params) +{ + auto array = params[1], value = params[2]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + return JsonMngr->ArrayAppendValue(array, value); +} + +//native bool:json_array_append_string(JSON:array, const string[]); +static cell AMX_NATIVE_CALL amxx_json_array_append_string(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + int len; + return JsonMngr->ArrayAppendString(array, MF_GetAmxString(amx, params[2], 0, &len)); +} + +//native bool:json_array_append_number(JSON:array, number); +static cell AMX_NATIVE_CALL amxx_json_array_append_number(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayAppendNum(array, params[2]); +} + +//native bool:json_array_append_real(JSON:array, Float:number); +static cell AMX_NATIVE_CALL amxx_json_array_append_real(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayAppendNum(array, amx_ctof(params[2])); +} + +//native bool:json_array_append_bool(JSON:array, bool:boolean); +static cell AMX_NATIVE_CALL amxx_json_array_append_bool(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayAppendBool(array, params[2] != 0); +} + +//native bool:json_array_append_null(JSON:array); +static cell AMX_NATIVE_CALL amxx_json_array_append_null(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayAppendNull(array); +} + +//native bool:json_array_remove(JSON:array, index); +static cell AMX_NATIVE_CALL amxx_json_array_remove(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayRemove(array, params[2]); +} + +//native bool:json_array_clear(JSON:array); +static cell AMX_NATIVE_CALL amxx_json_array_clear(AMX *amx, cell *params) +{ + auto array = params[1]; + if (!JsonMngr->IsValidHandle(array, Handle_Array)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON array! %d", array); + return 0; + } + + return JsonMngr->ArrayClear(array); +} + +//native JSON:json_object_get_value(const JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_get_value(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return -1; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + JS_Handle handle; + auto result = JsonMngr->ObjectGetValue(object, name, &handle, params[3] != 0); + + return (result) ? handle : -1; +} + +//native json_object_get_string(const JSON:object, const name[], buffer[], maxlen, bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_get_string(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + auto string = JsonMngr->ObjectGetString(object, name, params[5] != 0); + + return MF_SetAmxStringUTF8Char(amx, params[3], string, strlen(string), params[4]); +} + +//native json_object_get_number(const JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_get_number(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return static_cast(JsonMngr->ObjectGetNum(object, name, params[3] != 0)); +} + +//native Float:json_object_get_real(const JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_get_real(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + auto result = static_cast(JsonMngr->ObjectGetNum(object, name, params[3] != 0)); + + return amx_ftoc(result); +} + +//native bool:json_object_get_bool(const JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_get_bool(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectGetBool(object, name, params[3] != 0); +} + +//native json_object_get_count(const JSON:object); +static cell AMX_NATIVE_CALL amxx_json_object_get_count(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + return JsonMngr->ObjectGetCount(object); +} + +//native json_object_get_name(const JSON:object, index, buffer[], maxlen); +static cell AMX_NATIVE_CALL amxx_json_object_get_name(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + auto name = JsonMngr->ObjectGetName(object, params[2]); + + return MF_SetAmxStringUTF8Char(amx, params[3], name, strlen(name), params[4]); +} + +//native JSON:amxx_json_object_get_value_at(const JSON:object, index); +static cell AMX_NATIVE_CALL amxx_json_object_get_value_at(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return -1; + } + + JS_Handle valueHandle; + auto result = JsonMngr->ObjectGetValueAt(object, params[2], &valueHandle); + + return (result) ? valueHandle : -1; +} + +//native bool:json_object_has_value(const JSON:object, const name[], JSONType:type = JSONError, bool:dot_not = false); +static cell AMX_NATIVE_CALL amxx_json_object_has_value(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectHasValue(object, name, static_cast(params[3]), params[4] != 0); +} + +//native bool:json_object_set_value(JSON:object, const name[], JSON:value, bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_value(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectSetValue(object, name, params[3], params[4] != 0); +} + +//native bool:json_object_set_string(JSON:object, const name[], const string[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_string(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + auto string = MF_GetAmxString(amx, params[3], 1, &len); + + return JsonMngr->ObjectSetString(object, name, string, params[4] != 0); +} + +//native bool:json_object_set_number(JSON:object, const name[], number, bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_number(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectSetNum(object, name, params[3], params[4] != 0); +} + +//native bool:json_object_set_real(JSON:object, const name[], Float:number, bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_real(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectSetNum(object, name, amx_ctof(params[3]), params[4] != 0); +} + +//native bool:json_object_set_bool(JSON:object, const name[], bool:boolean, bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_bool(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectSetBool(object, name, params[3] != 0, params[4] != 0); +} + +//native bool:json_object_set_null(JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_set_null(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectSetNull(object, name, params[3] != 0); +} + +//native bool:json_object_remove(JSON:object, const name[], bool:dotfunc = false); +static cell AMX_NATIVE_CALL amxx_json_object_remove(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + int len; + auto name = MF_GetAmxString(amx, params[2], 0, &len); + + return JsonMngr->ObjectRemove(object, name, params[3] != 0); +} + +//native bool:json_object_clear(JSON:object); +static cell AMX_NATIVE_CALL amxx_json_object_clear(AMX *amx, cell *params) +{ + auto object = params[1]; + if (!JsonMngr->IsValidHandle(object, Handle_Object)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON object! %d", object); + return 0; + } + + return JsonMngr->ObjectClear(object); +} + +//native json_serial_size(const JSON:value, bool:pretty = false, bool:with_null_byte = false); +static cell AMX_NATIVE_CALL amxx_json_serial_size(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + auto size = JsonMngr->SerialSize(value, params[2] != 0); + + return (params[3]) ? size : size - 1; +} + +//native json_serial_to_string(const JSON:value, buffer[], maxlen, bool:pretty = false); +static cell AMX_NATIVE_CALL amxx_json_serial_to_string(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + auto result = JsonMngr->SerialToString(value, params[4] != 0); + auto written = (result) ? MF_SetAmxStringUTF8Char(amx, params[2], result, strlen(result), params[3]) : 0; + + JsonMngr->FreeString(result); + + return written; +} + +//native bool:json_serial_to_file(const JSON:value, const file[], bool:pretty = false); +static cell AMX_NATIVE_CALL amxx_json_serial_to_file(AMX *amx, cell *params) +{ + auto value = params[1]; + if (!JsonMngr->IsValidHandle(value)) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid JSON value! %d", value); + return 0; + } + + int len; + auto file = MF_GetAmxString(amx, params[2], 0, &len); + + char path[256]; + MF_BuildPathnameR(path, sizeof(path), "%s", file); + + return JsonMngr->SerialToFile(value, path, params[3] != 0); +} + +AMX_NATIVE_INFO JsonNatives[] = +{ + { "json_parse", amxx_json_parse }, + { "json_equals", amxx_json_equals }, + { "json_validate", amxx_json_validate }, + { "json_get_parent", amxx_json_get_parent }, + { "json_get_type", amxx_json_get_type }, + { "json_init_object", amxx_json_init_object }, + { "json_init_array", amxx_json_init_array }, + { "json_init_string", amxx_json_init_string }, + { "json_init_number", amxx_json_init_number }, + { "json_init_real", amxx_json_init_real }, + { "json_init_bool", amxx_json_init_bool }, + { "json_init_null", amxx_json_init_null }, + { "json_deep_copy", amxx_json_deep_copy }, + { "json_free", amxx_json_free }, + { "json_get_string", amxx_json_get_string }, + { "json_get_number", amxx_json_get_number }, + { "json_get_real", amxx_json_get_real }, + { "json_get_bool", amxx_json_get_bool }, + { "json_array_get_value", amxx_json_array_get_value }, + { "json_array_get_string", amxx_json_array_get_string }, + { "json_array_get_count", amxx_json_array_get_count }, + { "json_array_get_number", amxx_json_array_get_number }, + { "json_array_get_real", amxx_json_array_get_real }, + { "json_array_get_bool", amxx_json_array_get_bool }, + { "json_array_replace_value", amxx_json_array_replace_value }, + { "json_array_replace_string", amxx_json_array_replace_string }, + { "json_array_replace_number", amxx_json_array_replace_number }, + { "json_array_replace_real", amxx_json_array_replace_real }, + { "json_array_replace_bool", amxx_json_array_replace_bool }, + { "json_array_replace_null", amxx_json_array_replace_null }, + { "json_array_append_value", amxx_json_array_append_value }, + { "json_array_append_string", amxx_json_array_append_string }, + { "json_array_append_number", amxx_json_array_append_number }, + { "json_array_append_real", amxx_json_array_append_real }, + { "json_array_append_bool", amxx_json_array_append_bool }, + { "json_array_append_null", amxx_json_array_append_null }, + { "json_array_remove", amxx_json_array_remove }, + { "json_array_clear", amxx_json_array_clear }, + { "json_object_get_value", amxx_json_object_get_value }, + { "json_object_get_string", amxx_json_object_get_string }, + { "json_object_get_number", amxx_json_object_get_number }, + { "json_object_get_real", amxx_json_object_get_real }, + { "json_object_get_bool", amxx_json_object_get_bool }, + { "json_object_get_count", amxx_json_object_get_count }, + { "json_object_get_name", amxx_json_object_get_name }, + { "json_object_get_value_at", amxx_json_object_get_value_at }, + { "json_object_has_value", amxx_json_object_has_value }, + { "json_object_set_value", amxx_json_object_set_value }, + { "json_object_set_string", amxx_json_object_set_string }, + { "json_object_set_number", amxx_json_object_set_number }, + { "json_object_set_real", amxx_json_object_set_real }, + { "json_object_set_bool", amxx_json_object_set_bool }, + { "json_object_set_null", amxx_json_object_set_null }, + { "json_object_remove", amxx_json_object_remove }, + { "json_object_clear", amxx_json_object_clear }, + { "json_serial_size", amxx_json_serial_size }, + { "json_serial_to_string", amxx_json_serial_to_string }, + { "json_serial_to_file", amxx_json_serial_to_file }, + { nullptr, nullptr } +}; + +void OnAmxxAttach() +{ + JsonMngr = ke::MakeUnique(); + + MF_AddNatives(JsonNatives); + //MF_AddInterface(JsonMngr.get()); +} diff --git a/modules/json/moduleconfig.h b/modules/json/moduleconfig.h new file mode 100644 index 00000000..085e0b50 --- /dev/null +++ b/modules/json/moduleconfig.h @@ -0,0 +1,515 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license +// + +// +// Module Config +// + +#ifndef __MODULECONFIG_H__ +#define __MODULECONFIG_H__ + +#include + +// Module info +#define MODULE_NAME "JSON" +#define MODULE_VERSION AMXX_VERSION +#define MODULE_AUTHOR "AMX Mod X Dev Team" +#define MODULE_URL "http://www.amxmodx.org" +#define MODULE_LOGTAG "JSON" +#define MODULE_LIBRARY "json" +#define MODULE_LIBCLASS "" +// If you want the module not to be reloaded on mapchange, remove / comment out the next line +#define MODULE_RELOAD_ON_MAPCHANGE + +#ifdef __DATE__ +#define MODULE_DATE __DATE__ +#else // __DATE__ +#define MODULE_DATE "Unknown" +#endif // __DATE__ + +/** +* Add here any modules you want your module to depend on +* Terminate with a null pointer +*/ +/*const char * const MODULE_DEPENDENCIES[] = +{ + nullptr +};*/ + +// metamod plugin? +// #define USE_METAMOD + +// use memory manager/tester? +// note that if you use this, you cannot construct/allocate +// anything before the module attached (OnAmxxAttach). +// be careful of default constructors using new/malloc! +// #define MEMORY_TEST + +// Unless you use STL or exceptions, keep this commented. +// It allows you to compile without libstdc++.so as a dependency +// #define NO_ALLOC_OVERRIDES + +// Uncomment this if you are using MSVC8 or greater and want to fix some of the compatibility issues yourself +// #define NO_MSVC8_AUTO_COMPAT + +/** + * AMXX Init functions + * Also consider using FN_META_* + */ + +/** AMXX query */ +//#define FN_AMXX_QUERY OnAmxxQuery + +/** AMXX attach + * Do native functions init here (MF_AddNatives) + */ +#define FN_AMXX_ATTACH OnAmxxAttach + +/** AMXX Detach (unload) */ +// #define FN_AMXX_DETACH OnAmxxDetach + +/** All plugins loaded + * Do forward functions init here (MF_RegisterForward) + */ +// #define FN_AMXX_PLUGINSLOADED OnPluginsLoaded + +/** All plugins are about to be unloaded */ +//#define FN_AMXX_PLUGINSUNLOADING OnPluginsUnloading + +/** All plugins are now unloaded */ +//#define FN_AMXX_PLUGINSUNLOADED OnPluginsUnloaded + + +/**** METAMOD ****/ +// If your module doesn't use metamod, you may close the file now :) +#ifdef USE_METAMOD +// ---- +// Hook Functions +// Uncomment these to be called +// You can also change the function name + +// - Metamod init functions +// Also consider using FN_AMXX_* +// Meta query +//#define FN_META_QUERY OnMetaQuery +// Meta attach +//#define FN_META_ATTACH OnMetaAttach +// Meta dettach +//#define FN_META_DETACH OnMetaDetach + +// (wd) are Will Day's notes +// - GetEntityAPI2 functions +// #define FN_GameDLLInit GameDLLInit /* pfnGameInit() */ +// #define FN_DispatchSpawn DispatchSpawn /* pfnSpawn() */ +// #define FN_DispatchThink DispatchThink /* pfnThink() */ +// #define FN_DispatchUse DispatchUse /* pfnUse() */ +// #define FN_DispatchTouch DispatchTouch /* pfnTouch() */ +// #define FN_DispatchBlocked DispatchBlocked /* pfnBlocked() */ +// #define FN_DispatchKeyValue DispatchKeyValue /* pfnKeyValue() */ +// #define FN_DispatchSave DispatchSave /* pfnSave() */ +// #define FN_DispatchRestore DispatchRestore /* pfnRestore() */ +// #define FN_DispatchObjectCollsionBox DispatchObjectCollsionBox /* pfnSetAbsBox() */ +// #define FN_SaveWriteFields SaveWriteFields /* pfnSaveWriteFields() */ +// #define FN_SaveReadFields SaveReadFields /* pfnSaveReadFields() */ +// #define FN_SaveGlobalState SaveGlobalState /* pfnSaveGlobalState() */ +// #define FN_RestoreGlobalState RestoreGlobalState /* pfnRestoreGlobalState() */ +// #define FN_ResetGlobalState ResetGlobalState /* pfnResetGlobalState() */ +// #define FN_ClientConnect ClientConnect /* pfnClientConnect() (wd) Client has connected */ +// #define FN_ClientDisconnect ClientDisconnect /* pfnClientDisconnect() (wd) Player has left the game */ +// #define FN_ClientKill ClientKill /* pfnClientKill() (wd) Player has typed "kill" */ +// #define FN_ClientPutInServer ClientPutInServer /* pfnClientPutInServer() (wd) Client is entering the game */ +// #define FN_ClientCommand ClientCommand /* pfnClientCommand() (wd) Player has sent a command (typed or from a bind) */ +// #define FN_ClientUserInfoChanged ClientUserInfoChanged /* pfnClientUserInfoChanged() (wd) Client has updated their setinfo structure */ +// #define FN_ServerActivate ServerActivate /* pfnServerActivate() (wd) Server is starting a new map */ +// #define FN_ServerDeactivate ServerDeactivate /* pfnServerDeactivate() (wd) Server is leaving the map (shutdown or changelevel); SDK2 */ +// #define FN_PlayerPreThink PlayerPreThink /* pfnPlayerPreThink() */ +// #define FN_PlayerPostThink PlayerPostThink /* pfnPlayerPostThink() */ +// #define FN_StartFrame StartFrame /* pfnStartFrame() */ +// #define FN_ParmsNewLevel ParmsNewLevel /* pfnParmsNewLevel() */ +// #define FN_ParmsChangeLevel ParmsChangeLevel /* pfnParmsChangeLevel() */ +// #define FN_GetGameDescription GetGameDescription /* pfnGetGameDescription() Returns string describing current .dll. E.g. "TeamFotrress 2" "Half-Life" */ +// #define FN_PlayerCustomization PlayerCustomization /* pfnPlayerCustomization() Notifies .dll of new customization for player. */ +// #define FN_SpectatorConnect SpectatorConnect /* pfnSpectatorConnect() Called when spectator joins server */ +// #define FN_SpectatorDisconnect SpectatorDisconnect /* pfnSpectatorDisconnect() Called when spectator leaves the server */ +// #define FN_SpectatorThink SpectatorThink /* pfnSpectatorThink() Called when spectator sends a command packet (usercmd_t) */ +// #define FN_Sys_Error Sys_Error /* pfnSys_Error() Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. SDK2 */ +// #define FN_PM_Move PM_Move /* pfnPM_Move() (wd) SDK2 */ +// #define FN_PM_Init PM_Init /* pfnPM_Init() Server version of player movement initialization; (wd) SDK2 */ +// #define FN_PM_FindTextureType PM_FindTextureType /* pfnPM_FindTextureType() (wd) SDK2 */ +// #define FN_SetupVisibility SetupVisibility /* pfnSetupVisibility() Set up PVS and PAS for networking for this client; (wd) SDK2 */ +// #define FN_UpdateClientData UpdateClientData /* pfnUpdateClientData() Set up data sent only to specific client; (wd) SDK2 */ +// #define FN_AddToFullPack AddToFullPack /* pfnAddToFullPack() (wd) SDK2 */ +// #define FN_CreateBaseline CreateBaseline /* pfnCreateBaseline() Tweak entity baseline for network encoding allows setup of player baselines too.; (wd) SDK2 */ +// #define FN_RegisterEncoders RegisterEncoders /* pfnRegisterEncoders() Callbacks for network encoding; (wd) SDK2 */ +// #define FN_GetWeaponData GetWeaponData /* pfnGetWeaponData() (wd) SDK2 */ +// #define FN_CmdStart CmdStart /* pfnCmdStart() (wd) SDK2 */ +// #define FN_CmdEnd CmdEnd /* pfnCmdEnd() (wd) SDK2 */ +// #define FN_ConnectionlessPacket ConnectionlessPacket /* pfnConnectionlessPacket() (wd) SDK2 */ +// #define FN_GetHullBounds GetHullBounds /* pfnGetHullBounds() (wd) SDK2 */ +// #define FN_CreateInstancedBaselines CreateInstancedBaselines /* pfnCreateInstancedBaselines() (wd) SDK2 */ +// #define FN_InconsistentFile InconsistentFile /* pfnInconsistentFile() (wd) SDK2 */ +// #define FN_AllowLagCompensation AllowLagCompensation /* pfnAllowLagCompensation() (wd) SDK2 */ + +// - GetEntityAPI2_Post functions +// #define FN_GameDLLInit_Post GameDLLInit_Post +// #define FN_DispatchSpawn_Post DispatchSpawn_Post +// #define FN_DispatchThink_Post DispatchThink_Post +// #define FN_DispatchUse_Post DispatchUse_Post +// #define FN_DispatchTouch_Post DispatchTouch_Post +// #define FN_DispatchBlocked_Post DispatchBlocked_Post +// #define FN_DispatchKeyValue_Post DispatchKeyValue_Post +// #define FN_DispatchSave_Post DispatchSave_Post +// #define FN_DispatchRestore_Post DispatchRestore_Post +// #define FN_DispatchObjectCollsionBox_Post DispatchObjectCollsionBox_Post +// #define FN_SaveWriteFields_Post SaveWriteFields_Post +// #define FN_SaveReadFields_Post SaveReadFields_Post +// #define FN_SaveGlobalState_Post SaveGlobalState_Post +// #define FN_RestoreGlobalState_Post RestoreGlobalState_Post +// #define FN_ResetGlobalState_Post ResetGlobalState_Post +// #define FN_ClientConnect_Post ClientConnect_Post +// #define FN_ClientDisconnect_Post ClientDisconnect_Post +// #define FN_ClientKill_Post ClientKill_Post +// #define FN_ClientPutInServer_Post ClientPutInServer_Post +// #define FN_ClientCommand_Post ClientCommand_Post +// #define FN_ClientUserInfoChanged_Post ClientUserInfoChanged_Post +// #define FN_ServerActivate_Post ServerActivate_Post +// #define FN_ServerDeactivate_Post ServerDeactivate_Post +// #define FN_PlayerPreThink_Post PlayerPreThink_Post +// #define FN_PlayerPostThink_Post PlayerPostThink_Post +// #define FN_StartFrame_Post StartFrame_Post +// #define FN_ParmsNewLevel_Post ParmsNewLevel_Post +// #define FN_ParmsChangeLevel_Post ParmsChangeLevel_Post +// #define FN_GetGameDescription_Post GetGameDescription_Post +// #define FN_PlayerCustomization_Post PlayerCustomization_Post +// #define FN_SpectatorConnect_Post SpectatorConnect_Post +// #define FN_SpectatorDisconnect_Post SpectatorDisconnect_Post +// #define FN_SpectatorThink_Post SpectatorThink_Post +// #define FN_Sys_Error_Post Sys_Error_Post +// #define FN_PM_Move_Post PM_Move_Post +// #define FN_PM_Init_Post PM_Init_Post +// #define FN_PM_FindTextureType_Post PM_FindTextureType_Post +// #define FN_SetupVisibility_Post SetupVisibility_Post +// #define FN_UpdateClientData_Post UpdateClientData_Post +// #define FN_AddToFullPack_Post AddToFullPack_Post +// #define FN_CreateBaseline_Post CreateBaseline_Post +// #define FN_RegisterEncoders_Post RegisterEncoders_Post +// #define FN_GetWeaponData_Post GetWeaponData_Post +// #define FN_CmdStart_Post CmdStart_Post +// #define FN_CmdEnd_Post CmdEnd_Post +// #define FN_ConnectionlessPacket_Post ConnectionlessPacket_Post +// #define FN_GetHullBounds_Post GetHullBounds_Post +// #define FN_CreateInstancedBaselines_Post CreateInstancedBaselines_Post +// #define FN_InconsistentFile_Post InconsistentFile_Post +// #define FN_AllowLagCompensation_Post AllowLagCompensation_Post + +// - GetEngineAPI functions +// #define FN_PrecacheModel PrecacheModel +// #define FN_PrecacheSound PrecacheSound +// #define FN_SetModel SetModel +// #define FN_ModelIndex ModelIndex +// #define FN_ModelFrames ModelFrames +// #define FN_SetSize SetSize +// #define FN_ChangeLevel ChangeLevel +// #define FN_GetSpawnParms GetSpawnParms +// #define FN_SaveSpawnParms SaveSpawnParms +// #define FN_VecToYaw VecToYaw +// #define FN_VecToAngles VecToAngles +// #define FN_MoveToOrigin MoveToOrigin +// #define FN_ChangeYaw ChangeYaw +// #define FN_ChangePitch ChangePitch +// #define FN_FindEntityByString FindEntityByString +// #define FN_GetEntityIllum GetEntityIllum +// #define FN_FindEntityInSphere FindEntityInSphere +// #define FN_FindClientInPVS FindClientInPVS +// #define FN_EntitiesInPVS EntitiesInPVS +// #define FN_MakeVectors MakeVectors +// #define FN_AngleVectors AngleVectors +// #define FN_CreateEntity CreateEntity +// #define FN_RemoveEntity RemoveEntity +// #define FN_CreateNamedEntity CreateNamedEntity +// #define FN_MakeStatic MakeStatic +// #define FN_EntIsOnFloor EntIsOnFloor +// #define FN_DropToFloor DropToFloor +// #define FN_WalkMove WalkMove +// #define FN_SetOrigin SetOrigin +// #define FN_EmitSound EmitSound +// #define FN_EmitAmbientSound EmitAmbientSound +// #define FN_TraceLine TraceLine +// #define FN_TraceToss TraceToss +// #define FN_TraceMonsterHull TraceMonsterHull +// #define FN_TraceHull TraceHull +// #define FN_TraceModel TraceModel +// #define FN_TraceTexture TraceTexture +// #define FN_TraceSphere TraceSphere +// #define FN_GetAimVector GetAimVector +// #define FN_ServerCommand ServerCommand +// #define FN_ServerExecute ServerExecute +// #define FN_engClientCommand engClientCommand +// #define FN_ParticleEffect ParticleEffect +// #define FN_LightStyle LightStyle +// #define FN_DecalIndex DecalIndex +// #define FN_PointContents PointContents +// #define FN_MessageBegin MessageBegin +// #define FN_MessageEnd MessageEnd +// #define FN_WriteByte WriteByte +// #define FN_WriteChar WriteChar +// #define FN_WriteShort WriteShort +// #define FN_WriteLong WriteLong +// #define FN_WriteAngle WriteAngle +// #define FN_WriteCoord WriteCoord +// #define FN_WriteString WriteString +// #define FN_WriteEntity WriteEntity +// #define FN_CVarRegister CVarRegister +// #define FN_CVarGetFloat CVarGetFloat +// #define FN_CVarGetString CVarGetString +// #define FN_CVarSetFloat CVarSetFloat +// #define FN_CVarSetString CVarSetString +// #define FN_AlertMessage AlertMessage +// #define FN_EngineFprintf EngineFprintf +// #define FN_PvAllocEntPrivateData PvAllocEntPrivateData +// #define FN_PvEntPrivateData PvEntPrivateData +// #define FN_FreeEntPrivateData FreeEntPrivateData +// #define FN_SzFromIndex SzFromIndex +// #define FN_AllocString AllocString +// #define FN_GetVarsOfEnt GetVarsOfEnt +// #define FN_PEntityOfEntOffset PEntityOfEntOffset +// #define FN_EntOffsetOfPEntity EntOffsetOfPEntity +// #define FN_IndexOfEdict IndexOfEdict +// #define FN_PEntityOfEntIndex PEntityOfEntIndex +// #define FN_FindEntityByVars FindEntityByVars +// #define FN_GetModelPtr GetModelPtr +// #define FN_RegUserMsg RegUserMsg +// #define FN_AnimationAutomove AnimationAutomove +// #define FN_GetBonePosition GetBonePosition +// #define FN_FunctionFromName FunctionFromName +// #define FN_NameForFunction NameForFunction +// #define FN_ClientPrintf ClientPrintf +// #define FN_ServerPrint ServerPrint +// #define FN_Cmd_Args Cmd_Args +// #define FN_Cmd_Argv Cmd_Argv +// #define FN_Cmd_Argc Cmd_Argc +// #define FN_GetAttachment GetAttachment +// #define FN_CRC32_Init CRC32_Init +// #define FN_CRC32_ProcessBuffer CRC32_ProcessBuffer +// #define FN_CRC32_ProcessByte CRC32_ProcessByte +// #define FN_CRC32_Final CRC32_Final +// #define FN_RandomLong RandomLong +// #define FN_RandomFloat RandomFloat +// #define FN_SetView SetView +// #define FN_Time Time +// #define FN_CrosshairAngle CrosshairAngle +// #define FN_LoadFileForMe LoadFileForMe +// #define FN_FreeFile FreeFile +// #define FN_EndSection EndSection +// #define FN_CompareFileTime CompareFileTime +// #define FN_GetGameDir GetGameDir +// #define FN_Cvar_RegisterVariable Cvar_RegisterVariable +// #define FN_FadeClientVolume FadeClientVolume +// #define FN_SetClientMaxspeed SetClientMaxspeed +// #define FN_CreateFakeClient CreateFakeClient +// #define FN_RunPlayerMove RunPlayerMove +// #define FN_NumberOfEntities NumberOfEntities +// #define FN_GetInfoKeyBuffer GetInfoKeyBuffer +// #define FN_InfoKeyValue InfoKeyValue +// #define FN_SetKeyValue SetKeyValue +// #define FN_SetClientKeyValue SetClientKeyValue +// #define FN_IsMapValid IsMapValid +// #define FN_StaticDecal StaticDecal +// #define FN_PrecacheGeneric PrecacheGeneric +// #define FN_GetPlayerUserId GetPlayerUserId +// #define FN_BuildSoundMsg BuildSoundMsg +// #define FN_IsDedicatedServer IsDedicatedServer +// #define FN_CVarGetPointer CVarGetPointer +// #define FN_GetPlayerWONId GetPlayerWONId +// #define FN_Info_RemoveKey Info_RemoveKey +// #define FN_GetPhysicsKeyValue GetPhysicsKeyValue +// #define FN_SetPhysicsKeyValue SetPhysicsKeyValue +// #define FN_GetPhysicsInfoString GetPhysicsInfoString +// #define FN_PrecacheEvent PrecacheEvent +// #define FN_PlaybackEvent PlaybackEvent +// #define FN_SetFatPVS SetFatPVS +// #define FN_SetFatPAS SetFatPAS +// #define FN_CheckVisibility CheckVisibility +// #define FN_DeltaSetField DeltaSetField +// #define FN_DeltaUnsetField DeltaUnsetField +// #define FN_DeltaAddEncoder DeltaAddEncoder +// #define FN_GetCurrentPlayer GetCurrentPlayer +// #define FN_CanSkipPlayer CanSkipPlayer +// #define FN_DeltaFindField DeltaFindField +// #define FN_DeltaSetFieldByIndex DeltaSetFieldByIndex +// #define FN_DeltaUnsetFieldByIndex DeltaUnsetFieldByIndex +// #define FN_SetGroupMask SetGroupMask +// #define FN_engCreateInstancedBaseline engCreateInstancedBaseline +// #define FN_Cvar_DirectSet Cvar_DirectSet +// #define FN_ForceUnmodified ForceUnmodified +// #define FN_GetPlayerStats GetPlayerStats +// #define FN_AddServerCommand AddServerCommand +// #define FN_Voice_GetClientListening Voice_GetClientListening +// #define FN_Voice_SetClientListening Voice_SetClientListening +// #define FN_GetPlayerAuthId GetPlayerAuthId + +// - GetEngineAPI_Post functions +// #define FN_PrecacheModel_Post PrecacheModel_Post +// #define FN_PrecacheSound_Post PrecacheSound_Post +// #define FN_SetModel_Post SetModel_Post +// #define FN_ModelIndex_Post ModelIndex_Post +// #define FN_ModelFrames_Post ModelFrames_Post +// #define FN_SetSize_Post SetSize_Post +// #define FN_ChangeLevel_Post ChangeLevel_Post +// #define FN_GetSpawnParms_Post GetSpawnParms_Post +// #define FN_SaveSpawnParms_Post SaveSpawnParms_Post +// #define FN_VecToYaw_Post VecToYaw_Post +// #define FN_VecToAngles_Post VecToAngles_Post +// #define FN_MoveToOrigin_Post MoveToOrigin_Post +// #define FN_ChangeYaw_Post ChangeYaw_Post +// #define FN_ChangePitch_Post ChangePitch_Post +// #define FN_FindEntityByString_Post FindEntityByString_Post +// #define FN_GetEntityIllum_Post GetEntityIllum_Post +// #define FN_FindEntityInSphere_Post FindEntityInSphere_Post +// #define FN_FindClientInPVS_Post FindClientInPVS_Post +// #define FN_EntitiesInPVS_Post EntitiesInPVS_Post +// #define FN_MakeVectors_Post MakeVectors_Post +// #define FN_AngleVectors_Post AngleVectors_Post +// #define FN_CreateEntity_Post CreateEntity_Post +// #define FN_RemoveEntity_Post RemoveEntity_Post +// #define FN_CreateNamedEntity_Post CreateNamedEntity_Post +// #define FN_MakeStatic_Post MakeStatic_Post +// #define FN_EntIsOnFloor_Post EntIsOnFloor_Post +// #define FN_DropToFloor_Post DropToFloor_Post +// #define FN_WalkMove_Post WalkMove_Post +// #define FN_SetOrigin_Post SetOrigin_Post +// #define FN_EmitSound_Post EmitSound_Post +// #define FN_EmitAmbientSound_Post EmitAmbientSound_Post +// #define FN_TraceLine_Post TraceLine_Post +// #define FN_TraceToss_Post TraceToss_Post +// #define FN_TraceMonsterHull_Post TraceMonsterHull_Post +// #define FN_TraceHull_Post TraceHull_Post +// #define FN_TraceModel_Post TraceModel_Post +// #define FN_TraceTexture_Post TraceTexture_Post +// #define FN_TraceSphere_Post TraceSphere_Post +// #define FN_GetAimVector_Post GetAimVector_Post +// #define FN_ServerCommand_Post ServerCommand_Post +// #define FN_ServerExecute_Post ServerExecute_Post +// #define FN_engClientCommand_Post engClientCommand_Post +// #define FN_ParticleEffect_Post ParticleEffect_Post +// #define FN_LightStyle_Post LightStyle_Post +// #define FN_DecalIndex_Post DecalIndex_Post +// #define FN_PointContents_Post PointContents_Post +// #define FN_MessageBegin_Post MessageBegin_Post +// #define FN_MessageEnd_Post MessageEnd_Post +// #define FN_WriteByte_Post WriteByte_Post +// #define FN_WriteChar_Post WriteChar_Post +// #define FN_WriteShort_Post WriteShort_Post +// #define FN_WriteLong_Post WriteLong_Post +// #define FN_WriteAngle_Post WriteAngle_Post +// #define FN_WriteCoord_Post WriteCoord_Post +// #define FN_WriteString_Post WriteString_Post +// #define FN_WriteEntity_Post WriteEntity_Post +// #define FN_CVarRegister_Post CVarRegister_Post +// #define FN_CVarGetFloat_Post CVarGetFloat_Post +// #define FN_CVarGetString_Post CVarGetString_Post +// #define FN_CVarSetFloat_Post CVarSetFloat_Post +// #define FN_CVarSetString_Post CVarSetString_Post +// #define FN_AlertMessage_Post AlertMessage_Post +// #define FN_EngineFprintf_Post EngineFprintf_Post +// #define FN_PvAllocEntPrivateData_Post PvAllocEntPrivateData_Post +// #define FN_PvEntPrivateData_Post PvEntPrivateData_Post +// #define FN_FreeEntPrivateData_Post FreeEntPrivateData_Post +// #define FN_SzFromIndex_Post SzFromIndex_Post +// #define FN_AllocString_Post AllocString_Post +// #define FN_GetVarsOfEnt_Post GetVarsOfEnt_Post +// #define FN_PEntityOfEntOffset_Post PEntityOfEntOffset_Post +// #define FN_EntOffsetOfPEntity_Post EntOffsetOfPEntity_Post +// #define FN_IndexOfEdict_Post IndexOfEdict_Post +// #define FN_PEntityOfEntIndex_Post PEntityOfEntIndex_Post +// #define FN_FindEntityByVars_Post FindEntityByVars_Post +// #define FN_GetModelPtr_Post GetModelPtr_Post +// #define FN_RegUserMsg_Post RegUserMsg_Post +// #define FN_AnimationAutomove_Post AnimationAutomove_Post +// #define FN_GetBonePosition_Post GetBonePosition_Post +// #define FN_FunctionFromName_Post FunctionFromName_Post +// #define FN_NameForFunction_Post NameForFunction_Post +// #define FN_ClientPrintf_Post ClientPrintf_Post +// #define FN_ServerPrint_Post ServerPrint_Post +// #define FN_Cmd_Args_Post Cmd_Args_Post +// #define FN_Cmd_Argv_Post Cmd_Argv_Post +// #define FN_Cmd_Argc_Post Cmd_Argc_Post +// #define FN_GetAttachment_Post GetAttachment_Post +// #define FN_CRC32_Init_Post CRC32_Init_Post +// #define FN_CRC32_ProcessBuffer_Post CRC32_ProcessBuffer_Post +// #define FN_CRC32_ProcessByte_Post CRC32_ProcessByte_Post +// #define FN_CRC32_Final_Post CRC32_Final_Post +// #define FN_RandomLong_Post RandomLong_Post +// #define FN_RandomFloat_Post RandomFloat_Post +// #define FN_SetView_Post SetView_Post +// #define FN_Time_Post Time_Post +// #define FN_CrosshairAngle_Post CrosshairAngle_Post +// #define FN_LoadFileForMe_Post LoadFileForMe_Post +// #define FN_FreeFile_Post FreeFile_Post +// #define FN_EndSection_Post EndSection_Post +// #define FN_CompareFileTime_Post CompareFileTime_Post +// #define FN_GetGameDir_Post GetGameDir_Post +// #define FN_Cvar_RegisterVariable_Post Cvar_RegisterVariable_Post +// #define FN_FadeClientVolume_Post FadeClientVolume_Post +// #define FN_SetClientMaxspeed_Post SetClientMaxspeed_Post +// #define FN_CreateFakeClient_Post CreateFakeClient_Post +// #define FN_RunPlayerMove_Post RunPlayerMove_Post +// #define FN_NumberOfEntities_Post NumberOfEntities_Post +// #define FN_GetInfoKeyBuffer_Post GetInfoKeyBuffer_Post +// #define FN_InfoKeyValue_Post InfoKeyValue_Post +// #define FN_SetKeyValue_Post SetKeyValue_Post +// #define FN_SetClientKeyValue_Post SetClientKeyValue_Post +// #define FN_IsMapValid_Post IsMapValid_Post +// #define FN_StaticDecal_Post StaticDecal_Post +// #define FN_PrecacheGeneric_Post PrecacheGeneric_Post +// #define FN_GetPlayerUserId_Post GetPlayerUserId_Post +// #define FN_BuildSoundMsg_Post BuildSoundMsg_Post +// #define FN_IsDedicatedServer_Post IsDedicatedServer_Post +// #define FN_CVarGetPointer_Post CVarGetPointer_Post +// #define FN_GetPlayerWONId_Post GetPlayerWONId_Post +// #define FN_Info_RemoveKey_Post Info_RemoveKey_Post +// #define FN_GetPhysicsKeyValue_Post GetPhysicsKeyValue_Post +// #define FN_SetPhysicsKeyValue_Post SetPhysicsKeyValue_Post +// #define FN_GetPhysicsInfoString_Post GetPhysicsInfoString_Post +// #define FN_PrecacheEvent_Post PrecacheEvent_Post +// #define FN_PlaybackEvent_Post PlaybackEvent_Post +// #define FN_SetFatPVS_Post SetFatPVS_Post +// #define FN_SetFatPAS_Post SetFatPAS_Post +// #define FN_CheckVisibility_Post CheckVisibility_Post +// #define FN_DeltaSetField_Post DeltaSetField_Post +// #define FN_DeltaUnsetField_Post DeltaUnsetField_Post +// #define FN_DeltaAddEncoder_Post DeltaAddEncoder_Post +// #define FN_GetCurrentPlayer_Post GetCurrentPlayer_Post +// #define FN_CanSkipPlayer_Post CanSkipPlayer_Post +// #define FN_DeltaFindField_Post DeltaFindField_Post +// #define FN_DeltaSetFieldByIndex_Post DeltaSetFieldByIndex_Post +// #define FN_DeltaUnsetFieldByIndex_Post DeltaUnsetFieldByIndex_Post +// #define FN_SetGroupMask_Post SetGroupMask_Post +// #define FN_engCreateInstancedBaseline_Post engCreateInstancedBaseline_Post +// #define FN_Cvar_DirectSet_Post Cvar_DirectSet_Post +// #define FN_ForceUnmodified_Post ForceUnmodified_Post +// #define FN_GetPlayerStats_Post GetPlayerStats_Post +// #define FN_AddServerCommand_Post AddServerCommand_Post +// #define FN_Voice_GetClientListening_Post Voice_GetClientListening_Post +// #define FN_Voice_SetClientListening_Post Voice_SetClientListening_Post +// #define FN_GetPlayerAuthId_Post GetPlayerAuthId_Post + +// #define FN_OnFreeEntPrivateData OnFreeEntPrivateData +// #define FN_GameShutdown GameShutdown +// #define FN_ShouldCollide ShouldCollide + +// #define FN_OnFreeEntPrivateData_Post OnFreeEntPrivateData_Post +// #define FN_GameShutdown_Post GameShutdown_Post +// #define FN_ShouldCollide_Post ShouldCollide_Post + + +#endif // USE_METAMOD + +#endif // __MODULECONFIG_H__ diff --git a/modules/json/msvc12/json.sln b/modules/json/msvc12/json.sln new file mode 100644 index 00000000..d4737bb7 --- /dev/null +++ b/modules/json/msvc12/json.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "json", "json.vcxproj", "{4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Debug|Win32.ActiveCfg = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Debug|Win32.Build.0 = Debug|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Release|Win32.ActiveCfg = Release|Win32 + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {89382960-2F97-4398-AE0F-812780760B7E} + EndGlobalSection +EndGlobal diff --git a/modules/json/msvc12/json.vcxproj b/modules/json/msvc12/json.vcxproj new file mode 100644 index 00000000..a6d0c836 --- /dev/null +++ b/modules/json/msvc12/json.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {4CF9D220-F318-4D1C-81DF-E2377A2D8FFE} + json + Win32Proj + + + + DynamicLibrary + MultiByte + v120_xp + + + DynamicLibrary + MultiByte + v120_xp + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + true + false + AllRules.ruleset + + + AllRules.ruleset + + + $(ProjectName)_amxx + $(ProjectName)_amxx + + + + Disabled + ..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl;..\..\..\third_party;..\..\..\third_party\parson;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + WIN32;DEBUG;_WINDOWS;_USRDLL;JSON_EXPORTS;HAVE_STDINT_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + false + + + Level3 + ProgramDatabase + + + true + $(OutDir)json.pdb + Windows + $(OutDir)json.lib + MachineX86 + LIBCMT; + false + + + + + ..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl;..\..\..\third_party;..\..\..\third_party\parson;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;JSON_EXPORTS;HAVE_STDINT_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + + + Level3 + ProgramDatabase + + + true + Windows + true + true + $(OutDir)json.lib + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/json/msvc12/json.vcxproj.filters b/modules/json/msvc12/json.vcxproj.filters new file mode 100644 index 00000000..8c67d724 --- /dev/null +++ b/modules/json/msvc12/json.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {959a9831-eb53-4faa-b073-dad6790bc123} + + + {72ab2480-daa0-43cc-9fe5-5710998b27b0} + + + {c1f7d9b4-ea37-4980-b17f-f4ff55cb1990} + + + {259f5990-1378-4291-9f8e-d6e0ad89173a} + + + {ad24e0a6-afc1-4eb3-9c2d-2aaac86aa36c} + + + {dc99c702-9fc8-4e8c-ba44-959a066cdbe2} + + + {aaebd544-6579-4a7e-9c6b-2ccc3c0280be} + + + + + Source Files + + + Source Files + + + Module SDK\SDK Base + + + Third Party\Parson + + + + + Header Files + + + Header Files + + + Module SDK + + + Module SDK\SDK Base + + + Third Party\Parson + + + + + Pawn Includes + + + \ No newline at end of file diff --git a/modules/json/version.rc b/modules/json/version.rc new file mode 100644 index 00000000..26bd500f --- /dev/null +++ b/modules/json/version.rc @@ -0,0 +1,101 @@ +// Microsoft Visual C++ generated resource script. +// +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION AMXX_VERSION_FILE + PRODUCTVERSION AMXX_VERSION_FILE + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "AMX Mod X" + VALUE "FileDescription", "AMX Mod X" + VALUE "FileVersion", AMXX_VERSION + VALUE "InternalName", MODULE_LIBRARY + VALUE "LegalCopyright", "Copyright (c) AMX Mod X Dev Team" + VALUE "OriginalFilename", MODULE_LIBRARY "_amxx.dll" + VALUE "ProductName", MODULE_NAME + VALUE "ProductVersion", AMXX_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/include/json.inc b/plugins/include/json.inc new file mode 100644 index 00000000..687421b6 --- /dev/null +++ b/plugins/include/json.inc @@ -0,0 +1,779 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// JSON Natives +// + +#if defined _json_included + #endinput +#endif +#define _json_included + +#pragma reqlib json +#if !defined AMXMODX_NOAUTOLOAD + #pragma loadlib json +#endif + +/* + * JSON types + */ +enum JSONType +{ + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +}; + +/* + * JSON invalid handle + */ +enum JSON +{ + Invalid_JSON = -1 +} + +/** + * Helper macros for checking type + */ +#define json_is_object(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONObject) +#define json_is_array(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONArray) +#define json_is_string(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONString) +#define json_is_number(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONNumber) +#define json_is_bool(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONBoolean) +#define json_is_null(%1) (%1 != Invalid_JSON && json_get_type(%1) == JSONNull) +#define json_is_true(%1) (%1 != Invalid_JSON && json_is_bool(%1) && json_get_bool(%1)) +#define json_is_false(%1) (%1 != Invalid_JSON && json_is_bool(%1) && !json_get_bool(%1)) + +/** + * Parses JSON string or a file that contains JSON. + * + * @note Needs to be freed using json_free() native. + * + * @param string String to parse + * @param is_file True to treat string param as filename, false otherwise + * @param with_comments True if parsing JSON includes comments (it will ignore them), false otherwise + * + * @return JSON handle, Invalid_JSONValue if error occurred + */ +native JSON:json_parse(const string[], bool:is_file = false, bool:with_comments = false); + +/** + * Checks if the first value is the same as the second one. + * + * @param value1 JSON handle + * @param value2 JSON handle + * + * @return True if they are the same, false otherwise + * @error If passed value is not a valid handle + */ +native bool:json_equals(const JSON:value1, const JSON:value2); + +/** + * Validates json by checking if object have identically named + * fields with matching types. + * + * @note Schema {"name":"", "age":0} will validate + * {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + * but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + * + * @note In case of arrays, only first value in schema + * is checked against all values in tested array. + * + * @note Empty objects ({}) validate all objects, + * empty arrays ([]) validate all arrays, + * null validates values of every type. + * + * @param schema JSON handle + * @param value JSON handle + * + * @return True if passed value is valid, false otherwise + * @error If a schema handle or value handle is invalid + */ +native bool:json_validate(const JSON:schema, const JSON:value); + +/** + * Gets value's parent handle. + * + * @note Parent's handle needs to be freed using json_free() native. + * + * @param value JSON handle + * + * @return Parent's handle + */ +native JSON:json_get_parent(const JSON:value); + +/** + * Gets JSON type of passed value. + * + * @param value JSON handle + * + * @return JSON type (JSONType constants) + * @error If a value handle is invalid + */ +native JSONType:json_get_type(const JSON:value); + +/** + * Inits an empty object. + * + * @note Needs to be freed using json_free() native. + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_object(); + +/** + * Inits an empty array. + * + * @note Needs to be freed using json_free() native. + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_array(); + +/** + * Inits string data. + * + * @note Needs to be freed using json_free() native. + * + * @param value String that the handle will be initialized with + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_string(const value[]); + +/** + * Inits a number. + * + * @note Needs to be freed using json_free() native. + * + * @param value Integer number that the handle will be initialized with + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_number(value); + +/** + * Inits a real number. + * + * @note Needs to be freed using json_free() native. + * + * @param value Real number that the handle will be initialized with + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_real(Float:value); + +/** + * Inits a boolean value. + * + * @note Needs to be freed using json_free() native. + * + * @param value Boolean value that the handle will be initialized with + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_bool(bool:value); + +/** + * Inits a null. + * + * @note Needs to be freed using json_free() native. + * + * @return JSON handle, Invalid_JSON if error occurred + */ +native JSON:json_init_null(); + +/** + * Creates deep copy of passed value. + * + * @note Needs to be freed using json_free() native. + * + * @param value JSON handle to be copied + * + * @return JSON handle, Invalid_JSON if error occurred + * @error If passed value is not a valid handle + */ +native JSON:json_deep_copy(const JSON:value); + +/** + * Frees handle. + * + * @param handle JSON handle to be freed + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid handle + */ +native bool:json_free(&JSON:handle); + +/** + * Gets string data. + * + * @param value JSON handle + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * + * @return The number of cells written to the buffer + * @error If passed value is not a valid handle + */ +native json_get_string(const JSON:value, buffer[], maxlen); + +/** + * Gets a number. + * + * @param value JSON handle + * + * @return Number + * @error If passed value is not a valid handle + */ +native json_get_number(const JSON:value); + +/** + * Gets a real number. + * + * @param value JSON handle + * + * @return Real number + * @error If passed value is not a valid handle + */ +native Float:json_get_real(const JSON:value); + +/** + * Gets a boolean value. + * + * @param value JSON handle + * + * @return Boolean value + * @error If passed value is not a valid handle + */ +native bool:json_get_bool(const JSON:value); + +/** + * Gets a value from the array. + * + * @note Needs to be freed using json_free() native. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * + * @return JSON handle, Invalid_JSON if error occurred + * @error If passed handle is not a valid array + */ +native JSON:json_array_get_value(const JSON:array, index); + +/** + * Gets string data from the array. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * + * @return The number of cells written to the buffer + * @error If passed handle is not a valid array + */ +native json_array_get_string(const JSON:array, index, buffer[], maxlen); + +/** + * Gets a number from the array. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * + * @return The number as integer + * @error If passed handle is not a valid array + */ +native json_array_get_number(const JSON:array, index); + +/** + * Gets a real number from the array. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * + * @return The number as float + * @error If passed handle is not a valid array + */ +native Float:json_array_get_real(const JSON:array, index); + +/** + * Gets a boolean value from the array. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * + * @return Boolean value + * @error If passed handle is not a valid array + */ +native bool:json_array_get_bool(const JSON:array, index); + +/** + * Gets count of the elements in the array. + * + * @param array Array handle + * + * @return Number of elements in the array + * @error If passed handle is not a valid array + */ +native json_array_get_count(const JSON:array); + +/** + * Replaces an element in the array with value. + * + * @param array Array handle + * @param index Position in the array to be replaced + * @param value JSON handle to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_value(JSON:array, index, const JSON:value); + +/** + * Replaces an element in the array with string data. + * + * @param array Array handle + * @param index Position in the array to be replaced + * @param string String to copy + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_string(JSON:array, index, const string[]); + +/** + * Replaces an element in the array with number. + * + * @param array Array handle + * @param index Position in the array to be replaced + * @param number Number to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_number(JSON:array, index, number); + +/** + * Replaces an element in the array with real number. + * + * @param array Array handle + * @param index Position in the array to be replaced + * @param number Real number to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_real(JSON:array, index, Float:number); + +/** + * Replaces an element in the array with boolean value. + * + * @param array Array handle + * @param index Position in the array to be replaced + * @param boolean Boolean value to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_bool(JSON:array, index, bool:boolean); + +/** + * Replaces an element in the array with null. + * + * @param array Array handle + * @param index Position in the array to be replaced + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_replace_null(JSON:array, index); + +/** + * Appends a value in the array. + * + * @param array Array handle + * @param value JSON handle to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_value(JSON:array, const JSON:value); + +/** + * Appends string data in the array. + * + * @param array Array handle + * @param string String to copy + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_string(JSON:array, const string[]); + +/** + * Appends a number in the array. + * + * @param array Array handle + * @param number Number to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_number(JSON:array, number); + +/** + * Appends a real number in the array. + * + * @param array Array handle + * @param number Real number to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_real(JSON:array, Float:number); + +/** + * Appends a boolean value in the array. + * + * @param array Array handle + * @param boolean Boolean value to set + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_bool(JSON:array, bool:boolean); + +/** + * Appends a null in the array. + * + * @param array Array handle + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_append_null(JSON:array); + +/** + * Removes an element from the array. + * + * @note Order of values in array may change during execution. + * + * @param array Array handle + * @param index Position in the array (starting from 0) + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_remove(JSON:array, index); + +/** + * Removes all elements from the array. + * + * @param array Array handle + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid array + */ +native bool:json_array_clear(JSON:array); + +/** + * Gets a value from the object. + * + * @note Needs to be freed using json_free() native. + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return JSON handle, Invalid_JSON if error occurred + * @error If passed handle is not a valid object + */ +native JSON:json_object_get_value(const JSON:object, const name[], bool:dot_not = false); + +/** + * Gets string data from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * @param dot_not True to use dot notation, false to not + * + * @return The number of cells written to the buffer + * @error If passed handle is not a valid object + */ +native json_object_get_string(const JSON:object, const name[], buffer[], maxlen, bool:dot_not = false); + +/** + * Gets a number from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return Number + * @error If passed handle is not a valid object + */ +native json_object_get_number(const JSON:object, const name[], bool:dot_not = false); + +/** + * Gets a real number from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return Real number + * @error If passed handle is not a valid object + */ +native Float:json_object_get_real(const JSON:object, const name[], bool:dot_not = false); + +/** + * Gets a boolean value from the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return Boolean value + * @error If passed handle is not a valid object + */ +native bool:json_object_get_bool(const JSON:object, const name[], bool:dot_not = false); + +/** + * Gets count of the keys in the object. + * + * @param object Object handle + * + * @return Keys count + * @error If passed handle is not a valid object + */ +native json_object_get_count(const JSON:object); + +/** + * Gets name of the object's key. + * + * @param object Object handle + * @param index Position from which get key name + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * + * @return The number of cells written to the buffer + * @error If passed handle is not a valid object + */ +native json_object_get_name(const JSON:object, index, buffer[], maxlen); + +/** + * Gets a value at the specified position from the object. + * + * @note Needs to be freed using json_free() native. + * + * @param object Object handle + * @param index Position from which get key name + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * + * @return The number of cells written to the buffer + * @error If passed handle is not a valid object + */ +native JSON:json_object_get_value_at(const JSON:object, index); + +/** + * Checks if the object has a value with a specific name and type. + * + * @param object Object handle + * @param name Key name + * @param type Type of value, if JSONError type will not be checked + * @param dot_not True to use dot notation, false to not + * + * @return True if has, false if not + * @error If passed handle is not a valid object + */ +native bool:json_object_has_value(const JSON:object, const name[], JSONType:type = JSONError, bool:dot_not = false); + +/** + * Sets a value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param value JSON handle to set + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_value(JSON:object, const name[], const JSON:value, bool:dot_not = false); + +/** + * Sets string data in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param string String to copy + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_string(JSON:object, const name[], const string[], bool:dot_not = false); + +/** + * Sets a number in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param number Number to set + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_number(JSON:object, const name[], number, bool:dot_not = false); + +/** + * Sets a real number in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param number Real number to set + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_real(JSON:object, const name[], Float:number, bool:dot_not = false); + +/** + * Sets a boolean value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param boolean Boolean value to set + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_bool(JSON:object, const name[], bool:boolean, bool:dot_not = false); + +/** + * Sets a null in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * @note It also removes the old value if any. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_set_null(JSON:object, const name[], bool:dot_not = false); + +/** + * Removes a key and its value in the object. + * + * @note If dot notation is used some values may be inaccessible + * because valid names in JSON can contain dots. + * + * @param object Object handle + * @param name Key name + * @param dot_not True to use dot notation, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_remove(JSON:object, const name[], bool:dot_not = false); + +/** + * Removes all keys and their values in the object. + * + * @param object Object handle + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid object + */ +native bool:json_object_clear(JSON:object); + +/** + * Gets size of serialization. + * + * @param value JSON handle + * @param pretty True to count size for pretty format, false to not + * @param null_byte True to include null byte, false to not + * + * @return Size of serialized string + * @error If passed handle is not a valid value + */ +native json_serial_size(const JSON:value, bool:pretty = false, bool:null_byte = false); + +/** + * Copies serialized string to the buffer. + * + * @param value JSON handle + * @param buffer Buffer to copy string to + * @param maxlen Maximum size of the buffer + * @param pretty True to format pretty JSON string, false to not + * + * @return The number of cells written to the buffer + * @error If passed handle is not a valid value + */ +native json_serial_to_string(const JSON:value, buffer[], maxlen, bool:pretty = false); + +/** + * Copies serialized string to the file. + * + * @param value JSON handle + * @param file Path to the file + * @param pretty True to format pretty JSON string, false to not + * + * @return True if succeed, false otherwise + * @error If passed handle is not a valid value + */ +native bool:json_serial_to_file(const JSON:value, const file[], bool:pretty = false); diff --git a/plugins/testsuite/json_test.sma b/plugins/testsuite/json_test.sma new file mode 100644 index 00000000..873220e3 --- /dev/null +++ b/plugins/testsuite/json_test.sma @@ -0,0 +1,494 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +#include +#include + +//For encoding +new buffer[500]; + +public plugin_init() +{ + register_plugin("JSON Test", "1.0", "Ni3znajomy"); + register_srvcmd("json_test_encode", "cmdJSONTestEncode"); + register_srvcmd("json_test_decode", "cmdJSONTestDecode"); + register_srvcmd("json_test_replace", "cmdJSONTestReplace"); + register_srvcmd("json_test_validate", "cmdJSONTestValidate"); + register_srvcmd("json_test_has_key", "cmdJSONTestHasKey"); + register_srvcmd("json_test_remove", "cmdJSONTestRemove"); +} + +public cmdJSONTestEncode() +{ + if (read_argc() < 3) + { + server_print("Usage: json_test_encode "); + return; + } + // Use init funcs? + new bool:init = bool:read_argv_int(1); + + //Pretty + new bool:pretty = bool:read_argv_int(2); + + // Create root array + new JSON:root_array = json_init_array(); + // Init & setup object + new JSON:object = json_init_object(); + if (!init) + { + json_object_set_string(object, "string", "https://alliedmods.net"); + json_object_set_number(object, "number", 45); + json_object_set_real(object, "real", -5.31); + json_object_set_null(object, "null.null"); + json_object_set_bool(object, "bool.true", true, true); + } + else + { + ObjectSetKey(object, "string", json_init_string("https://alliedmods.net")); + ObjectSetKey(object, "number", json_init_number(45)); + ObjectSetKey(object, "real", json_init_real(-5.31)); + ObjectSetKey(object, "null.null", json_init_null()); + ObjectSetKey(object, "bool.true", json_init_bool(true), true); + } + + // Add object to root array + ArrayAppendValue(root_array, object); + + new JSON:parent = json_get_parent(root_array); + if (parent != Invalid_JSON) + { + server_print("Root array has parent!"); + json_free(parent); + json_free(root_array); + return; + } + + // Append some values to root array + if (!init) + { + json_array_append_real(root_array, -31.1); + json_array_append_number(root_array, -42); + json_array_append_bool(root_array, false); + json_array_append_null(root_array); + } + else + { + ArrayAppendValue(root_array, json_init_real(-31.1)); + ArrayAppendValue(root_array, json_init_number(-42)); + ArrayAppendValue(root_array, json_init_bool(false)); + ArrayAppendValue(root_array, json_init_null()); + } + + // Serialiaze to file and to buffer + json_serial_to_file(root_array, "json_encode_test_to_file.txt", pretty); + json_serial_to_string(root_array, buffer, charsmax(buffer), pretty); + + // Put buffer's content to file + new file = fopen("json_encode_test_to_string.txt", "wt"); + if (!file) + { + server_print("Couldn't create file"); + return; + } + + fputs(file, buffer); + fclose(file); + + server_print("Encoding done (%d bytes)", json_serial_size(root_array, pretty)); + json_free(root_array); +} + +public cmdJSONTestDecode() +{ + // Check if encode command was run + if (!strlen(buffer)) + { + server_print("Run ^"json_test_encode^" first!"); + return; + } + + new JSON:root_array = json_parse(buffer); + new JSON:for_compare = json_parse("json_encode_test_to_file.txt", true); + + // Check if they are the same + if (root_array != for_compare || !json_is_array(root_array)) + { + if (root_array != Invalid_JSON) + json_free(root_array); + + if (for_compare != Invalid_JSON) + json_free(for_compare); + + server_print("Root value is not array!"); + return; + } + + // We don't need this anymore + json_free(for_compare); + + DecodeArray(root_array); + + json_free(root_array); +} + +//Creating inversed root array +public cmdJSONTestReplace() +{ + // Check if encode command was run + if (!strlen(buffer)) + { + server_print("Run ^"json_test_encode^" first!"); + return; + } + + new JSON:root_array_orig = json_parse(buffer); + new JSON:root_array_copy = json_deep_copy(root_array_orig); + json_free(root_array_orig); + + //Replace null with object + new JSON:object = json_array_get_value(root_array_copy, 0); + json_array_replace_value(root_array_copy, 4, object); + json_free(object); + + //Replace object with null + json_array_replace_null(root_array_copy, 0); + + //Replace bool with real and vice versa + new Float:realnum = json_array_get_real(root_array_copy, 1); + new bool:boolval = json_array_get_bool(root_array_copy, 3); + json_array_replace_real(root_array_copy, 3, realnum); + json_array_replace_bool(root_array_copy, 1, boolval); + + //Replace number with random + json_array_replace_number(root_array_copy, 2, random(42)); + + json_serial_to_file(root_array_copy, "json_replace_test.txt", true); + + json_free(root_array_copy); + + server_print("Values replaced!"); +} + +public cmdJSONTestValidate() +{ + // Check if encode command was run + if (!strlen(buffer)) + { + server_print("Run ^"json_test_encode^" first!"); + return; + } + + if (read_argc() < 2) + { + server_print("Usage: json_test_validate "); + return; + } + + //Should validating be succeed? + new bool:success = bool:read_argv_int(1); + + //Create schema + new JSON:schema = json_init_object(); + + if (success) + json_object_set_string(schema, "string", ""); + else + json_object_set_real(schema, "string", 0.0); + + json_object_set_number(schema, "number", 0); + json_object_set_null(schema, "real"); //Null validate all types + + new JSON:root_array = json_parse(buffer); //Get root array + new JSON:object = json_array_get_value(root_array, 0); //Get object from it + + new bool:result = json_validate(schema, object); //Validate object with our schema + + server_print("Validate %d! (result: %d)", result, result == success); + + json_free(object); + json_free(schema); + json_free(root_array); +} + +public cmdJSONTestHasKey() +{ + // Check if encode command was run + if (!strlen(buffer)) + { + server_print("Run ^"json_test_encode^" first!"); + return; + } + + if (read_argc() < 2) + { + server_print("Usage: json_test_has_key "); + server_print("Available types:^nn - null^ns - string^nr - number^no - object^na - array^nb - boolean"); + return; + } + + //Get root array + new JSON:root_array = json_parse(buffer); + new JSON:object = json_array_get_value(root_array, 0); //Get object + new keyname[32], type[10]; + new JSONType:jtype = JSONError; + + read_argv(1, keyname, charsmax(keyname)); //Key name that have to be found + new bool:dotnot = bool:read_argv_int(2); //Use dot natation? (optional) + read_argv(3, type, charsmax(type)); //Type of searched value (optional) + + //Get type + switch(type[0]) + { + case 'n': jtype = JSONNull; + case 's': jtype = JSONString; + case 'r': jtype = JSONNumber; + case 'o': jtype = JSONObject; + case 'a': jtype = JSONArray; + case 'b': jtype = JSONBoolean; + } + + new bool:found = json_object_has_value(object, keyname, jtype, dotnot); + + if (jtype == JSONError) + server_print("Key %s%s!%s Found!", keyname, (dotnot) ? " using dotnot" : "", (found) ? "" : " Not"); + else + { + GetTypeName(jtype, type, charsmax(type)); //Get type as string + server_print("Key %s (type: %s)%s!%s Found!", keyname, type, (dotnot) ? " using dotnot" : "", (found) ? "" : " Not"); + } + + json_free(object); + json_free(root_array); +} + +public cmdJSONTestRemove() +{ + // Check if encode command was run + if (!strlen(buffer)) + { + server_print("Run ^"json_test_encode^" first!"); + return; + } + + if (read_argc() < 3) + { + server_print("Usage: json_test_has_key "); + server_print("Available types:^na - array^no - object"); + return; + } + + //Get root array + new JSON:root_array = json_parse(buffer); + new type[10], bool:is_object, bool:success; + + read_argv(1, type, charsmax(type)); //Type + is_object = type[0] == 'o'; //Is type object? + if (!is_object && type[0] != 'a') //If not, is it array? + { + server_print("Unknown type: %c", type[0]); //Fail, unknown type + return; + } + + if (is_object) + { + new keyname[32]; + new JSON:object = json_array_get_value(root_array, 0); + + read_argv(2, keyname, charsmax(keyname)); //Key to delete or "clear" if object have to be cleared + new bool:dotnot = bool:read_argv_int(3); //Use dot notation? + + if (equal(keyname, "clear")) + success = json_object_clear(object); + else + success = json_object_remove(object, keyname, dotnot); + + json_free(object); + } + else + { + new index = read_argv_int(2); //Entry to delete or -1 if array have to be cleared + + if (index == -1) + success = json_array_clear(root_array); + else + success = json_array_remove(root_array, index); + } + + //Dump result + json_serial_to_file(root_array, "json_remove_test.txt", true); //Use pretty format for better view + + json_free(root_array); + + server_print("Removing %s! (Results dumped)", (success) ? "succeed" : "failed"); +} + +ObjectSetKey(JSON:object, const key[], JSON:node, bool:dot_not = false) +{ + json_object_set_value(object, key, node, dot_not); + json_free(node); +} + +ArrayAppendValue(JSON:array, JSON:node) +{ + json_array_append_value(array, node); + json_free(node); +} + +DecodeArray(&JSON:array) +{ + //for storing string data + new tempbuf[2][100]; + new JSON:array_value, JSON:parent_value; + for (new i = 0; i < json_array_get_count(array); i++) + { + array_value = json_array_get_value(array, i); + parent_value = json_get_parent(array_value); + + if (parent_value != array) + { + json_free(parent_value); + json_free(array_value); + + server_print("[Array] Parent value differs!"); + break; + } + + json_free(parent_value); + + switch (json_get_type(array_value)) + { + case JSONNull: server_print("Array Index %d (Null)", i); + case JSONString: + { + json_get_string(array_value, tempbuf[0], charsmax(tempbuf[])); + json_array_get_string(array, i, tempbuf[1], charsmax(tempbuf[])); + + server_print("Array Index %d (String) value: %s | index: %s", i, tempbuf[0], tempbuf[1]); + } + case JSONNumber: + { + new num1 = json_get_number(array_value), num2 = json_array_get_number(array, i); + new Float:num3 = json_get_real(array_value), Float:num4 = json_array_get_real(array, i); + + server_print("Array Index %d (Number/Real) value: %d/%f | index: %d/%f", i, num1, num3, num2, num4); + } + case JSONObject: + { + new iCount = json_object_get_count(array_value); + server_print("Array Index %d (Object) %d elements", i, iCount); + + DecodeObject(array_value); + } + case JSONArray: + { + new iCount = json_array_get_count(array_value); + server_print("Array Index %d (Array) %d elements", i, iCount); + + DecodeArray(array_value); + } + case JSONBoolean: + { + new bool:val1 = json_get_bool(array_value), bool:val2 = json_array_get_bool(array, i); + server_print("Array Index %d (Bool) value %d | index %d", i, val1, val2); + } + } + + json_free(array_value); + } +} + +DecodeObject(&JSON:object) +{ + //for storing string data + new tempbuf[2][100]; + new key[30]; + new JSON:obj_value, JSON:parent_value; + for (new i = 0; i < json_object_get_count(object); i++) + { + json_object_get_name(object, i, key, charsmax(key)); + obj_value = json_object_get_value_at(object, i); + parent_value = json_get_parent(obj_value); + + if (parent_value != object) + { + json_free(parent_value); + json_free(obj_value); + + server_print("[Object] Parent value differs!"); + break; + } + + json_free(parent_value); + + switch (json_get_type(obj_value)) + { + case JSONNull: server_print(" Object Key ^"%s^" (Null)", key); + case JSONString: + { + json_get_string(obj_value, tempbuf[0], charsmax(tempbuf[])); + json_object_get_string(object, key, tempbuf[1], charsmax(tempbuf[])); + + server_print(" Object Key ^"%s^" (String) value: %s | key: %s", key, tempbuf[0], tempbuf[1]); + } + case JSONNumber: + { + new num1 = json_get_number(obj_value), num2 = json_object_get_number(object, key); + new Float:num3 = json_get_real(obj_value), Float:num4 = json_object_get_real(object, key); + + server_print(" Object Key ^"%s^" (Number/Real) value: %d/%f | key: %d/%f", key, num1, num3, num2, num4); + } + case JSONObject: + { + new iCount = json_object_get_count(obj_value); + server_print(" Object Key ^"%s^" (Object) %d elements", key, iCount); + + //Let's get its value by dot notation + if (equal(key, "bool")) + { + //dotnot + new bool:val1 = json_object_get_bool(object, "bool.true", true); + new bool:val2 = json_object_get_bool(obj_value, "true"); + json_object_get_name(obj_value, 0, key, charsmax(key)); + + server_print(" Object Key ^"%s^" (Bool) dot %d | nodot: %d", key, val1, val2); + } + else + DecodeObject(obj_value); + } + case JSONArray: + { + new iCount = json_array_get_count(obj_value); + server_print(" Object Key ^"%s^" (Array) %d elements", key, iCount); + + DecodeArray(obj_value); + } + case JSONBoolean: + { + new bool:val1 = json_get_bool(obj_value), bool:val2 = json_object_get_bool(object, key); + server_print(" Object Key ^"%s^" (Bool) value %d | index %d", key, val1, val2); + } + } + + json_free(obj_value); + } +} + +GetTypeName(JSONType:type, buffer[], maxlen) +{ + switch(type) + { + case JSONNull: copy(buffer, maxlen, "Null"); + case JSONString: copy(buffer, maxlen, "String"); + case JSONNumber: copy(buffer, maxlen, "Number"); + case JSONObject: copy(buffer, maxlen, "Object"); + case JSONArray: copy(buffer, maxlen, "Array"); + case JSONBoolean: copy(buffer, maxlen, "Boolean"); + } +} diff --git a/public/licenses/ACKNOWLEDGEMENTS.txt b/public/licenses/ACKNOWLEDGEMENTS.txt index cbfd58be..d318e932 100644 --- a/public/licenses/ACKNOWLEDGEMENTS.txt +++ b/public/licenses/ACKNOWLEDGEMENTS.txt @@ -131,6 +131,31 @@ 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. + +-------------------------------------------------------------- +Parson, as used in the JSON module +-------------------------------------------------------------- +Copyright (c) 2012 - 2017 Krzysztof Gabis + +Licensed under The MIT License. + +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 diff --git a/support/PackageScript b/support/PackageScript index 92d79897..fb754562 100644 --- a/support/PackageScript +++ b/support/PackageScript @@ -1,4 +1,4 @@ -# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: +# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: import os builder.SetBuildFolder('packages') @@ -251,6 +251,7 @@ scripting_files = [ 'testsuite/fwdtest1.sma', 'testsuite/fwdtest2.sma', 'testsuite/hashing_test.sma', + 'testsuite/json_test.sma', 'testsuite/logtest.sma', 'testsuite/menutest.sma', 'testsuite/native_test.sma', @@ -297,6 +298,7 @@ scripting_files = [ 'include/fun.inc', 'include/gameconfig.inc', 'include/geoip.inc', + 'include/json.inc', 'include/lang.inc', 'include/ns.inc', 'include/ns_const.inc', @@ -399,17 +401,17 @@ for datafile in datafiles: source = os.path.join(builder.sourcePath, 'plugins', 'lang', datafile), output_path = folder_map['base/addons/amxmodx/data/lang'] ) - - + + CopyFiles('gamedata/modules.games', 'base/addons/amxmodx/data/gamedata/modules.games', - [ + [ 'master.games.txt', 'game.cstrike.txt', ] ) CopyFiles('gamedata/common.games', 'base/addons/amxmodx/data/gamedata/common.games', - [ + [ 'master.games.txt', 'functions.engine.txt', 'globalvars.engine.txt', @@ -417,7 +419,7 @@ CopyFiles('gamedata/common.games', 'base/addons/amxmodx/data/gamedata/common.gam ) CopyFiles('gamedata/common.games/hostages.games/cstrike', 'base/addons/amxmodx/data/gamedata/common.games/hostages.games/cstrike', - [ + [ 'offsets-chostageimprov.txt', 'offsets-chostagemanager.txt', 'offsets-hostagefollowstate.txt', @@ -426,7 +428,7 @@ CopyFiles('gamedata/common.games/hostages.games/cstrike', 'base/addons/amxmodx/d ) CopyFiles('gamedata/common.games/others.games/cstrike', 'base/addons/amxmodx/data/gamedata/common.games/others.games/cstrike', - [ + [ 'offsets-csound.txt', 'offsets-cunifiedsignals.txt', 'offsets-cvoicegamemgr.txt', @@ -434,7 +436,7 @@ CopyFiles('gamedata/common.games/others.games/cstrike', 'base/addons/amxmodx/dat ) CopyFiles('gamedata/common.games/entities.games/cstrike', 'base/addons/amxmodx/data/gamedata/common.games/entities.games/cstrike', - [ + [ 'offsets-cairtank.txt', 'offsets-cak47.txt', 'offsets-cambientgeneric.txt', @@ -550,7 +552,7 @@ CopyFiles('gamedata/common.games/entities.games/cstrike', 'base/addons/amxmodx/d ) CopyFiles('gamedata/common.games/entities.games/dod', 'base/addons/amxmodx/data/gamedata/common.games/entities.games/dod', - [ + [ 'offsets-c30cal.txt', 'offsets-calliedbarney.txt', 'offsets-calliedgrunt.txt', @@ -712,7 +714,7 @@ CopyFiles('gamedata/common.games/entities.games/dod', 'base/addons/amxmodx/data/ ) CopyFiles('gamedata/common.games/entities.games/tfc', 'base/addons/amxmodx/data/gamedata/common.games/entities.games/tfc', - [ + [ 'offsets-cactanimating.txt', 'offsets-cairtank.txt', 'offsets-cambientgeneric.txt', @@ -841,7 +843,7 @@ CopyFiles('gamedata/common.games/entities.games/tfc', 'base/addons/amxmodx/data/ ) CopyFiles('gamedata/common.games/entities.games/gearbox', 'base/addons/amxmodx/data/gamedata/common.games/entities.games/gearbox', - [ + [ 'offsets-cactanimating.txt', 'offsets-cagrunt.txt', 'offsets-cairtank.txt', @@ -1081,7 +1083,7 @@ CopyFiles('gamedata/common.games/entities.games/gearbox', 'base/addons/amxmodx/d ) CopyFiles('gamedata/common.games/entities.games/valve', 'base/addons/amxmodx/data/gamedata/common.games/entities.games/valve', - [ + [ 'offsets-cactanimating.txt', 'offsets-cagrunt.txt', 'offsets-cairtank.txt', @@ -1224,13 +1226,13 @@ CopyFiles('gamedata/common.games/entities.games/valve', 'base/addons/amxmodx/dat ) CopyFiles('gamedata/common.games/gamerules.games', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games', - [ + [ 'master.games.txt', ] ) CopyFiles('gamedata/common.games/gamerules.games/cstrike', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games/cstrike', - [ + [ 'offsets-cgamerules.txt', 'offsets-chalflifemultiplay.txt', 'offsets-chalflifetraining.txt', @@ -1238,14 +1240,14 @@ CopyFiles('gamedata/common.games/gamerules.games/cstrike', 'base/addons/amxmodx/ ) CopyFiles('gamedata/common.games/gamerules.games/dod', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games/dod', - [ + [ 'offsets-cdodteamplay.txt', 'offsets-cspdodrules.txt', ] ) CopyFiles('gamedata/common.games/gamerules.games/gearbox', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games/gearbox', - [ + [ 'offsets-chalflifecoopplay.txt', 'offsets-chalflifectfplay.txt', 'offsets-chalflifemultiplay.txt', @@ -1254,7 +1256,7 @@ CopyFiles('gamedata/common.games/gamerules.games/gearbox', 'base/addons/amxmodx/ ) CopyFiles('gamedata/common.games/gamerules.games/tfc', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games/tfc', - [ + [ 'offsets-chalflifemultiplay.txt', 'offsets-chalflifeteamplay.txt', 'offsets-cteamfortress.txt', @@ -1262,7 +1264,7 @@ CopyFiles('gamedata/common.games/gamerules.games/tfc', 'base/addons/amxmodx/data ) CopyFiles('gamedata/common.games/gamerules.games/valve', 'base/addons/amxmodx/data/gamedata/common.games/gamerules.games/valve', - [ + [ 'offsets-chalflifemultiplay.txt', 'offsets-chalflifeteamplay.txt', ] diff --git a/third_party/parson/parson.c b/third_party/parson/parson.c new file mode 100644 index 00000000..3f89b7f4 --- /dev/null +++ b/third_party/parson/parson.c @@ -0,0 +1,1998 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2017 Krzysztof Gabis + + 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. +*/ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif /* _CRT_SECURE_NO_WARNINGS */ +#endif /* _MSC_VER */ + +#include "parson.h" + +#include +#include +#include +#include +#include +#include + +/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you + * don't have to. */ +#define sscanf THINK_TWICE_ABOUT_USING_SSCANF + +#define STARTING_CAPACITY 16 +#define MAX_NESTING 2048 +#define FLOAT_FORMAT "%1.17g" + +#define SIZEOF_TOKEN(a) (sizeof(a) - 1) +#define SKIP_CHAR(str) ((*str)++) +#define SKIP_WHITESPACES(str) while (isspace(**str)) { SKIP_CHAR(str); } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#undef malloc +#undef free + +static JSON_Malloc_Function parson_malloc = malloc; +static JSON_Free_Function parson_free = free; + +#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */ + +/* Type definitions */ +typedef union json_value_value { + char *string; + double number; + JSON_Object *object; + JSON_Array *array; + int boolean; + int null; +} JSON_Value_Value; + +struct json_value_t { + JSON_Value *parent; + JSON_Value_Type type; + JSON_Value_Value value; +}; + +struct json_object_t { + JSON_Value *wrapping_value; + char **names; + JSON_Value **values; + size_t count; + size_t capacity; +}; + +struct json_array_t { + JSON_Value *wrapping_value; + JSON_Value **items; + size_t count; + size_t capacity; +}; + +/* Various */ +static char * read_file(const char *filename); +static void remove_comments(char *string, const char *start_token, const char *end_token); +static char * parson_strndup(const char *string, size_t n); +static char * parson_strdup(const char *string); +static int hex_char_to_int(char c); +static int parse_utf16_hex(const char *string, unsigned int *result); +static int num_bytes_in_utf8_sequence(unsigned char c); +static int verify_utf8_sequence(const unsigned char *string, int *len); +static int is_valid_utf8(const char *string, size_t string_len); +static int is_decimal(const char *string, size_t length); + +/* JSON Object */ +static JSON_Object * json_object_init(JSON_Value *wrapping_value); +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value); +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity); +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); +static void json_object_free(JSON_Object *object); + +/* JSON Array */ +static JSON_Array * json_array_init(JSON_Value *wrapping_value); +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value); +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); +static void json_array_free(JSON_Array *array); + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string); + +/* Parser */ +static JSON_Status skip_quotes(const char **string); +static int parse_utf16(const char **unprocessed, char **processed); +static char * process_string(const char *input, size_t len); +static char * get_quoted_string(const char **string); +static JSON_Value * parse_object_value(const char **string, size_t nesting); +static JSON_Value * parse_array_value(const char **string, size_t nesting); +static JSON_Value * parse_string_value(const char **string); +static JSON_Value * parse_boolean_value(const char **string); +static JSON_Value * parse_number_value(const char **string); +static JSON_Value * parse_null_value(const char **string); +static JSON_Value * parse_value(const char **string, size_t nesting); + +/* Serialization */ +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf); +static int json_serialize_string(const char *string, char *buf); +static int append_indent(char *buf, int level); +static int append_string(char *buf, const char *string); + +/* Various */ +static char * parson_strndup(const char *string, size_t n) { + char *output_string = (char*)parson_malloc(n + 1); + if (!output_string) { + return NULL; + } + output_string[n] = '\0'; + strncpy(output_string, string, n); + return output_string; +} + +static char * parson_strdup(const char *string) { + return parson_strndup(string, strlen(string)); +} + +static int hex_char_to_int(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +static int parse_utf16_hex(const char *s, unsigned int *result) { + int x1, x2, x3, x4; + if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') { + return 0; + } + x1 = hex_char_to_int(s[0]); + x2 = hex_char_to_int(s[1]); + x3 = hex_char_to_int(s[2]); + x4 = hex_char_to_int(s[3]); + if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) { + return 0; + } + *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4); + return 1; +} + +static int num_bytes_in_utf8_sequence(unsigned char c) { + if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) { + return 0; + } else if ((c & 0x80) == 0) { /* 0xxxxxxx */ + return 1; + } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */ + return 2; + } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */ + return 3; + } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */ + return 4; + } + return 0; /* won't happen */ +} + +static int verify_utf8_sequence(const unsigned char *string, int *len) { + unsigned int cp = 0; + *len = num_bytes_in_utf8_sequence(string[0]); + + if (*len == 1) { + cp = string[0]; + } else if (*len == 2 && IS_CONT(string[1])) { + cp = string[0] & 0x1F; + cp = (cp << 6) | (string[1] & 0x3F); + } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) { + cp = ((unsigned char)string[0]) & 0xF; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) { + cp = string[0] & 0x7; + cp = (cp << 6) | (string[1] & 0x3F); + cp = (cp << 6) | (string[2] & 0x3F); + cp = (cp << 6) | (string[3] & 0x3F); + } else { + return 0; + } + + /* overlong encodings */ + if ((cp < 0x80 && *len > 1) || + (cp < 0x800 && *len > 2) || + (cp < 0x10000 && *len > 3)) { + return 0; + } + + /* invalid unicode */ + if (cp > 0x10FFFF) { + return 0; + } + + /* surrogate halves */ + if (cp >= 0xD800 && cp <= 0xDFFF) { + return 0; + } + + return 1; +} + +static int is_valid_utf8(const char *string, size_t string_len) { + int len = 0; + const char *string_end = string + string_len; + while (string < string_end) { + if (!verify_utf8_sequence((const unsigned char*)string, &len)) { + return 0; + } + string += len; + } + return 1; +} + +static int is_decimal(const char *string, size_t length) { + if (length > 1 && string[0] == '0' && string[1] != '.') { + return 0; + } + if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { + return 0; + } + while (length--) { + if (strchr("xX", string[length])) { + return 0; + } + } + return 1; +} + +static char * read_file(const char * filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + long pos; + char *file_contents; + if (!fp) { + return NULL; + } + fseek(fp, 0L, SEEK_END); + pos = ftell(fp); + if (pos < 0) { + fclose(fp); + return NULL; + } + file_size = pos; + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { + fclose(fp); + return NULL; + } + if (fread(file_contents, file_size, 1, fp) < 1) { + if (ferror(fp)) { + fclose(fp); + parson_free(file_contents); + return NULL; + } + } + fclose(fp); + file_contents[file_size] = '\0'; + return file_contents; +} + +static void remove_comments(char *string, const char *start_token, const char *end_token) { + int in_string = 0, escaped = 0; + size_t i; + char *ptr = NULL, current_char; + size_t start_token_len = strlen(start_token); + size_t end_token_len = strlen(end_token); + if (start_token_len == 0 || end_token_len == 0) { + return; + } + while ((current_char = *string) != '\0') { + if (current_char == '\\' && !escaped) { + escaped = 1; + string++; + continue; + } else if (current_char == '\"' && !escaped) { + in_string = !in_string; + } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { + for(i = 0; i < start_token_len; i++) { + string[i] = ' '; + } + string = string + start_token_len; + ptr = strstr(string, end_token); + if (!ptr) { + return; + } + for (i = 0; i < (ptr - string) + end_token_len; i++) { + string[i] = ' '; + } + string = ptr + end_token_len - 1; + } + escaped = 0; + string++; + } +} + +/* JSON Object */ +static JSON_Object * json_object_init(JSON_Value *wrapping_value) { + JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); + if (new_obj == NULL) { + return NULL; + } + new_obj->wrapping_value = wrapping_value; + new_obj->names = (char**)NULL; + new_obj->values = (JSON_Value**)NULL; + new_obj->capacity = 0; + new_obj->count = 0; + return new_obj; +} + +static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { + size_t index = 0; + if (object == NULL || name == NULL || value == NULL) { + return JSONFailure; + } + if (json_object_get_value(object, name) != NULL) { + return JSONFailure; + } + if (object->count >= object->capacity) { + size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); + if (json_object_resize(object, new_capacity) == JSONFailure) { + return JSONFailure; + } + } + index = object->count; + object->names[index] = parson_strdup(name); + if (object->names[index] == NULL) { + return JSONFailure; + } + value->parent = json_object_get_wrapping_value(object); + object->values[index] = value; + object->count++; + return JSONSuccess; +} + +static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity) { + char **temp_names = NULL; + JSON_Value **temp_values = NULL; + + if ((object->names == NULL && object->values != NULL) || + (object->names != NULL && object->values == NULL) || + new_capacity == 0) { + return JSONFailure; /* Shouldn't happen */ + } + temp_names = (char**)parson_malloc(new_capacity * sizeof(char*)); + if (temp_names == NULL) { + return JSONFailure; + } + temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (temp_values == NULL) { + parson_free(temp_names); + return JSONFailure; + } + if (object->names != NULL && object->values != NULL && object->count > 0) { + memcpy(temp_names, object->names, object->count * sizeof(char*)); + memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*)); + } + parson_free(object->names); + parson_free(object->values); + object->names = temp_names; + object->values = temp_values; + object->capacity = new_capacity; + return JSONSuccess; +} + +static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { + size_t i, name_length; + for (i = 0; i < json_object_get_count(object); i++) { + name_length = strlen(object->names[i]); + if (name_length != n) { + continue; + } + if (strncmp(object->names[i], name, n) == 0) { + return object->values[i]; + } + } + return NULL; +} + +static void json_object_free(JSON_Object *object) { + size_t i; + for (i = 0; i < object->count; i++) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + } + parson_free(object->names); + parson_free(object->values); + parson_free(object); +} + +/* JSON Array */ +static JSON_Array * json_array_init(JSON_Value *wrapping_value) { + JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); + if (new_array == NULL) { + return NULL; + } + new_array->wrapping_value = wrapping_value; + new_array->items = (JSON_Value**)NULL; + new_array->capacity = 0; + new_array->count = 0; + return new_array; +} + +static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) { + if (array->count >= array->capacity) { + size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); + if (json_array_resize(array, new_capacity) == JSONFailure) { + return JSONFailure; + } + } + value->parent = json_array_get_wrapping_value(array); + array->items[array->count] = value; + array->count++; + return JSONSuccess; +} + +static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) { + JSON_Value **new_items = NULL; + if (new_capacity == 0) { + return JSONFailure; + } + new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); + if (new_items == NULL) { + return JSONFailure; + } + if (array->items != NULL && array->count > 0) { + memcpy(new_items, array->items, array->count * sizeof(JSON_Value*)); + } + parson_free(array->items); + array->items = new_items; + array->capacity = new_capacity; + return JSONSuccess; +} + +static void json_array_free(JSON_Array *array) { + size_t i; + for (i = 0; i < array->count; i++) { + json_value_free(array->items[i]); + } + parson_free(array->items); + parson_free(array); +} + +/* JSON Value */ +static JSON_Value * json_value_init_string_no_copy(char *string) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONString; + new_value->value.string = string; + return new_value; +} + +/* Parser */ +static JSON_Status skip_quotes(const char **string) { + if (**string != '\"') { + return JSONFailure; + } + SKIP_CHAR(string); + while (**string != '\"') { + if (**string == '\0') { + return JSONFailure; + } else if (**string == '\\') { + SKIP_CHAR(string); + if (**string == '\0') { + return JSONFailure; + } + } + SKIP_CHAR(string); + } + SKIP_CHAR(string); + return JSONSuccess; +} + +static int parse_utf16(const char **unprocessed, char **processed) { + unsigned int cp, lead, trail; + int parse_succeeded = 0; + char *processed_ptr = *processed; + const char *unprocessed_ptr = *unprocessed; + unprocessed_ptr++; /* skips u */ + parse_succeeded = parse_utf16_hex(unprocessed_ptr, &cp); + if (!parse_succeeded) { + return JSONFailure; + } + if (cp < 0x80) { + processed_ptr[0] = (char)cp; /* 0xxxxxxx */ + } else if (cp < 0x800) { + processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */ + processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr += 1; + } else if (cp < 0xD800 || cp > 0xDFFF) { + processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */ + processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr += 2; + } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */ + lead = cp; + unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */ + if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') { + return JSONFailure; + } + parse_succeeded = parse_utf16_hex(unprocessed_ptr, &trail); + if (!parse_succeeded || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */ + return JSONFailure; + } + cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000; + processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */ + processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr += 3; + } else { /* trail surrogate before lead surrogate */ + return JSONFailure; + } + unprocessed_ptr += 3; + *processed = processed_ptr; + *unprocessed = unprocessed_ptr; + return JSONSuccess; +} + + +/* Copies and processes passed string up to supplied length. +Example: "\u006Corem ipsum" -> lorem ipsum */ +static char* process_string(const char *input, size_t len) { + const char *input_ptr = input; + size_t initial_size = (len + 1) * sizeof(char); + size_t final_size = 0; + char *output = NULL, *output_ptr = NULL, *resized_output = NULL; + output = (char*)parson_malloc(initial_size); + if (output == NULL) { + goto error; + } + output_ptr = output; + while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < len) { + if (*input_ptr == '\\') { + input_ptr++; + switch (*input_ptr) { + case '\"': *output_ptr = '\"'; break; + case '\\': *output_ptr = '\\'; break; + case '/': *output_ptr = '/'; break; + case 'b': *output_ptr = '\b'; break; + case 'f': *output_ptr = '\f'; break; + case 'n': *output_ptr = '\n'; break; + case 'r': *output_ptr = '\r'; break; + case 't': *output_ptr = '\t'; break; + case 'u': + if (parse_utf16(&input_ptr, &output_ptr) == JSONFailure) { + goto error; + } + break; + default: + goto error; + } + } else if ((unsigned char)*input_ptr < 0x20) { + goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ + } else { + *output_ptr = *input_ptr; + } + output_ptr++; + input_ptr++; + } + *output_ptr = '\0'; + /* resize to new length */ + final_size = (size_t)(output_ptr-output) + 1; + /* todo: don't resize if final_size == initial_size */ + resized_output = (char*)parson_malloc(final_size); + if (resized_output == NULL) { + goto error; + } + memcpy(resized_output, output, final_size); + parson_free(output); + return resized_output; +error: + parson_free(output); + return NULL; +} + +/* Return processed contents of a string between quotes and + skips passed argument to a matching quote. */ +static char * get_quoted_string(const char **string) { + const char *string_start = *string; + size_t string_len = 0; + JSON_Status status = skip_quotes(string); + if (status != JSONSuccess) { + return NULL; + } + string_len = *string - string_start - 2; /* length without quotes */ + return process_string(string_start + 1, string_len); +} + +static JSON_Value * parse_value(const char **string, size_t nesting) { + if (nesting > MAX_NESTING) { + return NULL; + } + SKIP_WHITESPACES(string); + switch (**string) { + case '{': + return parse_object_value(string, nesting + 1); + case '[': + return parse_array_value(string, nesting + 1); + case '\"': + return parse_string_value(string); + case 'f': case 't': + return parse_boolean_value(string); + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return parse_number_value(string); + case 'n': + return parse_null_value(string); + default: + return NULL; + } +} + +static JSON_Value * parse_object_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_object(), *new_value = NULL; + JSON_Object *output_object = json_value_get_object(output_value); + char *new_key = NULL; + if (output_value == NULL || **string != '{') { + return NULL; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == '}') { /* empty object */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_key = get_quoted_string(string); + if (new_key == NULL) { + json_value_free(output_value); + return NULL; + } + SKIP_WHITESPACES(string); + if (**string != ':') { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + new_value = parse_value(string, nesting); + if (new_value == NULL) { + parson_free(new_key); + json_value_free(output_value); + return NULL; + } + if (json_object_add(output_object, new_key, new_value) == JSONFailure) { + parson_free(new_key); + json_value_free(new_value); + json_value_free(output_value); + return NULL; + } + parson_free(new_key); + SKIP_WHITESPACES(string); + if (**string != ',') { + break; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != '}' || /* Trim object after parsing is over */ + json_object_resize(output_object, json_object_get_count(output_object)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_array_value(const char **string, size_t nesting) { + JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL; + JSON_Array *output_array = json_value_get_array(output_value); + if (!output_value || **string != '[') { + return NULL; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + if (**string == ']') { /* empty array */ + SKIP_CHAR(string); + return output_value; + } + while (**string != '\0') { + new_array_value = parse_value(string, nesting); + if (new_array_value == NULL) { + json_value_free(output_value); + return NULL; + } + if (json_array_add(output_array, new_array_value) == JSONFailure) { + json_value_free(new_array_value); + json_value_free(output_value); + return NULL; + } + SKIP_WHITESPACES(string); + if (**string != ',') { + break; + } + SKIP_CHAR(string); + SKIP_WHITESPACES(string); + } + SKIP_WHITESPACES(string); + if (**string != ']' || /* Trim array after parsing is over */ + json_array_resize(output_array, json_array_get_count(output_array)) == JSONFailure) { + json_value_free(output_value); + return NULL; + } + SKIP_CHAR(string); + return output_value; +} + +static JSON_Value * parse_string_value(const char **string) { + JSON_Value *value = NULL; + char *new_string = get_quoted_string(string); + if (new_string == NULL) { + return NULL; + } + value = json_value_init_string_no_copy(new_string); + if (value == NULL) { + parson_free(new_string); + return NULL; + } + return value; +} + +static JSON_Value * parse_boolean_value(const char **string) { + size_t true_token_size = SIZEOF_TOKEN("true"); + size_t false_token_size = SIZEOF_TOKEN("false"); + if (strncmp("true", *string, true_token_size) == 0) { + *string += true_token_size; + return json_value_init_boolean(1); + } else if (strncmp("false", *string, false_token_size) == 0) { + *string += false_token_size; + return json_value_init_boolean(0); + } + return NULL; +} + +static JSON_Value * parse_number_value(const char **string) { + char *end; + double number = 0; + errno = 0; + number = strtod(*string, &end); + if (errno || !is_decimal(*string, end - *string)) { + return NULL; + } + *string = end; + return json_value_init_number(number); +} + +static JSON_Value * parse_null_value(const char **string) { + size_t token_size = SIZEOF_TOKEN("null"); + if (strncmp("null", *string, token_size) == 0) { + *string += token_size; + return json_value_init_null(); + } + return NULL; +} + +/* Serialization */ +#define APPEND_STRING(str) do { written = append_string(buf, (str));\ + if (written < 0) { return -1; }\ + if (buf != NULL) { buf += written; }\ + written_total += written; } while(0) + +#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\ + if (written < 0) { return -1; }\ + if (buf != NULL) { buf += written; }\ + written_total += written; } while(0) + +static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty, char *num_buf) +{ + const char *key = NULL, *string = NULL; + JSON_Value *temp_value = NULL; + JSON_Array *array = NULL; + JSON_Object *object = NULL; + size_t i = 0, count = 0; + double num = 0.0; + int written = -1, written_total = 0; + + switch (json_value_get_type(value)) { + case JSONArray: + array = json_value_get_array(value); + count = json_array_get_count(array); + APPEND_STRING("["); + if (count > 0 && is_pretty) { + APPEND_STRING("\n"); + } + for (i = 0; i < count; i++) { + if (is_pretty) { + APPEND_INDENT(level+1); + } + temp_value = json_array_get_value(array, i); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + if (i < (count - 1)) { + APPEND_STRING(","); + } + if (is_pretty) { + APPEND_STRING("\n"); + } + } + if (count > 0 && is_pretty) { + APPEND_INDENT(level); + } + APPEND_STRING("]"); + return written_total; + case JSONObject: + object = json_value_get_object(value); + count = json_object_get_count(object); + APPEND_STRING("{"); + if (count > 0 && is_pretty) { + APPEND_STRING("\n"); + } + for (i = 0; i < count; i++) { + key = json_object_get_name(object, i); + if (key == NULL) { + return -1; + } + if (is_pretty) { + APPEND_INDENT(level+1); + } + written = json_serialize_string(key, buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + APPEND_STRING(":"); + if (is_pretty) { + APPEND_STRING(" "); + } + temp_value = json_object_get_value(object, key); + written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + if (i < (count - 1)) { + APPEND_STRING(","); + } + if (is_pretty) { + APPEND_STRING("\n"); + } + } + if (count > 0 && is_pretty) { + APPEND_INDENT(level); + } + APPEND_STRING("}"); + return written_total; + case JSONString: + string = json_value_get_string(value); + if (string == NULL) { + return -1; + } + written = json_serialize_string(string, buf); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + return written_total; + case JSONBoolean: + if (json_value_get_boolean(value)) { + APPEND_STRING("true"); + } else { + APPEND_STRING("false"); + } + return written_total; + case JSONNumber: + num = json_value_get_number(value); + if (buf != NULL) { + num_buf = buf; + } + written = sprintf(num_buf, FLOAT_FORMAT, num); + if (written < 0) { + return -1; + } + if (buf != NULL) { + buf += written; + } + written_total += written; + return written_total; + case JSONNull: + APPEND_STRING("null"); + return written_total; + case JSONError: + return -1; + default: + return -1; + } +} + +static int json_serialize_string(const char *string, char *buf) { + size_t i = 0, len = strlen(string); + char c = '\0'; + int written = -1, written_total = 0; + APPEND_STRING("\""); + for (i = 0; i < len; i++) { + c = string[i]; + switch (c) { + case '\"': APPEND_STRING("\\\""); break; + case '\\': APPEND_STRING("\\\\"); break; + case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */ + case '\b': APPEND_STRING("\\b"); break; + case '\f': APPEND_STRING("\\f"); break; + case '\n': APPEND_STRING("\\n"); break; + case '\r': APPEND_STRING("\\r"); break; + case '\t': APPEND_STRING("\\t"); break; + case '\x00': APPEND_STRING("\\u0000"); break; + case '\x01': APPEND_STRING("\\u0001"); break; + case '\x02': APPEND_STRING("\\u0002"); break; + case '\x03': APPEND_STRING("\\u0003"); break; + case '\x04': APPEND_STRING("\\u0004"); break; + case '\x05': APPEND_STRING("\\u0005"); break; + case '\x06': APPEND_STRING("\\u0006"); break; + case '\x07': APPEND_STRING("\\u0007"); break; + /* '\x08' duplicate: '\b' */ + /* '\x09' duplicate: '\t' */ + /* '\x0a' duplicate: '\n' */ + case '\x0b': APPEND_STRING("\\u000b"); break; + /* '\x0c' duplicate: '\f' */ + /* '\x0d' duplicate: '\r' */ + case '\x0e': APPEND_STRING("\\u000e"); break; + case '\x0f': APPEND_STRING("\\u000f"); break; + case '\x10': APPEND_STRING("\\u0010"); break; + case '\x11': APPEND_STRING("\\u0011"); break; + case '\x12': APPEND_STRING("\\u0012"); break; + case '\x13': APPEND_STRING("\\u0013"); break; + case '\x14': APPEND_STRING("\\u0014"); break; + case '\x15': APPEND_STRING("\\u0015"); break; + case '\x16': APPEND_STRING("\\u0016"); break; + case '\x17': APPEND_STRING("\\u0017"); break; + case '\x18': APPEND_STRING("\\u0018"); break; + case '\x19': APPEND_STRING("\\u0019"); break; + case '\x1a': APPEND_STRING("\\u001a"); break; + case '\x1b': APPEND_STRING("\\u001b"); break; + case '\x1c': APPEND_STRING("\\u001c"); break; + case '\x1d': APPEND_STRING("\\u001d"); break; + case '\x1e': APPEND_STRING("\\u001e"); break; + case '\x1f': APPEND_STRING("\\u001f"); break; + default: + if (buf != NULL) { + buf[0] = c; + buf += 1; + } + written_total += 1; + break; + } + } + APPEND_STRING("\""); + return written_total; +} + +static int append_indent(char *buf, int level) { + int i; + int written = -1, written_total = 0; + for (i = 0; i < level; i++) { + APPEND_STRING(" "); + } + return written_total; +} + +static int append_string(char *buf, const char *string) { + if (buf == NULL) { + return (int)strlen(string); + } + return sprintf(buf, "%s", string); +} + +#undef APPEND_STRING +#undef APPEND_INDENT + +/* Parser API */ +JSON_Value * json_parse_file(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) { + return NULL; + } + output_value = json_parse_string(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_file_with_comments(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (file_contents == NULL) { + return NULL; + } + output_value = json_parse_string_with_comments(file_contents); + parson_free(file_contents); + return output_value; +} + +JSON_Value * json_parse_string(const char *string) { + if (string == NULL) { + return NULL; + } + if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') { + string = string + 3; /* Support for UTF-8 BOM */ + } + return parse_value((const char**)&string, 0); +} + +JSON_Value * json_parse_string_with_comments(const char *string) { + JSON_Value *result = NULL; + char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL; + string_mutable_copy = parson_strdup(string); + if (string_mutable_copy == NULL) { + return NULL; + } + remove_comments(string_mutable_copy, "/*", "*/"); + remove_comments(string_mutable_copy, "//", "\n"); + string_mutable_copy_ptr = string_mutable_copy; + result = parse_value((const char**)&string_mutable_copy_ptr, 0); + parson_free(string_mutable_copy); + return result; +} + +/* JSON Object API */ + +JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { + if (object == NULL || name == NULL) { + return NULL; + } + return json_object_nget_value(object, name, strlen(name)); +} + +const char * json_object_get_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_get_value(object, name)); +} + +double json_object_get_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_get_value(object, name)); +} + +JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_get_value(object, name)); +} + +JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_get_value(object, name)); +} + +int json_object_get_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_get_value(object, name)); +} + +JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) { + const char *dot_position = strchr(name, '.'); + if (!dot_position) { + return json_object_get_value(object, name); + } + object = json_value_get_object(json_object_nget_value(object, name, dot_position - name)); + return json_object_dotget_value(object, dot_position + 1); +} + +const char * json_object_dotget_string(const JSON_Object *object, const char *name) { + return json_value_get_string(json_object_dotget_value(object, name)); +} + +double json_object_dotget_number(const JSON_Object *object, const char *name) { + return json_value_get_number(json_object_dotget_value(object, name)); +} + +JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) { + return json_value_get_object(json_object_dotget_value(object, name)); +} + +JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) { + return json_value_get_array(json_object_dotget_value(object, name)); +} + +int json_object_dotget_boolean(const JSON_Object *object, const char *name) { + return json_value_get_boolean(json_object_dotget_value(object, name)); +} + +size_t json_object_get_count(const JSON_Object *object) { + return object ? object->count : 0; +} + +const char * json_object_get_name(const JSON_Object *object, size_t index) { + if (object == NULL || index >= json_object_get_count(object)) { + return NULL; + } + return object->names[index]; +} + +JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) { + if (object == NULL || index >= json_object_get_count(object)) { + return NULL; + } + return object->values[index]; +} + +JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) { + return object->wrapping_value; +} + +int json_object_has_value (const JSON_Object *object, const char *name) { + return json_object_get_value(object, name) != NULL; +} + +int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { + JSON_Value *val = json_object_get_value(object, name); + return val != NULL && json_value_get_type(val) == type; +} + +int json_object_dothas_value (const JSON_Object *object, const char *name) { + return json_object_dotget_value(object, name) != NULL; +} + +int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) { + JSON_Value *val = json_object_dotget_value(object, name); + return val != NULL && json_value_get_type(val) == type; +} + +/* JSON Array API */ +JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) { + if (array == NULL || index >= json_array_get_count(array)) { + return NULL; + } + return array->items[index]; +} + +const char * json_array_get_string(const JSON_Array *array, size_t index) { + return json_value_get_string(json_array_get_value(array, index)); +} + +double json_array_get_number(const JSON_Array *array, size_t index) { + return json_value_get_number(json_array_get_value(array, index)); +} + +JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) { + return json_value_get_object(json_array_get_value(array, index)); +} + +JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) { + return json_value_get_array(json_array_get_value(array, index)); +} + +int json_array_get_boolean(const JSON_Array *array, size_t index) { + return json_value_get_boolean(json_array_get_value(array, index)); +} + +size_t json_array_get_count(const JSON_Array *array) { + return array ? array->count : 0; +} + +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array) { + return array->wrapping_value; +} + +/* JSON Value API */ +JSON_Value_Type json_value_get_type(const JSON_Value *value) { + return value ? value->type : JSONError; +} + +JSON_Object * json_value_get_object(const JSON_Value *value) { + return json_value_get_type(value) == JSONObject ? value->value.object : NULL; +} + +JSON_Array * json_value_get_array(const JSON_Value *value) { + return json_value_get_type(value) == JSONArray ? value->value.array : NULL; +} + +const char * json_value_get_string(const JSON_Value *value) { + return json_value_get_type(value) == JSONString ? value->value.string : NULL; +} + +double json_value_get_number(const JSON_Value *value) { + return json_value_get_type(value) == JSONNumber ? value->value.number : 0; +} + +int json_value_get_boolean(const JSON_Value *value) { + return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; +} + +JSON_Value * json_value_get_parent (const JSON_Value *value) { + return value ? value->parent : NULL; +} + +void json_value_free(JSON_Value *value) { + switch (json_value_get_type(value)) { + case JSONObject: + json_object_free(value->value.object); + break; + case JSONString: + parson_free(value->value.string); + break; + case JSONArray: + json_array_free(value->value.array); + break; + default: + break; + } + parson_free(value); +} + +JSON_Value * json_value_init_object(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONObject; + new_value->value.object = json_object_init(new_value); + if (!new_value->value.object) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_array(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONArray; + new_value->value.array = json_array_init(new_value); + if (!new_value->value.array) { + parson_free(new_value); + return NULL; + } + return new_value; +} + +JSON_Value * json_value_init_string(const char *string) { + char *copy = NULL; + JSON_Value *value; + size_t string_len = 0; + if (string == NULL) { + return NULL; + } + string_len = strlen(string); + if (!is_valid_utf8(string, string_len)) { + return NULL; + } + copy = parson_strndup(string, string_len); + if (copy == NULL) { + return NULL; + } + value = json_value_init_string_no_copy(copy); + if (value == NULL) { + parson_free(copy); + } + return value; +} + +JSON_Value * json_value_init_number(double number) { + JSON_Value *new_value = NULL; + if ((number * 0.0) != 0.0) { /* nan and inf test */ + return NULL; + } + new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (new_value == NULL) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONNumber; + new_value->value.number = number; + return new_value; +} + +JSON_Value * json_value_init_boolean(int boolean) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONBoolean; + new_value->value.boolean = boolean ? 1 : 0; + return new_value; +} + +JSON_Value * json_value_init_null(void) { + JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); + if (!new_value) { + return NULL; + } + new_value->parent = NULL; + new_value->type = JSONNull; + return new_value; +} + +JSON_Value * json_value_deep_copy(const JSON_Value *value) { + size_t i = 0; + JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL; + const char *temp_string = NULL, *temp_key = NULL; + char *temp_string_copy = NULL; + JSON_Array *temp_array = NULL, *temp_array_copy = NULL; + JSON_Object *temp_object = NULL, *temp_object_copy = NULL; + + switch (json_value_get_type(value)) { + case JSONArray: + temp_array = json_value_get_array(value); + return_value = json_value_init_array(); + if (return_value == NULL) { + return NULL; + } + temp_array_copy = json_value_get_array(return_value); + for (i = 0; i < json_array_get_count(temp_array); i++) { + temp_value = json_array_get_value(temp_array, i); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_array_add(temp_array_copy, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONObject: + temp_object = json_value_get_object(value); + return_value = json_value_init_object(); + if (return_value == NULL) { + return NULL; + } + temp_object_copy = json_value_get_object(return_value); + for (i = 0; i < json_object_get_count(temp_object); i++) { + temp_key = json_object_get_name(temp_object, i); + temp_value = json_object_get_value(temp_object, temp_key); + temp_value_copy = json_value_deep_copy(temp_value); + if (temp_value_copy == NULL) { + json_value_free(return_value); + return NULL; + } + if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSONFailure) { + json_value_free(return_value); + json_value_free(temp_value_copy); + return NULL; + } + } + return return_value; + case JSONBoolean: + return json_value_init_boolean(json_value_get_boolean(value)); + case JSONNumber: + return json_value_init_number(json_value_get_number(value)); + case JSONString: + temp_string = json_value_get_string(value); + if (temp_string == NULL) { + return NULL; + } + temp_string_copy = parson_strdup(temp_string); + if (temp_string_copy == NULL) { + return NULL; + } + return_value = json_value_init_string_no_copy(temp_string_copy); + if (return_value == NULL) { + parson_free(temp_string_copy); + } + return return_value; + case JSONNull: + return json_value_init_null(); + case JSONError: + return NULL; + default: + return NULL; + } +} + +size_t json_serialization_size(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { + return JSONFailure; + } + written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL); + if (written < 0) { + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp == NULL) { + json_free_serialized_string(serialized_string); + return JSONFailure; + } + if (fputs(serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose(fp) == EOF) { + return_code = JSONFailure; + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) { + return NULL; + } + serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +size_t json_serialization_size_pretty(const JSON_Value *value) { + char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ + int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf); + return res < 0 ? 0 : (size_t)(res + 1); +} + +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { + int written = -1; + size_t needed_size_in_bytes = json_serialization_size_pretty(value); + if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) { + return JSONFailure; + } + written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL); + if (written < 0) { + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) { + JSON_Status return_code = JSONSuccess; + FILE *fp = NULL; + char *serialized_string = json_serialize_to_string_pretty(value); + if (serialized_string == NULL) { + return JSONFailure; + } + fp = fopen (filename, "w"); + if (fp == NULL) { + json_free_serialized_string(serialized_string); + return JSONFailure; + } + if (fputs(serialized_string, fp) == EOF) { + return_code = JSONFailure; + } + if (fclose(fp) == EOF) { + return_code = JSONFailure; + } + json_free_serialized_string(serialized_string); + return return_code; +} + +char * json_serialize_to_string_pretty(const JSON_Value *value) { + JSON_Status serialization_result = JSONFailure; + size_t buf_size_bytes = json_serialization_size_pretty(value); + char *buf = NULL; + if (buf_size_bytes == 0) { + return NULL; + } + buf = (char*)parson_malloc(buf_size_bytes); + if (buf == NULL) { + return NULL; + } + serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes); + if (serialization_result == JSONFailure) { + json_free_serialized_string(buf); + return NULL; + } + return buf; +} + +void json_free_serialized_string(char *string) { + parson_free(string); +} + +JSON_Status json_array_remove(JSON_Array *array, size_t ix) { + size_t to_move_bytes = 0; + if (array == NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + json_value_free(json_array_get_value(array, ix)); + to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value*); + memmove(array->items + ix, array->items + ix + 1, to_move_bytes); + array->count -= 1; + return JSONSuccess; +} + +JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) { + if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) { + return JSONFailure; + } + json_value_free(json_array_get_value(array, ix)); + value->parent = json_array_get_wrapping_value(array); + array->items[ix] = value; + return JSONSuccess; +} + +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_replace_null(JSON_Array *array, size_t i) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_array_replace_value(array, i, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_clear(JSON_Array *array) { + size_t i = 0; + if (array == NULL) { + return JSONFailure; + } + for (i = 0; i < json_array_get_count(array); i++) { + json_value_free(json_array_get_value(array, i)); + } + array->count = 0; + return JSONSuccess; +} + +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) { + if (array == NULL || value == NULL || value->parent != NULL) { + return JSONFailure; + } + return json_array_add(array, value); +} + +JSON_Status json_array_append_string(JSON_Array *array, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_number(JSON_Array *array, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_array_append_null(JSON_Array *array) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_array_append_value(array, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) { + size_t i = 0; + JSON_Value *old_value; + if (object == NULL || name == NULL || value == NULL || value->parent != NULL) { + return JSONFailure; + } + old_value = json_object_get_value(object, name); + if (old_value != NULL) { /* free and overwrite old value */ + json_value_free(old_value); + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + value->parent = json_object_get_wrapping_value(object); + object->values[i] = value; + return JSONSuccess; + } + } + } + /* add new key value pair */ + return json_object_add(object, name, value); +} + +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) { + return json_object_set_value(object, name, json_value_init_string(string)); +} + +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) { + return json_object_set_value(object, name, json_value_init_number(number)); +} + +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) { + return json_object_set_value(object, name, json_value_init_boolean(boolean)); +} + +JSON_Status json_object_set_null(JSON_Object *object, const char *name) { + return json_object_set_value(object, name, json_value_init_null()); +} + +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) { + const char *dot_pos = NULL; + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + JSON_Value *new_value = NULL; + if (value == NULL || name == NULL || value == NULL) { + return JSONFailure; + } + dot_pos = strchr(name, '.'); + if (dot_pos == NULL) { + return json_object_set_value(object, name, value); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + if (temp_obj == NULL) { + new_value = json_value_init_object(); + if (new_value == NULL) { + parson_free(current_name); + return JSONFailure; + } + if (json_object_add(object, current_name, new_value) == JSONFailure) { + json_value_free(new_value); + parson_free(current_name); + return JSONFailure; + } + temp_obj = json_object_get_object(object, current_name); + } + parson_free(current_name); + return json_object_dotset_value(temp_obj, dot_pos + 1, value); + } +} + +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) { + JSON_Value *value = json_value_init_string(string); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) { + JSON_Value *value = json_value_init_number(number); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) { + JSON_Value *value = json_value_init_boolean(boolean); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) { + JSON_Value *value = json_value_init_null(); + if (value == NULL) { + return JSONFailure; + } + if (json_object_dotset_value(object, name, value) == JSONFailure) { + json_value_free(value); + return JSONFailure; + } + return JSONSuccess; +} + +JSON_Status json_object_remove(JSON_Object *object, const char *name) { + size_t i = 0, last_item_index = 0; + if (object == NULL || json_object_get_value(object, name) == NULL) { + return JSONFailure; + } + last_item_index = json_object_get_count(object) - 1; + for (i = 0; i < json_object_get_count(object); i++) { + if (strcmp(object->names[i], name) == 0) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + if (i != last_item_index) { /* Replace key value pair with one from the end */ + object->names[i] = object->names[last_item_index]; + object->values[i] = object->values[last_item_index]; + } + object->count -= 1; + return JSONSuccess; + } + } + return JSONFailure; /* No execution path should end here */ +} + +JSON_Status json_object_dotremove(JSON_Object *object, const char *name) { + const char *dot_pos = strchr(name, '.'); + char *current_name = NULL; + JSON_Object *temp_obj = NULL; + if (dot_pos == NULL) { + return json_object_remove(object, name); + } else { + current_name = parson_strndup(name, dot_pos - name); + temp_obj = json_object_get_object(object, current_name); + parson_free(current_name); + if (temp_obj == NULL) { + return JSONFailure; + } + return json_object_dotremove(temp_obj, dot_pos + 1); + } +} + +JSON_Status json_object_clear(JSON_Object *object) { + size_t i = 0; + if (object == NULL) { + return JSONFailure; + } + for (i = 0; i < json_object_get_count(object); i++) { + parson_free(object->names[i]); + json_value_free(object->values[i]); + } + object->count = 0; + return JSONSuccess; +} + +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) { + JSON_Value *temp_schema_value = NULL, *temp_value = NULL; + JSON_Array *schema_array = NULL, *value_array = NULL; + JSON_Object *schema_object = NULL, *value_object = NULL; + JSON_Value_Type schema_type = JSONError, value_type = JSONError; + const char *key = NULL; + size_t i = 0, count = 0; + if (schema == NULL || value == NULL) { + return JSONFailure; + } + schema_type = json_value_get_type(schema); + value_type = json_value_get_type(value); + if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */ + return JSONFailure; + } + switch (schema_type) { + case JSONArray: + schema_array = json_value_get_array(schema); + value_array = json_value_get_array(value); + count = json_array_get_count(schema_array); + if (count == 0) { + return JSONSuccess; /* Empty array allows all types */ + } + /* Get first value from array, rest is ignored */ + temp_schema_value = json_array_get_value(schema_array, 0); + for (i = 0; i < json_array_get_count(value_array); i++) { + temp_value = json_array_get_value(value_array, i); + if (json_validate(temp_schema_value, temp_value) == JSONFailure) { + return JSONFailure; + } + } + return JSONSuccess; + case JSONObject: + schema_object = json_value_get_object(schema); + value_object = json_value_get_object(value); + count = json_object_get_count(schema_object); + if (count == 0) { + return JSONSuccess; /* Empty object allows all objects */ + } else if (json_object_get_count(value_object) < count) { + return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */ + } + for (i = 0; i < count; i++) { + key = json_object_get_name(schema_object, i); + temp_schema_value = json_object_get_value(schema_object, key); + temp_value = json_object_get_value(value_object, key); + if (temp_value == NULL) { + return JSONFailure; + } + if (json_validate(temp_schema_value, temp_value) == JSONFailure) { + return JSONFailure; + } + } + return JSONSuccess; + case JSONString: case JSONNumber: case JSONBoolean: case JSONNull: + return JSONSuccess; /* equality already tested before switch */ + case JSONError: default: + return JSONFailure; + } +} + +int json_value_equals(const JSON_Value *a, const JSON_Value *b) { + JSON_Object *a_object = NULL, *b_object = NULL; + JSON_Array *a_array = NULL, *b_array = NULL; + const char *a_string = NULL, *b_string = NULL; + const char *key = NULL; + size_t a_count = 0, b_count = 0, i = 0; + JSON_Value_Type a_type, b_type; + a_type = json_value_get_type(a); + b_type = json_value_get_type(b); + if (a_type != b_type) { + return 0; + } + switch (a_type) { + case JSONArray: + a_array = json_value_get_array(a); + b_array = json_value_get_array(b); + a_count = json_array_get_count(a_array); + b_count = json_array_get_count(b_array); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + if (!json_value_equals(json_array_get_value(a_array, i), + json_array_get_value(b_array, i))) { + return 0; + } + } + return 1; + case JSONObject: + a_object = json_value_get_object(a); + b_object = json_value_get_object(b); + a_count = json_object_get_count(a_object); + b_count = json_object_get_count(b_object); + if (a_count != b_count) { + return 0; + } + for (i = 0; i < a_count; i++) { + key = json_object_get_name(a_object, i); + if (!json_value_equals(json_object_get_value(a_object, key), + json_object_get_value(b_object, key))) { + return 0; + } + } + return 1; + case JSONString: + a_string = json_value_get_string(a); + b_string = json_value_get_string(b); + if (a_string == NULL || b_string == NULL) { + return 0; /* shouldn't happen */ + } + return strcmp(a_string, b_string) == 0; + case JSONBoolean: + return json_value_get_boolean(a) == json_value_get_boolean(b); + case JSONNumber: + return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */ + case JSONError: + return 1; + case JSONNull: + return 1; + default: + return 1; + } +} + +JSON_Value_Type json_type(const JSON_Value *value) { + return json_value_get_type(value); +} + +JSON_Object * json_object (const JSON_Value *value) { + return json_value_get_object(value); +} + +JSON_Array * json_array (const JSON_Value *value) { + return json_value_get_array(value); +} + +const char * json_string (const JSON_Value *value) { + return json_value_get_string(value); +} + +double json_number (const JSON_Value *value) { + return json_value_get_number(value); +} + +int json_boolean(const JSON_Value *value) { + return json_value_get_boolean(value); +} + +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) { + parson_malloc = malloc_fun; + parson_free = free_fun; +} diff --git a/third_party/parson/parson.h b/third_party/parson/parson.h new file mode 100644 index 00000000..6438c936 --- /dev/null +++ b/third_party/parson/parson.h @@ -0,0 +1,234 @@ +/* + Parson ( http://kgabis.github.com/parson/ ) + Copyright (c) 2012 - 2017 Krzysztof Gabis + + 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 parson_parson_h +#define parson_parson_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include /* size_t */ + +/* Types and enums */ +typedef struct json_object_t JSON_Object; +typedef struct json_array_t JSON_Array; +typedef struct json_value_t JSON_Value; + +enum json_value_type { + JSONError = -1, + JSONNull = 1, + JSONString = 2, + JSONNumber = 3, + JSONObject = 4, + JSONArray = 5, + JSONBoolean = 6 +}; +typedef int JSON_Value_Type; + +enum json_result_t { + JSONSuccess = 0, + JSONFailure = -1 +}; +typedef int JSON_Status; + +typedef void * (*JSON_Malloc_Function)(size_t); +typedef void (*JSON_Free_Function)(void *); + +/* Call only once, before calling any other function from parson API. If not called, malloc and free + from stdlib will be used for all allocations */ +void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); + +/* Parses first JSON value in a file, returns NULL in case of error */ +JSON_Value * json_parse_file(const char *filename); + +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + +/* Parses first JSON value in a string, returns NULL in case of error */ +JSON_Value * json_parse_string(const char *string); + +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + +/* Serialization */ +size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename); +char * json_serialize_to_string(const JSON_Value *value); + +/* Pretty serialization */ +size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */ +JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes); +JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename); +char * json_serialize_to_string_pretty(const JSON_Value *value); + +void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */ + +/* Comparing */ +int json_value_equals(const JSON_Value *a, const JSON_Value *b); + +/* Validation + This is *NOT* JSON Schema. It validates json by checking if object have identically + named fields with matching types. + For example schema {"name":"", "age":0} will validate + {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"}, + but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}. + In case of arrays, only first value in schema is checked against all values in tested array. + Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays, + null validates values of every type. + */ +JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value); + +/* + * JSON Object + */ +JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); +const char * json_object_get_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); +double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* dotget functions enable addressing values with dot notation in nested objects, + just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). + Because valid names in JSON can contain dots, some values may be inaccessible + this way. */ +JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); +const char * json_object_dotget_string (const JSON_Object *object, const char *name); +JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); +JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); +double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */ +int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */ + +/* Functions to get available names */ +size_t json_object_get_count (const JSON_Object *object); +const char * json_object_get_name (const JSON_Object *object, size_t index); +JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index); +JSON_Value * json_object_get_wrapping_value(const JSON_Object *object); + +/* Functions to check if object has a value with a specific name. Returned value is 1 if object has + * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */ +int json_object_has_value (const JSON_Object *object, const char *name); +int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); + +int json_object_dothas_value (const JSON_Object *object, const char *name); +int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type); + +/* Creates new name-value pair or frees and replaces old value with a new one. + * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_set_null(JSON_Object *object, const char *name); + +/* Works like dotget functions, but creates whole hierarchy if necessary. + * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value); +JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string); +JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number); +JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean); +JSON_Status json_object_dotset_null(JSON_Object *object, const char *name); + +/* Frees and removes name-value pair */ +JSON_Status json_object_remove(JSON_Object *object, const char *name); + +/* Works like dotget function, but removes name-value pair only on exact match. */ +JSON_Status json_object_dotremove(JSON_Object *object, const char *key); + +/* Removes all name-value pairs in object */ +JSON_Status json_object_clear(JSON_Object *object); + +/* + *JSON Array + */ +JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); +const char * json_array_get_string (const JSON_Array *array, size_t index); +JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); +JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); +double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ +int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ +size_t json_array_get_count (const JSON_Array *array); +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array); + +/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. + * Order of values in array may change during execution. */ +JSON_Status json_array_remove(JSON_Array *array, size_t i); + +/* Frees and removes from array value at given index and replaces it with given one. + * Does nothing and returns JSONFailure if index doesn't exist. + * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value); +JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string); +JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number); +JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean); +JSON_Status json_array_replace_null(JSON_Array *array, size_t i); + +/* Frees and removes all values from array */ +JSON_Status json_array_clear(JSON_Array *array); + +/* Appends new value at the end of array. + * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */ +JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value); +JSON_Status json_array_append_string(JSON_Array *array, const char *string); +JSON_Status json_array_append_number(JSON_Array *array, double number); +JSON_Status json_array_append_boolean(JSON_Array *array, int boolean); +JSON_Status json_array_append_null(JSON_Array *array); + +/* + *JSON Value + */ +JSON_Value * json_value_init_object (void); +JSON_Value * json_value_init_array (void); +JSON_Value * json_value_init_string (const char *string); /* copies passed string */ +JSON_Value * json_value_init_number (double number); +JSON_Value * json_value_init_boolean(int boolean); +JSON_Value * json_value_init_null (void); +JSON_Value * json_value_deep_copy (const JSON_Value *value); +void json_value_free (JSON_Value *value); + +JSON_Value_Type json_value_get_type (const JSON_Value *value); +JSON_Object * json_value_get_object (const JSON_Value *value); +JSON_Array * json_value_get_array (const JSON_Value *value); +const char * json_value_get_string (const JSON_Value *value); +double json_value_get_number (const JSON_Value *value); +int json_value_get_boolean(const JSON_Value *value); +JSON_Value * json_value_get_parent (const JSON_Value *value); + +/* Same as above, but shorter */ +JSON_Value_Type json_type (const JSON_Value *value); +JSON_Object * json_object (const JSON_Value *value); +JSON_Array * json_array (const JSON_Value *value); +const char * json_string (const JSON_Value *value); +double json_number (const JSON_Value *value); +int json_boolean(const JSON_Value *value); + +#ifdef __cplusplus +} +#endif + +#endif