/*
GameEngine and Editor
by John Ryland
Copyright (c) 2023
*/
////////////////////////////////////////////////////////////////////////////////////
// Schema Edit
#include "SchemaEdit.h"
#include "Utils.h"
#include <cassert>
#include <cstring>
#include <string>
#include <unordered_map>
// TODO : move to toolbox
template <class T, size_t N>
constexpr size_t array_size(const T (&array)[N]) noexcept
{
return N;
}
std::unordered_map<std::string,NumberTypeEnum> numberTypeEnumMap
{
{ "UInt8", E_UInt8 },
{ "UInt16", E_UInt16 },
{ "UInt32", E_UInt32 },
{ "UInt64", E_UInt64 },
{ "Int8", E_Int8 },
{ "Int16", E_Int16 },
{ "Int32", E_Int32 },
{ "Int64", E_Int64 },
{ "Float32", E_Float32 },
{ "Float64", E_Float64 },
{ "Ratio", E_Ratio },
{ "Angle", E_Angle }
};
std::unordered_map<std::string,ColorTypeEnum> colorTypeEnumMap
{
{ "RGB32", E_RGB32 },
{ "RGBA32", E_RGBA32 }
};
std::unordered_map<NumberTypeEnum,std::string> numberTypeEnumToString
{
{ E_UInt8, "UInt8" },
{ E_UInt16, "UInt16" },
{ E_UInt32, "UInt32" },
{ E_UInt64, "UInt64" },
{ E_Int8, "Int8" },
{ E_Int16, "Int16" },
{ E_Int32, "Int32" },
{ E_Int64, "Int64" },
{ E_Float32, "Float32" },
{ E_Float64, "Float64" },
{ E_Ratio, "Ratio" },
{ E_Angle, "Angle" }
};
// Idea is that the unordered_maps can be initialized from this
struct
{
VariableTypeEnum m_enumValue;
const char* m_toString;
const char* m_xmlTag;
const char* m_asCode;
}
variableTypeEnumNames[] =
{
{ VariableTypeEnum::E_Void , "Void" , "void" , "void " },
{ VariableTypeEnum::E_Bool , "Bool" , "bool" , "bool " },
{ VariableTypeEnum::E_Enum , "Enum" , "enum" , "enum " },
{ VariableTypeEnum::E_String , "String" , "string" , "char " },
{ VariableTypeEnum::E_Number , "Number" , "number" , "" },
{ VariableTypeEnum::E_EncodedNumber , "EncodedNumber" , "" , "" },
{ VariableTypeEnum::E_Color , "Color" , "color" , "" },
{ VariableTypeEnum::E_Entity , "Entity" , "entity" , "EntityId " },
{ VariableTypeEnum::E_Asset , "Asset" , "" , "" },
{ VariableTypeEnum::E_Struct , "Struct" , "struct" , "struct " },
{ VariableTypeEnum::E_Component , "Component" , "component" , "struct " },
};
static_assert(array_size(variableTypeEnumNames) == (size_t)VariableTypeEnum::E_Max, "Please update above to match the enum");
//// TODO: BEGIN begin some experimental code ideas on string<->enum mapping
#if 0
struct VTIt
{
int i;
VTIt& operator++() { ++i; return *this; }
std::pair<std::string,VariableTypeEnum> operator*()
{
return std::pair<std::string,VariableTypeEnum>{
variableTypeEnumNames[i].m_toString, variableTypeEnumNames[i].m_enumValue };
}
};
struct GenericIterator
{
int i;
inline GenericIterator& operator++() { ++i; return *this; }
inline bool operator!=(const GenericIterator& other) { return i!=other.i; }
};
struct VTIt2 : public GenericIterator
{
VTIt2(int x) { i = x; }
std::pair<VariableTypeEnum,const char*> operator*()
{
return std::make_pair<VariableTypeEnum,const char*>(std::move(variableTypeEnumNames[i].m_enumValue), std::move(variableTypeEnumNames[i].m_toString));
}
};
VariableTypeEnum VariableTypeEnumFromString(const std::string& val)
{
static std::unordered_map<std::string,VariableTypeEnum> sVariableTypeEnumFromString;
static bool sInited = false;
if (!sInited)
for (int i = 0; i < array_size(variableTypeEnumNames); ++i)
sVariableTypeEnumFromString[variableTypeEnumNames[i].m_toString] = variableTypeEnumNames[i].m_enumValue;
sInited = true;
return sVariableTypeEnumFromString[val];
}
std::string VariableTypeEnumToString(VariableTypeEnum val)
{
static std::unordered_map<VariableTypeEnum,const char*> sVariableTypeEnumToString(VTIt2{0}, VTIt2{array_size(variableTypeEnumNames)}, array_size(variableTypeEnumNames));
return sVariableTypeEnumToString[val];
}
#endif
//// TODO: END end some experimental code ideas on string<->enum mapping
std::unordered_map<VariableTypeEnum,std::string> variableTypeEnumToString
{
{ VariableTypeEnum::E_Void, "Void" },
{ VariableTypeEnum::E_Bool, "Bool" },
{ VariableTypeEnum::E_Enum, "Enum" },
{ VariableTypeEnum::E_String, "String" },
{ VariableTypeEnum::E_Number, "Number" },
{ VariableTypeEnum::E_EncodedNumber, "EncodedNumber" },
{ VariableTypeEnum::E_Entity, "Entity" },
{ VariableTypeEnum::E_Struct, "Struct" },
{ VariableTypeEnum::E_Component, "Component" },
};
std::unordered_map<VariableTypeEnum,std::string> variableTypeEnumToCType
{
{ VariableTypeEnum::E_Void, "void " },
{ VariableTypeEnum::E_Bool, "bool " },
{ VariableTypeEnum::E_Enum, "enum " },
{ VariableTypeEnum::E_String, "char " },
{ VariableTypeEnum::E_Color, "" },
{ VariableTypeEnum::E_Number, "" },
{ VariableTypeEnum::E_EncodedNumber, "" },
{ VariableTypeEnum::E_Entity, "EntityId " },
{ VariableTypeEnum::E_Struct, "struct " },
{ VariableTypeEnum::E_Component, "struct " },
};
std::unordered_map<ColorTypeEnum,std::string> colorTypeEnumToCType
{
{ E_RGB32, "rgb32 " },
{ E_RGBA32, "rgba32 " }
};
std::unordered_map<NumberTypeEnum,std::string> numberTypeEnumToCType
{
{ E_UInt8, "uint8_t " },
{ E_UInt16, "uint16_t " },
{ E_UInt32, "uint32_t " },
{ E_UInt64, "uint64_t " },
{ E_Int8, "int8_t " },
{ E_Int16, "int16_t " },
{ E_Int32, "int32_t " },
{ E_Int64, "int64_t " },
{ E_Float32, "float " },
{ E_Float64, "double " },
{ E_Ratio, "float " },
{ E_Angle, "float " }
};
DynNameValue CreateEnumValue(std::string name, uint64_t value)
{
DynNameValue val{ name, { {}, value } };
return val;
}
EnumDescription CreateEnum(DynSchema* schema, std::vector<DynNameValue> enumValues)
{
EnumDescription desc{ .numberOfEnumValues = (uint32_t)enumValues.size(), .nameValueTableOffset = (uint32_t)schema->enums.size() };
schema->enums.insert(schema->enums.end(), enumValues.begin(), enumValues.end());
return desc;
}
StructDescription CreateStruct(DynSchema* schema, std::vector<DynVariableDescription> fields)
{
StructDescription desc{ .numberOfFields = (uint32_t)fields.size(), .variableDescriptionTableOffset = (uint32_t)schema->variables.size() };
schema->variables.insert(schema->variables.end(), fields.begin(), fields.end());
return desc;
}
DynVariableDescription AddNumber(DynSchema* schema, std::string name, NumberDescription numberDescription)
{
DynVariableDescription desc{ name, { 0, {}, VariableTypeEnum::E_Number, .numberDescription = numberDescription } };
//schema->variables.push_back(desc);
return desc;
}
/*
DynVariableDescription AddEncodedNumber(DynSchema* schema, std::string name, EncodedNumberDescription encodedNumberDescription)
{
DynVariableDescription desc{ name, { {}, E_EncodedNumber, .encodedNumberDescription = encodedNumberDescription } };
//schema->variables.push_back(desc);
return desc;
}
*/
DynVariableDescription AddString(DynSchema* schema, std::string name, StringDescription stringDescription)
{
DynVariableDescription desc{ name, { 0, {}, VariableTypeEnum::E_String, .stringDescription = stringDescription } };
//schema->variables.push_back(desc);
return desc;
}
DynVariableDescription AddEnum(DynSchema* schema, std::string name, EnumDescription enumDescription)
{
DynVariableDescription desc{ name, { 0, {}, VariableTypeEnum::E_Enum, .enumDescription = enumDescription } };
//schema->variables.push_back(desc);
return desc;
}
DynVariableDescription AddBool(DynSchema* schema, std::string name)
{
return DynVariableDescription{ name, { 0, {}, VariableTypeEnum::E_Bool } };
}
DynVariableDescription AddColor(DynSchema* schema, std::string name, ColorDescription colorDescription)
{
return DynVariableDescription{ name, { 0, {}, VariableTypeEnum::E_Color, .colorDescription = colorDescription } };
}
DynVariableDescription AddStruct(DynSchema* schema, std::string name, StructDescription structDescription)
{
DynVariableDescription desc{ name, { 0, {}, VariableTypeEnum::E_Struct, .structDescription = structDescription } };
schema->variables.push_back(desc);
return desc;
}
DynVariableDescription AddComponent(DynSchema* schema, std::string name, StructDescription structDescription)
{
DynVariableDescription desc{ name, { 0, {}, VariableTypeEnum::E_Component, .structDescription = structDescription } };
schema->variables.push_back(desc);
return desc;
}
static
size_t CalculateStringsTotalSize(DynSchema* schema)
{
size_t stringsSize = 0;
for (const auto& val : schema->enums)
{
stringsSize += val.name.size();
}
for (const auto& val : schema->variables)
{
stringsSize += val.name.size();
}
return stringsSize;
}
static
void ConvertDynSchemaToFileSchema(DynSchema* schema, Schema* fileSchema)
{
size_t stringsSize = 0;
size_t elementIndex = 0;
for (auto& val : schema->enums)
{
size_t strSize = val.name.size();
memcpy(fileSchema->strings->text + stringsSize, val.name.c_str(), strSize);
val.desc.name.stringTableOffset = (uint32_t)stringsSize;
val.desc.name.stringLength = (uint32_t)strSize;
fileSchema->enums->values[elementIndex] = val.desc;
stringsSize += strSize;
elementIndex++;
}
elementIndex = 0;
for (auto& val : schema->variables)
{
size_t strSize = val.name.size();
memcpy(fileSchema->strings->text + stringsSize, val.name.c_str(), strSize);
val.desc.name.stringTableOffset = (uint32_t)stringsSize;
val.desc.name.stringLength = (uint32_t)strSize;
fileSchema->variables->variables[elementIndex] = val.desc;
stringsSize += strSize;
elementIndex++;
}
}
bool WriteSchemeFile(DynSchema* schema, const char* schemaFileName)
{
FILE* schemaFile = fopen(schemaFileName, "w+");
if (schemaFile)
{
size_t stringTableSize = CalculateStringsTotalSize(schema);
size_t nameValueTableSize = schema->enums.size();
size_t variableDescriptionTableSize = schema->variables.size();
size_t headerSize = sizeof(SchemaFileFormatHeader);
size_t stringTableFileOffset = headerSize;
size_t nameValueTableFileOffset = stringTableFileOffset + stringTableSize * sizeof(char);
size_t variableDescriptionTableFileOffset = nameValueTableFileOffset + nameValueTableSize * sizeof(NameValue);
size_t fileSize = variableDescriptionTableFileOffset + variableDescriptionTableSize * sizeof(VariableDescription);
SchemaFileFormatHeader fileHeader
{
.fileFormatCode = 0x24555542,
.fileSize = (uint32_t)fileSize,
.headerSize = (uint32_t)headerSize,
.stringTableSize = (uint32_t)stringTableSize,
.stringTableFileOffset = (uint32_t)stringTableFileOffset,
.nameValueTableSize = (uint32_t)nameValueTableSize,
.nameValueTableFileOffset = (uint32_t)nameValueTableFileOffset,
.variableDescriptionTableSize = (uint32_t)variableDescriptionTableSize,
.variableDescriptionTableFileOffset = (uint32_t)variableDescriptionTableFileOffset
};
auto res = fwrite(&fileHeader, sizeof(fileHeader), 1, schemaFile);
if (res != 1)
{
printf("write res: %lu\n", res);
}
else
{
Schema* fileSchema = (Schema*)malloc(sizeof(Schema));
fileSchema->strings = (StringTable*)malloc(sizeof(uint64_t) + sizeof(char) * fileHeader.stringTableSize);
fileSchema->enums = (NameValueTable*)malloc(sizeof(uint64_t) + sizeof(NameValue) * fileHeader.nameValueTableSize);
fileSchema->variables = (VariableDescriptionTable*)malloc(sizeof(uint64_t) + sizeof(VariableDescription) * fileHeader.variableDescriptionTableSize);
ConvertDynSchemaToFileSchema(schema, fileSchema);
//fseek(schemaFile, fileHeader.stringTableFileOffset, SEEK_SET);
fwrite(fileSchema->strings->text, fileHeader.stringTableSize * sizeof(char), 1, schemaFile);
//fseek(schemaFile, fileHeader.nameValueTableSize, SEEK_SET);
fwrite(fileSchema->enums->values, fileHeader.nameValueTableSize * sizeof(NameValue), 1, schemaFile);
//fseek(schemaFile, fileHeader.variableDescriptionTableFileOffset, SEEK_SET);
fwrite(fileSchema->variables->variables, fileHeader.variableDescriptionTableSize * sizeof(VariableDescription), 1, schemaFile);
DestroySchema(fileSchema);
}
fclose(schemaFile);
return true;
}
return false;
}
static
DynVariableDescription ConvertXmlToVariable(XmlTag* tag, DynSchema* dynSchema)
{
if (tag->tag == "struct" || tag->tag == "component")
{
std::vector<DynVariableDescription> fields;
for (auto& child : tag->children)
{
fields.push_back(ConvertXmlToVariable(&child, dynSchema));
}
if (tag->tag == "struct")
return AddStruct(dynSchema, tag->attribValues["name"], CreateStruct(dynSchema, fields));
else
return AddComponent(dynSchema, tag->attribValues["name"], CreateStruct(dynSchema, fields));
}
else if (tag->tag == "void")
{
// TODO
}
else if (tag->tag == "bool")
{
return AddBool(dynSchema, tag->attribValues["name"]);
}
else if (tag->tag == "color")
{
return AddColor(dynSchema, tag->attribValues["name"], { colorTypeEnumMap[tag->attribValues["type"]] });
}
else if (tag->tag == "enum")
{
std::vector<DynNameValue> enumValues;
for (auto& attrib : tag->attribValues)
{
if (attrib.first != "name")
{
enumValues.push_back(CreateEnumValue(attrib.first, StringToUInt64(attrib.second)));
}
}
return AddEnum(dynSchema, tag->attribValues["name"], CreateEnum(dynSchema, enumValues));
}
/*
else if (tag->tag == "encoded")
{
// TODO
return AddNumber(dynSchema, tag->attribValues["name"], { numberTypeEnumMap[tag->attribValues["type"]] });
}
*/
else if (tag->tag == "number")
{
return AddNumber(dynSchema, tag->attribValues["name"], { numberTypeEnumMap[tag->attribValues["type"]] });
}
else if (tag->tag == "entity")
{
return DynVariableDescription{ tag->attribValues["name"], { 0, {}, VariableTypeEnum::E_Entity, .entityDescription = {} } };
}
else if (tag->tag == "string")
{
return AddString(dynSchema, tag->attribValues["name"], { StringToUInt32(tag->attribValues["size"]) });
}
else
{
printf("Error, unknown tag type: %s\n", tag->tag.c_str());
}
DynVariableDescription nullVar;
return nullVar;
}
void ConvertXmlToSchema(XmlTag* rootTag, DynSchema* dynSchema)
{
printf("Root tag: %s\n", rootTag->tag.c_str());
assert(rootTag->tag == "root");
printf("Root tag: %s\n", rootTag->children[0].tag.c_str());
for (auto& child : rootTag->children)
{
assert(child.tag == "xml");
for (auto& grandChild : child.children)
{
ConvertXmlToVariable(&grandChild, dynSchema);
}
}
}
std::string ConvertRuntimeString(Schema* schema, String str)
{
const char* cstr = schema->strings->text + str.stringTableOffset;
return std::string(cstr, cstr + str.stringLength);
}
static
void DumpStructAsC(Schema* schema, const char* indent, const char* indentAdd, const char* name, StructDescription& structDescription)
{
printf("%sstruct %s\n%s{\n", indent, name, indent);//, str.c_str());
for (int i = 0; i < structDescription.numberOfFields; ++i)
{
auto* varDec = schema->variables->variables + structDescription.variableDescriptionTableOffset + i;
std::string str2 = ConvertRuntimeString(schema, varDec->name);
//std::string varType = variableTypeEnumToCType[varDec->variableType];
if (varDec->variableType == VariableTypeEnum::E_Number)
{
printf("%s%s%s %s;\n", indent, indentAdd, numberTypeEnumToCType[varDec->numberDescription.numberType].c_str(), str2.c_str());
}
else if (varDec->variableType == VariableTypeEnum::E_Color)
{
printf("%s%s%s %s;\n", indent, indentAdd, colorTypeEnumToCType[varDec->colorDescription.colorType].c_str(), str2.c_str());
}
else if (varDec->variableType == VariableTypeEnum::E_Struct)
{
std::string indent2 = indent;
indent2 += indentAdd;
DumpStructAsC(schema, indent2.c_str(), indentAdd, "", varDec->structDescription);
printf(" %s;\n", str2.c_str());
}
else if (varDec->variableType == VariableTypeEnum::E_String)
{
printf("%s%schar %s[%i];\n", indent, indentAdd, str2.c_str(), varDec->stringDescription.maximumCharacterCount);
}
else if (varDec->variableType == VariableTypeEnum::E_Enum)
{
std::string space = " ";
space.resize(str2.size() < 11 ? 11 - str2.size() : 1);
printf("%s%sEnum%s%s%s;\n", indent, indentAdd, str2.c_str(), space.c_str(), str2.c_str());
}
else
{
printf("%s%s%s %s;\n", indent, indentAdd, variableTypeEnumToCType[varDec->variableType].c_str(), str2.c_str());
//printf("%s%s%s %s;\n", indent, indentAdd, VariableTypeEnumToString(varDec->variableType).c_str(), str2.c_str());
}
}
printf("%s}", indent);
}
void DumpSchema(Schema* schema)
{
/*
printf("Enum value table:\n");
for (int i = 0; i < schema->enums->count; ++i)
{
auto& enumVal = schema->enums->values[i];
std::string str = ConvertRuntimeString(schema, enumVal.name);
printf(" '%s' = 0x%08lx\n", str.c_str(), enumVal.value);
}
printf("Variable type table:\n");
for (int i = 0; i < schema->variables->count; ++i)
{
auto& varDec = schema->variables->variables[i];
std::string str = ConvertRuntimeString(schema, varDec.name);
switch (varDec.variableType)
{
case E_Void:
printf(" '%s' = void\n", str.c_str());
break;
case E_Bool:
printf(" '%s' = bool\n", str.c_str());
break;
case E_Enum:
printf(" '%s' = enum[%i] {\n", str.c_str(), varDec.enumDescription.numberOfEnumValues);
for (int i = 0; i < varDec.enumDescription.numberOfEnumValues; ++i)
{
NameValue* nameValue = schema->enums->values + varDec.enumDescription.nameValueTableOffset + i;
std::string str2 = ConvertRuntimeString(schema, nameValue->name);
printf(" '%s' = %lu,\n", str2.c_str(), nameValue->value);
}
printf(" };\n");
break;
case E_String:
printf(" '%s' = string[%i]\n", str.c_str(), varDec.stringDescription.maximumCharacterCount);
break;
case E_Number:
printf(" '%s' = number: %s\n", str.c_str(), numberTypeEnumToString[varDec.numberDescription.numberType].c_str());
break;
case E_Entity:
printf(" '%s' = entity\n", str.c_str());
break;
case E_EncodedNumber:
printf(" '%s' = encoded_number\n", str.c_str());
break;
case E_Struct:
case E_Component:
if (varDec.variableType == VariableTypeEnum::E_Struct)
printf(" '%s' = struct {\n", str.c_str());
else
printf(" '%s' = component {\n", str.c_str());
for (int i = 0; i < varDec.structDescription.numberOfFields; ++i)
{
auto* varDec2 = schema->variables->variables + varDec.structDescription.variableDescriptionTableOffset + i;
std::string str2 = ConvertRuntimeString(schema, varDec2->name);
std::string varType = variableTypeEnumToString[varDec2->variableType];
printf(" %s '%s';\n", varType.c_str(), str2.c_str());
}
printf(" };\n");
break;
}
}
*/
printf("// Schema converted to C code:\n\n");
const char* indent = ""; // Current indent
const char* indentAdd = " "; // Amount to add when entering new scope
for (int i = 0; i < schema->variables->count; ++i)
{
auto& varDec = schema->variables->variables[i];
if (varDec.variableType == VariableTypeEnum::E_Enum)
{
std::string str = ConvertRuntimeString(schema, varDec.name);
printf("%senum Enum%s\n%s{\n", indent, str.c_str(), indent);
// vals
for (int i = 0; i < varDec.enumDescription.numberOfEnumValues; ++i)
{
NameValue* nameValue = schema->enums->values + varDec.enumDescription.nameValueTableOffset + i;
std::string str2 = ConvertRuntimeString(schema, nameValue->name);
std::string space = " ";
space.resize(str2.size() < 14 ? 14 - str2.size() : 1);
printf("%s%s%s%s = %lu,\n", indent, indentAdd, str2.c_str(), space.c_str(), nameValue->value);
}
printf("%s};\n\n", indent);
}
}
for (int i = 0; i < schema->variables->count; ++i)
{
auto& varDec = schema->variables->variables[i];
if (varDec.variableType == VariableTypeEnum::E_Component)
{
DumpStructAsC(schema, indent, indentAdd, ConvertRuntimeString(schema, varDec.name).c_str(), varDec.structDescription);
printf(";\n\n");
}
}
}