/** * Copyright (c) 2023 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "../../util/xml.hpp" #include "../../util/file.hpp" #include #include struct UIGenerated { bool align = false; bool alignGrid = false; std::string alignX; std::string alignY; std::string align0; std::string align1; std::string align2; std::string align3; bool isGrid = false; std::string rows; std::string columns; std::string gutterX; std::string gutterY; std::string type; std::string name; std::string parent; // Optionals bool hasColor = false; std::string color; }; std::vector split(std::string s, std::string delimiter) { size_t pos_start = 0, pos_end, delim_len = delimiter.length(); std::string token; std::vector res; while ((pos_end = s.find (delimiter, pos_start)) != std::string::npos) { token = s.substr (pos_start, pos_end - pos_start); pos_start = pos_end + delim_len; res.push_back(token); } res.push_back (s.substr (pos_start)); return res; } std::string alignmentFromRaw(std::string strRaw) { if(strRaw == "start") return "UI_COMPONENT_ALIGN_START"; if(strRaw == "middle") return "UI_COMPONENT_ALIGN_MIDDLE"; if(strRaw == "end") return "UI_COMPONENT_ALIGN_END"; if(strRaw == "stretch") return "UI_COMPONENT_ALIGN_STRETCH"; return ""; } static bool parseChildren( xml_t *currentNode, std::string parent, std::vector *items ) { // Confirm attributes auto attrName = xmlGetAttributeByName(currentNode, "name"); if(attrName == -1) { std::cout << "Missing name attribute." << std::endl; return false; } struct UIGenerated item; item.name = std::string(currentNode->attributeDatas[attrName] ); item.type = std::string(currentNode->node); item.parent = parent; // Standard Align auto attrAlign = xmlGetAttributeByName(currentNode, "align"); if(attrAlign != -1) { // Parse alignment std::string alignRaw(currentNode->attributeDatas[attrAlign]); std::vector alignmentParts = split(alignRaw, " "); if(alignmentParts.size() != 6) { std::cout << "Alignment is invalid" << std::endl; return false; } item.align = true; item.alignX = alignmentFromRaw(alignmentParts[0]); item.alignY = alignmentFromRaw(alignmentParts[1]); item.align0 = alignmentParts[2]; item.align1 = alignmentParts[3]; item.align2 = alignmentParts[4]; item.align3 = alignmentParts[5]; if(item.alignX.size() == 0) { std::cout << "X Align is invalid" << std::endl; return ""; } if(item.alignY.size() == 0) { std::cout << "Y Align is invalid" << std::endl; return ""; } } // Grid Align auto attrGridAlign = xmlGetAttributeByName(currentNode, "grid"); if(attrGridAlign != -1) { // Parse alignment std::string alignRaw(currentNode->attributeDatas[attrGridAlign]); std::vector alignmentParts = split(alignRaw, " "); if(alignmentParts.size() != 4) { std::cout << "Grid alignment is invalid" << std::endl; return false; } item.alignGrid = true; item.alignX = alignmentFromRaw(alignmentParts[0]); item.alignY = alignmentFromRaw(alignmentParts[1]); item.align0 = alignmentParts[2]; item.align1 = alignmentParts[3]; if(item.alignX.size() == 0) { std::cout << "X Align is invalid" << std::endl; return ""; } if(item.alignY.size() == 0) { std::cout << "Y Align is invalid" << std::endl; return ""; } } // Parse color auto attrColor = xmlGetAttributeByName(currentNode, "color"); if(attrColor != -1) { item.hasColor = true; item.color = currentNode->attributeDatas[attrColor]; } // Grid if(item.type == "UIGrid") { auto attrRows = xmlGetAttributeByName(currentNode, "rows"); auto attrCols = xmlGetAttributeByName(currentNode, "columns"); auto attrGutter = xmlGetAttributeByName(currentNode, "gutter"); if(attrRows == -1 || attrCols == -1) { std::cout << "Grid is invalid" << std::endl; return false; } item.isGrid = true; item.rows = currentNode->attributeDatas[attrRows]; item.columns = currentNode->attributeDatas[attrCols]; if(attrGutter != -1) { auto gutterParts = split(currentNode->attributeDatas[attrGutter], " "); if(gutterParts.size() != 2) { std::cout << "Gutter is invalid" << std::endl; return false; } item.gutterX = gutterParts[0]; item.gutterY = gutterParts[1]; } else { item.gutterX = "0"; item.gutterY = "0"; } } // Self items->push_back(item); // Children for(int32_t i = 0; i < currentNode->childrenCount; i++) { if(!parseChildren(currentNode->children + i, item.name, items)) return false; } return true; } int main(int argc, char *args[]) { if(argc != 3) { std::cout << "Invalid number of args for ui gen" << std::endl; return 1; } // Open input file. char fileIn[FILENAME_MAX]; fileNormalizeSlashes(args[1]); sprintf(fileIn, "%s", args[1]); FILE *fin = fopen(fileIn, "rb"); if(fin == NULL) { std::cout << "Failed to open input file " << fileIn << std::endl; return 1; } // Tell file len fseek(fin, 0, SEEK_END); auto len = ftell(fin); fseek(fin, 0, SEEK_SET); // Read data. char *buffer = (char *)malloc(sizeof(char) * (len + 1)); if(buffer == NULL) { std::cout << "Failed to create temporary memory." << std::endl; fclose(fin); return 1; } assetReadString(fin, buffer); fclose(fin); // Parse XML xml_t xml; xmlLoad(&xml, buffer); free(buffer); // Begin output std::string bufferOut = ""; // Imports bufferOut += "#pragma once\n"; for(int32_t i = 0; i < xml.attributeCount; i++) { std::string attrName = xml.attributeNames[i]; if( attrName == "name" ) continue; bufferOut += "#include \""; bufferOut += xml.attributeDatas[i]; bufferOut += "\"\n"; } bufferOut += "\n"; // Now prep class itself. auto attrName = xmlGetAttributeByName(&xml, "name"); if(attrName == -1) { std::cout << "Missing " << std::endl; xmlDispose(&xml); return 1; } std::string name = xml.attributeDatas[attrName]; // Children std::vector items; for(int32_t j = 0; j < xml.childrenCount; j++) { if(parseChildren(xml.children + j, "", &items)) continue; xmlDispose(&xml); return 1; } // Generate strings. bufferOut += "namespace Dawn {\n"; bufferOut += " class " + name + " : public UIEmpty {\n"; bufferOut += " public:\n"; auto it = items.begin(); while(it != items.end()) { auto c = *it; bufferOut += " " + c.type + " " + c.name + ";\n"; ++it; } bufferOut += "\n"; bufferOut += " " + name + "(UICanvas *canvas) : UIEmpty(canvas),\n"; it = items.begin(); while(it != items.end()) { auto c = *it; bufferOut += " " + c.name + "(canvas)"; if(it != items.end() - 1) bufferOut += ","; bufferOut += "\n"; ++it; } bufferOut += " {\n"; it = items.begin(); while(it != items.end()) { auto c = *it; bufferOut += "\n"; // Transform if(c.align) { bufferOut += " " + c.name + ".setTransform(\n"; bufferOut += " " + c.alignX + ", " + c.alignY + ",\n"; bufferOut += " glm::vec4(" + c.align0 + ", " + c.align1 + ", " + c.align2 + ", " + c.align3 + "),\n"; bufferOut += " 0.0f\n"; bufferOut += " );\n"; } // Color if(c.hasColor) { bufferOut += " " + c.name + ".color = " + c.color + ";\n"; } // Grid if(c.isGrid) { bufferOut += " " + c.name + ".setGridSize(\n"; bufferOut += " " + c.rows + ", " + c.columns + ",\n"; bufferOut += " " + c.gutterX + ", " + c.gutterY + "\n"; bufferOut += " );\n"; } // Parent setting if(c.alignGrid) { bufferOut += " " + c.parent + ".addToGrid(\n"; bufferOut += " &" + c.name + ",\n"; bufferOut += " " + c.align0 + ", " + c.align1 + ",\n"; bufferOut += " " + c.alignX + ", " + c.alignY + "\n"; bufferOut += " );\n"; } else { if(c.parent == "") { bufferOut += " this->addChild(&" + c.name + ");\n"; } else { bufferOut += " " + c.parent + ".addChild(&" + c.name + ");\n"; } } ++it; } bufferOut += " }\n"; bufferOut += " };\n"; bufferOut += "}"; xmlDispose(&xml); // Finished with XML data, now we can write data out. char fileOut[FILENAME_MAX]; fileNormalizeSlashes(args[2]); sprintf(fileOut, "%s.hpp", args[2]); fileMkdirp(fileOut); FILE *fout = fopen(fileOut, "wb"); if(fout == NULL) { std::cout << "Failed to open output file." << std::endl; return 1; } // Buffer out data. const char *bufferOutStr = bufferOut.c_str(); fwrite(bufferOutStr, sizeof(char), strlen(bufferOutStr), fout); fclose(fout); std::cout << "Generated UI " << fileOut << std::endl; // Cleanup return 0; }