fix: finish enums migration
This commit is contained in:
@@ -1,168 +0,0 @@
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
/**
|
||||
* Write enum declarations and accessible value into CSharp format.
|
||||
*/
|
||||
public class CSharpWriter {
|
||||
|
||||
// =========== C# Enum Declaration Writer ===========
|
||||
|
||||
/**
|
||||
* Get corredponding C# underlying type of given enum.
|
||||
* <p>
|
||||
* This is C# specific function.
|
||||
*
|
||||
* @param canUnsigned The parameter stored in Enum_t that indicate whether this
|
||||
* enum can use unsigned int as its underlying type.
|
||||
* @return The string form of its underlying type.
|
||||
*/
|
||||
private static String getEnumUnderlyingType(boolean canUnsigned) {
|
||||
return canUnsigned ? "uint" : "int";
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal real enum declaration writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
|
||||
throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.CSharp);
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum comment
|
||||
indent.briefComment(benum.mEnumComment);
|
||||
|
||||
// write enum start
|
||||
// write flasg attribute if it is
|
||||
if (benum.mUseFlags) {
|
||||
indent.puts("[Flags]");
|
||||
}
|
||||
indent.printf("public enum %s : %s {", benum.mEnumName, getEnumUnderlyingType(benum.mCanUnsigned));
|
||||
indent.inc();
|
||||
|
||||
// write enum entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
// write entry self
|
||||
if (enumEntry.mEntryValue == null) {
|
||||
indent.printf("%s,", enumEntry.mEntryName);
|
||||
} else {
|
||||
indent.printf("%s = %s,", enumEntry.mEntryName, enumEntry.mEntryValue);
|
||||
}
|
||||
|
||||
// write entry comment after member
|
||||
indent.afterMemberComment(enumEntry.mEntryComment);
|
||||
}
|
||||
|
||||
// write enum tail
|
||||
indent.dec();
|
||||
indent.puts("}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum declaration collection into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum declaration collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, prog);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum declaration into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum declaration collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
|
||||
// create collection from single enum
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, col);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// =========== C# Enum Accessible Value Writer ===========
|
||||
|
||||
/**
|
||||
* Internal real enum accessible value writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
|
||||
throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.CSharp);
|
||||
// write enum collections
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum desc header
|
||||
indent.printf(
|
||||
"public static readonly System.Collections.Generic.Dictionary<%s, string> %s = new System.Collections.Generic.Dictionary<%s, string>() {",
|
||||
benum.mEnumName, benum.mEnumName, benum.mEnumName);
|
||||
indent.inc();
|
||||
|
||||
// write enum desc entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
indent.printf("{ %s.%s, \"%s\" },", benum.mEnumName, enumEntry.mEntryName, enumEntry.mEntryName);
|
||||
}
|
||||
|
||||
// write enum tail
|
||||
indent.dec();
|
||||
indent.puts("};");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum accessible value collection into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum accessible value collection
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, prog);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum accessible value into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum accessible value collection
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum) throws Exception {
|
||||
// create a collection with single enum.
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, col);
|
||||
fs.close();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -48,11 +47,35 @@ public class CommonHelper {
|
||||
|
||||
switch (comment.getType()) {
|
||||
case CKGeneralLexer.CKGENERAL_LINE_COMMENT: {
|
||||
return removeStars(comment.getText().substring(2));
|
||||
// For line comment, we start to remove "//" prefix first
|
||||
String slashRemoved = comment.getText().substring(2);
|
||||
// Then remove successive starts
|
||||
String starsRemoved = removeSeperator(slashRemoved);
|
||||
// Do a strip for it.
|
||||
String stripped = starsRemoved.strip();
|
||||
// Then remove EOL to avoid unexpected line break.
|
||||
String eolRemoved = removeEol(stripped);
|
||||
// Okey
|
||||
return eolRemoved;
|
||||
}
|
||||
case CKGeneralLexer.CKGENERAL_BLOCK_COMMENT: {
|
||||
String cache = comment.getText();
|
||||
return removeStars(cache.substring(2, cache.length() - 4));
|
||||
// For block comment, we first cut "/*" head and "*/" tail.
|
||||
String blockComment = comment.getText();
|
||||
String slashRemoved = blockComment.substring(2, blockComment.length() - 4);
|
||||
|
||||
// Then we break it at line breaker and process each line one by one.
|
||||
String beautyComment = slashRemoved.lines().map((String line) -> {
|
||||
// Remove successive starts
|
||||
String starsRemoved = removeSeperator(line);
|
||||
// Do a strip for it.
|
||||
String stripped = starsRemoved.strip();
|
||||
// Then remove EOL to avoid unexpected line break.
|
||||
String eolRemoved = removeEol(stripped);
|
||||
// Line process is okey now
|
||||
return eolRemoved;
|
||||
}).collect(Collectors.joining("\n")); // Then re-join with fresh line breaker
|
||||
// Then return
|
||||
return beautyComment;
|
||||
}
|
||||
default:
|
||||
return comment.getText();
|
||||
@@ -73,9 +96,8 @@ public class CommonHelper {
|
||||
return null;
|
||||
|
||||
return tokens.stream().map(value -> {
|
||||
String text = cutComment(value).strip();
|
||||
return text + " ";
|
||||
}).collect(Collectors.joining(""));
|
||||
return cutComment(value);
|
||||
}).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
// =========== Number Operations ===========
|
||||
@@ -92,46 +114,13 @@ public class CommonHelper {
|
||||
|
||||
/**
|
||||
* Check whether Altlr captured CKGENERAL_NUM is a hex number.
|
||||
*
|
||||
*
|
||||
* @param numstr The captured number.
|
||||
* @return true if it is hex number.
|
||||
*/
|
||||
public static boolean isHexNumber(String numstr) {
|
||||
return numstr.startsWith("0x") || numstr.startsWith("0X");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert accepted string into Python cupported format.
|
||||
*
|
||||
* It actually just remove trail "UL".
|
||||
*
|
||||
* @param numstr The captured number.
|
||||
* @return The Python style number string.
|
||||
*/
|
||||
public static String convertToPythonNumber(String numstr) {
|
||||
return numstr.replaceFirst("[ulUL]+$", "");
|
||||
}
|
||||
|
||||
// =========== Parts ===========
|
||||
|
||||
enum CKParts {
|
||||
CK2, VxMath
|
||||
}
|
||||
|
||||
enum LangType {
|
||||
Cpp, Python, CSharp
|
||||
}
|
||||
|
||||
public static String getCKPartsNamespace(CKParts parts) {
|
||||
switch (parts) {
|
||||
case CK2:
|
||||
return "CK2";
|
||||
case VxMath:
|
||||
return "VxMath";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + parts);
|
||||
}
|
||||
}
|
||||
|
||||
// =========== File Operations ===========
|
||||
|
||||
@@ -182,80 +171,25 @@ public class CommonHelper {
|
||||
|
||||
// =========== String Process ===========
|
||||
|
||||
/**
|
||||
* Escape String
|
||||
*
|
||||
* Escape all characters which are invalid in string quote.
|
||||
*
|
||||
* @param strl The string need to be escaped.
|
||||
* @return The escaped string.
|
||||
* @see removeEol
|
||||
*/
|
||||
public static String escapeString(String strl) {
|
||||
return strl.replace("\\", "\\\\").replace("\t", "\\t").replace("\b", "\\b").replace("\n", "\\n")
|
||||
.replace("\r", "\\r").replace("\f", "\\f").replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all EOL (End of Line) characters.
|
||||
*
|
||||
* @param strl The string need to be processed.
|
||||
* @return The string eliminated all EOL.
|
||||
* @see escapeString
|
||||
*/
|
||||
public static String removeEol(String strl) {
|
||||
return strl.replace("\n", "").replace("\r", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove redundent star '*' (at least 5 continuous star chars)
|
||||
* Remove seperator bar consisted by '*' or '-' (at least 5 successive chars)
|
||||
*
|
||||
* @param cmt The string provided.
|
||||
* @return The string processed.
|
||||
*/
|
||||
public static String removeStars(String cmt) {
|
||||
public static String removeSeperator(String cmt) {
|
||||
// only remove at least 5 continuous star chars.
|
||||
return cmt.replaceAll("\\*{5,}", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get indent char by style
|
||||
*
|
||||
* @param use_tab Whether indent use Tab, not Space.
|
||||
* @return The indent chars
|
||||
*/
|
||||
public static String getIndentString(boolean use_tab) {
|
||||
if (use_tab)
|
||||
return "\t";
|
||||
else
|
||||
return " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-indent a block of string
|
||||
*
|
||||
* This function will first split block string by line. Then remove all indent
|
||||
* (strip Tab and Space). At last, re-indent and join each line
|
||||
*
|
||||
* @param block_str The string provided.
|
||||
* @param use_tab Use Tab, not Space as indent chars.
|
||||
* @param indent_level The level of indent, started by 0.
|
||||
* @return The re-indent string
|
||||
*/
|
||||
public static String reindentBlockString(String block_str, boolean use_tab, int indent_level) {
|
||||
// pre-create indent string
|
||||
String indentChars = getIndentString(use_tab);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < indent_level; ++i) {
|
||||
sb.append(indentChars);
|
||||
}
|
||||
String indents = sb.toString();
|
||||
|
||||
// split line
|
||||
return block_str.lines().map((String line) -> {
|
||||
// strip space and tab, then re-indent it.
|
||||
return indents + line.trim();
|
||||
}).collect(Collectors.joining(System.lineSeparator())); // then join with new line breaker and return.
|
||||
return cmt.replaceAll("[\\*\\-]{5,}", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Write enum declarations and accessible value into C++ format.
|
||||
*/
|
||||
public class CppWriter {
|
||||
|
||||
// =========== C++ Enum Declaration Writer ===========
|
||||
|
||||
/**
|
||||
* Get corredponding C++ underlying type of given enum.
|
||||
* <p>
|
||||
* This is C++ specific function.
|
||||
*
|
||||
* @param canUnsigned The parameter stored in Enum_t that indicate whether this
|
||||
* enum can use unsigned int as its underlying type.
|
||||
* @return The string form of its underlying type.
|
||||
*/
|
||||
private static String getEnumUnderlyingType(boolean canUnsigned) {
|
||||
return canUnsigned ? "CKDWORD" : "CKINT";
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal real enum declarations writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
|
||||
throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum comment
|
||||
indent.briefComment(benum.mEnumComment);
|
||||
|
||||
// write enum start
|
||||
indent.printf("enum class %s : %s {", benum.mEnumName, getEnumUnderlyingType(benum.mCanUnsigned));
|
||||
indent.inc();
|
||||
|
||||
// write enum entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
// write entry self
|
||||
if (enumEntry.mEntryValue == null) {
|
||||
indent.printf("%s,", enumEntry.mEntryName);
|
||||
} else {
|
||||
indent.printf("%s = %s,", enumEntry.mEntryName, enumEntry.mEntryValue);
|
||||
}
|
||||
|
||||
// write entry comment after member
|
||||
indent.afterMemberComment(enumEntry.mEntryComment);
|
||||
}
|
||||
|
||||
// write enum tail
|
||||
indent.dec();
|
||||
indent.puts("};");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum collection into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, prog);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
|
||||
// create an collection from single enum declaration
|
||||
// for suit the argument requirement of real writer.
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, col);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// =========== C++ Enum Accessible Value Writer ===========
|
||||
|
||||
/**
|
||||
* Internal real enum collection accessible value writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @param parts The part of these enum declarations. It will indicate the
|
||||
* namespace where find given enum collection.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog,
|
||||
CommonHelper.CKParts parts) throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
|
||||
|
||||
// write type defination (just to let user know what the type is)
|
||||
indent.puts("// struct GeneralReflection { const char8_t* mName; };");
|
||||
indent.puts("// template<typename _Ty, std::enable_if_t<std::is_enum_v<_Ty>, int> = 0>");
|
||||
indent.puts("// using GeneralReflectionArray = std::vector<std::pair<_Ty, GeneralReflection>>;");
|
||||
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
|
||||
// write declarations
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
indent.printf("extern const GeneralReflectionArray<LibCmo::%s::%s> %s;",
|
||||
CommonHelper.getCKPartsNamespace(parts), benum.mEnumName, benum.mEnumName);
|
||||
}
|
||||
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
|
||||
// write implements
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum desc header
|
||||
indent.printf("const GeneralReflectionArray<LibCmo::%s::%s> %s {", CommonHelper.getCKPartsNamespace(parts),
|
||||
benum.mEnumName, benum.mEnumName);
|
||||
indent.inc();
|
||||
|
||||
// write enum desc entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
indent.printf("{ LibCmo::%s::%s::%s, { u8\"%s\" } },", CommonHelper.getCKPartsNamespace(parts),
|
||||
benum.mEnumName, enumEntry.mEntryName, enumEntry.mEntryName);
|
||||
}
|
||||
|
||||
// write enum tail
|
||||
indent.dec();
|
||||
indent.puts("};");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum collection accessible value into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum collection accessible value
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @param parts The part of these enum declarations.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog, CommonHelper.CKParts parts)
|
||||
throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, prog, parts);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum accessible value into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum collection accessible value
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @param parts The part of these enum declarations.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum, CommonHelper.CKParts parts)
|
||||
throws Exception {
|
||||
// create a enum collection to fulfill the requirement of internal writer.
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, col, parts);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// =========== Specialized C++ Enum Accessible Value Writer ===========
|
||||
// Only accessible value part of CERROR and CK_CLASSID need to be specialized.
|
||||
// The enum self do not need special treat. Just write them normally.
|
||||
|
||||
/**
|
||||
* Specialized CKERROR accessible value writer.
|
||||
* <p>
|
||||
* The declaration of CKERROR do not need special treat. It is okey to use
|
||||
* common writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param errors The {@linkplain EnumsHelper.BEnum} instance storing CKERROR.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeCkErrorAccVal(String filename, EnumsHelper.BEnum errors) throws Exception {
|
||||
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
|
||||
|
||||
// write type defination (just to let user know what the type is)
|
||||
indent.puts("// struct CkErrorReflection { const char8_t* mName; const char8_t* mDescription; };");
|
||||
indent.puts("// using CkErrorReflectionArray = std::vector<std::pair<LibCmo::CK2::CKERROR, CkErrorReflection>>;");
|
||||
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
|
||||
// write implementation
|
||||
indent.puts("const CkErrorReflectionArray CKERROR {");
|
||||
indent.inc();
|
||||
for (EnumsHelper.BEnumEntry entry : errors.mEntries) {
|
||||
String comment = CommonHelper.escapeString(entry.mEntryComment);
|
||||
if (comment == null)
|
||||
comment = "";
|
||||
|
||||
indent.printf("{ LibCmo::CK2::CKERROR::%s, { u8\"%s\", u8\"%s\" } },", entry.mEntryName, entry.mEntryName,
|
||||
comment);
|
||||
}
|
||||
indent.dec();
|
||||
indent.puts("};");
|
||||
|
||||
writer.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized CK_CLASSID accessible value writer.
|
||||
* <p>
|
||||
* The declaration of CK_CLASSID do not need special treat. It is okey to use
|
||||
* common writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param classids The {@linkplain EnumsHelper.BEnum} instance storing
|
||||
* CK_CLASSID.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeCkClassidAccVal(String filename, EnumsHelper.BEnum classids) throws Exception {
|
||||
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Cpp);
|
||||
|
||||
// write type defination (just to let user know what the type is)
|
||||
indent.puts("// struct CkClassidReflection { std::vector<const char8_t*> mHierarchy; };");
|
||||
indent.puts("// using CkClassidReflectionArray = std::vector<std::pair<LibCmo::CK2::CK_CLASSID, CkClassidReflection>>;");
|
||||
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
indent.puts("");
|
||||
|
||||
indent.puts("const CkClassidReflectionArray CK_CLASSID {");
|
||||
indent.inc();
|
||||
for (EnumsHelper.BEnumEntry entry : classids.mEntries) {
|
||||
EnumsHelper.BHierarchyEnumEntry specialized = (EnumsHelper.BHierarchyEnumEntry) entry;
|
||||
|
||||
String hierarchy = specialized.mHierarchy.stream().map(value -> value.mEntryName)
|
||||
.collect(Collectors.joining("\", u8\""));
|
||||
indent.printf("{ LibCmo::CK2::CK_CLASSID::%s, { { u8\"%s\" } } },", entry.mEntryName, hierarchy);
|
||||
}
|
||||
indent.dec();
|
||||
indent.puts("};");
|
||||
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
public class IndentHelper {
|
||||
public IndentHelper(OutputStreamWriter writer, CommonHelper.LangType lang_type) {
|
||||
mIndent = 0;
|
||||
mLangType = lang_type;
|
||||
mWriter = writer;
|
||||
|
||||
// set indent chars
|
||||
switch (mLangType) {
|
||||
case Cpp:
|
||||
case CSharp:
|
||||
mIndentChars = CommonHelper.getIndentString(true);
|
||||
break;
|
||||
case Python:
|
||||
mIndentChars = CommonHelper.getIndentString(false);
|
||||
break;
|
||||
default:
|
||||
mIndentChars = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private int mIndent;
|
||||
private CommonHelper.LangType mLangType;
|
||||
private String mIndentChars;
|
||||
private OutputStreamWriter mWriter;
|
||||
|
||||
private void rawIndent() throws Exception {
|
||||
for (int i = 0; i < mIndent; ++i) {
|
||||
mWriter.write(mIndentChars);
|
||||
}
|
||||
}
|
||||
|
||||
public void inc() {
|
||||
++mIndent;
|
||||
}
|
||||
|
||||
public void dec() {
|
||||
--mIndent;
|
||||
}
|
||||
|
||||
public void indent() throws Exception {
|
||||
mWriter.write(System.lineSeparator());
|
||||
rawIndent();
|
||||
}
|
||||
|
||||
public void puts(String data) throws Exception {
|
||||
indent();
|
||||
mWriter.write(data);
|
||||
}
|
||||
|
||||
public void printf(String fmt, Object... args) throws Exception {
|
||||
indent();
|
||||
mWriter.write(String.format(fmt, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write standard Doxygen document comment.
|
||||
* <p>
|
||||
* Usually be called before writing content.
|
||||
*
|
||||
* @param comment
|
||||
* @throws Exception
|
||||
*/
|
||||
public void briefComment(String comment) throws Exception {
|
||||
if (comment == null)
|
||||
return;
|
||||
|
||||
switch (mLangType) {
|
||||
case Cpp:
|
||||
case CSharp:
|
||||
puts("/**");
|
||||
|
||||
mWriter.write(System.lineSeparator());
|
||||
mWriter.write(CommonHelper.reindentBlockString(comment, true, mIndent));
|
||||
|
||||
puts(" */");
|
||||
break;
|
||||
case Python:
|
||||
puts("\"\"\"!");
|
||||
|
||||
mWriter.write(System.lineSeparator());
|
||||
mWriter.write(CommonHelper.reindentBlockString(comment, false, mIndent));
|
||||
|
||||
puts("\"\"\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write suffix style Doxygen document comment.
|
||||
* <p>
|
||||
* Usually be called after writing content.
|
||||
*
|
||||
* @param comment
|
||||
* @throws Exception
|
||||
*/
|
||||
public void afterMemberComment(String comment) throws Exception {
|
||||
if (comment == null)
|
||||
return;
|
||||
|
||||
mWriter.write(mIndentChars);
|
||||
switch (mLangType) {
|
||||
case Cpp:
|
||||
case CSharp:
|
||||
mWriter.write(String.format("/**< %s */", CommonHelper.removeEol(comment)));
|
||||
break;
|
||||
case Python:
|
||||
mWriter.write(String.format("##< %s", CommonHelper.removeEol(comment)));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class JsonWriter {
|
||||
// Export hierarchy if possible
|
||||
if (enumEntry instanceof EnumsHelper.BHierarchyEnumEntry hierarchyEnumEntry) {
|
||||
// We only export name in hierarchy.
|
||||
// Otherwise we may cause recursive calling.
|
||||
// Otherwise, we may cause recursive calling.
|
||||
JsonArray entries = new JsonArray();
|
||||
for (EnumsHelper.BHierarchyEnumEntry subEntry : hierarchyEnumEntry.mHierarchy) {
|
||||
entries.add(subEntry.mEntryName);
|
||||
@@ -54,7 +54,7 @@ public class JsonWriter {
|
||||
private static void writeJson(String filename, EnumsHelper.BEnumCollection enumCollection) throws Exception {
|
||||
OutputStreamWriter writer = CommonHelper.openOutputFile(filename);
|
||||
//Gson gsonInstance = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
Gson gsonInstance = new GsonBuilder().disableHtmlEscaping().create();
|
||||
Gson gsonInstance = new GsonBuilder().serializeNulls().disableHtmlEscaping().create();
|
||||
writer.write(gsonInstance.toJson(writeBEnumCollection(enumCollection)));
|
||||
writer.close();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ public class MainRunner {
|
||||
* <p>
|
||||
* This function will assume that given file only contain C++ "#define" syntax.
|
||||
* After reading it, it will re-organize it as an enum and return. This only is
|
||||
* used by CKERROR now. But it suit for more scenarios if there are something
|
||||
* like CKERROR in future.
|
||||
* used by CKERROR now. But it is suit for more scenarios if there are something
|
||||
* like CKERROR in the future.
|
||||
*
|
||||
* @param filename The file name relative to input directory for reading.
|
||||
* @param assignedEnumName The desired name of organized enum instance.
|
||||
@@ -106,89 +106,35 @@ public class MainRunner {
|
||||
// =========== CKERROR ===========
|
||||
EnumsHelper.BEnum ckerror = organiseDefines("CKERROR.txt", "CKERROR");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CKERROR.json"), ckerror);
|
||||
// CppWriter.writeEnum("CKERROR.hpp", ckerror);
|
||||
// PythonWriter.writeEnum("CKERROR.py", ckerror);
|
||||
// CSharpWriter.writeEnum("CKERROR.cs", ckerror);
|
||||
// CppWriter.writeCkErrorAccVal("CKERROR.AccVal.hpp", ckerror);
|
||||
// PythonWriter.writeAccVal("CKERROR.AccVal.py", ckerror);
|
||||
// CSharpWriter.writeAccVal("CKERROR.AccVal.cs", ckerror);
|
||||
|
||||
// =========== CK_CLASSID ===========
|
||||
EnumsHelper.BEnum classid = organiseClassid("CK_CLASSID.txt");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_CLASSID.json"), classid);
|
||||
// CppWriter.writeEnum("CK_CLASSID.hpp", classid);
|
||||
// PythonWriter.writeEnum("CK_CLASSID.py", classid);
|
||||
// CSharpWriter.writeEnum("CK_CLASSID.cs", classid);
|
||||
// CppWriter.writeCkClassidAccVal("CK_CLASSID.AccVal.hpp", classid);
|
||||
// PythonWriter.writeAccVal("CK_CLASSID.AccVal.py", classid);
|
||||
|
||||
// =========== Define2 ===========
|
||||
// Define2 do not need annotation output.
|
||||
// Because they are CKStateChunk used value which are not exposed to outside.
|
||||
EnumsHelper.BEnumCollection def2 = getEnumsCollection("Defines2.txt");
|
||||
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("Defines2.json"), def2);
|
||||
// CppWriter.writeEnums("Defines2.hpp", def2);
|
||||
// PythonWriter.writeEnums("Defines2.py", def2);
|
||||
// CSharpWriter.writeEnums("Defines2.cs", def2);
|
||||
|
||||
// =========== Combined enums ===========
|
||||
EnumsHelper.BEnumCollection ck2Enums = getEnumsCollection("CKEnums.txt"),
|
||||
vxEnums = getEnumsCollection("VxEnums.txt");
|
||||
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("CKEnums.json"), ck2Enums);
|
||||
JsonWriter.writeEnums(CommonHelper.getOutputFilePath("VxEnums.json"), vxEnums);
|
||||
|
||||
// CppWriter.writeEnums("CKEnums.hpp", ck2Enums);
|
||||
// PythonWriter.writeEnums("CKEnums.py", ck2Enums);
|
||||
// CSharpWriter.writeEnums("CKEnums.cs", ck2Enums);
|
||||
// CppWriter.writeAccVals("CKEnums.AccVal.hpp", ck2Enums, CommonHelper.CKParts.CK2);
|
||||
// PythonWriter.writeAccVals("CKEnums.AccVal.py", ck2Enums);
|
||||
// CSharpWriter.writeAccVals("CKEnums.AccVal.cs", ck2Enums);
|
||||
|
||||
// CppWriter.writeEnums("VxEnums.hpp", vxEnums);
|
||||
// PythonWriter.writeEnums("VxEnums.py", vxEnums);
|
||||
// CSharpWriter.writeEnums("VxEnums.cs", vxEnums);
|
||||
// CppWriter.writeAccVals("VxEnums.AccVal.hpp", vxEnums, CommonHelper.CKParts.VxMath);
|
||||
// PythonWriter.writeAccVals("VxEnums.AccVal.py", vxEnums);
|
||||
// CSharpWriter.writeAccVals("VxEnums.AccVal.cs", vxEnums);
|
||||
|
||||
// =========== Single enums ===========
|
||||
EnumsHelper.BEnum single;
|
||||
|
||||
single = organiseDefines("CK_STATECHUNK_CHUNKVERSION.txt", "CK_STATECHUNK_CHUNKVERSION");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_STATECHUNK_CHUNKVERSION.json"), single);
|
||||
// CppWriter.writeEnum("CK_STATECHUNK_CHUNKVERSION.hpp", single);
|
||||
// PythonWriter.writeEnum("CK_STATECHUNK_CHUNKVERSION.py", single);
|
||||
// CSharpWriter.writeEnum("CK_STATECHUNK_CHUNKVERSION.cs", single);
|
||||
// CppWriter.writeAccVal("CK_STATECHUNK_CHUNKVERSION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
|
||||
// PythonWriter.writeAccVal("CK_STATECHUNK_CHUNKVERSION.AccVal.py", single);
|
||||
// CSharpWriter.writeAccVal("CK_STATECHUNK_CHUNKVERSION.AccVal.cs", single);
|
||||
|
||||
single = organiseDefines("CK_STATECHUNK_DATAVERSION.txt", "CK_STATECHUNK_DATAVERSION");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_STATECHUNK_DATAVERSION.json"), single);
|
||||
// CppWriter.writeEnum("CK_STATECHUNK_DATAVERSION.hpp", single);
|
||||
// PythonWriter.writeEnum("CK_STATECHUNK_DATAVERSION.py", single);
|
||||
// CSharpWriter.writeEnum("CK_STATECHUNK_DATAVERSION.cs", single);
|
||||
// CppWriter.writeAccVal("CK_STATECHUNK_DATAVERSION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
|
||||
// PythonWriter.writeAccVal("CK_STATECHUNK_DATAVERSION.AccVal.py", single);
|
||||
// CSharpWriter.writeAccVal("CK_STATECHUNK_DATAVERSION.AccVal.cs", single);
|
||||
|
||||
single = organiseDefines("CK_BITMAPDATA_FLAGS.txt", "CK_BITMAPDATA_FLAGS");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_BITMAPDATA_FLAGS.json"), single);
|
||||
// CppWriter.writeEnum("CK_BITMAPDATA_FLAGS.hpp", single);
|
||||
// PythonWriter.writeEnum("CK_BITMAPDATA_FLAGS.py", single);
|
||||
// CSharpWriter.writeEnum("CK_BITMAPDATA_FLAGS.cs", single);
|
||||
// CppWriter.writeAccVal("CK_BITMAPDATA_FLAGS.AccVal.hpp", single, CommonHelper.CKParts.CK2);
|
||||
// PythonWriter.writeAccVal("CK_BITMAPDATA_FLAGS.AccVal.py", single);
|
||||
// CSharpWriter.writeAccVal("CK_BITMAPDATA_FLAGS.AccVal.cs", single);
|
||||
|
||||
single = organiseDefines("CK_CAMERA_PROJECTION.txt", "CK_CAMERA_PROJECTION");
|
||||
JsonWriter.writeEnum(CommonHelper.getOutputFilePath("CK_CAMERA_PROJECTION.json"), single);
|
||||
// CppWriter.writeEnum("CK_CAMERA_PROJECTION.hpp", single);
|
||||
// PythonWriter.writeEnum("CK_CAMERA_PROJECTION.py", single);
|
||||
// CSharpWriter.writeEnum("CK_CAMERA_PROJECTION.cs", single);
|
||||
// CppWriter.writeAccVal("CK_CAMERA_PROJECTION.AccVal.hpp", single, CommonHelper.CKParts.CK2);
|
||||
// PythonWriter.writeAccVal("CK_CAMERA_PROJECTION.AccVal.py", single);
|
||||
// CSharpWriter.writeAccVal("CK_CAMERA_PROJECTION.AccVal.cs", single);
|
||||
|
||||
// print message.
|
||||
System.out.println("Done");
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Write enum declarations and accessible value into Python format.
|
||||
*/
|
||||
public class PythonWriter {
|
||||
|
||||
// =========== Python Enum Declaration Writer ===========
|
||||
|
||||
/**
|
||||
* Internal real enum declaration writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteEnums(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
|
||||
throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Python);
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum start
|
||||
indent.printf("class %s(enum.IntEnum):", benum.mEnumName);
|
||||
indent.inc();
|
||||
|
||||
// write enum comment
|
||||
indent.briefComment(benum.mEnumComment);
|
||||
|
||||
// write enum entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
// write entry self
|
||||
if (enumEntry.mEntryValue == null) {
|
||||
indent.printf("%s = auto()", enumEntry.mEntryName);
|
||||
} else {
|
||||
indent.printf("%s = %s", enumEntry.mEntryName,
|
||||
CommonHelper.convertToPythonNumber(enumEntry.mEntryValue));
|
||||
}
|
||||
|
||||
// write entry comment after member
|
||||
indent.afterMemberComment(enumEntry.mEntryComment);
|
||||
}
|
||||
|
||||
// enum tail
|
||||
indent.dec();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum declaration collection into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum declaration collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnums(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, prog);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum declaration into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum declaration collection writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeEnum(String filename, EnumsHelper.BEnum _enum) throws Exception {
|
||||
// create collection from single enum
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteEnums(fs, col);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// =========== Python Enum Accessible Value Writer ===========
|
||||
|
||||
/**
|
||||
* Try generate human readable name from enum entry name.
|
||||
* <p>
|
||||
* This function is only served for Python code generation.
|
||||
* <p>
|
||||
* As you noticed, almost entries of CK enums are fully capital and splitted by
|
||||
* underline. This is really not good for human reading, especially those who
|
||||
* are not programmer. So this function will try give these programmer-oriented
|
||||
* entry name a human readable name as its display name. However, this extract
|
||||
* method is not perfect. It simply do some split and replacement so the
|
||||
* generated content may still not good for reader.
|
||||
*
|
||||
* @param entry_name The name of enum entry
|
||||
* @return A human readable entry name. No guaranteen that return value is must
|
||||
* human readable.
|
||||
*/
|
||||
private static String extractHumanReadableEntryName(String entry_name) {
|
||||
// remove first part (any content before underline '_')
|
||||
entry_name = entry_name.replaceFirst("^[a-zA-Z0-9]+_", "");
|
||||
|
||||
// lower all chars except first char
|
||||
if (entry_name.length() < 1)
|
||||
return entry_name;
|
||||
else
|
||||
return entry_name.substring(0, 1) + entry_name.substring(1).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal real enum accessible value writer.
|
||||
*
|
||||
* @param writer {@linkplain java.io.OutputStreamWriter} instance for writing.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void internalWriteAccVals(OutputStreamWriter writer, EnumsHelper.BEnumCollection prog)
|
||||
throws Exception {
|
||||
IndentHelper indent = new IndentHelper(writer, CommonHelper.LangType.Python);
|
||||
|
||||
// write implements
|
||||
for (EnumsHelper.BEnum benum : prog.mEnums) {
|
||||
// write enum desc header
|
||||
indent.printf("g_Annotation_%s: dict[int, EnumAnnotation] = {", benum.mEnumName);
|
||||
indent.inc();
|
||||
|
||||
// write enum desc entries
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
String comment = "";
|
||||
if (enumEntry.mEntryComment != null) {
|
||||
comment = CommonHelper.escapeString(enumEntry.mEntryComment);
|
||||
}
|
||||
|
||||
indent.printf("%s.%s.value: EnumAnnotation(\"%s\", \"%s\"),", benum.mEnumName, enumEntry.mEntryName,
|
||||
extractHumanReadableEntryName(enumEntry.mEntryName), comment);
|
||||
}
|
||||
|
||||
// write enum tail
|
||||
indent.dec();
|
||||
indent.puts("}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an enum accessible value collection into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum accessible value collection
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param prog {@linkplain EnumsHelper.BEnumCollection} instance for
|
||||
* writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVals(String filename, EnumsHelper.BEnumCollection prog) throws Exception {
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, prog);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single enum accessible value into given file.
|
||||
* <p>
|
||||
* Actually this is a wrapper of internal enum accessible value collection
|
||||
* writer.
|
||||
*
|
||||
* @param filename The name of written file.
|
||||
* @param _enum {@linkplain EnumsHelper.BEnum} instance for writing.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void writeAccVal(String filename, EnumsHelper.BEnum _enum) throws Exception {
|
||||
// create a collection with single enum.
|
||||
EnumsHelper.BEnumCollection col = new EnumsHelper.BEnumCollection();
|
||||
col.mEnums.add(_enum);
|
||||
// open file and write
|
||||
OutputStreamWriter fs = CommonHelper.openOutputFile(filename);
|
||||
internalWriteAccVals(fs, col);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user