2023-02-05 19:15:06 -08:00

218 lines
5.7 KiB
C++

/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
extern "C" {
#include "../../utils/common.h"
#include "../../utils/file.h"
#include "../../utils/csv.h"
#include "../../utils/xml.h"
#include <memory.h>
}
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
struct LanguageString {
std::string key;
std::string value;
};
int32_t parseString(
xml_t *stringNode,
std::string key,
std::map<std::string,std::vector<struct LanguageString>> *strings
) {
auto attrLang = xmlGetAttributeByName(stringNode, "lang");
if(attrLang == -1) {
std::cout << "String is missing lang parameter." << std::endl;
return -1;
}
std::string lang(stringNode->attributeDatas[attrLang]);
struct LanguageString str;
str.key = key;
str.value = std::string(stringNode->value);
auto existing = (*strings).find(lang);
if(existing == (*strings).end()) {
(*strings).insert(std::make_pair(lang, std::vector<struct LanguageString>()));
}
(*strings)[lang].push_back(str);
return 0;
}
int32_t parseGroup(
xml_t *groupNode,
std::string key,
std::map<std::string,std::vector<struct LanguageString>> *strings
) {
int32_t ret;
auto attrKey = xmlGetAttributeByName(groupNode, "key");
if(attrKey == -1) {
std::cout << "Group node is missing key" << std::endl;
return 1;
}
if(key.size() > 0) key += ".";
key += std::string(groupNode->attributeDatas[attrKey]);
for(int32_t i = 0; i < groupNode->childrenCount; i++) {
auto c = groupNode->children + i;
if(std::string(c->node) == "string") {
ret = parseString(c, key, strings);
if(ret != 0) return ret;
} else if(std::string(c->node) == "group") {
ret = parseGroup(c, key, strings);
if(ret != 0) return ret;
}
}
return 0;
}
int main(int argc, char *argv[]) {
if(argc != 3) {
std::cout << "Invalid number of arguments provided to language gen!" << std::endl;
return 1;
}
char *fileInName = argv[1];
fileNormalizeSlashes(fileInName);
FILE *fileIn = fopen(fileInName, "rb");
if(fileIn == NULL) {
std::cout << "Failed to open input file " << fileInName << std::endl;
return 1;
}
auto size = assetReadString(fileIn, NULL);
char *buffer = (char *)malloc(sizeof(char) * size);
if(buffer == NULL) {
std::cout << "Failed to allocate memory for locale string XML" << std::endl;
fclose(fileIn);
return 1;
}
assetReadString(fileIn, buffer);
fclose(fileIn);
xml_t xml;
xmlLoad(&xml, buffer);
free(buffer);
// Begin parsing. Start by looking for the <language> tags
std::vector<std::string> languages;
for(int32_t i = 0; i < xml.childrenCount; i++) {
auto c = xml.children + i;
if(std::string(c->node) != "language") continue;
auto attrName = xmlGetAttributeByName(c, "name");
if(attrName == -1) {
std::cout << "Missing name param on language node" << std::endl;
xmlDispose(&xml);
return 1;
}
languages.push_back(std::string(c->attributeDatas[attrName]));
}
// Now begin actually parsing
std::map<std::string, std::vector<struct LanguageString>> strings;
for(int32_t i = 0; i < xml.childrenCount; i++) {
auto c = xml.children + i;
if(std::string(c->node) == "group") {
auto ret = parseGroup(c, "", &strings);
if(ret != 0) {
xmlDispose(&xml);
return ret;
}
} else if(std::string(c->node) == "string") {
std::cout << "String cannot be a root node" << std::endl;
xmlDispose(&xml);
return 1;
}
}
xmlDispose(&xml);
// Now we validate each lang has each key.
std::vector<std::string> keys;
auto it = strings.begin();
while(it != strings.end()) {
auto it2 = it->second.begin();
while(it2 != it->second.end()) {
auto key = it2->key;
auto exist = std::find(keys.begin(), keys.end(), key);
if(exist == keys.end()) {
keys.push_back(key);
}
it2++;
}
++it;
}
// Now we actually parse each string, validating as we go.
it = strings.begin();
while(it != strings.end()) {
std::vector<std::string> itKeys;
std::string bufferOut = "";
auto it2 = it->second.begin();
while(it2 != it->second.end()) {
auto l = *it2;
itKeys.push_back(l.key);
bufferOut += l.key + "|" + l.value + "|";
it2++;
}
std::string filenameOut(argv[2]);
filenameOut += "/language_" + it->first + ".language";
char *filenameOutC = (char *)malloc(sizeof(char) * (filenameOut.size() + 1));
if(filenameOutC == NULL) {
std::cout << "Failed to allocate filename memory" << std::endl;
return 1;
}
strcpy(filenameOutC, filenameOut.c_str());
fileNormalizeSlashes(filenameOutC);
fileMkdirp(filenameOutC);
FILE *fileOut = fopen(filenameOutC, "wb");
free(filenameOutC);
if(fileOut == NULL) {
std::cout << "Failed to create output file " << filenameOut << std::endl;
return 1;
}
const char *strOut = bufferOut.c_str();
fwrite(strOut, sizeof(char), strlen(strOut), fileOut);
fclose(fileOut);
auto it3 = keys.begin();
while(it3 != keys.end()) {
auto key = *it3;
auto inIt = std::find(itKeys.begin(), itKeys.end(), key);
if(inIt == itKeys.end()) {
std::cout << "Locale " << it->first << " missing key " << key << std::endl;
}
it3++;
}
if(itKeys.size() != keys.size()) {
std::cout << "Locale is missing some keys, see above" << std::endl;
return 1;
}
++it;
}
return 0;
}