Files
2018-07-17 14:15:37 +02:00

492 lines
14 KiB
C++

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "util/refcount.h"
#include <stdint.h>
#include <string>
#include <fstream>
#include "main/initialization.h"
#include "scripts/binds.h"
#include "files.h"
#include "threads.h"
#include "manager.h"
namespace scripts {
class StrOutputStream {
public:
std::string value;
StrOutputStream() {}
void Put(char c) { value += c; }
void Flush() {}
};
static Threaded(rapidjson::MemoryPoolAllocator<>*) allocator = 0;
rapidjson::MemoryPoolAllocator<>& getAllocator() {
if(!allocator) {
allocator = new rapidjson::MemoryPoolAllocator<>();
addThreadCleanup([]() {
delete allocator;
allocator = 0;
});
}
return *allocator;
}
class JSONTree : public AtomicRefCounted {
public:
rapidjson::Document doc;
void parse(const std::string& str) {
doc.Parse<0>(str.c_str());
}
void readFile(const std::string& fname) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
doc.Parse<0>(getFileContents(fname).c_str());
}
void writeFile(const std::string& fname, bool pretty) {
if(!isAccessible(fname)) {
scripts::throwException("Cannot access file outside game or profile directories.");
return;
}
std::fstream file(fname, std::ios_base::out);
file << toString(pretty);
}
std::string toString(bool pretty) {
StrOutputStream stream;
if(pretty) {
rapidjson::PrettyWriter<StrOutputStream> writer(stream);
writer.SetIndent('\t', 1);
doc.Accept(writer);
}
else {
rapidjson::Writer<StrOutputStream> writer(stream);
doc.Accept(writer);
}
return stream.value;
}
rapidjson::Value* root() {
return &doc;
}
};
static JSONTree* makeJSONTree() {
return new JSONTree();
}
static std::string nodeString(rapidjson::Value* node) {
if(!node->IsString()) {
scripts::throwException("Node is not a string value.");
return "";
}
return std::string(node->GetString());
}
static int nodeBool(rapidjson::Value* node) {
if(!node->IsBool()) {
scripts::throwException("Node is not a boolean value.");
return 0;
}
return node->GetBool();
}
static int nodeInt(rapidjson::Value* node) {
if(!node->IsInt()) {
scripts::throwException("Node is not an int value.");
return 0;
}
return node->GetInt();
}
static unsigned nodeUint(rapidjson::Value* node) {
if(!node->IsUint()) {
scripts::throwException("Node is not a uint value.");
return 0;
}
return node->GetUint();
}
static int64_t nodeInt64(rapidjson::Value* node) {
if(!node->IsInt64()) {
scripts::throwException("Node is not an int64 value.");
return 0;
}
return node->GetInt64();
}
static uint64_t nodeUint64(rapidjson::Value* node) {
if(!node->IsUint64()) {
scripts::throwException("Node is not a uint64 value.");
return 0;
}
return node->GetUint64();
}
static double nodeDouble(rapidjson::Value* node) {
if(!node->IsDouble()) {
scripts::throwException("Node is not a double value.");
return 0;
}
return node->GetDouble();
}
static double nodeNumber(rapidjson::Value* node) {
if(node->IsDouble())
return node->GetDouble();
if(node->IsInt())
return (double)node->GetInt();
if(node->IsUint())
return (double)node->GetUint();
if(node->IsInt64())
return (double)node->GetInt64();
if(node->IsUint64())
return (double)node->GetUint64();
scripts::throwException("Node is not a numeric value.");
return 0;
}
static rapidjson::Value* findMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return 0;
}
rapidjson::Value::Member* mem = node->FindMember(name.c_str());
if(!mem)
return 0;
return &mem->value;
}
static void removeMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return;
}
node->RemoveMember(name.c_str());
}
static rapidjson::Value* getMember(rapidjson::Value* node, const std::string& name) {
if(!node->IsObject()) {
scripts::throwException("Node is not an object.");
return 0;
}
rapidjson::Value::Member* mem = node->FindMember(name.c_str());
if(mem)
return &mem->value;
rapidjson::Value nullValue;
node->AddMember(name.c_str(), getAllocator(), nullValue, getAllocator());
return findMember(node, name);
}
static void clearItems(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->Clear();
}
static rapidjson::Value* getItem(rapidjson::Value* node, unsigned index) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
if(index >= node->Size()) {
scripts::throwException("Index out of bounds.");
return 0;
}
return &(*node)[index];
}
static unsigned arrSize(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
return node->Size();
}
static void arrReserve(rapidjson::Value* node, unsigned amount) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->Reserve(amount, getAllocator());
}
static rapidjson::Value* arrPush(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return 0;
}
rapidjson::Value nullValue;
node->PushBack(nullValue, getAllocator());
return &(*node)[node->Size() - 1];
}
static void arrPop(rapidjson::Value* node) {
if(!node->IsArray()) {
scripts::throwException("Node is not an array.");
return;
}
node->PopBack();
}
static rapidjson::Value* setString(rapidjson::Value* node, const std::string& value) {
node->SetString(value.c_str(), value.size(), getAllocator());
return node;
}
void RegisterJSONBinds() {
ClassBind node("JSONNode", asOBJ_REF | asOBJ_NOCOUNT);
classdoc(node, "A node within a json tree. Careful when holding references, as"
" changes to the tree can invalidate them.");
//* Tree methods *//
ClassBind tree("JSONTree", asOBJ_REF);
classdoc(tree, "Represents a tree of json nodes that can be altered."
" Please make note that JSONNode@ references are not reference counted,"
" and can be invalidated by changes to the tree. Do not hold references"
" unless you know what you're doing.");
tree.addFactory("JSONTree@ f()", asFUNCTION(makeJSONTree));
tree.setReferenceFuncs(asMETHOD(JSONTree, grab), asMETHOD(JSONTree, drop));
tree.addMethod("void parse(const string& data)", asMETHOD(JSONTree, parse))
doc("Parse a json tree from string data.", "Data string to parse.");
tree.addMethod("void readFile(const string& filename)", asMETHOD(JSONTree, readFile))
doc("Read a json tree from a file.", "Filename of the file to read from.");
tree.addMethod("string toString(bool pretty = false)", asMETHOD(JSONTree, toString))
doc("Dump the json tree to a string.",
"Whether to pretty-print with newlines and indentation.",
"Output data string.");
tree.addMethod("void writeFile(const string& filename, bool pretty = false)",
asMETHOD(JSONTree, writeFile))
doc("Write a json tree to a file.", "Filename of the file to write to.",
"Whether to pretty-print with newlines and indentation.");
tree.addMethod("JSONNode@ get_root()", asMETHOD(JSONTree, root))
doc("", "The root json node in this tree.");
//* Node methods *//
node.addMethod("bool isNull()", asMETHOD(rapidjson::Value, IsNull))
doc("", "Whether the node is a null.");
node.addMethod("bool isFalse()", asMETHOD(rapidjson::Value, IsFalse))
doc("", "Whether the node is a false.");
node.addMethod("bool isTrue()", asMETHOD(rapidjson::Value, IsTrue))
doc("", "Whether the node is a true.");
node.addMethod("bool isBool()", asMETHOD(rapidjson::Value, IsBool))
doc("", "Whether the node is a boolean value (true or false).");
node.addMethod("bool isObject()", asMETHOD(rapidjson::Value, IsObject))
doc("", "Whether the node is an object / mapping.");
node.addMethod("bool isArray()", asMETHOD(rapidjson::Value, IsArray))
doc("", "Whether the node is an array.");
node.addMethod("bool isNumber()", asMETHOD(rapidjson::Value, IsNumber))
doc("", "Whether the node is a numerical type.");
node.addMethod("bool isInt()", asMETHOD(rapidjson::Value, IsInt))
doc("", "Whether the node is an integer.");
node.addMethod("bool isUint()", asMETHOD(rapidjson::Value, IsUint))
doc("", "Whether the node is an unsigned integer.");
node.addMethod("bool isInt64()", asMETHOD(rapidjson::Value, IsInt64))
doc("", "Whether the node is an long integer.");
node.addMethod("bool isUint64()", asMETHOD(rapidjson::Value, IsUint64))
doc("", "Whether the node is an long unsigned integer.");
node.addMethod("bool isDouble()", asMETHOD(rapidjson::Value, IsDouble))
doc("", "Whether the node is a double.");
node.addMethod("bool isString()", asMETHOD(rapidjson::Value, IsString))
doc("", "Whether the node is a string.");
node.addMethod("JSONNode& setNull()", asMETHOD(rapidjson::Value, SetNull))
doc("Set this node to a null value.", "This node.");
node.addMethod("JSONNode& setBool(bool value)",
asMETHOD(rapidjson::Value, SetBool))
doc("Set this node to a boolean value.",
"Boolean value to set to.",
"This node.");
node.addMethod("JSONNode& makeArray()",
asMETHOD(rapidjson::Value, SetArray))
doc("Set this node to be an array.",
"This node.");
node.addMethod("JSONNode& makeObject()",
asMETHOD(rapidjson::Value, SetObject))
doc("Set this node to be an object.",
"This node.");
node.addMethod("JSONNode& setInt(int value)",
asMETHOD(rapidjson::Value, SetInt))
doc("Set this node to an int value.",
"Integer value to set to.",
"This node.");
node.addMethod("JSONNode& setUint(uint value)",
asMETHOD(rapidjson::Value, SetUint))
doc("Set this node to an unsigned int value.",
"Unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& setInt64(int64 value)",
asMETHOD(rapidjson::Value, SetInt64))
doc("Set this node to a long int value.",
"Long integer value to set to.",
"This node.");
node.addMethod("JSONNode& setUint64(uint64 value)",
asMETHOD(rapidjson::Value, SetUint64))
doc("Set this node to a long unsigned int value.",
"Long unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& setDouble(double value)",
asMETHOD(rapidjson::Value, SetDouble))
doc("Set this node to a double value.",
"Double value to set to.",
"This node.");
node.addExternMethod("JSONNode& setString(const string&in value)",
asFUNCTION(setString))
doc("Set this node to a string value.",
"String value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(int value)",
asMETHOD(rapidjson::Value, SetInt))
doc("Set this node to an int value.",
"Integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(uint value)",
asMETHOD(rapidjson::Value, SetUint))
doc("Set this node to an unsigned int value.",
"Unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(int64 value)",
asMETHOD(rapidjson::Value, SetInt64))
doc("Set this node to a long int value.",
"Long integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(uint64 value)",
asMETHOD(rapidjson::Value, SetUint64))
doc("Set this node to a long unsigned int value.",
"Long unsigned integer value to set to.",
"This node.");
node.addMethod("JSONNode& opAssign(double value)",
asMETHOD(rapidjson::Value, SetDouble))
doc("Set this node to a double value.",
"Double value to set to.",
"This node.");
node.addExternMethod("JSONNode& opAssign(const string&in value)",
asFUNCTION(setString))
doc("Set this node to a string value.",
"String value to set to.",
"This node.");
node.addExternMethod("string getString()", asFUNCTION(nodeString))
doc("", "The string value of the node.");
node.addExternMethod("bool getBool()", asFUNCTION(nodeBool))
doc("", "The boolean value of the node.");
node.addExternMethod("int getInt()", asFUNCTION(nodeInt))
doc("", "The integer value of the node.");
node.addExternMethod("uint getUint()", asFUNCTION(nodeUint))
doc("", "The unsigned integer value of the node.");
node.addExternMethod("int64 getInt64()", asFUNCTION(nodeInt64))
doc("", "The long integer value of the node.");
node.addExternMethod("uint64 getUint64()", asFUNCTION(nodeUint64))
doc("", "The long unsigned integer value of the node.");
node.addExternMethod("double getDouble()", asFUNCTION(nodeDouble))
doc("", "The double value of the node.");
node.addExternMethod("double getNumber()", asFUNCTION(nodeNumber))
doc("", "The numeric value of the node.");
node.addExternMethod("JSONNode@ findMember(const string& name)",
asFUNCTION(findMember))
doc("Retrieve a member node from an object.",
"The name of the member.",
"Reference to the member node. Null if it does not exist.");
node.addExternMethod("JSONNode@ getMember(const string& name)",
asFUNCTION(getMember))
doc("Retrieve a member node from an object, creating it if it does not exist.",
"The name of the member.",
"Reference to the member node, can be a newly created null node.");
node.addExternMethod("JSONNode@ opIndex(const string& name)",
asFUNCTION(getMember))
doc("Retrieve a member node from an object, creating it if it does not exist.",
"The name of the member.",
"Reference to the member node, can be a newly created null node.");
node.addExternMethod("void removeMember(const string& name)", asFUNCTION(removeMember))
doc("Remove a member from an object.", "Name of the member to remove.");
node.addExternMethod("void clearItems()", asFUNCTION(clearItems))
doc("Clear all items from an array node.");
node.addExternMethod("JSONNode@ getItem(uint index)", asFUNCTION(getItem))
doc("Get an item from an array node.", "Index of the item.",
"Node at the specified index in the array.");
node.addExternMethod("JSONNode@ opIndex(uint index)", asFUNCTION(getItem))
doc("Get an item from an array node.", "Index of the item.",
"Node at the specified index in the array.");
node.addExternMethod("uint size()", asFUNCTION(arrSize))
doc("", "The size of the array node.");
node.addExternMethod("void reserve(uint amount)", asFUNCTION(arrReserve))
doc("Reserve an array node to hold an amount of items.",
"Amount of items to reserve for.");
node.addExternMethod("JSONNode@ pushBack()", asFUNCTION(arrPush))
doc("Add a new node to the end of an array node.",
"Newly created null node.");
node.addExternMethod("void popBack()", asFUNCTION(arrPop))
doc("Remove a node from the end of an array node.");
}
};