/** * @brief Write the properties definitions stored in the object. * @param object Object containing the properties definitions. * @param pex Binary to decompile. */ void Decompiler::PscCoder::writeProperties(const Pex::Object &object, const Pex::Binary &pex) { bool foundInfo = false; if (pex.getDebugInfo().getPropertyGroups().size()) { size_t totalProperties = 0; // If we have debug info, we have information on the order // they were in the original source file, so use that order. for (auto& propGroup : pex.getDebugInfo().getPropertyGroups()) { if (propGroup.getObjectName() == object.getName()) { totalProperties += propGroup.getNames().size(); } } if (totalProperties == object.getProperties().size()) { foundInfo = true; for (auto& propGroup : pex.getDebugInfo().getPropertyGroups()) { if (propGroup.getObjectName() == object.getName()) { int propertyIndent = 0; if (!propGroup.getGroupName().asString().empty()) { auto stream = indent(0); stream << "Group " << propGroup.getGroupName(); writeUserFlag(stream, propGroup, pex); write(stream); writeDocString(0, propGroup); propertyIndent = 1; } for (auto& propName : propGroup.getNames()) { for (auto& prop : object.getProperties()) { if (prop.getName() == propName) { writeProperty(propertyIndent, prop, object, pex); goto ContinueOrder; } } // If we get here, then we failed to find the struct // member in the debug info :( throw std::runtime_error("Unable to locate the property by the name of '" + propName.asString() + "' referenced in the debug info"); ContinueOrder: continue; } if (!propGroup.getGroupName().asString().empty()) { write(indent(0) << "EndGroup"); write(""); } } } } } if (!foundInfo) { for (auto& prop : object.getProperties()) writeProperty(0, prop, object, pex); } }
/** * @brief Write the struct member passed in. * @param member The member to output. * @param pex Binary to decompile. */ void Decompiler::PscCoder::writeStructMember(const Pex::StructInfo::Member& member, const Pex::Binary& pex) { auto stream = indent(1); stream << mapType(member.getTypeName().asString()) << " " << member.getName(); if (member.getValue().getType() != Pex::ValueType::None) { stream << " = " << member.getValue().toString(); } writeUserFlag(stream, member, pex); if (member.getConstFlag()) stream << " Const"; write(stream); writeDocString(1, member); }
/** * @brief Write the property definition. * @param i The indent level. * @param prop The property to write. * @param object Object containing the properties definitions. * @param pex Binary to decompile. */ void Decompiler::PscCoder::writeProperty(int i, const Pex::Property& prop, const Pex::Object &object, const Pex::Binary& pex) { auto stream = indent(i); auto isAutoReadOnly = !prop.hasAutoVar() && prop.isReadable() && !prop.isWritable() && prop.getReadFunction().getInstructions().size() == 1 && prop.getReadFunction().getInstructions()[0].getOpCode() == Pex::OpCode::RETURN && prop.getReadFunction().getInstructions()[0].getArgs().size() == 1; stream << mapType(prop.getTypeName().asString()) << " Property " << prop.getName(); if (prop.hasAutoVar()) { auto var = object.getVariables().findByName(prop.getAutoVarName()); if (var == nullptr) throw std::runtime_error("Auto variable for property not found"); auto initialValue = var->getDefaultValue(); if (initialValue.getType() != Pex::ValueType::None) stream << " = " << initialValue.toString(); stream << " Auto"; // The flags defined on the variable must be set on the property writeUserFlag(stream, *var, pex); if (var->getConstFlag()) stream << " Const"; } else if (isAutoReadOnly) { stream << " = " << prop.getReadFunction().getInstructions()[0].getArgs()[0].toString(); stream << " AutoReadOnly"; } writeUserFlag(stream, prop, pex); write(stream); writeDocString(i, prop); if (!prop.hasAutoVar() && !isAutoReadOnly) { if (prop.isReadable()) writeFunction(i + 1, prop.getReadFunction(), object, pex, "Get"); if (prop.isWritable()) writeFunction(i + 1, prop.getWriteFunction(), object, pex, "Set"); write(indent(i) << "EndProperty"); } }
/** * @brief Write an object contained in the binary. * @param object Object to decompile * @param pex Binary to decompile. */ void Decompiler::PscCoder::writeObject(const Pex::Object &object, const Pex::Binary &pex) { auto stream = indent(0); stream <<"ScriptName " << object.getName().asString(); if (! object.getParentClassName().asString().empty()) { stream << " extends " << object.getParentClassName().asString(); } if (object.getConstFlag()) stream << " Const"; writeUserFlag(stream, object, pex); write(stream); writeDocString(0, object); if (object.getStructInfos().size()) { write(""); write(";-- Structs -----------------------------------------"); writeStructs(object, pex); } if (object.getProperties().size()) { write(""); write(";-- Properties --------------------------------------"); writeProperties(object, pex); } if (object.getVariables().size()) { write(""); write(";-- Variables ---------------------------------------"); writeVariables(object, pex); } writeStates(object, pex); }
void DOMWriter::visitText(Text * node) { writeDocString(node->getNodeValue()); }
/** * @brief Decompile a function. * @param i The indentation level. * @param function The function to decompile. * @param object The Object containing the function. * @param pex Binary to decompile. * @param name Name of the function. This parameter override the name stored in the function object. */ void Decompiler::PscCoder::writeFunction(int i, const Pex::Function &function, const Pex::Object& object, const Pex::Binary &pex, const std::string &name) { std::string functionName = name; if (functionName.empty()) { functionName = function.getName().asString(); } bool isEvent = functionName.size() > 2 && !_stricmp(functionName.substr(0, 2).c_str(), "on"); if (functionName.size() > 9 && !_stricmp(functionName.substr(0, 9).c_str(), "::remote_")) { isEvent = true; functionName = functionName.substr(9); functionName[function.getParams()[0].getTypeName().asString().size()] = '.'; } if (functionName != "GetState" && functionName != "GotoState") { auto stream = indent(i); if (_stricmp(function.getReturnTypeName().asString().c_str(), "none") != 0) stream << mapType(function.getReturnTypeName().asString()) << " "; if (isEvent) stream << "Event "; else stream << "Function "; stream << functionName << "("; auto first = true; for (auto& param : function.getParams()) { if (first) { first = false; } else { stream << ", "; } stream << mapType(param.getTypeName().asString()) << " " << param.getName(); } stream << ")"; if (function.isGlobal()) { stream << " global"; } if (function.isNative()) { stream << " native"; } writeUserFlag(stream, function, pex); write(stream); writeDocString(i, function); if (! function.isNative()) { for (auto& line : PscDecompiler(function, object, m_CommentAsm)) { write(indent(i+1) << line); } if (isEvent) write(indent(i) << "EndEvent"); else write(indent(i) << "EndFunction"); } } else { write(indent(i) << "; Skipped compiler generated " << functionName); } }