mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2024-06-02 03:27:30 -04:00
257 lines
5.7 KiB
C++
257 lines
5.7 KiB
C++
// Json serialization engine. Json is used to store emulator settings, as well as for the JDI system (Json Debug Interface).
|
|
|
|
// All errors during deserialization are thrown by exceptions.
|
|
// During serializing, in theory, there shouldn't be any mistakes (if we are not enemies to ourselves and have not changed, for example, the Value type to some non-standard one).
|
|
|
|
#pragma once
|
|
|
|
class Json
|
|
{
|
|
// Foolproof
|
|
static const int MaxDepth = 255;
|
|
static const int MaxStringSize = 0x1000;
|
|
static const int MaxElements = 255; // Deserialize only
|
|
|
|
public:
|
|
class Value;
|
|
|
|
private:
|
|
|
|
// Recursive destruction of value and all its descendants
|
|
void DestroyValue(Value* value);
|
|
|
|
static wchar_t* CloneStr(const wchar_t* str);
|
|
static wchar_t* CloneAnsiStr(const char* str);
|
|
|
|
#pragma region "Serialization Related"
|
|
|
|
// Serialize context
|
|
|
|
struct SerializeContext
|
|
{
|
|
uint8_t** ptr;
|
|
size_t* actualSize;
|
|
size_t maxSize;
|
|
};
|
|
|
|
// Base procedure for generating the text.
|
|
static void EmitChar(SerializeContext* ctx, uint8_t val, bool sizeOnly);
|
|
|
|
static void EmitText(SerializeContext* ctx, const char* text, bool sizeOnly);
|
|
|
|
// Simple wchar_t to UTF-8 Converter + Escaping
|
|
static void EmitCodePoint(SerializeContext* ctx, int cp, bool sizeOnly);
|
|
static void EmitWcharString(SerializeContext* ctx, wchar_t* str, bool sizeOnly);
|
|
|
|
// Indentation
|
|
|
|
static void Indent(SerializeContext* ctx, int depth, bool sizeOnly);
|
|
|
|
#pragma endregion "Serialization Related"
|
|
|
|
#pragma region "De-Serialization Related"
|
|
|
|
// Deserialize
|
|
|
|
struct DeserializeContext
|
|
{
|
|
uint8_t* ptr;
|
|
size_t offset;
|
|
size_t maxSize;
|
|
};
|
|
|
|
static bool IsWhiteSpace(uint8_t value);
|
|
static bool IsControl(uint8_t value);
|
|
|
|
// Json Token
|
|
|
|
enum class TokenType : char
|
|
{
|
|
Unknown = '?',
|
|
EndOfStream = 'E',
|
|
ObjectStart = '{',
|
|
ObjectEnd = '}',
|
|
ArrayStart = '[',
|
|
ArrayEnd = ']',
|
|
Colon = ':',
|
|
Comma = ',',
|
|
Int = 'I',
|
|
Float = 'F',
|
|
True = 't',
|
|
False = 'f',
|
|
Null = 'N',
|
|
String = 'S',
|
|
};
|
|
|
|
class Token
|
|
{
|
|
public:
|
|
TokenType type = TokenType::Unknown;
|
|
union
|
|
{
|
|
uint64_t AsInt;
|
|
float AsFloat;
|
|
bool AsBool;
|
|
wchar_t* AsString;
|
|
} value;
|
|
Token()
|
|
{
|
|
value.AsString = nullptr;
|
|
}
|
|
};
|
|
|
|
static bool GetLiteral(DeserializeContext* ctx, Token& token);
|
|
static int FetchCodepoint(DeserializeContext* ctx);
|
|
static bool GetString(DeserializeContext* ctx, Token& token);
|
|
static bool IsAllowed(uint8_t val, char* allowed);
|
|
static bool GetFloat(DeserializeContext* ctx, Token& token);
|
|
static bool GetInt(DeserializeContext* ctx, Token& token);
|
|
|
|
static void GetToken(Token& token, DeserializeContext* ctx);
|
|
|
|
#pragma endregion "De-Serialization Related"
|
|
|
|
public:
|
|
|
|
// Json Value
|
|
|
|
enum class ValueType : char
|
|
{
|
|
Unknown = '?',
|
|
Object = 'O',
|
|
Array = 'A',
|
|
Int = 'I',
|
|
Float = 'F',
|
|
String = 'S',
|
|
Bool = 'B',
|
|
Null = 'N',
|
|
};
|
|
|
|
class Value
|
|
{
|
|
char* CloneName(const char* otherName);
|
|
char* CloneWcharName(const wchar_t* otherName);
|
|
void DeserializeObject(DeserializeContext* ctx);
|
|
void DeserializeArray(DeserializeContext* ctx);
|
|
|
|
public:
|
|
Value* parent = nullptr;
|
|
ValueType type = ValueType::Unknown;
|
|
// Name of object members.
|
|
// For simplicity, we do not support Non-Ansi Json value names (although full use of Utf-8 is allowed in the values)
|
|
char* name = nullptr;
|
|
union
|
|
{
|
|
float AsFloat;
|
|
uint64_t AsInt;
|
|
uint8_t AsUint8;
|
|
uint16_t AsUint16;
|
|
uint32_t AsUint32;
|
|
wchar_t* AsString;
|
|
bool AsBool;
|
|
} value;
|
|
std::list<Value*> children;
|
|
|
|
Value()
|
|
{
|
|
// Only the String value type requires the release of the stored value.
|
|
value.AsString = nullptr;
|
|
}
|
|
|
|
Value(Value* _parent)
|
|
{
|
|
// Only the String value type requires the release of the stored value.
|
|
value.AsString = nullptr;
|
|
this->parent = _parent;
|
|
}
|
|
|
|
~Value()
|
|
{
|
|
while (!children.empty())
|
|
{
|
|
Value* child = children.back();
|
|
children.pop_back();
|
|
delete child;
|
|
}
|
|
|
|
if (type == ValueType::String)
|
|
{
|
|
if (value.AsString)
|
|
{
|
|
delete[] value.AsString;
|
|
value.AsString = nullptr;
|
|
}
|
|
}
|
|
|
|
if (name != nullptr)
|
|
{
|
|
delete[] name;
|
|
name = nullptr;
|
|
}
|
|
}
|
|
|
|
void Serialize(SerializeContext* ctx, int depth, bool sizeOnly);
|
|
void Deserialize(DeserializeContext* ctx, wchar_t* keyName);
|
|
|
|
// Dynamic modification
|
|
|
|
Value* AddInt(const char* keyName, int _value);
|
|
Value* AddUInt16(const char* keyName, uint16_t _value);
|
|
Value* AddUInt32(const char* keyName, uint32_t _value);
|
|
Value* AddUInt64(const char* keyName, uint64_t _value);
|
|
Value* AddFloat(const char* keyName, float _value);
|
|
Value* AddNull(const char* keyName);
|
|
Value* AddBool(const char* keyName, bool _value);
|
|
Value* AddString(const char* keyName, const wchar_t* str);
|
|
Value* AddAnsiString(const char* keyName, const char* str);
|
|
Value* ReplaceString(const wchar_t* str);
|
|
Value* AddObject(const char* keyName);
|
|
Value* AddArray(const char* keyName);
|
|
Value* AddValue(const char* keyName, Value* value);
|
|
Value* Add(Value* _parent, Value* other);
|
|
Value* Replace(Value* _parent, Value* other);
|
|
|
|
// Access
|
|
|
|
Value* ByName(const char* byName);
|
|
Value* ByType(const ValueType byType);
|
|
|
|
void SetName(const char* newName)
|
|
{
|
|
if (name != nullptr)
|
|
{
|
|
delete[] name;
|
|
}
|
|
name = CloneName(newName);
|
|
}
|
|
};
|
|
|
|
/// <summary>
|
|
/// Deserialized root.
|
|
/// </summary>
|
|
Value root;
|
|
|
|
// Api
|
|
|
|
~Json()
|
|
{
|
|
DestroyValue(&root);
|
|
}
|
|
|
|
// The process of serialization is that we recursively walk through all the values, passing in each "running" context
|
|
// which has the pointer to the generated text and the current text size.
|
|
|
|
void Serialize(void* text, size_t maxTextSize, size_t& actualTextSize);
|
|
void GetSerializedTextSize(void* text, size_t maxTextSize, size_t& actualTextSize);
|
|
void Deserialize(void* text, size_t textSize);
|
|
|
|
// Clone
|
|
|
|
void Clone(Json* other);
|
|
|
|
// Merge
|
|
|
|
void Merge(Json* other);
|
|
|
|
};
|