// Source code is decompiled from a .class file using FernFlower decompiler. package re3lib; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; import java.io.IOException; import java.io.Writer; import java.util.ArrayDeque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public class DataTypeWriter { public HashSet blacklistedTypes; private static String[] INTEGRAL_TYPES = new String[] { "char", "short", "int", "long", "long long", "__int64", "float", "double", "long double", "void" }; private static String[] INTEGRAL_MODIFIERS = new String[] { "signed", "unsigned", "const", "static", "volatile", "mutable" }; private static String EOL = System.getProperty("line.separator"); private Set resolved; private Map resolvedTypeMap; private Set deferredCompositeDeclarations; private ArrayDeque deferredTypeFIFO; private Set deferredTypes; private int writerDepth; private Writer writer; private DataTypeManager dtm; private DataOrganization dataOrganization; private AnnotationHandler annotator; private boolean cppStyleComments; public DataTypeWriter(DataTypeManager dtm, Writer writer) throws IOException { this(dtm, writer, new DefaultAnnotationHandler()); } public DataTypeWriter(DataTypeManager dtm, Writer writer, boolean cppStyleComments) throws IOException { this(dtm, writer, new DefaultAnnotationHandler(), cppStyleComments); } public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator) throws IOException { this(dtm, writer, annotator, false); } public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator, boolean cppStyleComments) throws IOException { this.blacklistedTypes = new HashSet<>(); this.resolved = new HashSet(); this.resolvedTypeMap = new HashMap(); this.deferredCompositeDeclarations = new HashSet(); this.deferredTypeFIFO = new ArrayDeque(); this.deferredTypes = new HashSet(); this.writerDepth = 0; this.cppStyleComments = false; this.dtm = dtm; if (dtm != null) { this.dataOrganization = dtm.getDataOrganization(); } if (this.dataOrganization == null) { this.dataOrganization = DataOrganizationImpl.getDefaultOrganization(); } this.writer = writer; this.annotator = annotator; this.cppStyleComments = cppStyleComments; if (dtm != null) { // this.writeBuiltInDeclarations(dtm); } } private String comment(String text) { if (text == null) { return ""; } else { return this.cppStyleComments ? "// " + text : "/* " + text + " */"; } } public void write(DataTypeManager dataTypeManager, TaskMonitor monitor) throws IOException, CancelledException { this.write(dataTypeManager.getRootCategory(), monitor); } public void write(Category category, TaskMonitor monitor) throws IOException, CancelledException { DataType[] dataTypes = category.getDataTypes(); this.write(dataTypes, monitor); Category[] subCategories = category.getCategories(); Category[] var5 = subCategories; int var6 = subCategories.length; for (int var7 = 0; var7 < var6; ++var7) { Category subCategory = var5[var7]; if (monitor.isCancelled()) { return; } this.write(subCategory, monitor); } } public void write(DataType[] dataTypes, TaskMonitor monitor) throws IOException, CancelledException { monitor.initialize((long) dataTypes.length); int cnt = 0; DataType[] var4 = dataTypes; int var5 = dataTypes.length; for (int var6 = 0; var6 < var5; ++var6) { DataType dataType = var4[var6]; monitor.checkCancelled(); this.write(dataType, monitor); ++cnt; monitor.setProgress((long) cnt); } } public void write(List dataTypes, TaskMonitor monitor) throws IOException, CancelledException { this.write(dataTypes, monitor, true); } public void write(List dataTypes, TaskMonitor monitor, boolean throwExceptionOnInvalidType) throws IOException, CancelledException { monitor.initialize((long) dataTypes.size()); int cnt = 0; Iterator var5 = dataTypes.iterator(); while (var5.hasNext()) { DataType dataType = (DataType) var5.next(); monitor.checkCancelled(); this.write(dataType, monitor, throwExceptionOnInvalidType); ++cnt; monitor.setProgress((long) cnt); } } private void deferWrite(DataType dt) { if (!this.resolved.contains(dt) && !this.deferredTypes.contains(dt)) { this.deferredTypes.add(dt); this.deferredTypeFIFO.addLast(dt); } } void write(DataType dt, TaskMonitor monitor) throws IOException, CancelledException { this.doWrite(dt, monitor, true); } void write(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType) throws IOException, CancelledException { this.doWrite(dt, monitor, throwExceptionOnInvalidType); } private void doWrite(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType) throws IOException, CancelledException { if (dt != null) { if (blacklistedTypes.contains(dt.getDisplayName())) return; if (!(dt instanceof FunctionDefinition)) { if (dt instanceof FactoryDataType) { IllegalArgumentException iae = new IllegalArgumentException("Factory data types may not be written"); if (throwExceptionOnInvalidType) { throw iae; } Msg.error(this, "Factory data types may not be written - type: " + dt); } if (!(dt instanceof Pointer) && !(dt instanceof Array) && !(dt instanceof BitFieldDataType)) { dt = dt.clone(this.dtm); if (!this.resolved.contains(dt)) { this.resolved.add(dt); DataType resolvedType = (DataType) this.resolvedTypeMap.get(dt.getName()); if (resolvedType != null) { if (!resolvedType.isEquivalent(dt)) { if (dt instanceof TypeDef) { DataType baseType = ((TypeDef) dt).getBaseDataType(); if ((resolvedType instanceof Composite || resolvedType instanceof Enum) && baseType.isEquivalent(resolvedType)) { return; } } this.writer.write(EOL); Writer var10000 = this.writer; String var10002 = dt.getPathName(); var10000.write(this .comment("WARNING! conflicting data type names: " + var10002 + " - " + resolvedType.getPathName())); this.writer.write(EOL); this.writer.write(EOL); } } else { this.resolvedTypeMap.put(dt.getName(), dt); ++this.writerDepth; if (dt.equals(DataType.DEFAULT)) { this.writer.write("typedef unsigned char " + DataType.DEFAULT.getName() + ";"); this.writer.write(EOL); this.writer.write(EOL); } else if (dt instanceof Dynamic) { this.writeDynamicBuiltIn((Dynamic) dt, monitor); } else if (dt instanceof Structure) { Structure struct = (Structure) dt; this.writeCompositePreDeclaration(struct, monitor); this.deferredCompositeDeclarations.add(struct); } else if (dt instanceof Union) { Union union = (Union) dt; this.writeCompositePreDeclaration(union, monitor); this.deferredCompositeDeclarations.add(union); } else if (dt instanceof Enum) { this.writeEnum((Enum) dt, monitor); } else if (dt instanceof TypeDef) { this.writeTypeDef((TypeDef) dt, monitor); } else if (dt instanceof BuiltInDataType) { this.writeBuiltIn((BuiltInDataType) dt, monitor); } else if (!(dt instanceof BitFieldDataType)) { this.writer.write(EOL); this.writer.write(EOL); this.writer.write(this.comment("Unable to write datatype. Type unrecognized: " + dt.getClass())); this.writer.write(EOL); this.writer.write(EOL); } if (this.writerDepth == 1) { this.writeDeferredDeclarations(monitor); } --this.writerDepth; } } } else { this.write(this.getBaseDataType(dt), monitor); } } } } private void writeDeferredDeclarations(TaskMonitor monitor) throws IOException, CancelledException { while (!this.deferredTypes.isEmpty()) { DataType dt = (DataType) this.deferredTypeFIFO.removeFirst(); this.deferredTypes.remove(dt); this.write(dt, monitor); } this.writeDeferredCompositeDeclarations(monitor); this.deferredCompositeDeclarations.clear(); } private DataType getBaseArrayTypedefType(DataType dt) { while (true) { if (dt != null) { if (dt instanceof TypeDef) { dt = ((TypeDef) dt).getBaseDataType(); continue; } if (dt instanceof Array) { dt = ((Array) dt).getDataType(); continue; } } return dt; } } private boolean containsComposite(Composite container, Composite contained) { DataTypeComponent[] var3 = container.getDefinedComponents(); int var4 = var3.length; for (int var5 = 0; var5 < var4; ++var5) { DataTypeComponent component = var3[var5]; DataType dt = this.getBaseArrayTypedefType(component.getDataType()); if (dt instanceof Composite && dt.getName().equals(contained.getName()) && dt.isEquivalent(contained)) { return true; } } return false; } private void writeDeferredCompositeDeclarations(TaskMonitor monitor) throws IOException, CancelledException { int cnt = this.deferredCompositeDeclarations.size(); if (cnt != 0) { LinkedList list = new LinkedList(this.deferredCompositeDeclarations); if (list.size() > 1) { int sortChange = 1; while (sortChange != 0) { sortChange = 0; for (int i = cnt - 1; i > 0; --i) { if (this.resortComposites(list, i)) { ++sortChange; } } } } Iterator var6 = list.iterator(); while (var6.hasNext()) { Composite composite = (Composite) var6.next(); this.writeCompositeBody(composite, monitor); } } } private boolean resortComposites(List list, int index) { int listSize = list.size(); if (listSize <= 0) { return false; } else { Composite composite = (Composite) list.get(index); for (int i = 0; i < index; ++i) { Composite other = (Composite) list.get(i); if (this.containsComposite(other, composite)) { list.remove(index); list.add(i, composite); composite = null; return true; } } return false; } } private String getDynamicComponentString(Dynamic dynamicType, String fieldName, int length) { if (dynamicType.canSpecifyLength()) { DataType replacementBaseType = dynamicType.getReplacementBaseType(); if (replacementBaseType != null) { replacementBaseType = replacementBaseType.clone(this.dtm); int elementLen = replacementBaseType.getLength(); if (elementLen > 0) { int elementCnt = (length + elementLen - 1) / elementLen; return replacementBaseType.getDisplayName() + " " + fieldName + "[" + elementCnt + "]"; } String var10001 = dynamicType.getClass().getSimpleName(); Msg.error(this, var10001 + " returned bad replacementBaseType: " + replacementBaseType.getClass().getSimpleName()); } } return null; } private void writeCompositePreDeclaration(Composite composite, TaskMonitor monitor) throws IOException, CancelledException { String compositeType = composite instanceof Structure ? "struct" : "union"; this.writer.write("typedef " + compositeType + " " + composite.getDisplayName() + " " + composite.getDisplayName() + ", *P" + composite.getDisplayName() + ";"); this.writer.write(EOL); this.writer.write(EOL); DataTypeComponent[] var4 = composite.getComponents(); int var5 = var4.length; for (int var6 = 0; var6 < var5; ++var6) { DataTypeComponent component = var4[var6]; if (monitor.isCancelled()) { break; } DataType componentType = component.getDataType(); this.deferWrite(componentType); this.getTypeDeclaration((String) null, componentType, component.getLength(), true, monitor); } } private void writeCompositeBody(Composite composite, TaskMonitor monitor) throws IOException, CancelledException { String compositeType = composite instanceof Structure ? "struct" : "union"; StringBuilder sb = new StringBuilder(); sb.append(compositeType + " " + composite.getDisplayName() + " {"); String descrip = composite.getDescription(); if (descrip != null && descrip.length() > 0) { String var10001 = this.comment(descrip); sb.append(" " + var10001); } sb.append(EOL); DataTypeComponent[] var6 = composite.getComponents(); int var7 = var6.length; for (int var8 = 0; var8 < var7; ++var8) { DataTypeComponent component = var6[var8]; monitor.checkCancelled(); this.writeComponent(component, composite, sb, monitor); } sb.append(this.annotator.getSuffix(composite, (DataTypeComponent) null)); sb.append("};"); this.writer.write(sb.toString()); this.writer.write(EOL); this.writer.write(EOL); } private void writeComponent(DataTypeComponent component, Composite composite, StringBuilder sb, TaskMonitor monitor) throws IOException, CancelledException { sb.append(" "); sb.append(this.annotator.getPrefix(composite, component)); String fieldName = component.getFieldName(); if (fieldName == null || fieldName.length() == 0) { fieldName = component.getDefaultFieldName(); } DataType componentDataType = component.getDataType(); sb.append(this.getTypeDeclaration(fieldName, componentDataType, component.getLength(), false, monitor)); sb.append(";"); sb.append(this.annotator.getSuffix(composite, component)); String comment = component.getComment(); if (comment != null && comment.length() > 0) { String var10001 = this.comment(comment); sb.append(" " + var10001); } sb.append(EOL); } private String getTypeDeclaration(String name, DataType dataType, int instanceLength, boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException { if (name == null) { name = ""; } StringBuilder sb = new StringBuilder(); String componentString = null; if (dataType instanceof Dynamic) { componentString = this.getDynamicComponentString((Dynamic) dataType, name, instanceLength); if (componentString != null) { sb.append(componentString); } else { sb.append(this.comment("ignoring dynamic datatype inside composite: " + dataType.getDisplayName())); sb.append(EOL); } } if (componentString == null) { if (dataType instanceof BitFieldDataType) { BitFieldDataType bfDt = (BitFieldDataType) dataType; name = name + ":" + bfDt.getDeclaredBitSize(); dataType = bfDt.getBaseDataType(); } label44: while (true) { while (!(dataType instanceof Array)) { if (!(dataType instanceof Pointer)) { break label44; } Pointer pointer = (Pointer) dataType; DataType elem = pointer.getDataType(); if (elem == null) { break label44; } name = "*" + name; dataType = elem; if (elem instanceof Array) { name = "(" + name + ")"; } } Array array = (Array) dataType; name = name + "[" + array.getNumElements() + "]"; dataType = array.getDataType(); } DataType baseDataType = this.getBaseDataType(dataType); if (baseDataType instanceof FunctionDefinition) { componentString = this.getFunctionPointerString((FunctionDefinition) baseDataType, name, dataType, writeEnabled, monitor); } else { String var10000 = this.getDataTypePrefix(dataType); componentString = var10000 + dataType.getDisplayName(); if (name.length() != 0) { componentString = componentString + " " + name; } } sb.append(componentString); } return sb.toString(); } private String getDataTypePrefix(DataType dataType) { dataType = this.getBaseDataType(dataType); if (dataType instanceof Structure) { return "struct "; } else if (dataType instanceof Union) { return "union "; } else { return dataType instanceof Enum ? "enum " : ""; } } private void writeEnum(Enum enumm, TaskMonitor monitor) throws IOException { String enumName = enumm.getDisplayName(); Writer var10000; String var10001; if (enumName.startsWith("define_") && enumName.length() > 7 && enumm.getCount() == 1) { long val = enumm.getValues()[0]; var10000 = this.writer; var10001 = enumName.substring(7); var10000.append("#define " + var10001 + " " + Long.toString(val)); this.writer.write(EOL); this.writer.write(EOL); } else { this.writer.write("enum " + enumName + " {"); String description = enumm.getDescription(); if (description != null && description.length() != 0) { var10000 = this.writer; var10001 = this.comment(description); var10000.write(" " + var10001); } this.writer.write(EOL); String[] names = enumm.getNames(); for (int j = 0; j < names.length; ++j) { this.writer.write(" "); this.writer.write(this.annotator.getPrefix(enumm, names[j])); this.writer.write(names[j]); this.writer.write("="); this.writer.write(Long.toString(enumm.getValue(names[j]))); String comment = enumm.getComment(names[j]); if (!comment.isBlank()) { var10000 = this.writer; var10001 = this.comment(comment); var10000.write(" " + var10001); } this.writer.write(this.annotator.getSuffix(enumm, names[j])); if (j < names.length - 1) { this.writer.write(","); } this.writer.write(EOL); } this.writer.write("};"); this.writer.write(EOL); this.writer.write(EOL); } } private void writeTypeDef(TypeDef typeDef, TaskMonitor monitor) throws IOException, CancelledException { String typedefName = typeDef.getDisplayName(); DataType dataType = typeDef.getDataType(); String dataTypeName = dataType.getDisplayName(); if (!this.isIntegral(typedefName, dataTypeName)) { DataType baseType = typeDef.getBaseDataType(); label125: { try { if (!(baseType instanceof Composite) && !(baseType instanceof Enum)) { if (!(baseType instanceof Pointer) || !typedefName.startsWith("P")) { break label125; } DataType dt = ((Pointer) baseType).getDataType(); if (dt instanceof TypeDef) { dt = ((TypeDef) dt).getBaseDataType(); } if (!(dt instanceof Composite) || !dt.getName().equals(typedefName.substring(1))) { break label125; } this.resolvedTypeMap.remove(typedefName); return; } if (!typedefName.equals(baseType.getName())) { break label125; } this.resolvedTypeMap.remove(typedefName); } finally { this.write(dataType, monitor); } return; } if (baseType instanceof Array && this.getBaseArrayTypedefType(baseType) instanceof Composite) { this.writeDeferredDeclarations(monitor); } String typedefString = this.getTypeDeclaration(typedefName, dataType, -1, true, monitor); this.writer.write("typedef " + typedefString + ";"); this.writer.write(EOL); this.writer.write(EOL); } } private boolean isIntegral(String typedefName, String basetypeName) { String[] var3 = INTEGRAL_TYPES; int var4 = var3.length; int var5; for (var5 = 0; var5 < var4; ++var5) { String type = var3[var5]; if (typedefName.equals(type)) { return true; } } boolean endsWithIntegralType = false; String[] var10 = INTEGRAL_TYPES; var5 = var10.length; int var13; for (var13 = 0; var13 < var5; ++var13) { String type = var10[var13]; if (typedefName.endsWith(" " + type)) { endsWithIntegralType = true; break; } } boolean containsIntegralModifier = false; String[] var12 = INTEGRAL_MODIFIERS; var13 = var12.length; for (int var14 = 0; var14 < var13; ++var14) { String modifier = var12[var14]; if (typedefName.indexOf(modifier + " ") >= 0 || typedefName.indexOf(" " + modifier) >= 0) { return true; } } if (endsWithIntegralType && containsIntegralModifier) { return true; } else if (typedefName.endsWith(" " + basetypeName)) { return containsIntegralModifier; } else { return false; } } private void writeDynamicBuiltIn(Dynamic dt, TaskMonitor monitor) throws IOException, CancelledException { DataType baseDt = dt.getReplacementBaseType(); if (baseDt != null) { this.write(baseDt, monitor); } } private void writeBuiltIn(BuiltInDataType dt, TaskMonitor monitor) throws IOException { String declaration = dt.getCTypeDeclaration(this.dataOrganization); if (declaration != null) { if (dt.getDisplayName() == "bool") return; this.writer.write(declaration); this.writer.write(EOL); } } private void writeBuiltInDeclarations(DataTypeManager manager) throws IOException { try { this.write(DataType.DEFAULT, TaskMonitor.DUMMY); SourceArchive builtInArchive = manager.getSourceArchive(DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID); if (builtInArchive == null) { return; } Iterator var3 = manager.getDataTypes(builtInArchive).iterator(); while (var3.hasNext()) { DataType dt = (DataType) var3.next(); if (!(dt instanceof Pointer) && !(dt instanceof FactoryDataType) && !(dt instanceof Dynamic)) { this.write(dt, TaskMonitor.DUMMY); } } } catch (CancelledException var5) { } this.writer.flush(); } private static String getArrayDimensions(Array arrayDt) { String dimensionString = "[" + arrayDt.getNumElements() + "]"; DataType dataType = arrayDt.getDataType(); if (dataType instanceof Array) { dimensionString = dimensionString + getArrayDimensions((Array) dataType); } return dimensionString; } private DataType getBaseDataType(DataType dt) { while (true) { if (dt != null) { if (dt instanceof Array) { Array array = (Array) dt; dt = array.getDataType(); continue; } if (dt instanceof Pointer) { Pointer pointer = (Pointer) dt; dt = pointer.getDataType(); continue; } if (dt instanceof BitFieldDataType) { BitFieldDataType bitfieldDt = (BitFieldDataType) dt; dt = bitfieldDt.getBaseDataType(); continue; } } return dt; } } private DataType getArrayBaseType(Array arrayDt) { DataType dataType; for (dataType = arrayDt.getDataType(); dataType instanceof Array; dataType = ((Array) dataType).getDataType()) { } return dataType; } private DataType getPointerBaseDataType(Pointer p) { DataType dt; for (dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) { } return dt; } private int getPointerDepth(Pointer p) { int depth = 1; for (DataType dt = p.getDataType(); dt instanceof Pointer; dt = ((Pointer) dt).getDataType()) { ++depth; } return depth; } private String getFunctionPointerString(FunctionDefinition fd, String name, DataType functionPointerArrayType, boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException { DataType originalType = functionPointerArrayType; StringBuilder sb = new StringBuilder(); DataType returnType = fd.getReturnType(); if (writeEnabled) { this.write(returnType, monitor); } sb.append("("); String arrayDecorations = ""; if (functionPointerArrayType instanceof Array a) { functionPointerArrayType = this.getArrayBaseType(a); arrayDecorations = getArrayDimensions(a); } if (functionPointerArrayType instanceof Pointer p) { for (int i = 0; i < this.getPointerDepth(p); ++i) { sb.append('*'); } if (name != null) { sb.append(' '); } functionPointerArrayType = this.getPointerBaseDataType(p); } if (!(functionPointerArrayType instanceof FunctionDefinition)) { this.writer.append(this .comment("Attempting output of invalid function pointer type declaration: " + originalType.getDisplayName())); } if (name != null) { sb.append(name); } if (arrayDecorations.length() != 0) { sb.append(arrayDecorations); } sb.append(")"); sb.append(this.getParameterListString(fd, false, writeEnabled, monitor)); DataType baseReturnType = this.getBaseDataType(returnType); if (baseReturnType instanceof FunctionDefinition) { return this.getFunctionPointerString((FunctionDefinition) baseReturnType, sb.toString(), returnType, writeEnabled, monitor); } else { String var10000 = returnType.getDisplayName(); return var10000 + " " + sb.toString(); } } private String getParameterListString(FunctionDefinition fd, boolean includeParamNames, boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException { StringBuilder buf = new StringBuilder(); buf.append("("); boolean hasVarArgs = fd.hasVarArgs(); ParameterDefinition[] parameters = fd.getArguments(); int n = parameters.length; for (int i = 0; i < n; ++i) { ParameterDefinition param = parameters[i]; String paramName = includeParamNames ? param.getName() : null; DataType dataType = param.getDataType(); if (writeEnabled) { this.write(dataType, monitor); } String argument = this.getTypeDeclaration(paramName, dataType, param.getLength(), writeEnabled, monitor); buf.append(argument); if (i < n - 1 || hasVarArgs) { buf.append(", "); } } if (hasVarArgs) { buf.append("..."); } if (n == 0 && !hasVarArgs) { buf.append(VoidDataType.dataType.getName()); } buf.append(")"); return buf.toString(); } }