diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/IFPSAsmLib/Assembler.cs b/IFPSAsmLib/Assembler.cs new file mode 100644 index 0000000..e5bb97f --- /dev/null +++ b/IFPSAsmLib/Assembler.cs @@ -0,0 +1,892 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using IFPSLib; +using IFPSLib.Types; +using IFPSLib.Emit; +using IFPSLib.Emit.FDecl; +using System.Runtime.InteropServices; +using System.Collections; +using System.ComponentModel; + +namespace IFPSAsmLib +{ + internal static class AssemblerExtensions + { + private enum LiteralParserState : byte + { + Default, + EscapedChar, + EscapedHex, + EscapedUnicode + } + + private static char NibbleToHex(this char ch0) + { + // taken from stackoverflow: https://stackoverflow.com/questions/3408706/hexadecimal-string-to-byte-array-in-c/67799940#67799940 + // Parser already ensured the char is a valid nibble, so the quick bithacking can be done here + return (char)((ch0 & 0xF) + (ch0 >> 6) | ((ch0 >> 3) & 0x8)); + } + + internal static string FromLiteral(this string input) + { + if (input[0] != input[input.Length - 1] || input[0] != Constants.STRING_CHAR) throw new ArgumentOutOfRangeException(nameof(input)); + StringBuilder literal = new StringBuilder(input.Length - 2); + var state = LiteralParserState.Default; + byte counter = 0; + char escapedChar = (char)0; + foreach (var c in input.Skip(1).Take(input.Length - 2)) + { + switch (state) + { + case LiteralParserState.Default: + if (c != '\\') literal.Append(c); + else state = LiteralParserState.EscapedChar; + break; + case LiteralParserState.EscapedChar: + state = LiteralParserState.Default; + switch (c) + { + case '"': literal.Append('"'); break; + case '\\': literal.Append('\\'); break; + case '0': literal.Append('\0'); break; + case 'a': literal.Append('\a'); break; + case 'b': literal.Append('\b'); break; + case 'f': literal.Append('\f'); break; + case 'n': literal.Append('\n'); break; + case 'r': literal.Append('\r'); break; + case 't': literal.Append('\t'); break; + case 'v': literal.Append('\v'); break; + case 'x': + state = LiteralParserState.EscapedHex; + counter = 0; + break; + case 'u': + state = LiteralParserState.EscapedUnicode; + counter = 0; + break; + } + break; + case LiteralParserState.EscapedHex: + escapedChar |= (char)(NibbleToHex(c) << ((2 - 1 - counter) * 4)); + counter++; + if (counter == 2) + { + literal.Append(escapedChar); + counter = 0; + escapedChar = (char)0; + state = LiteralParserState.Default; + } + break; + case LiteralParserState.EscapedUnicode: + escapedChar |= (char)(NibbleToHex(c) << ((4 - 1 - counter) * 4)); + counter++; + if (counter == 4) + { + literal.Append(escapedChar); + counter = 0; + escapedChar = (char)0; + state = LiteralParserState.Default; + } + break; + } + } + return literal.ToString(); + } + } + + public static class Assembler + { + private static IEnumerable OfType(this IEnumerable self, ElementParentType type) + { + return self.Where(x => x.Element.ParentType == type); + } + + private static IEnumerable OfType(this IEnumerable self, ElementParentType type) + { + return self.Where(x => x.ParentType == type); + } + + private static ParsedBody ExpectZeroOrOne(List parsed, ElementParentType type) + { + var elems = parsed.OfType(type); + var second = elems.Skip(1).FirstOrDefault(); + if (second != null) + { + second.Element.ThrowInvalid(); + } + return elems.FirstOrDefault(); + } + + private static bool IsExported(this ParserElement elem) + { + var value = elem.NextChild?.Value; + return value == Constants.ELEMENT_BODY_EXPORTED || value == Constants.ELEMENT_BODY_IMPORTED; + } + + private static void EnsureNoNextChild(this ParserElement val) + { + if (val.NextChild != null) val.NextChild.ThrowInvalid(); + } + + public static CustomAttribute ParseAttribute(ParserElement attr, Dictionary types, Dictionary functions) + { + // name (...) + CustomAttribute ret = new CustomAttribute(attr.Value); + for (var next = attr.Next; next != null; next = next.Next) + { + if (!types.TryGetValue(next.Value, out var immType)) next.ThrowInvalid(string.Format("In attribute {0}: Invalid type", ret.Name)); + var data = TryParseData(immType, next.NextChild.Value, functions); + if (data == null) next.NextChild.ThrowInvalid(string.Format("In attribute {0}: Invalid data", ret.Name)); + ret.Arguments.Add(data); + } + return ret; + } + + private static IType ParseType(ParserElement type, Dictionary types) + { + // .type [(export)] type(...) name + IType ret = null; + var baseType = type.Next; + ParserElement elemName = baseType.Next; + var child = baseType.NextChild; + ParserElement next = child?.Next; + switch (baseType.Value) + { + // primitive(enum_value) + case Constants.TYPE_PRIMITIVE: + child.EnsureNoNextChild(); + if (!Enum.TryParse(child.Value, out var typeCode)) child.ThrowInvalid("Invalid primitive type"); + if (!typeCode.IsPrimitive()) child.ThrowInvalid(); + if (next != null) next.ThrowInvalid(); + ret = new PrimitiveType(typeCode); + break; + // array(type) or array(type,length,start) + case Constants.TYPE_ARRAY: + if (next != null) next.ThrowInvalid(); + next = child.NextChild; + if (!types.TryGetValue(child.Value, out var childType)) child.ThrowInvalid("Invalid array type"); + if (next == null) + { + ret = new ArrayType(childType); + } else + { + if (next.Next != null) next.Next.ThrowInvalid(); + if (!int.TryParse(next.Value, out var arrLen)) next.ThrowInvalid("Invalid static array length"); + int idxStart = 0; + next = next.NextChild; + if (next != null) + { + if (next.Next != null) next.Next.ThrowInvalid(); + if (!int.TryParse(next.Value, out idxStart)) next.ThrowInvalid("Invalid static array start index"); + } + ret = new StaticArrayType(childType, arrLen, idxStart); + } + break; + // class(internalName) + case Constants.TYPE_CLASS: + child.EnsureNoNextChild(); + if (next != null) next.ThrowInvalid(); + child.ExpectValidName(); + ret = new ClassType(child.Value); + break; + // interface(guidString) + case Constants.TYPE_COM_INTERFACE: + child.EnsureNoNextChild(); + if (next != null) next.ThrowInvalid(); + child.ExpectString(); + if (!Guid.TryParse(child.Value.FromLiteral(), out var guid)) child.ThrowInvalid("Invalid COM interface GUID"); + ret = new IFPSLib.Types.ComInterfaceType(guid); + break; + // funcptr(declaration) + case Constants.TYPE_FUNCTION_POINTER: + child = baseType.Next; + if (child.NextChild == null) child.ThrowInvalid(); + if (next != null) next.ThrowInvalid(); + // returnsval|void + bool returnsVal = child.Value == Constants.FUNCTION_RETURN_VAL; + if (!returnsVal && child.Value != Constants.FUNCTION_RETURN_VOID) child.ThrowInvalid("Invalid function pointer return type"); + child = child.NextChild; + var argList = new List(); + if (child.Value != "") + { + for (next = child; next != null; next = next.NextChild) + { + if (next.Next != null) next.Next.ThrowInvalid(); + var isInVal = next.Value == Constants.FUNCTION_ARG_IN || next.Value == Constants.FUNCTION_ARG_VAL; + if (!isInVal && next.Value != Constants.FUNCTION_ARG_OUT && next.Value != Constants.FUNCTION_ARG_REF) + child.ThrowInvalid("Invalid function pointer argument type"); + argList.Add(isInVal ? FunctionArgumentType.In : FunctionArgumentType.Out); + } + } + ret = new FunctionPointerType(returnsVal, argList); + baseType = baseType.Next; + break; + // record(types...) + case Constants.TYPE_RECORD: + if (next != null) next.ThrowInvalid(); + var typeList = new List(); + for (next = child; next != null; next = next.NextChild) + { + if (!types.TryGetValue(next.Value, out childType)) next.ThrowInvalid("Invalid record element type"); + typeList.Add(childType); + } + ret = new RecordType(typeList); + break; + // set(bitsize) + case Constants.TYPE_SET: + child.EnsureNoNextChild(); + if (!int.TryParse(child.Value, out var setLen) || setLen > 0x100) child.ThrowInvalid("Invalid set bit size"); + if (next != null) next.ThrowInvalid(); + ret = new SetType(setLen); + break; + default: + baseType.ThrowInvalid(); + break; + } + + ret.Exported = type.IsExported(); + ret.Name = baseType.Next.Value; + if (types.ContainsKey(ret.Name)) baseType.Next.ThrowInvalid("Type already defined"); + types.Add(ret.Name, ret); + return ret; + } + + private static GlobalVariable ParseGlobal(ParserElement global, Dictionary types, Dictionary globals, int index) + { + // .global (export) type name + var next = global.Next; + if (!types.TryGetValue(next.Value, out var type)) next.ThrowInvalid("Global has unknown type"); + next = next.Next; + var ret = GlobalVariable.Create(index, type, next.Value); + if (globals.ContainsKey(ret.Name)) next.ThrowInvalid("Global already defined"); + ret.Exported = global.IsExported(); + globals.Add(ret.Name, ret); + return ret; + } + + private static NativeCallingConvention ParseCC(this ParserElement elem) + { + switch (elem.Value) + { + case Constants.FUNCTION_FASTCALL: + return NativeCallingConvention.Register; + case Constants.FUNCTION_PASCAL: + return NativeCallingConvention.Pascal; + case Constants.FUNCTION_CDECL: + return NativeCallingConvention.CDecl; + case Constants.FUNCTION_STDCALL: + return NativeCallingConvention.Stdcall; + default: + elem.ThrowInvalid(); // shouldn't get here, parser should have detected invalid calling convention already + return NativeCallingConvention.Stdcall; + } + } + + private static IFunction ParseFunction(ParserElement function, Dictionary types, Dictionary functions) + { + IFunction ret = null; + var next = function.Next; + ParserElement child = null; + bool isExternal = next.Value == Constants.FUNCTION_EXTERNAL; + if (isExternal) + { + next = next.Next; + var ext = new ExternalFunction(); + ret = ext; + switch (next.Value) + { + case Constants.FUNCTION_EXTERNAL_INTERNAL: + ext.Declaration = new Internal(); + break; + case Constants.FUNCTION_EXTERNAL_DLL: + // dll(dllname,procname[,delayload][,alteredsearchpath]) + child = next.NextChild; + var dll = new DLL(); + dll.DllName = child.Value.FromLiteral(); + child = child.NextChild; + dll.ProcedureName = child.Value.FromLiteral(); + child = child.NextChild; + if (child != null) + { + if (child.Value == Constants.FUNCTION_EXTERNAL_DLL_DELAYLOAD) dll.DelayLoad = true; + else dll.LoadWithAlteredSearchPath = true; + child = child.NextChild; + if (child != null) + { + if (child.Value == Constants.FUNCTION_EXTERNAL_DLL_DELAYLOAD) dll.DelayLoad = true; + else dll.LoadWithAlteredSearchPath = true; + } + } + // calling convention + next = next.Next; + dll.CallingConvention = next.ParseCC(); + + ext.Declaration = dll; + break; + case Constants.FUNCTION_EXTERNAL_CLASS: + // class(classname, procname, property) + child = next.NextChild; + var cls = new Class(); + cls.ClassName = child.Value; + child = child.NextChild; + cls.FunctionName = child.Value; + cls.IsProperty = child.NextChild != null; + // calling convention + next = next.Next; + cls.CallingConvention = next.ParseCC(); + + ext.Declaration = cls; + break; + case Constants.FUNCTION_EXTERNAL_COM: + // com(vtbl_index) + child = next.NextChild; + var com = new COM(); + com.VTableIndex = uint.Parse(child.Value); + // calling convention + next = next.Next; + com.CallingConvention = next.ParseCC(); + + ext.Declaration = com; + break; + } + next = next.Next; + } else + { + ret = new ScriptFunction(); + } + + // void|returnsval/type + // external cannot be type + if (next.Value != Constants.FUNCTION_RETURN_VOID) + { + if (isExternal) ret.ReturnArgument = UnknownType.Instance; + else + { + if (!types.TryGetValue(next.Value, out var retArg)) next.ThrowInvalid(string.Format("In function \"{0}\": Unknown type", ret.Name)); + ret.ReturnArgument = retArg; + } + } + next = next.Next; + + // name + ret.Name = next.Value; + if (functions.ContainsKey(ret.Name)) next.ThrowInvalid("Function already defined"); + + // arguments + ret.Arguments = new List(); + if (next.NextChild.Value != "") + { + for (child = next.NextChild; child != null; child = child.NextChild) + { + // __in|__val|__out|__ref type|__unknown name + var arg = new FunctionArgument(); + bool isInVal = child.Value == Constants.FUNCTION_ARG_IN || child.Value == Constants.FUNCTION_ARG_VAL; + if (!isInVal && child.Value != Constants.FUNCTION_ARG_OUT && child.Value == Constants.FUNCTION_ARG_REF) + child.ThrowInvalid(string.Format("In function \"{0}\": Unknown argument type", ret.Name)); + arg.ArgumentType = isInVal ? FunctionArgumentType.In : FunctionArgumentType.Out; + next = child.Next; + if (next == null) child.ThrowInvalid(); + // An external function does not have types here, a script function does. + if (isExternal) arg.Type = UnknownType.Instance; + else + { + if (!types.TryGetValue(next.Value, out var argType)) next.ThrowInvalid(string.Format("In function \"{0}\": Unknown type", ret.Name)); + arg.Type = argType; + } + if (next.Next != null) + { + next = next.Next; + + next.ExpectValidName(); + arg.Name = next.Value; + } + + ret.Arguments.Add(arg); + } + } + + ret.Exported = function.IsExported(); + functions.Add(ret.Name, ret); + return ret; + } + + private static bool TryParse(string str, out T val) + { + try + { + val = str.Parse(); + return true; + } catch + { + val = default; + return false; + } + } + + private static T Parse(this string val) + { + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(val); + } + + private static TypedData TryParseData(IType itype, string val, Dictionary functions) + { + var type = itype as PrimitiveType; + switch (itype.BaseType) + { + case PascalTypeCode.S8: + if (!TryParse(val, out var _sbyte)) return null; + return TypedData.Create(type, _sbyte); + case PascalTypeCode.U8: + if (!TryParse(val, out var _byte)) return null; + return TypedData.Create(type, _byte); + case PascalTypeCode.Char: + val = val.FromLiteral(); + if (val.Length != 1) return null; + if (val[0] > 0xff) return null; + return TypedData.Create(type, val[0]); + + case PascalTypeCode.S16: + if (!TryParse(val, out var _short)) return null; + return TypedData.Create(type, _short); + case PascalTypeCode.U16: + if (!TryParse(val, out var _ushort)) return null; + return TypedData.Create(type, _ushort); + case PascalTypeCode.WideChar: + val = val.FromLiteral(); + if (val.Length != 1) return null; + return TypedData.Create(type, val[0]); + + case PascalTypeCode.S32: + if (!TryParse(val, out var _int)) return null; + return TypedData.Create(type, _int); + case PascalTypeCode.U32: + if (!TryParse(val, out var _uint)) return null; + return TypedData.Create(type, _uint); + + case PascalTypeCode.S64: + if (!TryParse(val, out var _long)) return null; + return TypedData.Create(type, _long); + + case PascalTypeCode.Single: + if (!float.TryParse(val, out var _float)) return null; + return TypedData.Create(type, _float); + case PascalTypeCode.Double: + if (!double.TryParse(val, out var _double)) return null; + return TypedData.Create(type, _double); + case PascalTypeCode.Extended: + if (!decimal.TryParse(val, out var _decimal)) return null; + return TypedData.Create(type, _decimal); + + case PascalTypeCode.Currency: + if (!decimal.TryParse(val, out var _currency)) return null; + return TypedData.Create(type, new CurrencyWrapper(_currency)); + + case PascalTypeCode.PChar: + case PascalTypeCode.String: + val = val.FromLiteral(); + if (val.Any(x => x > 0xff)) return null; + return TypedData.Create(type, val); + + case PascalTypeCode.WideString: + case PascalTypeCode.UnicodeString: + return TypedData.Create(type, val.FromLiteral()); + + case PascalTypeCode.ProcPtr: + if (!functions.TryGetValue(val, out var func)) return null; + return TypedData.Create(itype as FunctionPointerType, func); + + case PascalTypeCode.Set: + if (!val.StartsWith(Constants.INTEGER_BINARY)) return null; + var ba = new BitArray(val.Length - 2); + for (int i = ba.Length - 1; i >= 0; i--) + { + var chr = val[i + 2]; + bool bit = chr == Constants.BINARY_TRUE; + if (!bit && chr != Constants.BINARY_FALSE) return null; + ba[i] = bit; + } + return TypedData.Create(type, ba); + + default: + return null; + } + } + + private static IVariable ParseVariable( + string value, + ScriptFunction function, + Dictionary globals, + Dictionary aliases + ) + { + if (aliases.TryGetValue(value, out var realVar)) value = realVar; + if (globals.TryGetValue(value, out var global)) return global; + for (int i = 0; i < function.Arguments.Count; i++) + { + if (function.Arguments[i].Name != value) continue; + return function.CreateArgumentVariable(i); + } + value = value.ToLower(); + if (value == Constants.VARIABLE_RET) return function.CreateReturnVariable(); + if (!value.StartsWith(Constants.VARIABLE_LOCAL_PREFIX)) return null; + if (!int.TryParse(value.Substring(Constants.VARIABLE_LOCAL_PREFIX.Length), out var idx)) return null; + if (idx < 0) return null; + return LocalVariable.Create(idx - 1); + } + + private static Operand ParseOperandValue( + ParserElement value, + ScriptFunction function, + Dictionary types, + Dictionary globals, + Dictionary functions, + Dictionary aliases, + Dictionary defines + ) + { + // first: check define + if (value.NextChild == null && defines.TryGetValue(value.Value, out var defined)) + value = defined; + + // immediate: type(value) + // variable: name + // immediate index: name[int_elem] + // variable index: name[elem_var] + if (value.NextChild != null) + { + // immediate + if (!types.TryGetValue(value.Value, out var immType)) value.ThrowInvalid(string.Format("In function {0}: Invalid type", function.Name)); + var data = TryParseData(immType, value.NextChild.Value, functions); + if (data == null) value.NextChild.ThrowInvalid(string.Format("In function {0}: Invalid data", function.Name)); + return new Operand(data); + } + + var val = value.Value; + var indexOfStart = val.AsSpan().IndexOf(Constants.START_ARRAY_INDEX); + if (val[val.Length - 1] != Constants.END_ARRAY_INDEX || indexOfStart == -1) + { + // variable + var variable = ParseVariable(val, function, globals, aliases); + if (variable == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name)); + return new Operand(variable); + } + + var baseName = val.Substring(0, indexOfStart); + string element = val.Substring(indexOfStart + 1, val.Length - indexOfStart - 2); + var baseVar = ParseVariable(baseName, function, globals, aliases); + if (baseVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name)); + if (uint.TryParse(element, out var elementIdx)) return new Operand(baseVar, elementIdx); + var elemVar = ParseVariable(element, function, globals, aliases); + if (elemVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name)); + return new Operand(baseVar, elemVar); + } + + private static Instruction ParseInstructionFirstPass( + ParserElement insn, + ScriptFunction function, + Dictionary types, + Dictionary globals, + Dictionary functions, + Dictionary aliases, + Dictionary placeholderTable, + Dictionary defines + ) + { + ParserElement next = null; + + if (!OpCodes.ByName.TryGetValue(insn.Value, out var opcode)) insn.ThrowInvalid(string.Format("In function \"{0}\": Unknown opcode", function.Name)); + + switch (opcode.OperandType) + { + case OperandType.InlineNone: + return Instruction.Create(opcode); + case OperandType.InlineBrTarget: + case OperandType.InlineBrTargetValue: + case OperandType.InlineEH: + placeholderTable.Add(insn, function.Instructions.Count); + return Instruction.Create(OpCodes.Nop); + case OperandType.InlineValue: + case OperandType.InlineValueSF: + if (insn.Next == null) insn.ThrowInvalid(); + return Instruction.Create(opcode, ParseOperandValue(insn.Next, function, types, globals, functions, aliases, defines)); + case OperandType.InlineValueValue: + { + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + var op0 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.Create(opcode, op0, op1); + } + case OperandType.InlineFunction: + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!functions.TryGetValue(next.Value, out var funcOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown function", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.Create(opcode, funcOp); + case OperandType.InlineType: + { + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.Create(opcode, typeOp); + } + case OperandType.InlineCmpValue: + { + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + var op0 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + var op2 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.Create(opcode, op0, op1, op2); + } + case OperandType.InlineCmpValueType: + { + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + var op0 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.Create(opcode, op0, op1, typeOp); + } + case OperandType.InlineTypeVariable: + { + if (opcode.Code != Code.SetStackType) insn.ThrowInvalid(string.Format("In function \"{0}\": Unknown opcode", function.Name)); + next = next.Next; + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name)); + next = next.Next; + next.ExpectValidName(); + var variable = ParseVariable(next.Value, function, globals, aliases); + if (variable == null) next.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + return Instruction.CreateSetStackType(typeOp, variable); + } + default: + insn.ThrowInvalid(string.Format("In function \"{0}\": Unknown opcode", function.Name)); + return null; + } + } + + private static void ParseLabelEH(ScriptFunction function, ParserElement next, Dictionary labels, out int idx) + { + if (next.Value == Constants.LABEL_NULL) idx = -1; + else if (!labels.TryGetValue(next.Value, out idx)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown label", function.Name)); + } + + private static void ParseInstructions( + ScriptFunction function, + IReadOnlyList instructions, + Dictionary types, + Dictionary globals, + Dictionary functions, + Dictionary defines + ) + { + // element=>index table for any instruction that references another + var placeholderTable = new Dictionary(); + var labels = new Dictionary(); + var aliases = new Dictionary(); + + // first pass: set up all possible instructions, placeholders for those that reference labels. + // for a label, put it in the table and move on + // for an alias, put it in the table and move on + foreach (var insn in instructions) + { + if (insn.ParentType == ElementParentType.Attribute) continue; + if (insn.ParentType == ElementParentType.Label) + { + if (labels.ContainsKey(insn.Value)) insn.ThrowInvalid(string.Format("In function \"{0}\": Label already used", function.Name)); + labels.Add(insn.Value, function.Instructions.Count); + continue; + } + if (insn.ParentType == ElementParentType.Alias) + { + if (aliases.ContainsKey(insn.Next.Value)) insn.Next.ThrowInvalid(string.Format("In function \"{0}\": Alias already used", function.Name)); + aliases.Add(insn.Next.Value, insn.Next.Next.Value); + continue; + } + if (insn.ParentType != ElementParentType.Instruction) insn.ThrowInvalid(); + + function.Instructions.Add(ParseInstructionFirstPass(insn, function, types, globals, functions, aliases, placeholderTable, defines)); + } + + // second pass: fix up instructions that reference labels. + ParserElement next = null; + int labelIdx = default; + foreach (var placeholder in placeholderTable) + { + var insn = placeholder.Key; + + var opcode = OpCodes.ByName[insn.Value]; // already checked earlier, no chance of KeyNotFoundException + + switch (opcode.OperandType) + { + case OperandType.InlineBrTarget: + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!labels.TryGetValue(next.Value, out labelIdx)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown label", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + function.Instructions[placeholder.Value].Replace(Instruction.Create(opcode, function.Instructions[labelIdx])); + function.Instructions[labelIdx].Referenced = true; + break; + case OperandType.InlineBrTargetValue: + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!labels.TryGetValue(next.Value, out labelIdx)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown label", function.Name)); + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); + if (next.Next != null) next.Next.ThrowInvalid(); + function.Instructions[placeholder.Value].Replace(Instruction.Create(opcode, function.Instructions[labelIdx], op1)); + function.Instructions[labelIdx].Referenced = true; + break; + case OperandType.InlineEH: + { + Span labelIdxes = stackalloc int[4]; + next = insn.Next; + if (next == null) insn.ThrowInvalid(); + next.ExpectValidName(); + next.EnsureNoNextChild(); + ParseLabelEH(function, next, labels, out labelIdxes[0]); + var catchStart = next; + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + next.ExpectValidName(); + next.EnsureNoNextChild(); + ParseLabelEH(function, next, labels, out labelIdxes[1]); + var finallyStart = next; + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + next.ExpectValidName(); + next.EnsureNoNextChild(); + ParseLabelEH(function, next, labels, out labelIdxes[2]); + var finallyCatch = next; + if (next.Next == null) next.ThrowInvalid(); + next = next.Next; + next.ExpectValidName(); + next.EnsureNoNextChild(); + if (!labels.TryGetValue(next.Value, out labelIdxes[3])) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown label", function.Name)); + if (next.Next != null) next.Next.ThrowInvalid(); + if (labelIdxes[0] == -1 && labelIdxes[1] == -1) + { + catchStart.ThrowInvalid(string.Format("In function \"{0}\": Finally start and catch start can not both be", function.Name)); + } + function.Instructions[placeholder.Value].Replace(Instruction.CreateStartEH( + labelIdxes[0] == -1 ? null : function.Instructions[labelIdxes[0]], + labelIdxes[1] == -1 ? null : function.Instructions[labelIdxes[1]], + labelIdxes[2] == -1 ? null : function.Instructions[labelIdxes[2]], + labelIdxes[3] == -1 ? null : function.Instructions[labelIdxes[3]] + )); + for (int i = 0; i < labelIdxes.Length; i++) + { + if (labelIdxes[i] == -1) continue; + function.Instructions[labelIdxes[i]].Referenced = true; + } + break; + } + default: + insn.ThrowInvalid(string.Format("In function \"{0}\": Unknown opcode", function.Name)); + break; + } + } + } + + public static Script Assemble(List parsed) + { + int version = Script.VERSION_HIGHEST; + { + var elemVersion = ExpectZeroOrOne(parsed, ElementParentType.FileVersion)?.Element.Next?.Value; + if (elemVersion != null) version = int.Parse(elemVersion); + } + var ret = new Script(version); + + string entryPoint = ExpectZeroOrOne(parsed, ElementParentType.EntryPoint)?.Element.Next?.Value; + + // types + var typesTable = new Dictionary(); + foreach (var typeElem in parsed.OfType(ElementParentType.Type)) + { + var type = ParseType(typeElem.Element, typesTable); + foreach (var attr in typeElem.Children.OfType(ElementParentType.Attribute)) + type.Attributes.Add(ParseAttribute(attr, typesTable, null)); + ret.Types.Add(type); + } + + // globals + var globalsTable = new Dictionary(); + { + var i = 0; + foreach (var globalElem in parsed.OfType(ElementParentType.GlobalVariable)) + { + ret.GlobalVariables.Add(ParseGlobal(globalElem.Element, typesTable, globalsTable, i)); + i++; + } + } + + // defines + var definesTable = new Dictionary(); + foreach (var define in parsed.OfType(ElementParentType.Define)) + { + var name = define.Element.Next.Next; + if (definesTable.ContainsKey(name.Value)) name.ThrowInvalid("Immediate value already defined"); + definesTable.Add(define.Element.Next.Next.Value, define.Element.Next); + } + + // functions + var functionsTable = new Dictionary(); + var funcElemTable = new Dictionary(); + foreach (var funcElem in parsed.OfType(ElementParentType.Function)) + { + var func = ParseFunction(funcElem.Element, typesTable, functionsTable); + foreach (var attr in funcElem.Children.OfType(ElementParentType.Attribute)) + func.Attributes.Add(ParseAttribute(attr, typesTable, functionsTable)); + ret.Functions.Add(func); + var sf = func as ScriptFunction; + if (sf == null) continue; + funcElemTable.Add(funcElem, sf); + } + + foreach (var funcElem in parsed.OfType(ElementParentType.Function).Where(x => x.Children.Any())) + { + ParseInstructions(funcElemTable[funcElem], funcElem.Children, typesTable, globalsTable, functionsTable, definesTable); + if (funcElemTable[funcElem].Instructions.Count == 0) + { + funcElem.Element.Tokens.Last().ThrowInvalid(string.Format("In function {0}: no instructions specified", funcElemTable[funcElem].Name)); + } + } + + if (entryPoint != null) ret.EntryPoint = functionsTable[entryPoint]; + + return ret; + } + + public static Script Assemble(string str) => Assemble(Parser.Parse(str)); + } +} diff --git a/IFPSAsmLib/Constants.cs b/IFPSAsmLib/Constants.cs new file mode 100644 index 0000000..128234d --- /dev/null +++ b/IFPSAsmLib/Constants.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSAsmLib +{ + internal static class Constants + { + internal const char START_BODY = '('; + internal const char NEXT_BODY = ','; + internal const char END_BODY = ')'; + internal const char START_ARRAY_INDEX = '['; + internal const char END_ARRAY_INDEX = ']'; + internal const char START_ATTRIBUTE = '['; + internal const char END_ATTRIBUTE = ']'; + + internal const char STRING_CHAR = '"'; + internal const char COMMENT_START = ';'; + internal const char ELEMENT_START = '.'; + internal const char LABEL_SPECIFIER = ':'; + + internal const char BINARY_FALSE = '0'; + internal const char BINARY_TRUE = '1'; + + internal const string ELEMENT_VERSION = ".version"; + internal const string ELEMENT_ENTRYPOINT = ".entry"; + internal const string ELEMENT_TYPE = ".type"; + internal const string ELEMENT_ALIAS = ".alias"; + internal const string ELEMENT_DEFINE = ".define"; + internal const string ELEMENT_GLOBALVARIABLE = ".global"; + internal const string ELEMENT_FUNCTION = ".function"; + + internal const string ELEMENT_BODY_EXPORTED = "export"; + internal const string ELEMENT_BODY_IMPORTED = "import"; + + internal const string FUNCTION_EXTERNAL = "external"; + + internal const string FUNCTION_EXTERNAL_INTERNAL = "internal"; + internal const string FUNCTION_EXTERNAL_DLL = "dll"; + internal const string FUNCTION_EXTERNAL_COM = "com"; + internal const string FUNCTION_EXTERNAL_CLASS = "class"; + + internal const string FUNCTION_EXTERNAL_CLASS_PROPERTY = "property"; + + internal const string FUNCTION_EXTERNAL_DLL_DELAYLOAD = "delayload"; + internal const string FUNCTION_EXTERNAL_DLL_ALTEREDSEARCHPATH = "alteredsearchpath"; + + internal const string FUNCTION_FASTCALL = "__fastcall"; + internal const string FUNCTION_PASCAL = "__pascal"; + internal const string FUNCTION_CDECL = "__cdecl"; + internal const string FUNCTION_STDCALL = "__stdcall"; + + internal const string FUNCTION_RETURN_VOID = "void"; + internal const string FUNCTION_RETURN_VAL = "returnsval"; + + internal const string FUNCTION_ARG_IN = "__in"; + internal const string FUNCTION_ARG_OUT = "__out"; + internal const string FUNCTION_ARG_VAL = "__val"; // same as in + internal const string FUNCTION_ARG_REF = "__ref"; // same as out + + internal const string TYPE_PRIMITIVE = "primitive"; + internal const string TYPE_ARRAY = "array"; + internal const string TYPE_CLASS = "class"; + internal const string TYPE_COM_INTERFACE = "interface"; + internal const string TYPE_FUNCTION_POINTER = "funcptr"; + internal const string TYPE_RECORD = "record"; + internal const string TYPE_SET = "set"; + + internal const string INTEGER_BINARY = "0b"; + + internal const string VARIABLE_RET = "retval"; + internal const string VARIABLE_LOCAL_PREFIX = "var"; + + internal const string LABEL_NULL = "null"; + } +} diff --git a/IFPSAsmLib/IFPSAsmLib.csproj b/IFPSAsmLib/IFPSAsmLib.csproj new file mode 100644 index 0000000..3c71811 --- /dev/null +++ b/IFPSAsmLib/IFPSAsmLib.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/IFPSAsmLib/Parser.cs b/IFPSAsmLib/Parser.cs new file mode 100644 index 0000000..5797978 --- /dev/null +++ b/IFPSAsmLib/Parser.cs @@ -0,0 +1,995 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Collections; + +namespace IFPSAsmLib +{ + internal enum ParserSeparatorType : byte + { + StartBody, + NextBody, + EndBody, + NextOrEndBody, + NextOrStartBody, + Unknown + } + internal static class ParserExtensions + { + private static bool Matches(this char input, ParserSeparatorType type) + { + switch (type) + { + case ParserSeparatorType.StartBody: + return input == Constants.START_BODY; + case ParserSeparatorType.NextBody: + return input == Constants.NEXT_BODY; + case ParserSeparatorType.EndBody: + return input == Constants.END_BODY; + case ParserSeparatorType.NextOrEndBody: + return input == Constants.NEXT_BODY || input == Constants.END_BODY; + case ParserSeparatorType.NextOrStartBody: + return input == Constants.NEXT_BODY || input == Constants.START_BODY; + default: + return false; + } + } + + private static ParserSeparatorType GetSeparator(this char input) + { + if (input == Constants.START_BODY) return ParserSeparatorType.StartBody; + if (input == Constants.NEXT_BODY) return ParserSeparatorType.NextBody; + if (input == Constants.END_BODY) return ParserSeparatorType.EndBody; + return ParserSeparatorType.Unknown; + } + + private static void ThrowEof(ParserLocation location) + { + throw new InvalidDataException(string.Format("Unexpected end of file [:{0},{1}]", location.Line, location.Column)); + } + internal static string ToLiteral(this string input) + { + StringBuilder literal = new StringBuilder(input.Length + 2); + literal.Append(Constants.STRING_CHAR); + foreach (var c in input) + { + switch (c) + { + case '\"': literal.Append("\\\""); break; + case '\\': literal.Append(@"\\"); break; + case '\0': literal.Append(@"\0"); break; + case '\a': literal.Append(@"\a"); break; + case '\b': literal.Append(@"\b"); break; + case '\f': literal.Append(@"\f"); break; + case '\n': literal.Append(@"\n"); break; + case '\r': literal.Append(@"\r"); break; + case '\t': literal.Append(@"\t"); break; + case '\v': literal.Append(@"\v"); break; + default: + // ASCII printable character + if ((c >= 0x20 && c <= 0x7e) || !char.IsControl(c)) + { + literal.Append(c); + // As UTF16 escaped character + } + else if (c < 0x100) + { + literal.Append(@"\x"); + literal.Append(((int)c).ToString("x2")); + } + else + { + literal.Append(@"\u"); + literal.Append(((int)c).ToString("x4")); + } + break; + } + } + literal.Append(Constants.STRING_CHAR); + return literal.ToString(); + } + + internal static bool AdvanceToNextLine(this ReadOnlySpan str, ref ParserLocation location) + { + while (str[location.Offset] != '\n') + { + location.AdvanceOffset(); + if (location.Offset >= str.Length) return false; + } + location.AdvanceOffset(); + location = location.AdvanceLine(); + return true; + } + + internal static bool AdvanceWhiteSpace(this ReadOnlySpan str, ref ParserLocation location, bool disallowNewLine) + { + if (location.Offset >= str.Length) return false; + while (char.IsWhiteSpace(str[location.Offset]) || str[location.Offset] == Constants.COMMENT_START) + { + var offset = location.Offset; + if (disallowNewLine && (str[offset] == '\r' || str[offset] == '\n' || str[offset] == Constants.COMMENT_START)) { + throw new InvalidDataException(string.Format("Unexpected new line [:{0},{1}]", location.Line, location.Column)); + } + if (!disallowNewLine && str[offset] == Constants.COMMENT_START) break; + location.AdvanceOffset(); + if (str[offset] == '\n') + { + location = location.AdvanceLine(); + } + if (location.Offset >= str.Length) + { + if (!disallowNewLine) return false; + ThrowEof(location); + } + } + return true; + } + + internal static bool AdvanceWhiteSpaceUntilNewLine(this ReadOnlySpan str, ref ParserLocation location) + { + while (char.IsWhiteSpace(str[location.Offset]) || str[location.Offset] == Constants.COMMENT_START) + { + var offset = location.Offset; + if (str[offset] == Constants.COMMENT_START) return true; + location.AdvanceOffset(); + if (str[offset] == '\r' || str[offset] == '\n') + { + if (str[offset] == '\n') + { + location = location.AdvanceLine(); + } + return true; + } + if (location.Offset >= str.Length) + ThrowEof(location); + } + return false; + } + + private static bool IsValidNibble(this char input) + { + return + (input >= '0' && input <= '9') || + (input >= 'a' && input <= 'f') || + (input >= 'A' && input <= 'F') + ; + } + + internal static ParserEntity GetEntity(this ReadOnlySpan str, ref ParserLocation location, ParserSeparatorType? separatorType = null, bool optional = true) + { + var origLoc = location.Clone(); + var start = location.Offset; + var length = 0; + bool inQuotes = false; + byte escapingCount = 0; + byte escapingState = 0; + for (var chr = str[location.Offset]; inQuotes || !char.IsWhiteSpace(chr); location.AdvanceOffset(), length++) + { + if (location.Offset >= str.Length) ThrowEof(location); + chr = str[location.Offset]; + // allow for escaped data in quotes + if (inQuotes) + { + if (escapingState == 1) + { + switch (chr) + { + case '\"': + case '\\': + case '0': + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + escapingCount = escapingState = 0; + continue; + case 'x': // 2 hexits + escapingCount += 2; + break; + case 'u': // 4 hexits + escapingCount += 4; + break; + default: + throw new InvalidDataException(string.Format("Invalid escape character '{0}' [:{1},{2}]", chr, location.Line, location.Column)); + } + escapingState++; + } + else if (escapingState == (escapingCount - 1)) + { + escapingCount = escapingState = 0; + } + else if (escapingState == 0) + { + if (chr == '\\') + { + escapingState = escapingCount = 1; + } + else if (chr == '\"') + { + inQuotes = false; + } + } + else + { + if (!chr.IsValidNibble()) + throw new InvalidDataException(string.Format("Invalid hex digit '{0}' [:{1},{2}]", chr, location.Line, location.Column)); + escapingState++; + } + continue; + } else if (chr == '\"') + { + inQuotes = true; + continue; + } + + if (!separatorType.HasValue) continue; + if (chr.Matches(separatorType.Value)) break; + } + if (char.IsWhiteSpace(str[start + length - 1])) length--; + // Allow for whitespace before the separator, do not allow cr or lf. + var found = ParserSeparatorType.Unknown; + if (separatorType.HasValue) + { + for (var chr = str[location.Offset]; char.IsWhiteSpace(chr) && chr != '\r' && chr != '\n'; location.AdvanceOffset()) + { + // no operation + } + // Separator must be at this point, if not optional. + found = str[location.Offset].GetSeparator(); + var matches = str[location.Offset].Matches(separatorType.Value); + if (!optional && !matches) + { + throw new InvalidDataException(string.Format("After {0}: expected '{1}', got '{2}' {3}", str.Slice(start, length).ToString(), separatorType.Value, found, location)); + } + if (matches) location.AdvanceOffset(); + } + return new ParserEntity(str.Slice(start, length), origLoc, found); + } + + internal static void EnsureBodyEmpty(this ReadOnlySpan str, ref ParserLocation location) + { + if (location.Offset >= str.Length) ThrowEof(location); + if (str[location.Offset] != Constants.END_BODY) throw new InvalidDataException(string.Format("Expected empty declaration body {0}", location)); + location.AdvanceOffset(); + } + } + + internal class ParserLocation + { + internal int Offset; + internal int Line; + internal int Column; + + internal ParserLocation Clone() + { + return new ParserLocation() + { + Offset = Offset, + Line = Line, + Column = Column + }; + } + + internal ParserLocation AdvanceLine() + { + return new ParserLocation() + { + Offset = Offset, + Line = Line + 1, + Column = 0 + }; + } + + internal void AdvanceOffset() + { + Offset++; + Column++; + } + + public override string ToString() + { + return string.Format("[:{0},{1}]", Line, Column); + } + + internal string ToStringWithOffset(int offset) + { + return string.Format("[:{0},{1}]", Line, Column + offset); + } + } + + internal readonly ref struct ParserEntity + { + internal readonly ReadOnlySpan Value; + internal readonly ParserLocation Location; + internal readonly ParserSeparatorType? SeparatorType; + + internal bool FoundSeparator => SeparatorType.HasValue; + + internal ParserEntity(ReadOnlySpan val, ParserLocation loc, ParserSeparatorType type) + { + Value = val; + Location = loc; + SeparatorType = null; + if (type != ParserSeparatorType.Unknown) SeparatorType = type; + } + + public override string ToString() + { + return Value.ToString(); + } + + internal string ToStringForError() + { + return string.Format("{0} {1}", Value.ToString().ToLiteral(), Location); + } + + internal string ToStringForError(int offset) + { + return string.Format("{0} {1}", Value.ToString().ToLiteral(), Location.ToStringWithOffset(offset)); + } + + internal void ThrowInvalid() + { + throw new InvalidDataException(string.Format("Invalid token {0}", ToStringForError())); + } + + internal bool Equals(string str) + { + if (Value.Length != str.Length) return false; + for (int i = 0; i < Value.Length; i++) + { + if (Value[i] != str[i]) return false; + } + return true; + } + + internal void ExpectToken(string token) + { + if (!Equals(token)) ThrowInvalid(); + } + + internal void ExpectValidName() + { + var indexOf = Value.IndexOfAny(Constants.STRING_CHAR, Constants.START_BODY, Constants.END_BODY); + if (indexOf == -1) return; + ThrowInvalid(); + } + + internal void ExpectString() + { + if (Value.Length < 2) ThrowInvalid(); + if (Value[0] != Constants.STRING_CHAR) ThrowInvalid(); + if (Value[Value.Length - 1] != Constants.STRING_CHAR) ThrowInvalid(); + } + + internal void ExpectSeparatorType(ParserSeparatorType type) + { + var actualType = SeparatorType.HasValue ? SeparatorType.Value : ParserSeparatorType.Unknown; + if (type == ParserSeparatorType.NextOrEndBody) + { + if (actualType == ParserSeparatorType.NextBody || actualType == ParserSeparatorType.EndBody) type = actualType; + } + if (type == ParserSeparatorType.NextOrStartBody) + { + if (actualType == ParserSeparatorType.NextBody || actualType == ParserSeparatorType.StartBody) type = actualType; + } + + if (type != actualType) + throw new InvalidDataException(string.Format("After {0}: expected '{1}', got '{2}' {3}", Value.ToString(), type, actualType, Location)); + } + } + + public enum ElementParentType : byte + { + Unknown, + FileVersion, + EntryPoint, + Attribute, + Type, + GlobalVariable, + Function, + Alias, + Define, + Instruction, + Label, + } + + public class ParserElement + { + /// + /// Value of this element + /// + public readonly string Value; + /// + /// The next element on this line; null if not present. + /// + public ParserElement Next { get; internal set; } = null; + /// + /// The next child element (elements inside brackets); null if not present. + /// + public ParserElement NextChild { get; internal set; } = null; + /// + /// Line number of this element + /// + public readonly int Line; + /// + /// Column index of this element + /// + public readonly int Column; + /// + /// Parent element type + /// + public ElementParentType ParentType; + + internal ParserElement(ParserEntity entity, ElementParentType type) + { + Value = entity.ToString(); + Line = entity.Location.Line; + Column = entity.Location.Column; + ParentType = type; + } + + public IEnumerable Children + { + get + { + for (var next = NextChild; next != null; next = next.NextChild) yield return next; + } + } + + public IEnumerable Tokens + { + get + { + yield return this; + for (var next = Next; next != null; next = next.Next) yield return next; + } + } + + internal string ToStringForError() + { + return string.Format("{0} [:{1},{2}]", Value.ToLiteral(), Line, Column); + } + + internal void ThrowInvalid() + { + ThrowInvalid("Invalid token"); + } + + internal void ThrowInvalid(string error) + { + throw new InvalidDataException(string.Format("{0} {1}", error, ToStringForError())); + } + + internal void ExpectValidName() + { + var indexOf = Value.AsSpan().IndexOfAny(Constants.STRING_CHAR, Constants.START_BODY, Constants.END_BODY); + if (indexOf == -1) return; + ThrowInvalid(); + } + + internal void ExpectString() + { + if (Value.Length < 2) ThrowInvalid(); + if (Value[0] != Constants.STRING_CHAR) ThrowInvalid(); + if (Value[Value.Length - 1] != Constants.STRING_CHAR) ThrowInvalid(); + } + + public override string ToString() + { + return Value; + } + } + + public class ParsedBody + { + /// + /// The main element; type, function, global, or entry point. + /// + public readonly ParserElement Element; + /// + /// Children instruction elements of a function element. + /// + public readonly IReadOnlyList Children; + + private static IReadOnlyList s_EmptyList = new List(0).AsReadOnly(); + + internal ParsedBody(ParserElement elem) : this(elem, s_EmptyList) + { + + } + + internal ParsedBody(ParserElement elem, List list) : this(elem, list == null ? s_EmptyList : list.AsReadOnly()) + { + + } + + private ParsedBody(ParserElement elem, IReadOnlyList list) + { + Element = elem; + Children = list; + } + } + + public static class Parser + { + private static void ParseTokenWithUnknownBody( + ReadOnlySpan str, + ref ParserLocation location, + ElementParentType parentType, + ParserElement current, + ref ParserElement next, + bool baseType + ) + { + ParserElement currentChild = null, nextChild = null; + str.AdvanceWhiteSpace(ref location, true); + var typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, false); + typeNext.ExpectValidName(); + current.Next = next = new ParserElement(typeNext, parentType); + if (baseType && parentType == ElementParentType.Type && next.Value == "funcptr") + { + ParseTokenWithUnknownBody(str, ref location, parentType, next, ref next, false); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false); + typeNext.ExpectToken(string.Empty); + return; + } + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, true); + currentChild = nextChild = next.NextChild = new ParserElement(typeNext, parentType); + while (!typeNext.FoundSeparator || typeNext.SeparatorType.Value != ParserSeparatorType.EndBody) + { + bool nextIsChild = typeNext.FoundSeparator; + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, true); + if (nextIsChild) + { + currentChild.NextChild = new ParserElement(typeNext, parentType); + currentChild = nextChild = currentChild.NextChild; + } + else + { + nextChild.Next = new ParserElement(typeNext, parentType); + nextChild = nextChild.Next; + } + } + } + + internal static ParserElement Parse(this ReadOnlySpan str, ref ParserLocation location) + { + ParserElement current = null, next = null, nextChild = null; + var parentType = default(ElementParentType); + while (true) + { + if (!str.AdvanceWhiteSpace(ref location, false)) return null; + var chr = str[location.Offset]; + // comment + if (chr == Constants.COMMENT_START) + { + if (!str.AdvanceToNextLine(ref location)) return null; + continue; + } + // element + if (chr == Constants.ELEMENT_START) + { + var type = str.GetEntity(ref location, ParserSeparatorType.StartBody, true); + var typeString = type.ToString(); + switch (typeString) + { + case Constants.ELEMENT_VERSION: + // .version int + if (type.FoundSeparator) str.EnsureBodyEmpty(ref location); + parentType = ElementParentType.FileVersion; + current = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + var typeNext = str.GetEntity(ref location); + if (!int.TryParse(typeNext.Value.ToString(), out var __int)) + typeNext.ThrowInvalid(); + current.Next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) current.Next.ThrowInvalid(); + return current; + case Constants.ELEMENT_ENTRYPOINT: + // .entry function_name + if (type.FoundSeparator) str.EnsureBodyEmpty(ref location); + parentType = ElementParentType.EntryPoint; + current = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + current.Next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) current.Next.ThrowInvalid(); + return current; + case Constants.ELEMENT_TYPE: + // .type [(export)] type(...) name + parentType = ElementParentType.Type; + current = new ParserElement(type, parentType); + if (type.FoundSeparator) + { + str.AdvanceWhiteSpace(ref location, true); + var typeChild = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + if (!typeChild.Value.IsEmpty) + { + if (!typeChild.Equals(Constants.ELEMENT_BODY_IMPORTED)) + typeChild.ExpectToken(Constants.ELEMENT_BODY_EXPORTED); + current.NextChild = new ParserElement(typeChild, parentType); + } + } + + // type(...), will be looked at later + ParseTokenWithUnknownBody(str, ref location, parentType, current, ref next, true); + + // name + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + next.Next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) next.Next.ThrowInvalid(); + return current; + case Constants.ELEMENT_ALIAS: + // .alias varname additionalname + if (type.FoundSeparator) str.EnsureBodyEmpty(ref location); + parentType = ElementParentType.Alias; + current = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + current.Next = next = new ParserElement(typeNext, parentType); + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + next.Next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) next.Next.ThrowInvalid(); + return current; + case Constants.ELEMENT_DEFINE: + // .define immediate_operand defname + if (type.FoundSeparator) str.EnsureBodyEmpty(ref location); + parentType = ElementParentType.Define; + current = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, false); + typeNext.ExpectValidName(); + current.Next = next = new ParserElement(typeNext, parentType); + // operand is an immediate value: type(data) + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + next.NextChild = new ParserElement(typeNext, parentType); + // name + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + next.Next = next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) next.ThrowInvalid(); + return current; + case Constants.ELEMENT_GLOBALVARIABLE: + // .global [(export)] typename varname + parentType = ElementParentType.GlobalVariable; + current = next = new ParserElement(type, parentType); + if (type.FoundSeparator) + { + str.AdvanceWhiteSpace(ref location, true); + var typeChild = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + if (!typeChild.Value.IsEmpty) + { + if (!typeChild.Equals(Constants.ELEMENT_BODY_IMPORTED)) + typeChild.ExpectToken(Constants.ELEMENT_BODY_EXPORTED); + current.NextChild = new ParserElement(typeChild, parentType); + } + } + + // typename + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + next.Next = new ParserElement(typeNext, parentType); + next = next.Next; + + // varname + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + next.Next = new ParserElement(typeNext, parentType); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) next.Next.ThrowInvalid(); + return current; + case Constants.ELEMENT_FUNCTION: + // function + // .function [(export)] external callingconv internal|com(vtblindex)|class(...)|dll(...) returnsval|void name (...) + // .function [(export)] void|typename name(...) + + parentType = ElementParentType.Function; + current = next = new ParserElement(type, parentType); + if (type.FoundSeparator) + { + str.AdvanceWhiteSpace(ref location, true); + var typeChild = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + if (!typeChild.Value.IsEmpty) + { + if (!typeChild.Equals(Constants.ELEMENT_BODY_IMPORTED)) + typeChild.ExpectToken(Constants.ELEMENT_BODY_EXPORTED); + current.NextChild = new ParserElement(typeChild, parentType); + } + } + + // external|void|typename + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location); + typeNext.ExpectValidName(); + current.Next = new ParserElement(typeNext, parentType); + next = next.Next; + + str.AdvanceWhiteSpace(ref location, true); + if (next.Value == Constants.FUNCTION_EXTERNAL) + { + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, true); + typeNext.ExpectValidName(); + if (typeNext.Equals(Constants.FUNCTION_EXTERNAL_INTERNAL)) + { + typeNext.ExpectSeparatorType(ParserSeparatorType.Unknown); + next.Next = next = new ParserElement(typeNext, parentType); + } + else + { + if (typeNext.Equals(Constants.FUNCTION_EXTERNAL_COM)) + { + // com(vtblindex) + typeNext.ExpectSeparatorType(ParserSeparatorType.StartBody); + next.Next = next = new ParserElement(typeNext, parentType); + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + if (!int.TryParse(typeNext.Value.ToString(), out __int)) + typeNext.ThrowInvalid(); + next.NextChild = new ParserElement(typeNext, parentType); + } + else if (typeNext.Equals(Constants.FUNCTION_EXTERNAL_CLASS)) + { + // class(classname,funcname[,property]) + typeNext.ExpectSeparatorType(ParserSeparatorType.StartBody); + next.Next = next = new ParserElement(typeNext, parentType); + // classname + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextBody, false); + typeNext.ExpectValidName(); + next.NextChild = nextChild = new ParserElement(typeNext, parentType); + // funcname + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false); + typeNext.ExpectValidName(); + nextChild.NextChild = nextChild = new ParserElement(typeNext, parentType); + // property + if (typeNext.SeparatorType == ParserSeparatorType.NextBody) + { + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + typeNext.ExpectToken(Constants.FUNCTION_EXTERNAL_CLASS_PROPERTY); + nextChild.NextChild = nextChild = new ParserElement(typeNext, parentType); + } + } + else if (typeNext.Equals(Constants.FUNCTION_EXTERNAL_DLL)) + { + // dll(dllname,procname[,delayload][,alteredsearchpath]) + typeNext.ExpectSeparatorType(ParserSeparatorType.StartBody); + next.Next = next = new ParserElement(typeNext, parentType); + // dllname + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextBody, false); + typeNext.ExpectString(); + next.NextChild = nextChild = new ParserElement(typeNext, parentType); + // procname + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false); + typeNext.ExpectString(); + nextChild.NextChild = nextChild = new ParserElement(typeNext, parentType); + bool firstWasDelayLoad = false; + // delayload|alteredsearchpath + if (typeNext.SeparatorType == ParserSeparatorType.NextBody) + { + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false); + if (!typeNext.Equals(Constants.FUNCTION_EXTERNAL_DLL_DELAYLOAD)) + { + if (!typeNext.Equals(Constants.FUNCTION_EXTERNAL_DLL_ALTEREDSEARCHPATH)) typeNext.ThrowInvalid(); + } + else firstWasDelayLoad = true; + nextChild.NextChild = nextChild = new ParserElement(typeNext, parentType); + } + // other delayload|alteredsearchpath + if (typeNext.SeparatorType == ParserSeparatorType.NextBody) + { + str.AdvanceWhiteSpace(ref location, true); + typeNext = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + typeNext.ExpectToken( + firstWasDelayLoad ? + Constants.FUNCTION_EXTERNAL_DLL_ALTEREDSEARCHPATH : + Constants.FUNCTION_EXTERNAL_DLL_DELAYLOAD + ); + nextChild.NextChild = nextChild = new ParserElement(typeNext, parentType); + } + } + else + typeNext.ThrowInvalid(); + + str.AdvanceWhiteSpace(ref location, true); + // calling convention + typeNext = str.GetEntity(ref location); + if ( + !typeNext.Equals(Constants.FUNCTION_FASTCALL) && + !typeNext.Equals(Constants.FUNCTION_PASCAL) && + !typeNext.Equals(Constants.FUNCTION_CDECL) && + !typeNext.Equals(Constants.FUNCTION_STDCALL) + ) + typeNext.ThrowInvalid(); + next.Next = next = new ParserElement(typeNext, parentType); + } + str.AdvanceWhiteSpace(ref location, true); + // returnsval|void + typeNext = str.GetEntity(ref location); + if (!typeNext.Equals(Constants.FUNCTION_RETURN_VAL) && !typeNext.Equals(Constants.FUNCTION_RETURN_VOID)) typeNext.ThrowInvalid(); + next.Next = next = new ParserElement(typeNext, parentType); + } + + // name(...), will be looked at later + ParseTokenWithUnknownBody(str, ref location, parentType, next, ref next, false); + + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) next.ThrowInvalid(); + return current; + default: + type.ThrowInvalid(); + break; + } + } + // attribute + if (chr == Constants.START_ATTRIBUTE) + { + parentType = ElementParentType.Attribute; + location.AdvanceOffset(); + str.AdvanceWhiteSpace(ref location, true); + // name(operands) + var type = str.GetEntity(ref location, ParserSeparatorType.StartBody, false); + type.ExpectValidName(); + current = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + // operands + next = current; + if (str[location.Offset] != Constants.END_BODY) + { + do + { + // must be immediate value: type(data) + type = str.GetEntity(ref location, ParserSeparatorType.StartBody, false); + type.ExpectValidName(); + next.Next = next = new ParserElement(type, parentType); + str.AdvanceWhiteSpace(ref location, true); + type = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + next.NextChild = new ParserElement(type, parentType); + // next token may be a comma or end, let's find out + str.AdvanceWhiteSpace(ref location, true); + // expecting a blank token + type = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false); + type.ExpectToken(string.Empty); + } while (type.SeparatorType == ParserSeparatorType.NextBody); + } + else location.AdvanceOffset(); + str.AdvanceWhiteSpace(ref location, true); + if (str[location.Offset] != Constants.END_ATTRIBUTE) + { + type.ThrowInvalid(); + } + location.AdvanceOffset(); + if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) type.ThrowInvalid(); + return current; + } + + // instruction or label + parentType = ElementParentType.Label; + var data = str.GetEntity(ref location); + data.ExpectValidName(); + if (data.Value[data.Value.Length - 1] == Constants.LABEL_SPECIFIER) // label + { + data = new ParserEntity( + data.Value.Slice(0, data.Value.Length - 1), + data.Location, + data.SeparatorType.HasValue ? data.SeparatorType.Value : ParserSeparatorType.Unknown + ); + var label = new ParserElement(data, parentType); + if (label.Value == Constants.LABEL_NULL) label.ThrowInvalid(); + return label; + } + // instruction + parentType = ElementParentType.Instruction; + // opcode ... + current = new ParserElement(data, parentType); + if (str.AdvanceWhiteSpaceUntilNewLine(ref location)) return current; + // operands + next = current; + do + { + data = str.GetEntity(ref location, ParserSeparatorType.NextOrStartBody, true); + data.ExpectValidName(); + next.Next = next = new ParserElement(data, parentType); + if (data.FoundSeparator && data.SeparatorType == ParserSeparatorType.StartBody) + { + // operand is an immediate value: type(data) + str.AdvanceWhiteSpace(ref location, true); + data = str.GetEntity(ref location, ParserSeparatorType.EndBody, false); + next.NextChild = new ParserElement(data, parentType); + // next token may be a comma or newline, let's find out + if (str.AdvanceWhiteSpaceUntilNewLine(ref location)) break; + // expecting a blank token with NextBody + data = str.GetEntity(ref location, ParserSeparatorType.NextBody, false); + data.ExpectToken(string.Empty); + } + } while (!str.AdvanceWhiteSpaceUntilNewLine(ref location)); + return current; + } + } + + public static List Parse(ReadOnlySpan str) + { + var location = new ParserLocation(); + + var ret = new List(); + ParserElement last = null; + List list = null; + List attributes = null; + do + { + var elem = str.Parse(ref location); + if (elem == null) + { + if (last != null) + { + ret.Add(new ParsedBody(last, list)); + } + break; + } + + switch (elem.ParentType) + { + case ElementParentType.Unknown: + elem.ThrowInvalid(); + break; + case ElementParentType.Attribute: + if (attributes == null) attributes = new List(); + attributes.Add(elem); + break; + case ElementParentType.Instruction: + case ElementParentType.Label: + case ElementParentType.Alias: + if (attributes != null) elem.ThrowInvalid(); + if (list == null) list = new List(); + if (last == null) elem.ThrowInvalid(); + list.Add(elem); + break; + case ElementParentType.Function: + if (last != null) + { + ret.Add(new ParsedBody(last, list)); + list = null; + } + list = attributes; + attributes = null; + last = elem; + break; + default: + if (last != null) + { + ret.Add(new ParsedBody(last, list)); + last = null; + list = null; + } + if (elem.ParentType != ElementParentType.Type && attributes != null) elem.ThrowInvalid(); + ret.Add(new ParsedBody(elem, attributes)); + attributes = null; + break; + } + } while (true); + return ret; + } + + public static List Parse(string str) => Parse(str.AsSpan()); + } +} diff --git a/IFPSLib.Tests/CompiledCode.bin b/IFPSLib.Tests/CompiledCode.bin new file mode 100644 index 0000000..09d34e4 Binary files /dev/null and b/IFPSLib.Tests/CompiledCode.bin differ diff --git a/IFPSLib.Tests/CompiledCode.txt b/IFPSLib.Tests/CompiledCode.txt new file mode 100644 index 0000000..c28eb3d --- /dev/null +++ b/IFPSLib.Tests/CompiledCode.txt @@ -0,0 +1,3175 @@ +.version 23 + +.entry !MAIN + +.type primitive(Pointer) Pointer +.type primitive(U32) U32 +.type primitive(Variant) Variant +.type primitive(PChar) PChar +.type primitive(Currency) Currency +.type primitive(Extended) Extended +.type primitive(Double) Double +.type primitive(Single) Single +.type primitive(S64) S64 +.type primitive(String) String +.type primitive(U32) U32_2 +.type primitive(S32) S32 +.type primitive(S16) S16 +.type primitive(U16) U16 +.type primitive(S8) S8 +.type(export) funcptr(void()) ANYMETHOD +.type primitive(String) String_2 +.type primitive(UnicodeString) UnicodeString +.type primitive(UnicodeString) UnicodeString_2 +.type primitive(String) String_3 +.type primitive(UnicodeString) UnicodeString_3 +.type primitive(WideString) WideString +.type primitive(WideChar) WideChar +.type primitive(WideChar) WideChar_2 +.type primitive(Char) Char +.type primitive(U8) U8 +.type primitive(U16) U16_2 +.type primitive(U32) U32_3 +.type(export) primitive(U8) BOOLEAN +.type primitive(U8) U8_2 +.type(export) class(TWIZARDFORM) TWIZARDFORM +.type(export) class(TMAINFORM) TMAINFORM +.type(export) class(TUNINSTALLPROGRESSFORM) TUNINSTALLPROGRESSFORM +.type(export) class(TDOWNLOADWIZARDPAGE) TDOWNLOADWIZARDPAGE +.type(export) class(TPANEL) TPANEL +.type(export) class(TBITMAPIMAGE) TBITMAPIMAGE +.type funcptr(void(__in,__in,__in)) Type36 +.type(export) class(TFONT) TFONT +.type(export) array(Pointer) !OPENARRAYOFCONST +.type array(Variant) Type39 +.type(export) class(TBITMAP) TBITMAP +.type(export) class(TCONTROL) TCONTROL +.type(export) class(TWINCONTROL) TWINCONTROL +.type(export) class(TNEWBUTTON) TNEWBUTTON +.type(export) class(TNEWSTATICTEXT) TNEWSTATICTEXT +.type(export) interface("00020400-0000-0000-c000-000000000046") IDISPATCH +.type(export) array(Variant) !OPENARRAYOFVARIANT +.type(export) funcptr(returnsval(__in,__in,__in,__in)) TONDOWNLOADPROGRESS +.type(export) primitive(U16) TSETUPMESSAGEID +.type(export) class(TNEWPROGRESSBAR) TNEWPROGRESSBAR +.type(export) class(TNEWRADIOBUTTON) TNEWRADIOBUTTON +.type(export) class(TCOMPONENT) TCOMPONENT +.type(export) class(TNEWNOTEBOOKPAGE) TNEWNOTEBOOKPAGE +.type(export) primitive(U8) TPANELBEVEL +.type(export) primitive(U8) TEXECWAIT +.type(export) primitive(U8) TMSGBOXTYPE +.type(export) class(TOBJECT) TOBJECT +.type(export) funcptr(void(__in)) TNOTIFYEVENT +.type(export) primitive(U8) TUNINSTALLSTEP +.type(export) primitive(U8) TSETUPSTEP + +.global(export) TWIZARDFORM WIZARDFORM +.global(export) TMAINFORM MAINFORM +.global(export) TUNINSTALLPROGRESSFORM UNINSTALLPROGRESSFORM +.global TDOWNLOADWIZARDPAGE Global3 +.global TPANEL Global4 +.global S32 Global5 +.global S32 Global6 +.global S32 Global7 +.global S32 Global8 +.global S32 Global9 +.global TBITMAPIMAGE Global10 + +.function(export) void !MAIN() + ret + +.function(export) external dll("files:mediaplayer.dll","DSGetLastError") __stdcall returnsval files:mediaplayer.dll!DSGetLastError(__out __unknown) + +.function(export) external dll("files:mediaplayer.dll","DSPlayMediaFile") __stdcall returnsval files:mediaplayer.dll!DSPlayMediaFile() + +.function(export) external dll("files:mediaplayer.dll","DSStopMediaPlay") __stdcall returnsval files:mediaplayer.dll!DSStopMediaPlay() + +.function(export) external dll("files:mediaplayer.dll","DSSetVolume") __stdcall returnsval files:mediaplayer.dll!DSSetVolume(__in __unknown) + +.function(export) external dll("files:mediaplayer.dll","DSSetBalance") __stdcall returnsval files:mediaplayer.dll!DSSetBalance(__in __unknown) + +.function(export) external dll("files:mediaplayer.dll","DSInitializeAudioFile") __stdcall returnsval files:mediaplayer.dll!DSInitializeAudioFile(__in __unknown,__in __unknown) + +.function(export) external dll("files:mediaplayer.dll","DSInitializeVideoFile") __stdcall returnsval files:mediaplayer.dll!DSInitializeVideoFile(__in __unknown,__in __unknown,__out __unknown,__out __unknown,__in __unknown) + +.function(export) external dll("kernel32.dll","ExitProcess") __stdcall void kernel32.dll!ExitProcess(__in __unknown) + +.function(export) S32 GETSCALINGFACTOR() + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype TFONT ; StackCount = 3 + pushtype TWIZARDFORM ; StackCount = 4 + assign Var4, WIZARDFORM + pushvar Var3 ; StackCount = 5 + call TFORM->FONT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call TFONT->PIXELSPERINCH + pop ; StackCount = 3 + pop ; StackCount = 2 + ge Var1, Var2, S32(168) + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_70 + assign RetVal, S32(175) + jump loc_22b +loc_70: + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype TFONT ; StackCount = 3 + pushtype TWIZARDFORM ; StackCount = 4 + assign Var4, WIZARDFORM + pushvar Var3 ; StackCount = 5 + call TFORM->FONT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call TFONT->PIXELSPERINCH + pop ; StackCount = 3 + pop ; StackCount = 2 + ge Var1, Var2, S32(144) + pop ; StackCount = 1 + jz loc_125, Var1 + pushtype BOOLEAN ; StackCount = 2 + pushtype S32 ; StackCount = 3 + pushtype TFONT ; StackCount = 4 + pushtype TWIZARDFORM ; StackCount = 5 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 6 + call TFORM->FONT + pop ; StackCount = 5 + pop ; StackCount = 4 + pushvar Var3 ; StackCount = 5 + call TFONT->PIXELSPERINCH + pop ; StackCount = 4 + pop ; StackCount = 3 + lt Var2, Var3, S32(168) + pop ; StackCount = 2 + and Var1, Var2 + pop ; StackCount = 1 +loc_125: + sfz Var1 + pop ; StackCount = 0 + jf loc_146 + assign RetVal, S32(150) + jump loc_22b +loc_146: + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype TFONT ; StackCount = 3 + pushtype TWIZARDFORM ; StackCount = 4 + assign Var4, WIZARDFORM + pushvar Var3 ; StackCount = 5 + call TFORM->FONT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call TFONT->PIXELSPERINCH + pop ; StackCount = 3 + pop ; StackCount = 2 + ge Var1, Var2, S32(120) + pop ; StackCount = 1 + jz loc_1fb, Var1 + pushtype BOOLEAN ; StackCount = 2 + pushtype S32 ; StackCount = 3 + pushtype TFONT ; StackCount = 4 + pushtype TWIZARDFORM ; StackCount = 5 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 6 + call TFORM->FONT + pop ; StackCount = 5 + pop ; StackCount = 4 + pushvar Var3 ; StackCount = 5 + call TFONT->PIXELSPERINCH + pop ; StackCount = 4 + pop ; StackCount = 3 + lt Var2, Var3, S32(144) + pop ; StackCount = 2 + and Var1, Var2 + pop ; StackCount = 1 +loc_1fb: + sfz Var1 + pop ; StackCount = 0 + jf loc_21c + assign RetVal, S32(125) + jump loc_22b +loc_21c: + assign RetVal, S32(100) +loc_22b: + ret + +.function(export) external class(TFORM, FONT) __pascal void TFORM->FONT(__in __unknown,__in __unknown) + +.function(export) external class(TFONT, PIXELSPERINCH) __pascal void TFONT->PIXELSPERINCH(__in __unknown,__in __unknown) + +.function(export) void LOADEMBEDEDSCALEDBITMAP(__in TBITMAPIMAGE Arg1,__in UnicodeString_2 Arg2) + pushtype UnicodeString_2 ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushtype !OPENARRAYOFCONST ; StackCount = 3 + pushtype !OPENARRAYOFCONST ; StackCount = 4 + pushtype S32 ; StackCount = 5 + assign Var5, S32(2) + pushvar Var4 ; StackCount = 6 + call SETARRAYLENGTH + pop ; StackCount = 5 + pop ; StackCount = 4 + cpval Var4[0], Arg2 + pushtype S32 ; StackCount = 5 + pushvar Var5 ; StackCount = 6 + call GETSCALINGFACTOR + pop ; StackCount = 5 + cpval Var4[1], Var5 + pop ; StackCount = 4 + assign Var3, Var4 + pop ; StackCount = 3 + pushtype UnicodeString_2 ; StackCount = 4 + assign Var4, UnicodeString_3("%s %d.bmp") + pushvar Var1 ; StackCount = 5 + call FORMAT + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, Var1 + call EXTRACTTEMPORARYFILE + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_3 ; StackCount = 4 + assign Var4, UnicodeString_3("{tmp}\\") + add Var4, Var1 + assign Var3, Var4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call EXPANDCONSTANT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, Var2 + pushtype TBITMAP ; StackCount = 4 + pushtype TBITMAPIMAGE ; StackCount = 5 + assign Var5, Arg1 + pushvar Var4 ; StackCount = 6 + call TBITMAPIMAGE->BITMAP + pop ; StackCount = 5 + pop ; StackCount = 4 + call TGRAPHIC->LOADFROMFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype BOOLEAN ; StackCount = 3 + pushtype UnicodeString_2 ; StackCount = 4 + assign Var4, Var2 + pushvar Var3 ; StackCount = 5 + call DELETEFILE + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + ret + +.function(export) external internal returnsval FORMAT(__in __unknown,__in __unknown) + +.function(export) external internal void SETARRAYLENGTH() + +.function(export) external internal void EXTRACTTEMPORARYFILE(__in __unknown) + +.function(export) external internal returnsval EXPANDCONSTANT(__in __unknown) + +.function(export) external class(TBITMAPIMAGE, BITMAP) __pascal void TBITMAPIMAGE->BITMAP(__in __unknown,__in __unknown) + +.function(export) external class(TGRAPHIC, LOADFROMFILE) __fastcall void TGRAPHIC->LOADFROMFILE(__in __unknown) + +.function(export) external internal returnsval DELETEFILE(__in __unknown) + +.function(export) void CENTERINPARENT2(__in TCONTROL Arg1) + pushtype BOOLEAN ; StackCount = 1 + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + pushvar Var1 ; StackCount = 3 + call !ASSIGNED + pop ; StackCount = 2 + pop ; StackCount = 1 + jz loc_6d, Var1 + pushtype BOOLEAN ; StackCount = 2 + pushtype TWINCONTROL ; StackCount = 3 + pushtype TCONTROL ; StackCount = 4 + assign Var4, Arg1 + pushvar Var3 ; StackCount = 5 + call TCONTROL->PARENT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call !ASSIGNED + pop ; StackCount = 3 + pop ; StackCount = 2 + and Var1, Var2 + pop ; StackCount = 1 +loc_6d: + sfz Var1 + pop ; StackCount = 0 + jf loc_14d + pushtype S32 ; StackCount = 1 + pushtype TWINCONTROL ; StackCount = 2 + pushtype TCONTROL ; StackCount = 3 + assign Var3, Arg1 + pushvar Var2 ; StackCount = 4 + call TCONTROL->PARENT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushvar Var1 ; StackCount = 3 + call TCONTROL->WIDTH + pop ; StackCount = 2 + pop ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype TCONTROL ; StackCount = 3 + assign Var3, Arg1 + pushvar Var2 ; StackCount = 4 + call TCONTROL->WIDTH + pop ; StackCount = 3 + pop ; StackCount = 2 + sub Var1, Var2 + pop ; StackCount = 1 + div Var1, S32(2) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->LEFT + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 2 + assign Var2, Global3 + pushvar Var1 ; StackCount = 3 + call TWIZARDPAGE->SURFACEHEIGHT + pop ; StackCount = 2 + pop ; StackCount = 1 + sub Var1, S32(40) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_14d: + ret + +.function(export) external internal returnsval !ASSIGNED(__in __unknown) + +.function(export) external class(TCONTROL, PARENT) __pascal void TCONTROL->PARENT(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, WIDTH) __pascal void TCONTROL->WIDTH(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, LEFT, property) __pascal void TCONTROL->LEFT(__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDPAGE, SURFACEHEIGHT) __pascal void TWIZARDPAGE->SURFACEHEIGHT(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, TOP, property) __pascal void TCONTROL->TOP(__in __unknown,__in __unknown) + +.function(export) void CENTERINPARENT(__in TCONTROL Arg1) + pushtype BOOLEAN ; StackCount = 1 + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + pushvar Var1 ; StackCount = 3 + call !ASSIGNED + pop ; StackCount = 2 + pop ; StackCount = 1 + jz loc_6d, Var1 + pushtype BOOLEAN ; StackCount = 2 + pushtype TWINCONTROL ; StackCount = 3 + pushtype TCONTROL ; StackCount = 4 + assign Var4, Arg1 + pushvar Var3 ; StackCount = 5 + call TCONTROL->PARENT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call !ASSIGNED + pop ; StackCount = 3 + pop ; StackCount = 2 + and Var1, Var2 + pop ; StackCount = 1 +loc_6d: + sfz Var1 + pop ; StackCount = 0 + jf loc_42a + pushtype S32 ; StackCount = 1 + pushtype TWINCONTROL ; StackCount = 2 + pushtype TCONTROL ; StackCount = 3 + assign Var3, Arg1 + pushvar Var2 ; StackCount = 4 + call TCONTROL->PARENT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushvar Var1 ; StackCount = 3 + call TCONTROL->WIDTH + pop ; StackCount = 2 + pop ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype TCONTROL ; StackCount = 3 + assign Var3, Arg1 + pushvar Var2 ; StackCount = 4 + call TCONTROL->WIDTH + pop ; StackCount = 3 + pop ; StackCount = 2 + sub Var1, Var2 + pop ; StackCount = 1 + div Var1, S32(2) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->LEFT + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global6 + jz loc_147, Var1 + pushtype BOOLEAN ; StackCount = 2 + lt Var2, Global5, Global7 + and Var1, Var2 + pop ; StackCount = 1 +loc_147: + sfz Var1 + pop ; StackCount = 0 + jf loc_1da + pushtype S32 ; StackCount = 1 + assign Var1, S32(20) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(500) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->WIDTH_2 + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(300) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->HEIGHT + pop ; StackCount = 1 + pop ; StackCount = 0 + jump loc_42a +loc_1da: + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global7 + jz loc_21d, Var1 + pushtype BOOLEAN ; StackCount = 2 + lt Var2, Global5, Global8 + and Var1, Var2 + pop ; StackCount = 1 +loc_21d: + sfz Var1 + pop ; StackCount = 0 + jf loc_2b0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(20) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(634) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->WIDTH_2 + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(382) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->HEIGHT + pop ; StackCount = 1 + pop ; StackCount = 0 + jump loc_42a +loc_2b0: + pushtype BOOLEAN ; StackCount = 1 + le Var1, Global5, Global8 + jz loc_2f3, Var1 + pushtype BOOLEAN ; StackCount = 2 + gt Var2, Global9, Global8 + and Var1, Var2 + pop ; StackCount = 1 +loc_2f3: + sfz Var1 + pop ; StackCount = 0 + jf loc_386 + pushtype S32 ; StackCount = 1 + assign Var1, S32(20) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(779) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->WIDTH_2 + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(469) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->HEIGHT + pop ; StackCount = 1 + pop ; StackCount = 0 + jump loc_42a +loc_386: + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global9 + sfz Var1 + pop ; StackCount = 0 + jf loc_42a + pushtype S32 ; StackCount = 1 + assign Var1, S32(20) + pushtype TCONTROL ; StackCount = 2 + assign Var2, Arg1 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(975) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->WIDTH_2 + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(585) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TCONTROL->HEIGHT + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_42a: + ret + +.function(export) external class(TCONTROL, WIDTH, property) __pascal void TCONTROL->WIDTH_2(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, HEIGHT, property) __pascal void TCONTROL->HEIGHT(__in __unknown,__in __unknown) + +.function(export) BOOLEAN ONDOWNLOADPROGRESS(__in UnicodeString_2 Arg1,__in UnicodeString_2 Arg2,__in S64 Arg3,__in S64 Arg4) + pushtype BOOLEAN ; StackCount = 1 + assign Var1, BOOLEAN(0) + pushtype TNEWBUTTON ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TDOWNLOADWIZARDPAGE->ABORTBUTTON + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->VISIBLE + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + assign Var1, BOOLEAN(0) + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->VISIBLE + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 2 + assign Var2, Global3 + pushvar Var1 ; StackCount = 3 + call TWIZARDPAGE->SURFACEHEIGHT + pop ; StackCount = 2 + pop ; StackCount = 1 + sub Var1, S32(70) + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("en") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_1be + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Downloading Additional Files") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_1be: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("fr") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_286 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Téléchargement de fichiers additionnels") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_286: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("de") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_340 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Zusätliche Dateien herunterladen") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_340: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("it") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_3f0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Download di file aggiuntivi") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_3f0: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("es") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_4aa + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Descargando archivos adicionales") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_4aa: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("jp") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_540 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("追加ファイルをダウンロード中") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_540: + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ACTIVELANGUAGE + pop ; StackCount = 2 + eq Var1, Var2, UnicodeString_3("ko") + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_5ce + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("추가 파일 다운로드") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_5ce: + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + assign Var1, BOOLEAN(0) + pushtype TNEWBUTTON ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TDOWNLOADWIZARDPAGE->ABORTBUTTON + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->VISIBLE + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(16777215) + pushtype TPANEL ; StackCount = 2 + assign Var2, Global4 + call TPANEL->COLOR + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + eq Var1, Arg3, Arg4 + sfz Var1 + pop ; StackCount = 0 + jf loc_7d2 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + pushtype !OPENARRAYOFCONST ; StackCount = 2 + pushtype !OPENARRAYOFCONST ; StackCount = 3 + pushtype S32 ; StackCount = 4 + assign Var4, S32(1) + pushvar Var3 ; StackCount = 5 + call SETARRAYLENGTH + pop ; StackCount = 4 + pop ; StackCount = 3 + cpval Var3[0], Arg2 + assign Var2, Var3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, UnicodeString_3("Successfully downloaded file to {tmp}: %s") + pushvar Var1 ; StackCount = 4 + call FORMAT + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 + call LOG + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_7d2: + pushtype BOOLEAN ; StackCount = 1 + ne Var1, Arg4, S32(0) + sfz Var1 + pop ; StackCount = 0 + jf loc_926 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + pushtype !OPENARRAYOFCONST ; StackCount = 2 + pushtype !OPENARRAYOFCONST ; StackCount = 3 + pushtype S32 ; StackCount = 4 + assign Var4, S32(2) + pushvar Var3 ; StackCount = 5 + call SETARRAYLENGTH + pop ; StackCount = 4 + pop ; StackCount = 3 + cpval Var3[0], Arg3 + cpval Var3[1], Arg4 + assign Var2, Var3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, UnicodeString_3(" %d of %d bytes done.") + pushvar Var1 ; StackCount = 4 + call FORMAT + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 + call LOG + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + jump loc_a33 +loc_926: + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + pushtype !OPENARRAYOFCONST ; StackCount = 2 + pushtype !OPENARRAYOFCONST ; StackCount = 3 + pushtype S32 ; StackCount = 4 + assign Var4, S32(1) + pushvar Var3 ; StackCount = 5 + call SETARRAYLENGTH + pop ; StackCount = 4 + pop ; StackCount = 3 + cpval Var3[0], Arg3 + assign Var2, Var3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, UnicodeString_3(" %d bytes done.") + pushvar Var1 ; StackCount = 4 + call FORMAT + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 + call LOG + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_a33: + assign RetVal, BOOLEAN(1) + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + ret + +.function(export) external class(TDOWNLOADWIZARDPAGE, ABORTBUTTON) __pascal void TDOWNLOADWIZARDPAGE->ABORTBUTTON(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, VISIBLE, property) __pascal void TCONTROL->VISIBLE(__in __unknown,__in __unknown) + +.function(export) external class(TOUTPUTPROGRESSWIZARDPAGE, MSG2LABEL) __pascal void TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL(__in __unknown,__in __unknown) + +.function(export) external class(TNEWSTATICTEXT, CAPTION, property) __pascal void TNEWSTATICTEXT->CAPTION(__in __unknown,__in __unknown) + +.function(export) external class(TOUTPUTPROGRESSWIZARDPAGE, MSG1LABEL) __pascal void TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL(__in __unknown,__in __unknown) + +.function(export) external internal returnsval ACTIVELANGUAGE() + +.function(export) external class(TPANEL, COLOR, property) __pascal void TPANEL->COLOR(__in __unknown,__in __unknown) + +.function(export) external internal void LOG(__in __unknown) + +.function(export) void UNZIP(__in PChar Arg1,__in PChar Arg2) + pushtype Variant ; StackCount = 1 + pushtype Variant ; StackCount = 2 + pushtype Variant ; StackCount = 3 + pushtype Variant ; StackCount = 4 + pushtype Variant ; StackCount = 5 + pushtype Variant ; StackCount = 6 + pushtype BOOLEAN ; StackCount = 7 + pushtype UnicodeString_2 ; StackCount = 8 + assign Var8, Arg1 + pushvar Var7 ; StackCount = 9 + call FILEEXISTS + pop ; StackCount = 8 + pop ; StackCount = 7 + sfz Var7 + pop ; StackCount = 6 + jf loc_32f + pushtype BOOLEAN ; StackCount = 7 + pushtype UnicodeString_2 ; StackCount = 8 + assign Var8, Arg2 + pushvar Var7 ; StackCount = 9 + call FORCEDIRECTORIES + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pushtype IDISPATCH ; StackCount = 7 + pushtype UnicodeString_2 ; StackCount = 8 + assign Var8, UnicodeString_3("Shell.Application") + pushvar Var7 ; StackCount = 9 + call CREATEOLEOBJECT + pop ; StackCount = 8 + pop ; StackCount = 7 + assign Var1, Var7 + pop ; StackCount = 6 + assign Var2, Arg1 + assign Var3, Arg2 + pushtype !OPENARRAYOFVARIANT ; StackCount = 7 + pushtype !OPENARRAYOFVARIANT ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(1) + pushvar Var8 ; StackCount = 10 + call SETARRAYLENGTH + pop ; StackCount = 9 + pop ; StackCount = 8 + assign Var8[0], Var2 + assign Var7, Var8 + pop ; StackCount = 7 + pushtype String_3 ; StackCount = 8 + assign Var8, String_3("NameSpace") + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype IDISPATCH ; StackCount = 10 + assign Var10, Var1 + pushvar Var4 ; StackCount = 11 + call IDISPATCHINVOKE + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pushtype !OPENARRAYOFVARIANT ; StackCount = 7 + pushtype !OPENARRAYOFVARIANT ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(1) + pushvar Var8 ; StackCount = 10 + call SETARRAYLENGTH + pop ; StackCount = 9 + pop ; StackCount = 8 + assign Var8[0], Var3 + assign Var7, Var8 + pop ; StackCount = 7 + pushtype String_3 ; StackCount = 8 + assign Var8, String_3("NameSpace") + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype IDISPATCH ; StackCount = 10 + assign Var10, Var1 + pushvar Var5 ; StackCount = 11 + call IDISPATCHINVOKE + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pushtype !OPENARRAYOFVARIANT ; StackCount = 7 + pushtype !OPENARRAYOFVARIANT ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(0) + pushvar Var8 ; StackCount = 10 + call SETARRAYLENGTH + pop ; StackCount = 9 + pop ; StackCount = 8 + assign Var7, Var8 + pop ; StackCount = 7 + pushtype String_3 ; StackCount = 8 + assign Var8, String_3("Items") + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype IDISPATCH ; StackCount = 10 + assign Var10, Var4 + pushvar Var6 ; StackCount = 11 + call IDISPATCHINVOKE + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pushtype Variant ; StackCount = 7 + pushtype !OPENARRAYOFVARIANT ; StackCount = 8 + pushtype !OPENARRAYOFVARIANT ; StackCount = 9 + pushtype S32 ; StackCount = 10 + assign Var10, S32(2) + pushvar Var9 ; StackCount = 11 + call SETARRAYLENGTH + pop ; StackCount = 10 + pop ; StackCount = 9 + assign Var9[0], Var6 + assign Var9[1], S32(20) + assign Var8, Var9 + pop ; StackCount = 8 + pushtype String_3 ; StackCount = 9 + assign Var9, String_3("CopyHere") + pushtype BOOLEAN ; StackCount = 10 + assign Var10, BOOLEAN(0) + pushtype IDISPATCH ; StackCount = 11 + assign Var11, Var5 + pushvar Var7 ; StackCount = 12 + call IDISPATCHINVOKE + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 +loc_32f: + ret + +.function(export) external internal returnsval FILEEXISTS(__in __unknown) + +.function(export) external internal returnsval FORCEDIRECTORIES(__in __unknown) + +.function(export) external internal returnsval CREATEOLEOBJECT(__in __unknown) + +.function(export) external internal returnsval IDISPATCHINVOKE(__in __unknown,__in __unknown,__in __unknown,__in __unknown) + +.function(export) void INITIALIZEWIZARD() + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call GETSCALINGFACTOR + pop ; StackCount = 2 + eq Var1, Var2, S32(125) + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_df + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE2 + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_df: + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call GETSCALINGFACTOR + pop ; StackCount = 2 + eq Var1, Var2, S32(150) + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_1be + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE2 + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_1be: + pushtype BOOLEAN ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call GETSCALINGFACTOR + pop ; StackCount = 2 + eq Var1, Var2, S32(175) + pop ; StackCount = 1 + sfz Var1 + pop ; StackCount = 0 + jf loc_29d + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("WizardImage") + pushtype TBITMAPIMAGE ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->WIZARDBITMAPIMAGE2 + pop ; StackCount = 3 + pop ; StackCount = 2 + call LOADEMBEDEDSCALEDBITMAP + pop ; StackCount = 1 + pop ; StackCount = 0 +loc_29d: + pushtype TFONT ; StackCount = 1 + pushtype TWIZARDFORM ; StackCount = 2 + assign Var2, WIZARDFORM + pushvar Var1 ; StackCount = 3 + call TFORM->FONT + pop ; StackCount = 2 + pop ; StackCount = 1 + pushvar Global5 ; StackCount = 2 + call TFONT->PIXELSPERINCH + pop ; StackCount = 1 + pop ; StackCount = 0 + assign Global6, S32(96) + assign Global7, S32(120) + assign Global8, S32(144) + assign Global9, S32(168) + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("Animation-with-icon.avi") + call EXTRACTTEMPORARYFILE + pop ; StackCount = 0 + pushtype TONDOWNLOADPROGRESS ; StackCount = 1 + assign Var1, TONDOWNLOADPROGRESS(ONDOWNLOADPROGRESS) + pushtype UnicodeString_2 ; StackCount = 2 + pushtype TSETUPMESSAGEID ; StackCount = 3 + assign Var3, TSETUPMESSAGEID(152) + pushvar Var2 ; StackCount = 4 + call SETUPMESSAGE + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype TSETUPMESSAGEID ; StackCount = 4 + assign Var4, TSETUPMESSAGEID(252) + pushvar Var3 ; StackCount = 5 + call SETUPMESSAGE + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Global3 ; StackCount = 4 + call CREATEDOWNLOADPAGE + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global6 + jz loc_43b, Var1 + pushtype BOOLEAN ; StackCount = 2 + lt Var2, Global5, Global7 + and Var1, Var2 + pop ; StackCount = 1 +loc_43b: + sfz Var1 + pop ; StackCount = 0 + jf loc_47d + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("500x300.bmp") + call EXTRACTTEMPORARYFILE + pop ; StackCount = 0 + jump loc_5da +loc_47d: + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global7 + jz loc_4c0, Var1 + pushtype BOOLEAN ; StackCount = 2 + lt Var2, Global5, Global8 + and Var1, Var2 + pop ; StackCount = 1 +loc_4c0: + sfz Var1 + pop ; StackCount = 0 + jf loc_502 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("634x382.bmp") + call EXTRACTTEMPORARYFILE + pop ; StackCount = 0 + jump loc_5da +loc_502: + pushtype BOOLEAN ; StackCount = 1 + le Var1, Global5, Global8 + jz loc_545, Var1 + pushtype BOOLEAN ; StackCount = 2 + gt Var2, Global9, Global8 + and Var1, Var2 + pop ; StackCount = 1 +loc_545: + sfz Var1 + pop ; StackCount = 0 + jf loc_587 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("779x469.bmp") + call EXTRACTTEMPORARYFILE + pop ; StackCount = 0 + jump loc_5da +loc_587: + pushtype BOOLEAN ; StackCount = 1 + ge Var1, Global5, Global9 + sfz Var1 + pop ; StackCount = 0 + jf loc_5da + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("975x585.bmp") + call EXTRACTTEMPORARYFILE + pop ; StackCount = 0 +loc_5da: + pushtype S32 ; StackCount = 1 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 2 + assign Var2, Global3 + pushvar Var1 ; StackCount = 3 + call TWIZARDPAGE->SURFACEHEIGHT + pop ; StackCount = 2 + pop ; StackCount = 1 + sub Var1, S32(40) + pushtype TNEWPROGRESSBAR ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->PROGRESSBAR + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->TOP + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + assign Var1, BOOLEAN(0) + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TCONTROL->VISIBLE + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype UnicodeString_2 ; StackCount = 1 + assign Var1, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 2 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 3 + assign Var3, Global3 + pushvar Var2 ; StackCount = 4 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 3 + pop ; StackCount = 2 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype BOOLEAN ; StackCount = 1 + assign Var1, BOOLEAN(1) + pushtype TNEWRADIOBUTTON ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TWIZARDFORM->LICENSEACCEPTEDRADIO + pop ; StackCount = 3 + pop ; StackCount = 2 + call TRADIOBUTTON->CHECKED + pop ; StackCount = 1 + pop ; StackCount = 0 + pushtype S32 ; StackCount = 1 + assign Var1, S32(9) + pushtype TFONT ; StackCount = 2 + pushtype TWIZARDFORM ; StackCount = 3 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 4 + call TFORM->FONT + pop ; StackCount = 3 + pop ; StackCount = 2 + call TFONT->SIZE + pop ; StackCount = 1 + pop ; StackCount = 0 + ret + +.function(export) external class(TWIZARDFORM, WIZARDBITMAPIMAGE) __pascal void TWIZARDFORM->WIZARDBITMAPIMAGE(__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDFORM, WIZARDBITMAPIMAGE2) __pascal void TWIZARDFORM->WIZARDBITMAPIMAGE2(__in __unknown,__in __unknown) + +.function(export) external internal returnsval CREATEDOWNLOADPAGE(__in __unknown,__in __unknown,__in __unknown) + +.function(export) external internal returnsval SETUPMESSAGE(__in __unknown) + +.function(export) external class(TOUTPUTPROGRESSWIZARDPAGE, PROGRESSBAR) __pascal void TOUTPUTPROGRESSWIZARDPAGE->PROGRESSBAR(__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDFORM, LICENSEACCEPTEDRADIO) __pascal void TWIZARDFORM->LICENSEACCEPTEDRADIO(__in __unknown,__in __unknown) + +.function(export) external class(TRADIOBUTTON, CHECKED, property) __pascal void TRADIOBUTTON->CHECKED(__in __unknown,__in __unknown) + +.function(export) external class(TFONT, SIZE, property) __pascal void TFONT->SIZE(__in __unknown,__in __unknown) + +.function(export) void ONEMBEDDEDMEDIAPLAYEREVENT(__in S32 Arg1,__in S32 Arg2,__in S32 Arg3) + pushtype S32 ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype S32 ; StackCount = 3 + assign Var3, S32(16777215) + pushtype TPANEL ; StackCount = 4 + assign Var4, Global4 + call TPANEL->COLOR + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype BOOLEAN ; StackCount = 3 + eq Var3, Arg1, S32(1) + sfz Var3 + pop ; StackCount = 2 + jf loc_877 + pushtype TPANEL ; StackCount = 3 + assign Var3, Global4 + call TCONTROL->HIDE + pop ; StackCount = 2 + pushtype TPANEL ; StackCount = 3 + assign Var3, Global4 + pushvar Var1 ; StackCount = 4 + call TCONTROL->WIDTH + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype TPANEL ; StackCount = 3 + assign Var3, Global4 + pushvar Var2 ; StackCount = 4 + call TCONTROL->HEIGHT_2 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype TCOMPONENT ; StackCount = 3 + assign Var3, Global3 + pushtype U32_2 ; StackCount = 4 + assign Var4, U32_2(35) + pushvar Global10 ; StackCount = 5 + call TCONTROL->CREATE + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype S32 ; StackCount = 3 + assign Var3, S32(16777215) + pushtype TBITMAPIMAGE ; StackCount = 4 + assign Var4, Global10 + call TBITMAPIMAGE->BACKCOLOR + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype TBITMAPIMAGE ; StackCount = 3 + assign Var3, Global10 + call TCONTROL->SHOW + pop ; StackCount = 2 + pushtype S32 ; StackCount = 3 + pushtype TPANEL ; StackCount = 4 + assign Var4, Global4 + pushvar Var3 ; StackCount = 5 + call TCONTROL->WIDTH + pop ; StackCount = 4 + pop ; StackCount = 3 + pushtype TBITMAPIMAGE ; StackCount = 4 + assign Var4, Global10 + call TCONTROL->WIDTH_2 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype S32 ; StackCount = 3 + pushtype TPANEL ; StackCount = 4 + assign Var4, Global4 + pushvar Var3 ; StackCount = 5 + call TCONTROL->HEIGHT_2 + pop ; StackCount = 4 + pop ; StackCount = 3 + pushtype TBITMAPIMAGE ; StackCount = 4 + assign Var4, Global10 + call TCONTROL->HEIGHT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype BOOLEAN ; StackCount = 3 + ge Var3, Global5, Global6 + jz loc_1d4, Var3 + pushtype BOOLEAN ; StackCount = 4 + lt Var4, Global5, Global7 + and Var3, Var4 + pop ; StackCount = 3 +loc_1d4: + sfz Var3 + pop ; StackCount = 2 + jf loc_33f + pushtype BOOLEAN ; StackCount = 3 + pushtype BOOLEAN ; StackCount = 4 + assign Var4, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\500x300.bmp") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("{tmp}\\500x300.bmp") + pushvar Var6 ; StackCount = 8 + call EXPANDCONSTANT + pop ; StackCount = 7 + pop ; StackCount = 6 + pushvar Var3 ; StackCount = 7 + call FILECOPY + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_3 ; StackCount = 4 + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\500x300") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + assign Var4, Var5 + pop ; StackCount = 4 + add Var4, UnicodeString_3(".bmp") + assign Var3, Var4 + pop ; StackCount = 3 + pushtype TBITMAP ; StackCount = 4 + pushtype TBITMAPIMAGE ; StackCount = 5 + assign Var5, Global10 + pushvar Var4 ; StackCount = 6 + call TBITMAPIMAGE->BITMAP + pop ; StackCount = 5 + pop ; StackCount = 4 + call TGRAPHIC->LOADFROMFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + jump loc_817 +loc_33f: + pushtype BOOLEAN ; StackCount = 3 + ge Var3, Global5, Global7 + jz loc_382, Var3 + pushtype BOOLEAN ; StackCount = 4 + lt Var4, Global5, Global8 + and Var3, Var4 + pop ; StackCount = 3 +loc_382: + sfz Var3 + pop ; StackCount = 2 + jf loc_4ed + pushtype BOOLEAN ; StackCount = 3 + pushtype BOOLEAN ; StackCount = 4 + assign Var4, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\634x382.bmp") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("{tmp}\\634x382.bmp") + pushvar Var6 ; StackCount = 8 + call EXPANDCONSTANT + pop ; StackCount = 7 + pop ; StackCount = 6 + pushvar Var3 ; StackCount = 7 + call FILECOPY + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_3 ; StackCount = 4 + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\634x382") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + assign Var4, Var5 + pop ; StackCount = 4 + add Var4, UnicodeString_3(".bmp") + assign Var3, Var4 + pop ; StackCount = 3 + pushtype TBITMAP ; StackCount = 4 + pushtype TBITMAPIMAGE ; StackCount = 5 + assign Var5, Global10 + pushvar Var4 ; StackCount = 6 + call TBITMAPIMAGE->BITMAP + pop ; StackCount = 5 + pop ; StackCount = 4 + call TGRAPHIC->LOADFROMFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + jump loc_817 +loc_4ed: + pushtype BOOLEAN ; StackCount = 3 + le Var3, Global5, Global8 + jz loc_530, Var3 + pushtype BOOLEAN ; StackCount = 4 + gt Var4, Global9, Global8 + and Var3, Var4 + pop ; StackCount = 3 +loc_530: + sfz Var3 + pop ; StackCount = 2 + jf loc_69b + pushtype BOOLEAN ; StackCount = 3 + pushtype BOOLEAN ; StackCount = 4 + assign Var4, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\779x469.bmp") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("{tmp}\\779x469.bmp") + pushvar Var6 ; StackCount = 8 + call EXPANDCONSTANT + pop ; StackCount = 7 + pop ; StackCount = 6 + pushvar Var3 ; StackCount = 7 + call FILECOPY + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_3 ; StackCount = 4 + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\779x469") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + assign Var4, Var5 + pop ; StackCount = 4 + add Var4, UnicodeString_3(".bmp") + assign Var3, Var4 + pop ; StackCount = 3 + pushtype TBITMAP ; StackCount = 4 + pushtype TBITMAPIMAGE ; StackCount = 5 + assign Var5, Global10 + pushvar Var4 ; StackCount = 6 + call TBITMAPIMAGE->BITMAP + pop ; StackCount = 5 + pop ; StackCount = 4 + call TGRAPHIC->LOADFROMFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + jump loc_817 +loc_69b: + pushtype BOOLEAN ; StackCount = 3 + ge Var3, Global5, Global9 + sfz Var3 + pop ; StackCount = 2 + jf loc_817 + pushtype BOOLEAN ; StackCount = 3 + pushtype BOOLEAN ; StackCount = 4 + assign Var4, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\975x585.bmp") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("{tmp}\\975x585.bmp") + pushvar Var6 ; StackCount = 8 + call EXPANDCONSTANT + pop ; StackCount = 7 + pop ; StackCount = 6 + pushvar Var3 ; StackCount = 7 + call FILECOPY + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_3 ; StackCount = 4 + pushtype UnicodeString_2 ; StackCount = 5 + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("{app}\\975x585") + pushvar Var5 ; StackCount = 7 + call EXPANDCONSTANT + pop ; StackCount = 6 + pop ; StackCount = 5 + assign Var4, Var5 + pop ; StackCount = 4 + add Var4, UnicodeString_3(".bmp") + assign Var3, Var4 + pop ; StackCount = 3 + pushtype TBITMAP ; StackCount = 4 + pushtype TBITMAPIMAGE ; StackCount = 5 + assign Var5, Global10 + pushvar Var4 ; StackCount = 6 + call TBITMAPIMAGE->BITMAP + pop ; StackCount = 5 + pop ; StackCount = 4 + call TGRAPHIC->LOADFROMFILE + pop ; StackCount = 3 + pop ; StackCount = 2 +loc_817: + pushtype TWINCONTROL ; StackCount = 3 + pushtype TNEWNOTEBOOKPAGE ; StackCount = 4 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 5 + assign Var5, Global3 + pushvar Var4 ; StackCount = 6 + call TWIZARDPAGE->SURFACE + pop ; StackCount = 5 + pop ; StackCount = 4 + assign Var3, Var4 + pop ; StackCount = 3 + pushtype TBITMAPIMAGE ; StackCount = 4 + assign Var4, Global10 + call TCONTROL->PARENT_2 + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype TCONTROL ; StackCount = 3 + assign Var3, Global10 + call CENTERINPARENT + pop ; StackCount = 2 +loc_877: + ret + +.function(export) external class(TCONTROL, HIDE) __fastcall void TCONTROL->HIDE() + +.function(export) external class(TCONTROL, HEIGHT) __pascal void TCONTROL->HEIGHT_2(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, CREATE) __fastcall returnsval TCONTROL->CREATE(__in __unknown) + +.function(export) external class(TBITMAPIMAGE, BACKCOLOR, property) __pascal void TBITMAPIMAGE->BACKCOLOR(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, SHOW) __fastcall void TCONTROL->SHOW() + +.function(export) external internal returnsval FILECOPY(__in __unknown,__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDPAGE, SURFACE) __pascal void TWIZARDPAGE->SURFACE(__in __unknown,__in __unknown) + +.function(export) external class(TCONTROL, PARENT, property) __pascal void TCONTROL->PARENT_2(__in __unknown,__in __unknown) + +.function(export) BOOLEAN NEXTBUTTONCLICK(__in S32 Arg1) + pushtype S32 ; StackCount = 1 + pushtype S32 ; StackCount = 2 + pushtype S32 ; StackCount = 3 + pushtype WideString ; StackCount = 4 + pushtype UnicodeString_2 ; StackCount = 5 + pushtype S32 ; StackCount = 6 + pushtype S32 ; StackCount = 7 + pushtype S32 ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype TBITMAPIMAGE ; StackCount = 10 + pushtype TWIZARDFORM ; StackCount = 11 + assign Var11, WIZARDFORM + pushvar Var10 ; StackCount = 12 + call TWIZARDFORM->WIZARDSMALLBITMAPIMAGE + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->VISIBLE + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + eq Var9, Arg1, S32(10) + sfz Var9 + pop ; StackCount = 8 + jf loc_21cd + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype TNEWBUTTON ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TDOWNLOADWIZARDPAGE->ABORTBUTTON + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->VISIBLE + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 9 + assign Var9, Global3 + call TDOWNLOADWIZARDPAGE->SHOW + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("{app}\\") + pushvar Var10 ; StackCount = 12 + call EXPANDCONSTANT + pop ; StackCount = 11 + pop ; StackCount = 10 + pushvar Var9 ; StackCount = 11 + call CREATEDIR + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushtype BOOLEAN ; StackCount = 10 + assign Var10, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("{app}\\Animation-with-icon.avi") + pushvar Var11 ; StackCount = 13 + call EXPANDCONSTANT + pop ; StackCount = 12 + pop ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + pushtype UnicodeString_2 ; StackCount = 13 + assign Var13, UnicodeString_3("{tmp}\\Animation-with-icon.avi") + pushvar Var12 ; StackCount = 14 + call EXPANDCONSTANT + pop ; StackCount = 13 + pop ; StackCount = 12 + pushvar Var9 ; StackCount = 13 + call FILECOPY + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 10 + assign Var10, Global3 + pushvar Var9 ; StackCount = 11 + call TWIZARDPAGE->SURFACEHEIGHT + pop ; StackCount = 10 + pop ; StackCount = 9 + sub Var9, S32(70) + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG1LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->TOP + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 10 + assign Var10, Global3 + pushvar Var9 ; StackCount = 11 + call TWIZARDPAGE->SURFACEHEIGHT + pop ; StackCount = 10 + pop ; StackCount = 9 + sub Var9, S32(40) + pushtype TNEWPROGRESSBAR ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->PROGRESSBAR + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->TOP + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TCONTROL ; StackCount = 9 + pushtype TNEWPROGRESSBAR ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->PROGRESSBAR + pop ; StackCount = 11 + pop ; StackCount = 10 + assign Var9, Var10 + pop ; StackCount = 9 + call CENTERINPARENT2 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->VISIBLE + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(16777215) + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TNEWSTATICTEXT->COLOR + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TCOMPONENT ; StackCount = 9 + assign Var9, Global3 + pushtype U32_2 ; StackCount = 10 + assign Var10, U32_2(34) + pushvar Global4 ; StackCount = 11 + call TCONTROL->CREATE + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TPANELBEVEL ; StackCount = 9 + assign Var9, TPANELBEVEL(0) + pushtype TPANEL ; StackCount = 10 + assign Var10, Global4 + call TPANEL->BEVELOUTER + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TWINCONTROL ; StackCount = 9 + pushtype TNEWNOTEBOOKPAGE ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TWIZARDPAGE->SURFACE + pop ; StackCount = 11 + pop ; StackCount = 10 + assign Var9, Var10 + pop ; StackCount = 9 + pushtype TPANEL ; StackCount = 10 + assign Var10, Global4 + call TCONTROL->PARENT_2 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TCONTROL ; StackCount = 9 + assign Var9, Global4 + call CENTERINPARENT + pop ; StackCount = 8 + pushtype TPANEL ; StackCount = 9 + assign Var9, Global4 + pushvar Var2 ; StackCount = 10 + call TCONTROL->WIDTH + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TPANEL ; StackCount = 9 + assign Var9, Global4 + pushvar Var3 ; StackCount = 10 + call TCONTROL->HEIGHT_2 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(16777215) + pushtype TPANEL ; StackCount = 10 + assign Var10, Global4 + call TPANEL->COLOR + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype UnicodeString_3 ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("{app}") + pushvar Var10 ; StackCount = 12 + call EXPANDCONSTANT + pop ; StackCount = 11 + pop ; StackCount = 10 + assign Var9, Var10 + pop ; StackCount = 9 + add Var9, UnicodeString_3("\\Animation-with-icon.avi") + assign Var5, Var9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushvar Var9 ; StackCount = 10 + call ISWIN64 + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_720 + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("fa127e707421be7a57ee781b9bf25501241cbf963ec0ddd12a3937bf602837d7") + pushtype UnicodeString_2 ; StackCount = 10 + assign Var10, UnicodeString_3("64bit_Download.zip") + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("https://cloud.stellarinfo.com/upload/SDR_v10_2/10_2_64.zip") + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 12 + assign Var12, Global3 + call TDOWNLOADWIZARDPAGE->ADD + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + jump loc_88d +loc_720: + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("caab93a30c322b5696d03da9819f665309f04cc6bd2ede7e5ac55bf27937e1db") + pushtype UnicodeString_2 ; StackCount = 10 + assign Var10, UnicodeString_3("32bit_Download.zip") + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("https://cloud.stellarinfo.com/upload/SDR_v10_2/10_2_32.zip") + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 12 + assign Var12, Global3 + call TDOWNLOADWIZARDPAGE->ADD + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_88d: + starteh loc_219e, null, null, loc_21b6 + starteh null, loc_102f, null, loc_219c + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 10 + assign Var10, UnicodeString_3("") + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + call TOUTPUTPROGRESSWIZARDPAGE->SETTEXT + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TCONTROL ; StackCount = 9 + assign Var9, Global4 + call CENTERINPARENT + pop ; StackCount = 8 + pushtype TPANEL ; StackCount = 9 + assign Var9, Global4 + pushvar Var2 ; StackCount = 10 + call TCONTROL->WIDTH + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TPANEL ; StackCount = 9 + assign Var9, Global4 + pushvar Var3 ; StackCount = 10 + call TCONTROL->HEIGHT_2 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype TPANEL ; StackCount = 9 + assign Var9, Global4 + call TCONTROL->SHOW + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(16777215) + pushtype TPANEL ; StackCount = 10 + assign Var10, Global4 + call TPANEL->COLOR + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + assign Var9, BOOLEAN(0) + pushtype TNEWBUTTON ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TDOWNLOADWIZARDPAGE->ABORTBUTTON + pop ; StackCount = 11 + pop ; StackCount = 10 + call TCONTROL->VISIBLE + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushtype Type36 ; StackCount = 10 + assign Var10, Type36(ONEMBEDDEDMEDIAPLAYEREVENT) + pushtype Pointer ; StackCount = 11 + setptr Var11, Var3 + pushtype Pointer ; StackCount = 12 + setptr Var12, Var2 + pushtype S32 ; StackCount = 13 + pushtype TPANEL ; StackCount = 14 + assign Var14, Global4 + pushvar Var13 ; StackCount = 15 + call TWINCONTROL->HANDLE + pop ; StackCount = 14 + pop ; StackCount = 13 + pushtype WideString ; StackCount = 14 + assign Var14, Var5 + pushvar Var9 ; StackCount = 15 + call files:mediaplayer.dll!DSInitializeVideoFile + pop ; StackCount = 14 + pop ; StackCount = 13 + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(16777215) + pushtype TPANEL ; StackCount = 10 + assign Var10, Global4 + call TPANEL->COLOR + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushvar Var9 ; StackCount = 10 + call files:mediaplayer.dll!DSPlayMediaFile + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S64 ; StackCount = 9 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 10 + assign Var10, Global3 + pushvar Var9 ; StackCount = 11 + call TDOWNLOADWIZARDPAGE->DOWNLOAD + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushtype TNEWSTATICTEXT ; StackCount = 10 + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 11 + assign Var11, Global3 + pushvar Var10 ; StackCount = 12 + call TOUTPUTPROGRESSWIZARDPAGE->MSG2LABEL + pop ; StackCount = 11 + pop ; StackCount = 10 + call TNEWSTATICTEXT->CAPTION + pop ; StackCount = 9 + pop ; StackCount = 8 + assign RetVal, BOOLEAN(1) + pushtype BOOLEAN ; StackCount = 9 + pushvar Var9 ; StackCount = 10 + call ISWIN64 + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_d98 + pushtype BOOLEAN ; StackCount = 9 + pushtype BOOLEAN ; StackCount = 10 + assign Var10, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("{app}\\64bit_Download.zip") + pushvar Var11 ; StackCount = 13 + call EXPANDCONSTANT + pop ; StackCount = 12 + pop ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + pushtype UnicodeString_2 ; StackCount = 13 + assign Var13, UnicodeString_3("{tmp}\\64bit_Download.zip") + pushvar Var12 ; StackCount = 14 + call EXPANDCONSTANT + pop ; StackCount = 13 + pop ; StackCount = 12 + pushvar Var9 ; StackCount = 13 + call FILECOPY + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype String_3 ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("{app}\\") + pushvar Var10 ; StackCount = 12 + call EXPANDCONSTANT + pop ; StackCount = 11 + pop ; StackCount = 10 + assign Var9, Var10 + pop ; StackCount = 9 + pushtype String_3 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("{app}\\64bit_Download.zip") + pushvar Var11 ; StackCount = 13 + call EXPANDCONSTANT + pop ; StackCount = 12 + pop ; StackCount = 11 + assign Var10, Var11 + pop ; StackCount = 10 + call UNZIP + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushtype Pointer ; StackCount = 10 + setptr Var10, Var1 + pushtype TEXECWAIT ; StackCount = 11 + assign Var11, TEXECWAIT(1) + pushtype S32 ; StackCount = 12 + assign Var12, S32(0) + pushtype UnicodeString_2 ; StackCount = 13 + assign Var13, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 14 + assign Var14, UnicodeString_3("/verysilent") + pushtype UnicodeString_2 ; StackCount = 15 + pushtype UnicodeString_2 ; StackCount = 16 + assign Var16, UnicodeString_3("{app}\\K-Lite_Codec_Pack_1375_Basic.exe") + pushvar Var15 ; StackCount = 17 + call EXPANDCONSTANT + pop ; StackCount = 16 + pop ; StackCount = 15 + pushtype UnicodeString_2 ; StackCount = 16 + assign Var16, UnicodeString_3("") + pushvar Var9 ; StackCount = 17 + call SHELLEXEC + pop ; StackCount = 16 + pop ; StackCount = 15 + pop ; StackCount = 14 + pop ; StackCount = 13 + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + jump loc_102d +loc_d98: + pushtype BOOLEAN ; StackCount = 9 + pushtype BOOLEAN ; StackCount = 10 + assign Var10, BOOLEAN(0) + pushtype UnicodeString_2 ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("{app}\\32bit_Download.zip") + pushvar Var11 ; StackCount = 13 + call EXPANDCONSTANT + pop ; StackCount = 12 + pop ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + pushtype UnicodeString_2 ; StackCount = 13 + assign Var13, UnicodeString_3("{tmp}\\32bit_Download.zip") + pushvar Var12 ; StackCount = 14 + call EXPANDCONSTANT + pop ; StackCount = 13 + pop ; StackCount = 12 + pushvar Var9 ; StackCount = 13 + call FILECOPY + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype String_3 ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + assign Var11, UnicodeString_3("{app}\\") + pushvar Var10 ; StackCount = 12 + call EXPANDCONSTANT + pop ; StackCount = 11 + pop ; StackCount = 10 + assign Var9, Var10 + pop ; StackCount = 9 + pushtype String_3 ; StackCount = 10 + pushtype UnicodeString_2 ; StackCount = 11 + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("{app}\\32bit_Download.zip") + pushvar Var11 ; StackCount = 13 + call EXPANDCONSTANT + pop ; StackCount = 12 + pop ; StackCount = 11 + assign Var10, Var11 + pop ; StackCount = 10 + call UNZIP + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype BOOLEAN ; StackCount = 9 + pushtype Pointer ; StackCount = 10 + setptr Var10, Var1 + pushtype TEXECWAIT ; StackCount = 11 + assign Var11, TEXECWAIT(1) + pushtype S32 ; StackCount = 12 + assign Var12, S32(0) + pushtype UnicodeString_2 ; StackCount = 13 + assign Var13, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 14 + assign Var14, UnicodeString_3("/verysilent") + pushtype UnicodeString_2 ; StackCount = 15 + pushtype UnicodeString_2 ; StackCount = 16 + assign Var16, UnicodeString_3("{app}\\K-Lite_Codec_Pack_1375_Basic.exe") + pushvar Var15 ; StackCount = 17 + call EXPANDCONSTANT + pop ; StackCount = 16 + pop ; StackCount = 15 + pushtype UnicodeString_2 ; StackCount = 16 + assign Var16, UnicodeString_3("") + pushvar Var9 ; StackCount = 17 + call SHELLEXEC + pop ; StackCount = 16 + pop ; StackCount = 15 + pop ; StackCount = 14 + pop ; StackCount = 13 + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_102d: + endtry +loc_102f: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("en") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_11ad + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Installer needs to download additional components. Connect your computer to the internet to complete installation.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_11ad: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("fr") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1351 + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("L’installateur doit télécharger des composants supplémentaires. Connectez votre ordinateur à Internet pour une installation complète.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1351: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("de") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1501 + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Der Installer muss zusätzliche Komponenten herunterladen. Verbinden Sie Ihren Computer mit dem Internet, um die Installation abzuschließen.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1501: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("it") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1677 + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("L’impianto deve scaricare altri componenti. Collega il tuo computer a Internet per completare l’installazione.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1677: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("es") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1801 + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("El instalador necesita descargar componentes adicionales. Conecta tu ordenador a Internet para completar la instalación.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1801: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("jp") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1923 + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("インストーラーは追加のコンポーネントをダウンロードする必要があります.コンピュータをインターネットに接続して、インストールを完了します。") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1923: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("ko") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1a1d + pushtype S32 ; StackCount = 9 + assign Var9, S32(4) + pushtype S32 ; StackCount = 10 + assign Var10, S32(5) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(3) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("추가 구성 요소를 다운로드 해야 합니다.설치를 완료하려면 컴퓨터를 인터넷에 연결합니다.") + pushvar Var7 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1a1d: + pushtype BOOLEAN ; StackCount = 9 + eq Var9, Var7, S32(4) + sfz Var9 + pop ; StackCount = 8 + jf loc_1a44 +loc_1a44: + pushtype BOOLEAN ; StackCount = 9 + eq Var9, Var7, S32(2) + sfz Var9 + pop ; StackCount = 8 + jf loc_218e + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("en") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1b63 + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Do you want to cancel the installation process?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1b63: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("fr") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1c5d + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Voulez-vous annuler le processus d’installation?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1c5d: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("de") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1d55 + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Möchtest du den Installationsprozess abbrechen?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1d55: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("it") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1e49 + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("Vuole annullare il processo di installazione?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1e49: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("es") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1f37 + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("¿Desea cancelar el proceso de instalación?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1f37: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("jp") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_1ffb + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("インストールプロセスをキャンセルしますか?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_1ffb: + pushtype BOOLEAN ; StackCount = 9 + pushtype UnicodeString_2 ; StackCount = 10 + pushvar Var10 ; StackCount = 11 + call ACTIVELANGUAGE + pop ; StackCount = 10 + eq Var9, Var10, UnicodeString_3("ko") + pop ; StackCount = 9 + sfz Var9 + pop ; StackCount = 8 + jf loc_20bb + pushtype S32 ; StackCount = 9 + assign Var9, S32(7) + pushtype S32 ; StackCount = 10 + assign Var10, S32(4) + pushtype TMSGBOXTYPE ; StackCount = 11 + assign Var11, TMSGBOXTYPE(1) + pushtype UnicodeString_2 ; StackCount = 12 + assign Var12, UnicodeString_3("설치 프로세스를 취소 하시겠습니까?") + pushvar Var8 ; StackCount = 13 + call SUPPRESSIBLEMSGBOX + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 +loc_20bb: + pushtype BOOLEAN ; StackCount = 9 + eq Var9, Var8, S32(6) + sfz Var9 + pop ; StackCount = 8 + jf loc_218e + pushtype BOOLEAN ; StackCount = 9 + pushtype BOOLEAN ; StackCount = 10 + assign Var10, BOOLEAN(1) + pushtype BOOLEAN ; StackCount = 11 + assign Var11, BOOLEAN(1) + pushtype BOOLEAN ; StackCount = 12 + assign Var12, BOOLEAN(1) + pushtype UnicodeString_2 ; StackCount = 13 + pushtype UnicodeString_2 ; StackCount = 14 + pushtype UnicodeString_2 ; StackCount = 15 + assign Var15, UnicodeString_3("{app}\\") + pushvar Var14 ; StackCount = 16 + call EXPANDCONSTANT + pop ; StackCount = 15 + pop ; StackCount = 14 + pushvar Var13 ; StackCount = 15 + call EXTRACTFILEDIR + pop ; StackCount = 14 + pop ; StackCount = 13 + pushvar Var9 ; StackCount = 14 + call DELTREE + pop ; StackCount = 13 + pop ; StackCount = 12 + pop ; StackCount = 11 + pop ; StackCount = 10 + pop ; StackCount = 9 + pop ; StackCount = 8 + pushtype S32 ; StackCount = 9 + assign Var9, S32(9) + call kernel32.dll!ExitProcess + pop ; StackCount = 8 + jump loc_218e +loc_218e: + assign RetVal, BOOLEAN(0) + endcatch +loc_219c: + endtry +loc_219e: + pushtype TDOWNLOADWIZARDPAGE ; StackCount = 9 + assign Var9, Global3 + call TOUTPUTPROGRESSWIZARDPAGE->HIDE + pop ; StackCount = 8 + endfinally +loc_21b6: + pushtype BOOLEAN ; StackCount = 9 + pushvar Var9 ; StackCount = 10 + call files:mediaplayer.dll!DSStopMediaPlay + pop ; StackCount = 9 + pop ; StackCount = 8 + jump loc_21d9 +loc_21cd: + assign RetVal, BOOLEAN(1) +loc_21d9: + ret + +.function(export) external class(TWIZARDFORM, WIZARDSMALLBITMAPIMAGE) __pascal void TWIZARDFORM->WIZARDSMALLBITMAPIMAGE(__in __unknown,__in __unknown) + +.function(export) external class(TDOWNLOADWIZARDPAGE, SHOW) __fastcall void TDOWNLOADWIZARDPAGE->SHOW() + +.function(export) external internal returnsval CREATEDIR(__in __unknown) + +.function(export) external class(TNEWSTATICTEXT, COLOR, property) __pascal void TNEWSTATICTEXT->COLOR(__in __unknown,__in __unknown) + +.function(export) external class(TPANEL, BEVELOUTER, property) __pascal void TPANEL->BEVELOUTER(__in __unknown,__in __unknown) + +.function(export) external internal returnsval ISWIN64() + +.function(export) external class(TDOWNLOADWIZARDPAGE, ADD) __fastcall void TDOWNLOADWIZARDPAGE->ADD(__in __unknown,__in __unknown,__in __unknown) + +.function(export) external class(TOUTPUTPROGRESSWIZARDPAGE, SETTEXT) __fastcall void TOUTPUTPROGRESSWIZARDPAGE->SETTEXT(__in __unknown,__in __unknown) + +.function(export) external class(TWINCONTROL, HANDLE) __pascal void TWINCONTROL->HANDLE(__in __unknown,__in __unknown) + +.function(export) external class(TDOWNLOADWIZARDPAGE, DOWNLOAD) __fastcall returnsval TDOWNLOADWIZARDPAGE->DOWNLOAD() + +.function(export) external internal returnsval SHELLEXEC(__in __unknown,__in __unknown,__in __unknown,__in __unknown,__in __unknown,__in __unknown,__out __unknown) + +.function(export) external internal returnsval SUPPRESSIBLEMSGBOX(__in __unknown,__in __unknown,__in __unknown,__in __unknown) + +.function(export) external internal returnsval DELTREE(__in __unknown,__in __unknown,__in __unknown,__in __unknown) + +.function(export) external internal returnsval EXTRACTFILEDIR(__in __unknown) + +.function(export) external class(TOUTPUTPROGRESSWIZARDPAGE, HIDE) __fastcall void TOUTPUTPROGRESSWIZARDPAGE->HIDE() + +.function(export) void CURPAGECHANGED(__in S32 Arg1) + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, UnicodeString_3("{tmp}\\64bit_Download.zip") + pushvar Var2 ; StackCount = 4 + call EXPANDCONSTANT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushvar Var1 ; StackCount = 3 + call FILEEXISTS + pop ; StackCount = 2 + pop ; StackCount = 1 + setz Var1 + sfz Var1 + pop ; StackCount = 0 + jf loc_382 + pushtype BOOLEAN ; StackCount = 1 + pushtype UnicodeString_2 ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + assign Var3, UnicodeString_3("{tmp}\\32bit_Download.zip") + pushvar Var2 ; StackCount = 4 + call EXPANDCONSTANT + pop ; StackCount = 3 + pop ; StackCount = 2 + pushvar Var1 ; StackCount = 3 + call FILEEXISTS + pop ; StackCount = 2 + pop ; StackCount = 1 + setz Var1 + sfz Var1 + pop ; StackCount = 0 + jf loc_382 + pushtype S32 ; StackCount = 1 + assign Var1, Arg1 + pushtype BOOLEAN ; StackCount = 2 + eq Var2, S32(8), Var1 + jz loc_197, Var2 + pushtype TNOTIFYEVENT ; StackCount = 3 + pushtype TNEWBUTTON ; StackCount = 4 + pushtype TWIZARDFORM ; StackCount = 5 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 6 + call TWIZARDFORM->NEXTBUTTON + pop ; StackCount = 5 + pop ; StackCount = 4 + pushvar Var3 ; StackCount = 5 + call TBUTTON->ONCLICK + pop ; StackCount = 4 + pop ; StackCount = 3 + pushtype TOBJECT ; StackCount = 4 + pushtype TOBJECT ; StackCount = 5 + pushtype Pointer ; StackCount = 6 + setptr Var6, Var5 + call Class->SetNil + pop ; StackCount = 5 + assign Var4, Var5 + pop ; StackCount = 4 + callvar Var3 ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + jump loc_197 +loc_197: + pop ; StackCount = 2 + pop ; StackCount = 1 + pushtype S32 ; StackCount = 2 + assign Var1, Arg1 + pushtype BOOLEAN ; StackCount = 3 + eq Var2, S32(9), Var1 + jz loc_23a, Var2 + pushtype TNOTIFYEVENT ; StackCount = 4 + pushtype TNEWBUTTON ; StackCount = 5 + pushtype TWIZARDFORM ; StackCount = 6 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 7 + call TWIZARDFORM->NEXTBUTTON + pop ; StackCount = 6 + pop ; StackCount = 5 + pushvar Var3 ; StackCount = 6 + call TBUTTON->ONCLICK + pop ; StackCount = 5 + pop ; StackCount = 4 + pushtype TOBJECT ; StackCount = 5 + pushtype TOBJECT ; StackCount = 6 + pushtype Pointer ; StackCount = 7 + setptr Var6, Var5 + call Class->SetNil + pop ; StackCount = 6 + assign Var4, Var5 + pop ; StackCount = 5 + callvar Var3 ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + jump loc_23a +loc_23a: + pop ; StackCount = 3 + pop ; StackCount = 2 + pushtype S32 ; StackCount = 3 + assign Var1, Arg1 + pushtype BOOLEAN ; StackCount = 4 + eq Var2, S32(10), Var1 + jz loc_2dd, Var2 + pushtype TNOTIFYEVENT ; StackCount = 5 + pushtype TNEWBUTTON ; StackCount = 6 + pushtype TWIZARDFORM ; StackCount = 7 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 8 + call TWIZARDFORM->NEXTBUTTON + pop ; StackCount = 7 + pop ; StackCount = 6 + pushvar Var3 ; StackCount = 7 + call TBUTTON->ONCLICK + pop ; StackCount = 6 + pop ; StackCount = 5 + pushtype TOBJECT ; StackCount = 6 + pushtype TOBJECT ; StackCount = 7 + pushtype Pointer ; StackCount = 8 + setptr Var6, Var5 + call Class->SetNil + pop ; StackCount = 7 + assign Var4, Var5 + pop ; StackCount = 6 + callvar Var3 ; StackCount = 7 + pop ; StackCount = 6 + pop ; StackCount = 5 + jump loc_2dd +loc_2dd: + pop ; StackCount = 4 + pop ; StackCount = 3 + pushtype S32 ; StackCount = 4 + assign Var1, Arg1 + pushtype BOOLEAN ; StackCount = 5 + eq Var2, S32(12), Var1 + jz loc_380, Var2 + pushtype TNOTIFYEVENT ; StackCount = 6 + pushtype TNEWBUTTON ; StackCount = 7 + pushtype TWIZARDFORM ; StackCount = 8 + assign Var5, WIZARDFORM + pushvar Var4 ; StackCount = 9 + call TWIZARDFORM->NEXTBUTTON + pop ; StackCount = 8 + pop ; StackCount = 7 + pushvar Var3 ; StackCount = 8 + call TBUTTON->ONCLICK + pop ; StackCount = 7 + pop ; StackCount = 6 + pushtype TOBJECT ; StackCount = 7 + pushtype TOBJECT ; StackCount = 8 + pushtype Pointer ; StackCount = 9 + setptr Var6, Var5 + call Class->SetNil + pop ; StackCount = 8 + assign Var4, Var5 + pop ; StackCount = 7 + callvar Var3 ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + jump loc_380 +loc_380: + pop ; StackCount = 5 + pop ; StackCount = 4 +loc_382: + pushtype BOOLEAN ; StackCount = 5 + eq Var1, Arg1, S32(2) + sfz Var1 + pop ; StackCount = 4 + jf loc_45d + pushtype TNEWSTATICTEXT ; StackCount = 5 + pushtype TWIZARDFORM ; StackCount = 6 + assign Var2, WIZARDFORM + pushvar Var1 ; StackCount = 7 + call TWIZARDFORM->PAGEDESCRIPTIONLABEL + pop ; StackCount = 6 + pop ; StackCount = 5 + call TCONTROL->HIDE + pop ; StackCount = 4 + pushtype S32 ; StackCount = 5 + assign Var1, S32(50) + pushtype TNEWSTATICTEXT ; StackCount = 6 + pushtype TWIZARDFORM ; StackCount = 7 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 8 + call TWIZARDFORM->PAGENAMELABEL + pop ; StackCount = 7 + pop ; StackCount = 6 + call TCONTROL->HEIGHT + pop ; StackCount = 5 + pop ; StackCount = 4 + pushtype S32 ; StackCount = 5 + assign Var1, S32(12) + pushtype TFONT ; StackCount = 6 + pushtype TNEWSTATICTEXT ; StackCount = 7 + pushtype TWIZARDFORM ; StackCount = 8 + assign Var4, WIZARDFORM + pushvar Var3 ; StackCount = 9 + call TWIZARDFORM->PAGENAMELABEL + pop ; StackCount = 8 + pop ; StackCount = 7 + pushvar Var2 ; StackCount = 8 + call TNEWSTATICTEXT->FONT + pop ; StackCount = 7 + pop ; StackCount = 6 + call TFONT->SIZE + pop ; StackCount = 5 + pop ; StackCount = 4 +loc_45d: + pushtype BOOLEAN ; StackCount = 5 + eq Var1, Arg1, S32(6) + sfz Var1 + pop ; StackCount = 4 + jf loc_4c1 + pushtype S32 ; StackCount = 5 + assign Var1, S32(20) + pushtype TNEWSTATICTEXT ; StackCount = 6 + pushtype TWIZARDFORM ; StackCount = 7 + assign Var3, WIZARDFORM + pushvar Var2 ; StackCount = 8 + call TWIZARDFORM->SELECTDIRLABEL + pop ; StackCount = 7 + pop ; StackCount = 6 + call TCONTROL->HEIGHT + pop ; StackCount = 5 + pop ; StackCount = 4 +loc_4c1: + ret + +.function(export) external class(TWIZARDFORM, NEXTBUTTON) __pascal void TWIZARDFORM->NEXTBUTTON(__in __unknown,__in __unknown) + +.function(export) external class(TBUTTON, ONCLICK) __pascal void TBUTTON->ONCLICK(__in __unknown,__in __unknown) + +.function(export) external class(Class, SetNil) __pascal returnsval Class->SetNil(__out __unknown) + +.function(export) external class(TWIZARDFORM, PAGEDESCRIPTIONLABEL) __pascal void TWIZARDFORM->PAGEDESCRIPTIONLABEL(__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDFORM, PAGENAMELABEL) __pascal void TWIZARDFORM->PAGENAMELABEL(__in __unknown,__in __unknown) + +.function(export) external class(TNEWSTATICTEXT, FONT) __pascal void TNEWSTATICTEXT->FONT(__in __unknown,__in __unknown) + +.function(export) external class(TWIZARDFORM, SELECTDIRLABEL) __pascal void TWIZARDFORM->SELECTDIRLABEL(__in __unknown,__in __unknown) + +.function(export) void DEINITIALIZESETUP() + pushtype BOOLEAN ; StackCount = 1 + pushvar Var1 ; StackCount = 2 + call files:mediaplayer.dll!DSStopMediaPlay + pop ; StackCount = 1 + pop ; StackCount = 0 + ret + +.function(export) void CURUNINSTALLSTEPCHANGED(__in TUNINSTALLSTEP Arg1) + pushtype S32 ; StackCount = 1 + pushtype BOOLEAN ; StackCount = 2 + eq Var2, Arg1, TUNINSTALLSTEP(3) + sfz Var2 + pop ; StackCount = 1 + jf loc_17b + pushtype BOOLEAN ; StackCount = 2 + pushtype Pointer ; StackCount = 3 + setptr Var3, Var1 + pushtype TEXECWAIT ; StackCount = 4 + assign Var4, TEXECWAIT(0) + pushtype S32 ; StackCount = 5 + assign Var5, S32(5) + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 8 + assign Var8, UnicodeString_3("https://www.stellarinfo.com/uninstallation/?product=windows-data-recovery-professional.php") + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushvar Var2 ; StackCount = 10 + call SHELLEXEC + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 +loc_17b: + ret + +.function(export) void CURSTEPCHANGED(__in TSETUPSTEP Arg1) + pushtype S32 ; StackCount = 1 + pushtype BOOLEAN ; StackCount = 2 + eq Var2, Arg1, TSETUPSTEP(2) + sfz Var2 + pop ; StackCount = 1 + jf loc_11e + pushtype BOOLEAN ; StackCount = 2 + pushvar Var2 ; StackCount = 3 + call ISWIN64 + pop ; StackCount = 2 + sfz Var2 + pop ; StackCount = 1 + jf loc_b5 + pushtype BOOLEAN ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_2 ; StackCount = 4 + assign Var4, UnicodeString_3("{app}\\64bit_Download.zip") + pushvar Var3 ; StackCount = 5 + call EXPANDCONSTANT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call DELETEFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 + jump loc_11e +loc_b5: + pushtype BOOLEAN ; StackCount = 2 + pushtype UnicodeString_2 ; StackCount = 3 + pushtype UnicodeString_2 ; StackCount = 4 + assign Var4, UnicodeString_3("{app}\\32bit_Download.zip") + pushvar Var3 ; StackCount = 5 + call EXPANDCONSTANT + pop ; StackCount = 4 + pop ; StackCount = 3 + pushvar Var2 ; StackCount = 4 + call DELETEFILE + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 +loc_11e: + pushtype BOOLEAN ; StackCount = 2 + eq Var2, Arg1, TSETUPSTEP(3) + sfz Var2 + pop ; StackCount = 1 + jf loc_27e + pushtype BOOLEAN ; StackCount = 2 + pushtype Pointer ; StackCount = 3 + setptr Var3, Var1 + pushtype TEXECWAIT ; StackCount = 4 + assign Var4, TEXECWAIT(0) + pushtype S32 ; StackCount = 5 + assign Var5, S32(5) + pushtype UnicodeString_2 ; StackCount = 6 + assign Var6, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 7 + assign Var7, UnicodeString_3("") + pushtype UnicodeString_2 ; StackCount = 8 + assign Var8, UnicodeString_3("https://www.stellarinfo.com/installation/windows-data-recovery-professional.php") + pushtype UnicodeString_2 ; StackCount = 9 + assign Var9, UnicodeString_3("") + pushvar Var2 ; StackCount = 10 + call SHELLEXEC + pop ; StackCount = 9 + pop ; StackCount = 8 + pop ; StackCount = 7 + pop ; StackCount = 6 + pop ; StackCount = 5 + pop ; StackCount = 4 + pop ; StackCount = 3 + pop ; StackCount = 2 + pop ; StackCount = 1 +loc_27e: + ret + + diff --git a/IFPSLib.Tests/IFPSLib.Tests.csproj b/IFPSLib.Tests/IFPSLib.Tests.csproj new file mode 100644 index 0000000..893cad6 --- /dev/null +++ b/IFPSLib.Tests/IFPSLib.Tests.csproj @@ -0,0 +1,96 @@ + + + + + + Debug + AnyCPU + {8EEC05F9-82F0-4EC4-94C6-728F80D50959} + Library + Properties + IFPSLib.Tests + IFPSLib.Tests + v4.7.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + PackageReference + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + True + True + Resources.resx + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {63b7741e-d712-4277-b345-f5b77ac80eb1} + IFPSAsmLib + + + {e83bf0b3-4427-41e8-9a99-9df8951de711} + IFPSLib + False + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/IFPSLib.Tests/Properties/AssemblyInfo.cs b/IFPSLib.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e26337f --- /dev/null +++ b/IFPSLib.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("IFPSLib.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IFPSLib.Tests")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("8eec05f9-82f0-4ec4-94c6-728f80d50959")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/IFPSLib.Tests/Properties/Resources.Designer.cs b/IFPSLib.Tests/Properties/Resources.Designer.cs new file mode 100644 index 0000000..55dab82 --- /dev/null +++ b/IFPSLib.Tests/Properties/Resources.Designer.cs @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace IFPSLib.Tests.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("IFPSLib.Tests.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CompiledCode { + get { + object obj = ResourceManager.GetObject("CompiledCode", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized string similar to .version 23 + /// + ///.entry !MAIN + /// + ///.type primitive(Pointer) Pointer + ///.type primitive(U32) U32 + ///.type primitive(Variant) Variant + ///.type primitive(PChar) PChar + ///.type primitive(Currency) Currency + ///.type primitive(Extended) Extended + ///.type primitive(Double) Double + ///.type primitive(Single) Single + ///.type primitive(S64) S64 + ///.type primitive(String) String + ///.type primitive(U32) U32_2 + ///.type primitive(S32) S32 + ///.type primitive(S16) S16 + ///.type primitive(U16) U16 + ///.type primitive(S8) S8 + ///.type(export) funcptr(void()) ANY [rest of string was truncated]";. + /// + internal static string CompiledCodeDisasm { + get { + return ResourceManager.GetString("CompiledCodeDisasm", resourceCulture); + } + } + } +} diff --git a/IFPSLib.Tests/Properties/Resources.resx b/IFPSLib.Tests/Properties/Resources.resx new file mode 100644 index 0000000..52b96ff --- /dev/null +++ b/IFPSLib.Tests/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\CompiledCode.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\CompiledCode.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/IFPSLib.Tests/ScriptTest.cs b/IFPSLib.Tests/ScriptTest.cs new file mode 100644 index 0000000..f0f2fca --- /dev/null +++ b/IFPSLib.Tests/ScriptTest.cs @@ -0,0 +1,48 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +using IFPSLib; +using IFPSAsmLib; +using IFPSLib.Tests.Properties; + +namespace IFPSLib.Tests +{ + [TestClass] + public class ScriptTest + { + private static readonly string origB64 = Convert.ToBase64String(Resources.CompiledCode); + + [TestMethod] + public void TestLoadSave() + { + // Load the script. + var script = Script.Load(Resources.CompiledCode); + // Ensure it's not null. + Assert.IsNotNull(script); + // For an official script (compiled by inno setup), the entrypoint is the first function. + Assert.AreEqual(script.EntryPoint, script.Functions[0]); + // Save the script. + var savedBytes = script.Save(); + // Convert to base64 for later. + var saved = Convert.ToBase64String(savedBytes); + // Load the saved script. + var scriptSaved = Script.Load(savedBytes); + // Save again. + var savedTwice = Convert.ToBase64String(scriptSaved.Save()); + // Ensure both saved scripts equal each other. + Assert.AreEqual(saved, savedTwice); + // Ensure the saved script equals the original. + Assert.AreEqual(saved, origB64); + // Ensure the disassemblies are equal. + Assert.AreEqual(script.Disassemble(), scriptSaved.Disassemble()); + } + + [TestMethod] + public void TestAsm() + { + var script = Assembler.Assemble(Resources.CompiledCodeDisasm); + var savedB64 = Convert.ToBase64String(script.Save()); + Assert.AreEqual(savedB64, origB64); + } + } +} diff --git a/IFPSLib.Tests/app.config b/IFPSLib.Tests/app.config new file mode 100644 index 0000000..1696df6 --- /dev/null +++ b/IFPSLib.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/IFPSLib.Tests/packages.config b/IFPSLib.Tests/packages.config new file mode 100644 index 0000000..8e3e184 --- /dev/null +++ b/IFPSLib.Tests/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/IFPSLib/CustomAttribute.cs b/IFPSLib/CustomAttribute.cs new file mode 100644 index 0000000..8085a27 --- /dev/null +++ b/IFPSLib/CustomAttribute.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IFPSLib +{ + /// + /// Represents additional type/function metadata, similar to .NET attributes. + /// + public class CustomAttribute + { + /// + /// Attribute type name + /// + public string Name { get; set; } + + /// + /// Attribute arguments (indexed by constructor argument number). + /// + + public IList Arguments { get; internal set; } = new List(); + + public CustomAttribute(string name) + { + Name = name; + } + + internal static IList LoadList(BinaryReader br, Script script) + { + var listLen = br.Read(); + var ret = new List(listLen); + for (int idxAttr = 0; idxAttr < listLen; idxAttr++) + { + var nameLen = br.Read(); + var attr = new CustomAttribute(br.ReadAsciiString(nameLen)); + var argsLen = br.Read(); + attr.Arguments = new List(argsLen); + for (int idxArg = 0; idxArg < argsLen; idxArg++) + { + attr.Arguments.Add(TypedData.Load(br, script)); + } + ret.Add(attr); + } + return ret; + } + + internal static void SaveList(BinaryWriter bw, IList list, Script.SaveContext ctx) + { + bw.Write(list.Count); + for (int idxAttr = 0; idxAttr < list.Count; idxAttr++) + { + var attr = list[idxAttr]; + bw.WriteAsciiString(attr.Name.ToUpper(), true); + bw.Write(attr.Arguments.Count); + for (int idxArg = 0; idxArg < attr.Arguments.Count; idxArg++) + { + attr.Arguments[idxArg].Save(bw, ctx); + } + } + } + + public override string ToString() + { + var sb = new StringBuilder("["); + sb.Append(Name); + sb.Append('('); + for (int i = 0; i < Arguments.Count; i++) + { + if (i != 0) sb.Append(", "); + sb.Append(Arguments[i]); + } + sb.Append(")]"); + return sb.ToString(); + } + } +} diff --git a/IFPSLib/Emit/AluOpCode.cs b/IFPSLib/Emit/AluOpCode.cs new file mode 100644 index 0000000..ba9719c --- /dev/null +++ b/IFPSLib/Emit/AluOpCode.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Type of an ALU operation for the "calculate" instruction at the bytecode level. + /// + public enum AluOpCode : byte + { + Add, + Sub, + Mul, + Div, + Mod, + Shl, + Shr, + And, + Or, + Xor, + } +} diff --git a/IFPSLib/Emit/BytecodeOperandType.cs b/IFPSLib/Emit/BytecodeOperandType.cs new file mode 100644 index 0000000..1bb4e69 --- /dev/null +++ b/IFPSLib/Emit/BytecodeOperandType.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Type of a PascalScript operand at the bytecode level. + /// + internal enum BytecodeOperandType : byte + { + /// + /// Operand refers to a local or global variable, or an argument. + /// + Variable, + /// + /// Operand refers to an immediate typed constant. + /// + Immediate, + /// + /// Operand refers to an element of an indexed variable, where the index is specified as an immediate 32-bit constant. + /// + IndexedImmediate, + /// + /// Operand refers to an element of an indexed variable, where the index is specified as a local or global variable, or an argument. + /// + IndexedVariable + } +} diff --git a/IFPSLib/Emit/CmpOpCode.cs b/IFPSLib/Emit/CmpOpCode.cs new file mode 100644 index 0000000..185a9eb --- /dev/null +++ b/IFPSLib/Emit/CmpOpCode.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Type of a compare operation for the "cmp" instruction at the bytecode level. + /// + public enum CmpOpCode : byte + { + /// + /// Greater than or equal to + /// + Ge, + /// + /// Less than or equal to + /// + Le, + /// + /// Greater than + /// + Gt, + /// + /// Less than + /// + Lt, + /// + /// Not equal + /// + Ne, + /// + /// Equal + /// + Eq, + /// + /// If operand 2 is Variant[], checks if operand 1 when converted to COM VARIANT is in the array. + /// If operand 2 is a Set, checks if operand 1 is in the Set. + /// Invalid if operand 2 is any other type + /// + In, + /// + /// Operand 1 must be a Class, operand 2 is u32 type index. + /// The type referenced by operand 2 must be a Class. + /// Checks if operand 1 is of the Class type referenced by operand 2. + /// + Is + } +} diff --git a/IFPSLib/Emit/Code.cs b/IFPSLib/Emit/Code.cs new file mode 100644 index 0000000..b694a22 --- /dev/null +++ b/IFPSLib/Emit/Code.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// A PascalScript opcode at the bytecode level. + /// + public enum Code : ushort + { + Assign, + Calculate, + Push, + PushVar, + Pop, + Call, + Jump, + JumpNZ, + JumpZ, + Ret, + SetStackType, + PushType, + Compare, + CallVar, + SetPtr, // removed between 2003 and 2005 + SetZ, + Neg, + SetFlag, + JumpF, + StartEH, + PopEH, + Not, + Cpval, + Inc, + Dec, + PopJump, + PopPopJump, + Nop = 0xff, + + Add = (Calculate << 8) | (AluOpCode.Add), + Sub = (Calculate << 8) | (AluOpCode.Sub), + Mul = (Calculate << 8) | (AluOpCode.Mul), + Div = (Calculate << 8) | (AluOpCode.Div), + Mod = (Calculate << 8) | (AluOpCode.Mod), + Shl = (Calculate << 8) | (AluOpCode.Shl), + Shr = (Calculate << 8) | (AluOpCode.Shr), + And = (Calculate << 8) | (AluOpCode.And), + Or = (Calculate << 8) | (AluOpCode.Or), + Xor = (Calculate << 8) | (AluOpCode.Xor), + + Ge = (Compare << 8) | (CmpOpCode.Ge), + Le = (Compare << 8) | (CmpOpCode.Le), + Gt = (Compare << 8) | (CmpOpCode.Gt), + Lt = (Compare << 8) | (CmpOpCode.Lt), + Ne = (Compare << 8) | (CmpOpCode.Ne), + Eq = (Compare << 8) | (CmpOpCode.Eq), + In = (Compare << 8) | (CmpOpCode.In), + Is = (Compare << 8) | (CmpOpCode.Is), + + SetFlagNZ = (SetFlag << 8) | (SetFlagOpCode.NotZero), + SetFlagZ = (SetFlag << 8) | (SetFlagOpCode.Zero), + + EndTry = (PopEH << 8) | (PopEHOpCode.EndTry), + EndFinally = (PopEH << 8) | (PopEHOpCode.EndFinally), + EndCatch = (PopEH << 8) | (PopEHOpCode.EndCatch), + EndCF = (PopEH << 8) | (PopEHOpCode.EndHandler), + + UNKNOWN1 = 0xff00, + UNKNOWN_ALU, + UNKNOWN_CMP, + UNKNOWN_SF, + UNKNOWN_POPEH, + } +} diff --git a/IFPSLib/Emit/ExternalFunction.cs b/IFPSLib/Emit/ExternalFunction.cs new file mode 100644 index 0000000..8b97323 --- /dev/null +++ b/IFPSLib/Emit/ExternalFunction.cs @@ -0,0 +1,443 @@ +using Cysharp.Collections; +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IFPSLib.Emit +{ + namespace FDecl + { + public abstract class Base + { + internal bool HasReturnArgument; + + /// + /// If null, argument information is unknown. + /// + internal IList Arguments { get; set; } + + internal virtual string Name => ""; + + public abstract int Size { get; } + + + protected const string DllString = "dll:"; + protected const string ClassString = "class:"; + protected const string ComString = "intf:."; + + internal static Base Load(BinaryReader br) + { + var fdeclLen = br.Read(); + using (var fdeclMem = new NativeMemoryArray(fdeclLen, true)) + { + var fdeclSpan = fdeclMem.AsSpan(); + br.Read(fdeclSpan); + using (var brDecl = new BinaryReader(fdeclMem.AsStream(), Encoding.UTF8, true)) + { + if (fdeclSpan.EqualsAsciiString(0, DllString)) + { + brDecl.BaseStream.Position = DllString.Length; + return DLL.Load(brDecl); + } + else if (fdeclSpan.EqualsAsciiString(0, ClassString)) + { + brDecl.BaseStream.Position = ClassString.Length; + return Class.Load(brDecl); + } + else if (fdeclSpan.EqualsAsciiString(0, ComString)) + { + brDecl.BaseStream.Position = ComString.Length; + return COM.Load(brDecl); + } + else + { + return Internal.Load(brDecl); + } + } + } + } + + internal void Save(BinaryWriter bw, Script.SaveContext ctx) + { + using (var ms = new MemoryStream()) + { + using (var msbw = new BinaryWriter(ms, Encoding.UTF8, true)) + { + SaveCore(msbw, ctx); + if (ms.Length > uint.MaxValue) throw new InvalidOperationException("Declaration length is greater than 4GB"); + bw.Write((uint)ms.Length); + bw.Write(ms); + } + } + } + + internal abstract void SaveCore(BinaryWriter bw, Script.SaveContext ctx); + + protected void LoadArguments(BinaryReader br) + { + HasReturnArgument = br.ReadByte() != 0; + Arguments = FunctionArgument.LoadForExternal(br); + } + + protected void SaveArguments(BinaryWriter bw) + { + bw.Write(HasReturnArgument); + foreach (var arg in Arguments) + { + bw.Write(arg.ArgumentType == FunctionArgumentType.Out); + } + } + + protected int SizeOfArguments => sizeof(byte) + (sizeof(byte) * Arguments.Count); + } + + public abstract class BaseCC : Base + { + public NativeCallingConvention CallingConvention; + + public override string ToString() + { + switch (CallingConvention) + { + case NativeCallingConvention.Register: + return "__fastcall"; + case NativeCallingConvention.Pascal: + return "__pascal"; + case NativeCallingConvention.CDecl: + return "__cdecl"; + case NativeCallingConvention.Stdcall: + return "__stdcall"; + default: + return ""; + } + } + } + + public sealed class DLL : BaseCC + { + public string DllName; + public string ProcedureName; + public bool DelayLoad; + public bool LoadWithAlteredSearchPath; + + internal override string Name => string.Format("{0}!{1}", DllName, ProcedureName); + + internal static new DLL Load(BinaryReader br) + { + var ret = new DLL(); + ret.DllName = br.ReadAsciiStringTerminated(); + ret.ProcedureName = br.ReadAsciiStringTerminated(); + ret.CallingConvention = (NativeCallingConvention)br.ReadByte(); + ret.DelayLoad = br.ReadByte() != 0; + ret.LoadWithAlteredSearchPath = br.ReadByte() != 0; + + ret.LoadArguments(br); + + return ret; + } + + public override string ToString() + { + return string.Format( + "dll(\"{0}\",\"{1}\"{2}{3}) {4}", + DllName.Replace("\"", "\\\""), + ProcedureName.Replace("\"", "\\\""), + DelayLoad ? "delayload, " : "", + LoadWithAlteredSearchPath ? "alteredsearchpath" : "", + base.ToString() + ); + } + + public override int Size => + DllString.Length + + Encoding.ASCII.GetByteCount(DllName) + sizeof(byte) + + Encoding.ASCII.GetByteCount(ProcedureName) + sizeof(byte) + + sizeof(NativeCallingConvention) + + sizeof(byte) + sizeof(byte) + + SizeOfArguments; + + internal override void SaveCore(BinaryWriter bw, Script.SaveContext ctx) + { + bw.WriteAsciiString(DllString); + bw.WriteAsciiStringTerminated(DllName); + bw.WriteAsciiStringTerminated(ProcedureName); + bw.Write(CallingConvention); + bw.Write((byte)(DelayLoad ? 1 : 0)); + bw.Write((byte)(LoadWithAlteredSearchPath ? 1 : 0)); + SaveArguments(bw); + } + } + + public sealed class Class : BaseCC + { + public string ClassName; + public string FunctionName; + public bool IsProperty = false; + + internal override string Name => string.Format("{0}->{1}", ClassName, FunctionName); + + + private const byte TERMINATOR = (byte)'|'; + + internal static new Class Load(BinaryReader br) + { + var ret = new Class(); + + // check for a special type + if ((br.BaseStream.Length - br.BaseStream.Position) == 1) + { + ret.ClassName = "Class"; + ret.HasReturnArgument = true; + ret.CallingConvention = NativeCallingConvention.Pascal; + + var special = br.ReadByte(); + switch (special) + { + case (byte)'+': + ret.FunctionName = "CastToType"; + ret.Arguments = new List() + { + new FunctionArgument() { + Type = null, + ArgumentType = FunctionArgumentType.Out + }, + new FunctionArgument() + { + Type = null, + ArgumentType = FunctionArgumentType.Out + } + }; + break; + case (byte)'-': + ret.FunctionName = "SetNil"; + ret.Arguments = new List() + { + new FunctionArgument() { + Type = null, + ArgumentType = FunctionArgumentType.Out + }, + }; + break; + default: + throw new InvalidDataException(string.Format("Unknown special type: 0x{0:x2}", special)); + } + return ret; + } + + ret.ClassName = br.ReadAsciiStringTerminated(TERMINATOR); + ret.FunctionName = br.ReadAsciiStringTerminated(TERMINATOR); + ret.IsProperty = ret.FunctionName[ret.FunctionName.Length - 1] == '@'; + if (ret.IsProperty) + { + ret.FunctionName = ret.FunctionName.Substring(0, ret.FunctionName.Length - 1); + } + ret.CallingConvention = (NativeCallingConvention)br.ReadByte(); + ret.LoadArguments(br); + return ret; + } + + public override string ToString() + { + return string.Format( + "class({0}, {1}{2}) {3}", + ClassName, + FunctionName, + IsProperty ? ", property" : "", + base.ToString() + ); + } + + private int SizeBody() + { + if (ClassName == "Class" && (FunctionName == "CastToType" || FunctionName == "SetNil")) return 1; + + return Encoding.ASCII.GetByteCount(ClassName) + sizeof(byte) + + Encoding.ASCII.GetByteCount(FunctionName) + sizeof(byte) + + (IsProperty ? sizeof(byte) : 0) + + sizeof(NativeCallingConvention) + + SizeOfArguments; + } + + public override int Size => + ClassString.Length + SizeBody(); + + internal override void SaveCore(BinaryWriter bw, Script.SaveContext ctx) + { + bw.WriteAsciiString(ClassString); + + if (ClassName == "Class") + { + if (FunctionName == "CastToType") + { + bw.Write((byte)'+'); + return; + } + else if (FunctionName == "SetNil") + { + bw.Write((byte)'-'); + return; + } + } + + bw.WriteAsciiStringTerminated(ClassName, TERMINATOR); + var sb = new StringBuilder(FunctionName); + if (IsProperty) sb.Append('@'); + bw.WriteAsciiStringTerminated(sb.ToString(), TERMINATOR); + bw.Write(CallingConvention); + SaveArguments(bw); + } + + } + + public sealed class COM : BaseCC + { + public uint VTableIndex; + + internal override string Name => string.Format("CoInterface->vtbl[{0}]", VTableIndex); + + internal static new COM Load(BinaryReader br) + { + var ret = new COM(); + ret.VTableIndex = br.Read(); + ret.CallingConvention = (NativeCallingConvention)br.ReadByte(); + ret.LoadArguments(br); + return ret; + } + + public override string ToString() + { + return string.Format("com({0}) {1}", VTableIndex, base.ToString()); + } + + public override int Size => + ComString.Length + sizeof(uint) + sizeof(NativeCallingConvention) + SizeOfArguments; + + internal override void SaveCore(BinaryWriter bw, Script.SaveContext ctx) + { + bw.WriteAsciiString(ComString); + bw.Write(VTableIndex); + bw.Write(CallingConvention); + SaveArguments(bw); + } + } + + public sealed class Internal : Base + { + internal static new Internal Load(BinaryReader br) + { + var ret = new Internal(); + ret.LoadArguments(br); + return ret; + } + + public override string ToString() + { + return "internal"; + } + + public override int Size => SizeOfArguments; + + internal override void SaveCore(BinaryWriter bw, Script.SaveContext ctx) + { + SaveArguments(bw); + } + } + } + + /// + /// A native function that imports from a DLL or an internal implementation + /// + public class ExternalFunction : FunctionBase + { + public FDecl.Base Declaration = null; + + private bool ExportsName => string.IsNullOrEmpty(Declaration?.Name); + + public override IType ReturnArgument { get; set; } + + /// + /// If null, argument information is unknown. + /// + public override IList Arguments { + get => Declaration?.Arguments; + set + { + if (Declaration == null) throw new NullReferenceException("No declaration is present"); + Declaration.Arguments = value; + } + } + + internal static ExternalFunction Load(BinaryReader br, Script script, bool exported) + { + var ret = new ExternalFunction(); + ret.Exported = exported; + var namelen = br.ReadByte(); + if (namelen != 0) + ret.Name = br.ReadAsciiString(namelen); + if (exported) + { + ret.Declaration = FDecl.Base.Load(br); + if (ret.Declaration.HasReturnArgument) ret.ReturnArgument = UnknownType.Instance; + if (string.IsNullOrEmpty(ret.Name)) ret.Name = ret.Declaration.Name; + } + return ret; + } + + public override string ToString() + { + var sb = new StringBuilder(".function"); + if (Exported) sb.Append("(import)"); + sb.Append(" external "); + if (Declaration != null) + sb.Append(Declaration); + sb.Append(' '); + sb.Append(ReturnArgument != null ? "returnsval " : "void "); + sb.Append(Name); + if (Declaration != null) + { + sb.Append('('); + for (int i = 0; i < Arguments.Count; i++) + { + sb.Append(i == 0 ? "" : ","); + sb.Append(Arguments[i].ToString()); + } + sb.Append(')'); + } + return sb.ToString(); + } + + private byte NameLength() => (byte)Math.Min(0xff, Encoding.ASCII.GetByteCount(Name)); + + public override int Size => sizeof(byte) + NameLength() + (Declaration == null ? 0 : Declaration.Size); + + internal override void Save(BinaryWriter bw, Script.SaveContext ctx) + { + FunctionFlags flags = FunctionFlags.External; + + if (Exported) + { + flags |= FunctionFlags.Exported; + Declaration.HasReturnArgument = ReturnArgument != null; + } + + if (Attributes.Count != 0) flags |= FunctionFlags.HasAttributes; + + bw.Write(flags); + if (ExportsName) + { + bw.Write(NameLength()); + var internalName = Name.ToUpper(); + if (Name.Length > 0xff) internalName = Name.Substring(0, 0xff); + bw.WriteAsciiString(internalName); + } + else bw.Write(0); + + if (Declaration != null) + { + Declaration.Save(bw, ctx); + } + } + } +} diff --git a/IFPSLib/Emit/FlowControl.cs b/IFPSLib/Emit/FlowControl.cs new file mode 100644 index 0000000..658db51 --- /dev/null +++ b/IFPSLib/Emit/FlowControl.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Describes how an instruction alters control flow. + /// + public enum FlowControl + { + /// + /// Instruction branches to another block. + /// + Branch, + /// + /// Instruction calls a function. + /// + Call, + /// + /// Instruction branches to another block if a condition passes. + /// + Cond_Branch, + /// + /// Normal flow of control; to the next instruction. + /// + Next, + /// + /// Returns to the caller. + /// + Return + } +} diff --git a/IFPSLib/Emit/Instruction.cs b/IFPSLib/Emit/Instruction.cs new file mode 100644 index 0000000..1b668fc --- /dev/null +++ b/IFPSLib/Emit/Instruction.cs @@ -0,0 +1,678 @@ +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Collections; + +namespace IFPSLib.Emit +{ + public sealed class Instruction : IEnumerable + { + /// + /// The opcode + /// + public OpCode OpCode; + /// + /// The operands + /// + public IReadOnlyList Operands => m_Operands.AsReadOnly(); + /// + /// Offset of the instruction in the function body + /// + public uint Offset; + /// + /// Referenced by an operand of another instruction + /// + public bool Referenced; + + private List m_Operands = new List(); + + /// + /// Gets or sets the operand at the specified index. + /// + /// The zero-based index of the operand to get or set. + /// The operand at the specified index. + /// No operand exists at the given index; or the operand to set is incompatible with the existing operand. + public Operand this[int index] + { + get => m_Operands[index]; + set + { + if (!m_Operands[index].SimilarType(value)) throw new ArgumentOutOfRangeException(nameof(value)); + m_Operands[index] = value; + } + } + + /// + /// Returns an enumerator that iterates through the operands. + /// + /// An enumerator for the operands list. + public IEnumerator GetEnumerator() + { + return m_Operands.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the operands. + /// + /// An enumerator for the operands list. + IEnumerator IEnumerable.GetEnumerator() + { + return m_Operands.GetEnumerator(); + } + + /// + /// Default constructor + /// + internal Instruction() { } + + /// + /// Constructor + /// + /// Opcode + internal Instruction(OpCode opcode) + { + OpCode = opcode; + m_Operands = new List(0); + } + + /// + /// Constructor + /// + /// Opcode + /// Operands + internal Instruction(OpCode opcode, List operands) : this(opcode) + { + m_Operands = operands; + } + + /// + /// Creates a new instruction with no operand + /// + /// Opcode + /// New instruction + public static Instruction Create(OpCode opcode) + { + if (opcode.OperandType != OperandType.InlineNone) throw new ArgumentOutOfRangeException(nameof(opcode), "Must be a no-operand opcode"); + return new Instruction(opcode); + } + + + /// + /// Creates a new instruction with one operand + /// + /// Opcode + /// Operand + /// New instruction + public static Instruction Create(OpCode opcode, Operand operand) + { + if (opcode.OperandType != OperandType.InlineValue && opcode.OperandType != OperandType.InlineValueSF) + throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have a value operand"); + return new Instruction(opcode, new List(1) { operand }); + } + + + /// + /// Creates a new instruction with an immediate operand + /// + /// Opcode + /// Immediate operand + /// Type of operand, must be a PascalScript primitive + /// New instruction + public static Instruction Create(OpCode opcode, TType val) + => Create(opcode, Operand.Create(val)); + + /// + /// Creates a new instruction with a variable operand + /// + /// Opcode + /// Variable operand + /// New instruction + public static Instruction Create(OpCode opcode, IVariable var) + => Create(opcode, Operand.Create(var)); + + /// + /// Creates a new branch instruction + /// + /// Opcode + /// Branch target + /// New instruction + public static Instruction Create(OpCode opcode, Instruction target) + { + if (opcode.OperandType != OperandType.InlineBrTarget) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have an instruction operand"); + target.Referenced = true; + return new Instruction(opcode, new List(1) { Operand.Create(target) }); + } + + /// + /// Creates a new instruction with two operands + /// + /// Opcode + /// First operand + /// Second operand + /// New instruction + public static Instruction Create(OpCode opcode, Operand op0, Operand op1) + { + if (opcode.OperandType != OperandType.InlineValueValue) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have two value operands"); + return new Instruction(opcode, new List(2) { op0, op1 }); + } + + /// + /// Creates a new instruction with two variable operands + /// + /// Opcode + /// First operand + /// Second operand + /// New instruction + public static Instruction Create(OpCode opcode, IVariable op0, IVariable op1) + => Create(opcode, Operand.Create(op0), Operand.Create(op1)); + + /// + /// Creates a new instruction with a variable operand and an immediate operand + /// + /// Opcode + /// First operand + /// Second operand + /// Type of second operand, must be a PascalScript primitive + /// New instruction + public static Instruction Create(OpCode opcode, IVariable op0, TType val) + => Create(opcode, Operand.Create(op0), Operand.Create(val)); + + /// + /// Creates a new branch instruction with an operand + /// + /// Opcode + /// Branch target + /// Operand + /// New instruction + public static Instruction Create(OpCode opcode, Instruction target, Operand operand) + { + if (opcode.OperandType != OperandType.InlineBrTargetValue) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have an instruction and a value operand"); + return new Instruction(opcode, new List(2) { Operand.Create(target), operand }); + } + + /// + /// Creates a new branch instruction with a variable operand + /// + /// Opcode + /// Branch target + /// Operand + /// New instruction + public static Instruction Create(OpCode opcode, Instruction target, IVariable var) + => Create(opcode, target, Operand.Create(var)); + + /// + /// Creates a new call instruction + /// + /// Opcode + /// Function + /// New instruction + public static Instruction Create(OpCode opcode, IFunction func) + { + if (opcode.OperandType != OperandType.InlineFunction) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have a function operand"); + return new Instruction(opcode, new List(1) { Operand.Create(func) }); + } + + /// + /// Creates a new instruction with a type operand + /// + /// Opcode + /// Type + /// New instruction + public static Instruction Create(OpCode opcode, Types.IType type) + { + if (opcode.OperandType != OperandType.InlineType) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have a function operand"); + return new Instruction(opcode, new List(1) { Operand.Create(type) }); + } + + /// + /// Creates a new compare instruction + /// + /// Opcode + /// Destination + /// Left hand side + /// Right hand side + /// New instruction + public static Instruction Create(OpCode opcode, Operand op0, Operand op1, Operand op2) + { + if (opcode.OperandType != OperandType.InlineCmpValue) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have three value operands"); + return new Instruction(opcode, new List(3) { op0, op1, op2 }); + } + + /// + /// Creates a new compare instruction with two variables and an immediate + /// + /// Opcode + /// Destination + /// Left hand side + /// Right hand side + /// Type of right hand side, must be a PascalScript primitive + /// New instruction + public static Instruction Create(OpCode opcode, IVariable op0, IVariable op1, TType op2) + => Create(opcode, Operand.Create(op0), Operand.Create(op1), Operand.Create(op2)); + + /// + /// Creates a new compare instruction with three variables + /// + /// Opcode + /// Destination + /// Left hand side + /// Right hand side + /// New instruction + public static Instruction Create(OpCode opcode, IVariable op0, IVariable op1, IVariable op2) + => Create(opcode, Operand.Create(op0), Operand.Create(op1), Operand.Create(op2)); + + /// + /// Creates a new compare-type instruction + /// + /// Opcode + /// Destination + /// Left hand side + /// Right hand side (type) + /// New instruction + public static Instruction Create(OpCode opcode, Operand op0, Operand op1, Types.IType type) + { + if (opcode.OperandType != OperandType.InlineCmpValueType) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have two value operands and a type operand"); + return new Instruction(opcode, new List(3) { op0, op1, Operand.Create(type) }); + } + + /// + /// Creates a new compare-type instruction with two variables + /// + /// Opcode + /// Destination + /// Left hand side + /// Right hand side (type) + /// New instruction + public static Instruction Create(OpCode opcode, IVariable op0, IVariable op1, Types.IType type) + => Create(opcode, Operand.Create(op0), Operand.Create(op1), type); + + /// + /// Creates a new StartEH instruction + /// + /// Start of first finally block + /// Start of catch block + /// Start of second finally block + /// End of last exception handler block + /// New instruction + public static Instruction CreateStartEH(Instruction Finally, Instruction Catch, Instruction CatchFinally, Instruction End) + { + return new Instruction(OpCodes.StartEH, new List(4) { Operand.Create(Finally), Operand.Create(Catch), Operand.Create(CatchFinally), Operand.Create(End) }); + } + + /// + /// Creates a new StartEH instruction for a try-finally block + /// + /// Start of finally block + /// End of finally block + /// New instruction + public static Instruction CreateTryFinally(Instruction Finally, Instruction End) + => CreateStartEH(Finally, null, null, End); + + /// + /// Create a new StartEH instruction for a try-catch block + /// + /// Start of catch block + /// End of catch block + /// New instruction + public static Instruction CreateTryCatch(Instruction Catch, Instruction End) + => CreateStartEH(null, Catch, null, End); + + /// + /// Create a new StartEH instruction for a try-catch-finally block + /// + /// Start of catch block + /// Start of finally block + /// End of finally block + /// New instruction + public static Instruction CreateTryCatchFinally(Instruction Catch, Instruction Finally, Instruction End) + => CreateStartEH(null, Catch, Finally, End); + + /// + /// Creates a SetStackType instruction (removed after version 21 or 22) + /// + /// Type to set variable to + /// Variable to set + /// New instruction + public static Instruction CreateSetStackType(Types.IType type, IVariable variable) + { + return new Instruction(OpCodes.SetStackType, new List(2) { Operand.Create(type), Operand.Create(variable) }); + } + + /// + /// Replaces this instruction with the content of another. Use instead of replacing an instruction in an array in case it is referenced by another instruction. + /// + /// New instruction + public void Replace(Instruction insn) + { + OpCode = insn.OpCode; + m_Operands = insn.m_Operands; + Offset = insn.Offset; + Referenced = insn.Referenced; + } + + private static uint FixBranchOffset(BinaryReader br, uint data) + { + return data + (uint)br.BaseStream.Position; + } + + private static uint FixBranchOffsetEH(BinaryReader br, uint data) + { + if (data == uint.MaxValue) return uint.MaxValue; + return FixBranchOffset(br, data); + } + + private static uint ReadBranchOffset(BinaryReader br) + { + return FixBranchOffset(br, br.Read()); + } + + internal static Instruction Load(BinaryReader br, Script script, ScriptFunction function) + { + var ret = new Instruction(); + ret.Offset = (uint)br.BaseStream.Position; + OpCode opcode = null; + var opFirstByte = (Code)br.ReadByte(); + switch (opFirstByte) + { + case Code.Calculate: + var calcSecond = (AluOpCode)br.ReadByte(); + if (calcSecond > AluOpCode.Xor) opcode = OpCodes.UNKNOWN_ALU; + else opcode = OpCodes.AluOpCodes[(int)calcSecond]; + break; + case Code.Compare: + var cmpSecond = (CmpOpCode)br.ReadByte(); + if (cmpSecond > CmpOpCode.Is) opcode = OpCodes.UNKNOWN_CMP; + else opcode = OpCodes.CmpOpCodes[(int)cmpSecond]; + break; + case Code.PopEH: + var popEhSecond = (PopEHOpCode)br.ReadByte(); + if (popEhSecond > PopEHOpCode.EndHandler) opcode = OpCodes.UNKNOWN_POPEH; + else opcode = OpCodes.PopEHOpCodes[(int)popEhSecond]; + break; + case Code.SetFlag: + // placeholder + opcode = OpCodes.UNKNOWN_SF; + break; + case Code.SetStackType: + if (script.FileVersion >= Script.VERSION_MAX_SETSTACKTYPE) opcode = OpCodes.UNKNOWN1; + else opcode = OpCodes.SetStackType; + break; + default: + opcode = OpCodes.OneByteOpCodes[(int)opFirstByte]; + break; + } + + ret.OpCode = opcode; + switch (opcode.OperandType) + { + case OperandType.InlineNone: + ret.m_Operands = new List(0); + break; + case OperandType.InlineValue: + ret.m_Operands = new List(1) { Operand.LoadValue(br, script, function) }; + break; + case OperandType.InlineBrTarget: + ret.m_Operands = new List(1) { new Operand(new TypedData(InstructionType.Instance, ReadBranchOffset(br))) }; + break; + case OperandType.InlineValueValue: + ret.m_Operands = new List(2) { Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function) }; + break; + case OperandType.InlineBrTargetValue: + var brOffset = br.Read(); + var valOp = Operand.LoadValue(br, script, function); + ret.m_Operands = new List(2) { new Operand(new TypedData(InstructionType.Instance, FixBranchOffset(br, brOffset))), valOp }; + break; + case OperandType.InlineFunction: + ret.m_Operands = new List(1) { Operand.Create(script.Functions[(int)br.Read()]) }; + break; + case OperandType.InlineType: + ret.m_Operands = new List(1) { Operand.Create(script.Types[(int)br.Read()]) }; + break; + case OperandType.InlineCmpValue: + ret.m_Operands = new List(3) { Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function) }; + break; + case OperandType.InlineCmpValueType: + ret.m_Operands = new List(3) { + Operand.LoadValue(br, script, function), + Operand.LoadValue(br, script, function), + Operand.Create(script.Types[(int)br.Read()]) + }; + break; + case OperandType.InlineEH: + var br0 = br.Read(); + var br1 = br.Read(); + var br2 = br.Read(); + var br3 = br.Read(); + ret.m_Operands = new List(4) { + new Operand(new TypedData(InstructionType.Instance, FixBranchOffsetEH(br, br0))), + new Operand(new TypedData(InstructionType.Instance, FixBranchOffsetEH(br, br1))), + new Operand(new TypedData(InstructionType.Instance, FixBranchOffsetEH(br, br2))), + new Operand(new TypedData(InstructionType.Instance, FixBranchOffsetEH(br, br3))) + }; + break; + case OperandType.InlineTypeVariable: + ret.m_Operands = new List(2) { + Operand.Create(script.Types[(int)br.Read()]), + new Operand(VariableBase.Load(br, script, function)) + }; + return ret; + case OperandType.InlineValueSF: + ret.m_Operands = new List(1) { Operand.LoadValue(br, script, function) }; + var sfSecond = (SetFlagOpCode)br.ReadByte(); + if (sfSecond > SetFlagOpCode.Zero) opcode = OpCodes.UNKNOWN_SF; + else opcode = OpCodes.SetFlagOpCodes[(int)sfSecond]; + ret.OpCode = opcode; + break; + default: + throw new ArgumentOutOfRangeException(string.Format("Unknown OperandType {0}", opcode.OperandType)); + } + return ret; + } + + + + internal void FixUpBranchTargets(Dictionary table) + { + for (int i = 0; i < m_Operands.Count; i++) + { + var operand = m_Operands[i]; + if (operand.m_Type != BytecodeOperandType.Immediate) continue; + if (operand.ImmediateTyped.Type.BaseType != PascalTypeCode.Instruction) continue; + if (!(operand.Immediate is uint)) continue; + var target = operand.ImmediateAs(); + if (OpCode.Code == Code.StartEH && target == uint.MaxValue) m_Operands[i] = Operand.Create((Instruction)null); + else + { + m_Operands[i] = Operand.Create(table[target]); + table[target].Referenced = true; + } + } + } + + /// + /// Disassembles the instruction without labelling it + /// + /// Instruction text + public override string ToString() + { + return ToString(false); + } + + /// + /// Disassembles the instruction + /// + /// If true, labels the instruction if it's referenced by another instruction + /// Instruction text + public string ToString(bool forDisasm) + { + var sb = new StringBuilder(); + if (Referenced) sb.AppendLine(string.Format("loc_{0}:", Offset.ToString("x"))); + + if (forDisasm) sb.Append('\t'); + sb.Append(OpCode); + for (int i = 0; i < m_Operands.Count; i++) + { + sb.Append(i == 0 ? " " : ", "); + sb.Append(m_Operands[i]); + } + return sb.ToString(); + } + + /// + /// Gets the size of the instruction + /// + public int Size + { + get + { + var ret = OpCode.Size; + switch (OpCode.OperandType) + { + case OperandType.InlineNone: + break; + case OperandType.InlineValue: + case OperandType.InlineValueSF: + ret += m_Operands[0].Size; + break; + case OperandType.InlineBrTarget: + ret += sizeof(uint); + break; + case OperandType.InlineValueValue: + ret += m_Operands[0].Size + m_Operands[1].Size; + break; + case OperandType.InlineBrTargetValue: + ret += sizeof(uint) + m_Operands[1].Size; + break; + case OperandType.InlineFunction: + case OperandType.InlineType: + ret += sizeof(int); + break; + case OperandType.InlineCmpValue: + ret += m_Operands[0].Size + m_Operands[1].Size + m_Operands[2].Size; + break; + case OperandType.InlineCmpValueType: + ret += m_Operands[0].Size + m_Operands[1].Size + sizeof(int); + break; + case OperandType.InlineEH: + ret += sizeof(uint) * 4; + break; + case OperandType.InlineTypeVariable: + ret += sizeof(int) + m_Operands[1].Variable.Size; + break; + } + return ret; + } + } + + private void FixUpReference(int index, Dictionary table, bool allowedNull = false) + { + var insn = m_Operands[index].ImmediateAs(); + if (insn == null) + { + if (!allowedNull) throw new InvalidOperationException("Instruction is null"); + return; + } + if (!table.ContainsKey(insn)) throw new InvalidOperationException("Referenced instruction is not in the same function as the referencing instruction"); + insn.Referenced = true; + } + + internal void FixUpReferences(Dictionary table) + { + switch (OpCode.OperandType) + { + case OperandType.InlineBrTarget: + case OperandType.InlineBrTargetValue: + FixUpReference(0, table); + break; + case OperandType.InlineEH: + FixUpReference(0, table, true); + FixUpReference(1, table, true); + FixUpReference(2, table, true); + FixUpReference(3, table, true); + break; + } + } + + private uint GetEHOffset(Operand operand, Dictionary table) + { + var insn = operand.ImmediateAs(); + if (insn == null) return uint.MaxValue; + return table[insn] - Offset - (uint)Size; + } + + internal void Save(BinaryWriter bw, Script.SaveContext ctx, Dictionary table) + { + if (OpCode.Size == 2) + { + var firstByte = (byte)((int)OpCode.Code >> 8); + bw.Write(firstByte); + if (OpCode.OperandType != OperandType.InlineValueSF) + { + // not setflag, so second byte follows the first + bw.Write((byte)OpCode.Code); + } + } + else if (OpCode.Size == 1) + { + bw.Write((byte)OpCode.Code); + } + else throw new InvalidOperationException(string.Format("Invalid opcode size of {0} bytes", OpCode.Size)); + + switch (OpCode.OperandType) + { + case OperandType.InlineNone: + break; + case OperandType.InlineValue: + m_Operands[0].Save(bw, ctx); + break; + case OperandType.InlineBrTarget: + bw.Write(table[m_Operands[0].ImmediateAs()] - Offset - (uint)Size); + break; + case OperandType.InlineValueValue: + m_Operands[0].Save(bw, ctx); + m_Operands[1].Save(bw, ctx); + break; + case OperandType.InlineBrTargetValue: + bw.Write(table[m_Operands[0].ImmediateAs()] - Offset - (uint)Size); + m_Operands[1].Save(bw, ctx); + break; + case OperandType.InlineFunction: + bw.Write(ctx.GetFunctionIndex(m_Operands[0].ImmediateAs())); + break; + case OperandType.InlineType: + bw.Write(ctx.GetTypeIndex(m_Operands[0].ImmediateAs())); + break; + case OperandType.InlineCmpValue: + m_Operands[0].Save(bw, ctx); + m_Operands[1].Save(bw, ctx); + m_Operands[2].Save(bw, ctx); + break; + case OperandType.InlineCmpValueType: + m_Operands[0].Save(bw, ctx); + m_Operands[1].Save(bw, ctx); + bw.Write(ctx.GetTypeIndex(m_Operands[2].ImmediateAs())); + break; + case OperandType.InlineEH: + bw.Write(GetEHOffset(m_Operands[0], table)); + bw.Write(GetEHOffset(m_Operands[1], table)); + bw.Write(GetEHOffset(m_Operands[2], table)); + bw.Write(GetEHOffset(m_Operands[3], table)); + break; + case OperandType.InlineTypeVariable: + bw.Write(ctx.GetTypeIndex(m_Operands[0].ImmediateAs())); + ((VariableBase)m_Operands[1].Variable).Save(bw, ctx); + break; + case OperandType.InlineValueSF: + m_Operands[0].Save(bw, ctx); + // setflag; second opcode byte is after the operand for some reason + bw.Write((byte)OpCode.Code); + break; + default: + throw new ArgumentOutOfRangeException(string.Format("Unknown OperandType {0}", OpCode.OperandType)); + } + } + } +} diff --git a/IFPSLib/Emit/NativeCallingConvention.cs b/IFPSLib/Emit/NativeCallingConvention.cs new file mode 100644 index 0000000..dbf34a4 --- /dev/null +++ b/IFPSLib/Emit/NativeCallingConvention.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public enum NativeCallingConvention : byte + { + Register, + Pascal, + CDecl, + Stdcall + } +} diff --git a/IFPSLib/Emit/OpCode.cs b/IFPSLib/Emit/OpCode.cs new file mode 100644 index 0000000..e70b489 --- /dev/null +++ b/IFPSLib/Emit/OpCode.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public class OpCode + { + /// + /// Gets the size of the opcode, either 1 or 2 bytes. + /// + public int Size + { + get + { + var firstByte = (Code)(((short)Code) >> 8); + if (firstByte == Code.Compare || firstByte == Code.Calculate || firstByte == Code.PopEH || firstByte == Code.SetFlag) + return sizeof(short); + else return sizeof(byte); + } + } + + public OpCode(string name, byte first, byte second, OperandType operandType, FlowControl flowControl, StackBehaviour push, StackBehaviour pop) + : this(name, (Code)((first << 8) | second), operandType, flowControl, OpCodeType.Experimental, push, pop, true) + { } + + internal OpCode(string name, Code code, OperandType operandType, FlowControl flowControl, OpCodeType opCodeType, + StackBehaviour push = StackBehaviour.Push0, StackBehaviour pop = StackBehaviour.Pop0, bool experimental = false + ) + { + Name = name; + Code = code; + OperandType = operandType; + FlowControl = flowControl; + OpcodeType = opCodeType; + StackBehaviourPush = push; + StackBehaviourPop = pop; + if (!experimental) + { + OpCode[] arr = null; + switch ((Code)(((short)Code) >> 8)) + { + case Code.Calculate: + arr = OpCodes.AluOpCodes; + break; + case Code.Compare: + arr = OpCodes.CmpOpCodes; + break; + case Code.PopEH: + arr = OpCodes.PopEHOpCodes; + break; + case Code.SetFlag: + arr = OpCodes.SetFlagOpCodes; + break; + case 0: + arr = OpCodes.OneByteOpCodes; + break; + } + if (arr != null) arr[(short)Code & 0xff] = this; + OpCodes.m_OpCodesByName[name] = this; + } + } + + public override string ToString() + { + return Name; + } + + /// + /// The opcode name + /// + public readonly string Name; + /// + /// The opcode as a enum + /// + public readonly Code Code; + /// + /// Operand type + /// + public readonly OperandType OperandType; + /// + /// Flow control info + /// + public readonly FlowControl FlowControl; + /// + /// Opcode type + /// + public readonly OpCodeType OpcodeType; + /// + /// Push stack behaviour + /// + public readonly StackBehaviour StackBehaviourPush; + /// + /// Pop stack behaviour + /// + public readonly StackBehaviour StackBehaviourPop; + } +} diff --git a/IFPSLib/Emit/OpCodeType.cs b/IFPSLib/Emit/OpCodeType.cs new file mode 100644 index 0000000..7a1f6aa --- /dev/null +++ b/IFPSLib/Emit/OpCodeType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public enum OpCodeType : byte + { + Macro, + Prefix, + Primitive, + Experimental + } +} diff --git a/IFPSLib/Emit/OpCodes.cs b/IFPSLib/Emit/OpCodes.cs new file mode 100644 index 0000000..d37c9ea --- /dev/null +++ b/IFPSLib/Emit/OpCodes.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public static class OpCodes + { + + private static void FillTable(OpCode[] table, OpCode op) + { + for (int i = 0; i < table.Length; i++) + if (table[i] == null) table[i] = op; + } + + /// + /// All one-byte opcodes + /// + public static readonly OpCode[] OneByteOpCodes = new OpCode[byte.MaxValue + 1]; + + /// + /// All two-byte ALU opcodes (first byte is 0x01) + /// + public static readonly OpCode[] AluOpCodes = new OpCode[(int)AluOpCode.Xor + 1]; + + /// + /// All two-byte comparison opcodes (first byte is 0x0C) + /// + public static readonly OpCode[] CmpOpCodes = new OpCode[(int)CmpOpCode.Is + 1]; + + /// + /// All two-byte exception handler leave opcodes (first byte is 0x14) + /// + public static readonly OpCode[] PopEHOpCodes = new OpCode[(int)PopEHOpCode.EndHandler + 1]; + + /// + /// All two-byte set flag opcodes (first byte is 0x11). + /// The second byte is after the operand for some reason. + /// + public static readonly OpCode[] SetFlagOpCodes = new OpCode[(int)SetFlagOpCode.Zero + 1]; + + /// + /// All non-experimental opcodes, indexed by opcode name. + /// + public static IReadOnlyDictionary ByName => m_OpCodesByName; + + internal static readonly Dictionary m_OpCodesByName = new Dictionary(); + +#pragma warning disable 1591 // disable XML doc warning + public static readonly OpCode UNKNOWN1 = new OpCode("UNKNOWN1", Code.UNKNOWN1, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); + public static readonly OpCode UNKNOWN_ALU = new OpCode("UNKNOWN_ALU", Code.UNKNOWN_ALU, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); + public static readonly OpCode UNKNOWN_CMP = new OpCode("UNKNOWN_CMP", Code.UNKNOWN_CMP, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); + public static readonly OpCode UNKNOWN_POPEH = new OpCode("UNKNOWN_POPEH", Code.UNKNOWN_POPEH, OperandType.InlineNone, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); + public static readonly OpCode UNKNOWN_SF = new OpCode("UNKNOWN_SF", Code.UNKNOWN_SF, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Experimental, StackBehaviour.Push0, StackBehaviour.Pop0, true); + + /// + /// Loads the second value (usually an immediate) into the first value; op0 = op1
+ /// If op0 is a pointer, then op1 is written to that pointer; *op0 = op1 + ///
+ public static readonly OpCode Assign = new OpCode("assign", Code.Assign, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); + + /// + /// Adds the second value to the first value; op0 += op1 + /// + public static readonly OpCode Add = new OpCode("add", Code.Add, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Subtracts the second value from the first value; op0 -= op1 + /// + public static readonly OpCode Sub = new OpCode("sub", Code.Sub, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Multiplies the second value with the first value; op0 *= op1 + /// + public static readonly OpCode Mul = new OpCode("mul", Code.Mul, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Divides the second value from the first value; op0 /= op1 + /// + public static readonly OpCode Div = new OpCode("div", Code.Div, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Modulo divides the second value from the first value; op0 %= op1 + /// + public static readonly OpCode Mod = new OpCode("mod", Code.Mod, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Shifts the first value left by the second value; op0 <<= op1 + /// + public static readonly OpCode Shl = new OpCode("shl", Code.Shl, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Shifts the first value right by the second value; op0 >>= op1 + /// + public static readonly OpCode Shr = new OpCode("shr", Code.Shr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Bitwise ANDs the first value by the second value; op0 &= op1 + /// + public static readonly OpCode And = new OpCode("and", Code.And, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Bitwise ORs the first value by the second value; op0 |= op1 + /// + public static readonly OpCode Or = new OpCode("or", Code.Or, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Bitwise XORs the first value by the second value; op0 ^= op1 + /// + public static readonly OpCode Xor = new OpCode("xor", Code.Xor, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Macro); + + /// + /// Pushes the operand to the top of the stack. + /// + public static readonly OpCode Push = new OpCode("push", Code.Push, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); + /// + /// Pushes a pointer to the operand to the top of the stack. + /// + public static readonly OpCode PushVar = new OpCode("pushvar", Code.PushVar, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); + /// + /// Removes the value currently on top of the stack.
+ /// If that value is a return address, or the stack is empty, the operation is invalid. + ///
+ public static readonly OpCode Pop = new OpCode("pop", Code.Pop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1); + /// + /// Calls the function indicated by the provided index.
+ /// Return address gets pushed to the top of the stack (function index, offset to next instruction, stack pointer).

+ /// Calling convention: + /// + /// push arguments onto the stack, from last to first + /// if function returns a value, push a pointer to the return value + /// + ///
+ public static readonly OpCode Call = new OpCode("call", Code.Call, OperandType.InlineFunction, FlowControl.Call, OpCodeType.Primitive); + /// + /// Unconditionally branches to the target . + /// + public static readonly OpCode Jump = new OpCode("jump", Code.Jump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive); + /// + /// If the value operand is not zero, branch to the target . + /// + public static readonly OpCode JumpNZ = new OpCode("jnz", Code.JumpNZ, OperandType.InlineBrTargetValue, FlowControl.Cond_Branch, OpCodeType.Primitive); + /// + /// If the value operand is zero, branch to the target . + /// + public static readonly OpCode JumpZ = new OpCode("jz", Code.JumpZ, OperandType.InlineBrTargetValue, FlowControl.Cond_Branch, OpCodeType.Primitive); + /// + /// Returns from the current function.
+ /// Any finally blocks inside exception handlers will be executed first.
+ /// Pops the entirety of the current function's stack frame.
+ ///
+ public static readonly OpCode Ret = new OpCode("ret", Code.Ret, OperandType.InlineNone, FlowControl.Return, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Varpop); + /// + /// Removed between version 20 (1.20) and 22 (1.30), after that point this is an invalid opcode.
+ /// Destructs the given variable, creates a new one of the provided type.
+ /// If the provided type is then the operation is invalid. + ///
+ public static readonly OpCode SetStackType = new OpCode("setstacktype", Code.SetStackType, OperandType.InlineTypeVariable, FlowControl.Next, OpCodeType.Primitive); + /// + /// Pushes a new uninitialised value of the given type to the top of the stack. + /// + public static readonly OpCode PushType = new OpCode("pushtype", Code.PushType, OperandType.InlineType, FlowControl.Next, OpCodeType.Primitive, StackBehaviour.Push1); + + /// + /// op0 = op1 >= op2 + /// + public static readonly OpCode Ge = new OpCode("ge", Code.Ge, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// op0 = op1 <= op2 + /// + public static readonly OpCode Le = new OpCode("le", Code.Le, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// op0 = op1 > op2 + /// + public static readonly OpCode Gt = new OpCode("gt", Code.Gt, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// op0 = op1 < op2 + /// + public static readonly OpCode Lt = new OpCode("lt", Code.Lt, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// op0 = op1 != op2 + /// + public static readonly OpCode Ne = new OpCode("ne", Code.Ne, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// op0 = op1 == op2 + /// + public static readonly OpCode Eq = new OpCode("eq", Code.Eq, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// If operand 2 is Variant[], checks if operand 1 when converted to COM VARIANT is in the array.
+ /// If operand 2 is a Set, checks if operand 1 is in the Set.
+ /// Invalid if operand 2 is any other type.
+ /// Result (0 or 1) is placed in operand 0 + ///
+ public static readonly OpCode In = new OpCode("in", Code.In, OperandType.InlineCmpValue, FlowControl.Next, OpCodeType.Macro); + /// + /// Operand 1 must be a , operand 2 is a that must be a + /// Checks if operand 1 is of the referenced by operand 2. + /// + public static readonly OpCode Is = new OpCode("is", Code.Is, OperandType.InlineCmpValueType, FlowControl.Next, OpCodeType.Macro); + + /// + /// Calls the function indicated by the value operand (as a to the entry point). + /// + public static readonly OpCode CallVar = new OpCode("callvar", Code.CallVar, OperandType.InlineValue, FlowControl.Call, OpCodeType.Primitive, StackBehaviour.Push1); + /// + /// op0 must be a pointer.
+ /// If op1 is not a pointer, loads the equivalent address of op1 into op0; op0 = &op1
+ /// If op1 is a pointer, loads op1 into op0; op0 = op1 + ///
+ public static readonly OpCode SetPtr = new OpCode("setptr", Code.SetPtr, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// op0 = op0 == 0 + /// + public static readonly OpCode SetZ = new OpCode("setz", Code.SetZ, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// op0 = -op0 + /// + public static readonly OpCode Neg = new OpCode("neg", Code.Neg, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); + + /// + /// jf = op0 != 0 + /// + public static readonly OpCode SetFlagNZ = new OpCode("sfnz", Code.SetFlagNZ, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Macro); + /// + /// jf = op0 == 0 + /// + public static readonly OpCode SetFlagZ = new OpCode("sfz", Code.SetFlagZ, OperandType.InlineValueSF, FlowControl.Next, OpCodeType.Macro); + + /// + /// If the jump flag (set or unset by or ) is set, branch to the target + /// + public static readonly OpCode JumpF = new OpCode("jf", Code.JumpF, OperandType.InlineBrTarget, FlowControl.Next, OpCodeType.Primitive); + /// + /// Initialises an exception handler.
+ /// The operands are four target s which may be null.
+ /// op0 is the instruction that starts a Finally block. + /// op1 is the instruction that starts a Catch block. + /// op2 is the instruction that starts a Finally block after a Catch block. + /// op3 is the instruction after the last exception handler block. + ///
+ public static readonly OpCode StartEH = new OpCode("starteh", Code.StartEH, OperandType.InlineEH, FlowControl.Next, OpCodeType.Primitive); + + /// + /// Leaves the current Try block and unconditionally branches to the end of the exception handler. + /// + public static readonly OpCode EndTry = new OpCode("endtry", Code.EndTry, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); + /// + /// Leaves the current Finally block and unconditionally branches to the end of the exception handler. + /// + public static readonly OpCode EndFinally = new OpCode("endfinally", Code.EndFinally, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); + /// + /// Leaves the current Catch block and unconditionally branches to the end of the exception handler. + /// + public static readonly OpCode EndCatch = new OpCode("endcatch", Code.EndCatch, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); + /// + /// Leaves the current Finally block after a Catch block and unconditionally branches to the end of the exception handler. + /// + public static readonly OpCode EndCF = new OpCode("endcf", Code.EndCF, OperandType.InlineNone, FlowControl.Next, OpCodeType.Macro); + + /// + /// op0 = ~op0 + /// + public static readonly OpCode Not = new OpCode("not", Code.Not, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// *op0 = *op1 + /// + public static readonly OpCode Cpval = new OpCode("cpval", Code.Cpval, OperandType.InlineValueValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// op0++ + /// + public static readonly OpCode Inc = new OpCode("inc", Code.Inc, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// op0-- + /// + public static readonly OpCode Dec = new OpCode("dec", Code.Dec, OperandType.InlineValue, FlowControl.Next, OpCodeType.Primitive); + /// + /// Pops one value off of the stack (with no type restriction) and branches to the target + /// + public static readonly OpCode PopJump = new OpCode("popjump", Code.PopJump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop1); + /// + /// Pops two values off of the stack (with no type restriction) and branches to the target + /// + public static readonly OpCode PopPopJump = new OpCode("poppopjump", Code.PopPopJump, OperandType.InlineBrTarget, FlowControl.Branch, OpCodeType.Primitive, StackBehaviour.Push0, StackBehaviour.Pop2); + + /// + /// No operation + /// + public static readonly OpCode Nop = new OpCode("nop", Code.Nop, OperandType.InlineNone, FlowControl.Next, OpCodeType.Primitive); + + + +#pragma warning restore + static OpCodes() + { + FillTable(OneByteOpCodes, UNKNOWN1); + FillTable(AluOpCodes, UNKNOWN_ALU); + FillTable(CmpOpCodes, UNKNOWN_CMP); + FillTable(PopEHOpCodes, UNKNOWN_POPEH); + FillTable(SetFlagOpCodes, UNKNOWN_SF); + } + } +} diff --git a/IFPSLib/Emit/Operand.cs b/IFPSLib/Emit/Operand.cs new file mode 100644 index 0000000..dbc12fb --- /dev/null +++ b/IFPSLib/Emit/Operand.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// A PascalScript bytecode instruction operand. + /// + public class Operand + { + private struct IndexedVariableValue + { + internal IVariable Variable; + internal IndexedValue Index; + } + + [StructLayout(LayoutKind.Explicit)] + private struct IndexedValue + { + [FieldOffset(0)] + internal StrongBox m_Immediate; + [FieldOffset(0)] + internal IVariable Variable; + } + + [StructLayout(LayoutKind.Explicit)] + private struct Value + { + [FieldOffset(0)] + internal TypedData Immediate; + [FieldOffset(0)] + internal IVariable Variable; + [FieldOffset(0)] + internal IndexedVariableValue Indexed; + } + + internal BytecodeOperandType m_Type; + private Value m_Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T EnsureType(ref T value, BytecodeOperandType type) + { + if (m_Type != type) throw new InvalidOperationException(); + return value; + } + + private IndexedVariableValue Indexed + { + get + { + if (m_Type != BytecodeOperandType.IndexedImmediate && m_Type != BytecodeOperandType.IndexedVariable) throw new InvalidOperationException(); + return m_Value.Indexed; + } + } + + public TypedData ImmediateTyped => EnsureType(ref m_Value.Immediate, BytecodeOperandType.Immediate); + public object Immediate => ImmediateTyped.Value; + + public TType ImmediateAs() => ImmediateTyped.ValueAs(); + public IVariable Variable => EnsureType(ref m_Value.Variable, BytecodeOperandType.Variable); + + public IVariable IndexedVariable => Indexed.Variable; + public uint IndexImmediate => EnsureType(ref m_Value.Indexed.Index.m_Immediate.Value, BytecodeOperandType.IndexedImmediate); + public IVariable IndexVariable => EnsureType(ref m_Value.Indexed.Index.Variable, BytecodeOperandType.IndexedVariable); + + public Operand(TypedData imm) + { + m_Type = BytecodeOperandType.Immediate; + m_Value.Immediate = imm; + } + + public Operand(IVariable var) + { + m_Type = BytecodeOperandType.Variable; + m_Value.Variable = var; + } + + public Operand(IVariable arr, uint immIdx) + { + m_Type = BytecodeOperandType.IndexedImmediate; + m_Value.Indexed.Variable = arr; + m_Value.Indexed.Index.m_Immediate = new StrongBox(immIdx); + } + + public Operand(IVariable arr, IVariable varIdx) + { + m_Type = BytecodeOperandType.IndexedVariable; + m_Value.Indexed.Variable = arr; + m_Value.Indexed.Index.Variable = varIdx; + } + + public static Operand Create(Types.PrimitiveType type, TType value) + { + return new Operand(TypedData.Create(type, value)); + } + + public static Operand Create(TType value) + { + return new Operand(TypedData.Create(value)); + } + + internal static Operand Create(Types.IType value) + { + return new Operand(TypedData.Create(value)); + } + + internal static Operand Create(Instruction value) + { + return new Operand(TypedData.Create(value)); + } + + internal static Operand Create(IFunction value) + { + return new Operand(TypedData.Create(value)); + } + + public static Operand Create(IVariable arr, uint idxValue) + { + return new Operand(arr, idxValue); + } + + public static Operand Create(IVariable var) + { + return new Operand(var); + } + + public static Operand Create(IVariable arr, IVariable varIdx) + { + return new Operand(arr, varIdx); + } + + internal static Operand LoadValue(BinaryReader br, Script script, ScriptFunction function) + { + var type = (BytecodeOperandType)br.ReadByte(); + + switch (type) + { + case BytecodeOperandType.Variable: + return new Operand(VariableBase.Load(br, script, function)); + case BytecodeOperandType.Immediate: + return new Operand(TypedData.Load(br, script)); + case BytecodeOperandType.IndexedImmediate: + { + var variable = VariableBase.Load(br, script, function); + var idx = br.Read(); + return Create(variable, idx); + } + case BytecodeOperandType.IndexedVariable: + { + var variable = VariableBase.Load(br, script, function); + var idx = VariableBase.Load(br, script, function); + return new Operand(variable, idx); + } + default: + throw new ArgumentOutOfRangeException(string.Format("Invalid operand type {0}", (byte)type)); + } + } + + public override string ToString() + { + switch (m_Type) + { + case BytecodeOperandType.Variable: + return Variable.Name; + case BytecodeOperandType.Immediate: + return ImmediateTyped.ToString(); + case BytecodeOperandType.IndexedImmediate: + return String.Format("{0}[{1}]", IndexedVariable.Name, IndexImmediate); + case BytecodeOperandType.IndexedVariable: + return String.Format("{0}[{1}]", IndexedVariable.Name, IndexVariable.Name); + default: + return ""; + } + } + + public int Size + { + get + { + const int HEADER = sizeof(BytecodeOperandType); + switch (m_Type) + { + case BytecodeOperandType.Variable: + return Variable.Size + HEADER; + case BytecodeOperandType.Immediate: + switch (ImmediateTyped.Type.BaseType) + { + case Types.PascalTypeCode.Type: + case Types.PascalTypeCode.Instruction: + case Types.PascalTypeCode.Function: + return ImmediateTyped.Size; + default: + return ImmediateTyped.Size + HEADER; + } + case BytecodeOperandType.IndexedImmediate: + return IndexedVariable.Size + sizeof(uint) + HEADER; + case BytecodeOperandType.IndexedVariable: + return IndexedVariable.Size + IndexVariable.Size + HEADER; + default: + throw new InvalidOperationException(); + } + } + } + + internal void Save(BinaryWriter bw, Script.SaveContext ctx) + { + bw.Write(m_Type); + switch (m_Type) + { + case BytecodeOperandType.Variable: + ((VariableBase)Variable).Save(bw, ctx); + break; + case BytecodeOperandType.Immediate: + var baseType = ImmediateTyped.Type.BaseType; + if (baseType == Types.PascalTypeCode.Type || baseType == Types.PascalTypeCode.Instruction || baseType == Types.PascalTypeCode.Function) + throw new InvalidOperationException(string.Format("Immediate operand is of incorrect type {0}", baseType)); + ImmediateTyped.Save(bw, ctx); + break; + case BytecodeOperandType.IndexedImmediate: + ((VariableBase)IndexedVariable).Save(bw, ctx); + bw.Write(IndexImmediate); + break; + case BytecodeOperandType.IndexedVariable: + ((VariableBase)IndexedVariable).Save(bw, ctx); + ((VariableBase)IndexVariable).Save(bw, ctx); + break; + default: + throw new InvalidOperationException(); + } + } + + /// + /// Returns true if this operand is compatible with another, such that this operand can be overwritten in an instruction with the other. + /// + /// Other operand + /// True if overwriting is fine, false if not. + internal bool SimilarType(Operand value) + { + // Internal operand type must match. + if (m_Type != value.m_Type) return false; + // If immediate, and a psuedo-type, it must match. + if (m_Type == BytecodeOperandType.Immediate) + { + var baseType = ImmediateTyped.Type.BaseType; + if (baseType == Types.PascalTypeCode.Type || baseType == Types.PascalTypeCode.Instruction || baseType == Types.PascalTypeCode.Function) + { + return baseType == value.ImmediateTyped.Type.BaseType; + } + return baseType != Types.PascalTypeCode.Unknown; + } + return true; + } + } +} diff --git a/IFPSLib/Emit/OperandType.cs b/IFPSLib/Emit/OperandType.cs new file mode 100644 index 0000000..e2d203c --- /dev/null +++ b/IFPSLib/Emit/OperandType.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public enum OperandType : byte + { + /// + /// Variable or typed immediate + /// + InlineValue, + /// + /// No operand + /// + InlineNone, + /// + /// Signed 32-bit branch target + /// + InlineBrTarget, + /// + /// Two s + /// + InlineValueValue, + /// + /// and + /// + InlineBrTargetValue, + /// + /// 32-bit function index + /// + InlineFunction, + /// + /// 32-bit type index + /// + InlineType, + /// + /// Three s + /// + InlineCmpValue, + /// + /// Two s followed by + /// + InlineCmpValueType, + /// + /// Four unsigned 32-bit branch targets (uint.MaxValue for null) + /// + InlineEH, + /// + /// followed by a variable + /// + InlineTypeVariable, + /// + /// followed by second opcode byte + /// + InlineValueSF, + } +} diff --git a/IFPSLib/Emit/PopEHOpCode.cs b/IFPSLib/Emit/PopEHOpCode.cs new file mode 100644 index 0000000..faf843a --- /dev/null +++ b/IFPSLib/Emit/PopEHOpCode.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + + public enum PopEHOpCode : byte + { + EndTry, + EndFinally, + EndCatch, + EndHandler + } +} diff --git a/IFPSLib/Emit/ScriptFunction.cs b/IFPSLib/Emit/ScriptFunction.cs new file mode 100644 index 0000000..a89a12e --- /dev/null +++ b/IFPSLib/Emit/ScriptFunction.cs @@ -0,0 +1,251 @@ +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; + +namespace IFPSLib.Emit +{ + /// + /// Internal function implemented by script bytecode. + /// + public sealed class ScriptFunction : FunctionBase + { + public override IType ReturnArgument { get; set; } + + /// + /// If null, argument information is unknown. + /// + public override IList Arguments { get; set; } + + public IList Instructions = new List(); + + internal uint CodeOffset; + internal uint CodeLength; + + private const char ARGUMENT_TYPE_IN = '@'; + private const char ARGUMENT_TYPE_OUT = '!'; + + internal static ScriptFunction Load(BinaryReader br, Script script, bool exported) + { + + var ret = new ScriptFunction(); + ret.Exported = exported; + ret.CodeOffset = br.Read(); + ret.CodeLength = br.Read(); + + if (exported) + { + var nameLen = br.Read(); + ret.Name = br.ReadAsciiString(nameLen); + // Function declaration for a scriptfunction is ascii strings split by space + var decLen = br.Read(); + var decl = br.ReadAsciiString(decLen).Split(' '); + // First one is return type, or -1 for void + if (!int.TryParse(decl[0], out var retType) || retType == -1) ret.ReturnArgument = null; + else ret.ReturnArgument = script.Types[retType]; + + // Next are arguments. First char is the argument type ('@' means in, otherwise '!'), followed by the type index + ret.Arguments = new List(decl.Length - 1); + for (int i = 1; i < decl.Length; i++) { + var arg = new FunctionArgument(); + arg.ArgumentType = (decl[i][0] == ARGUMENT_TYPE_IN ? FunctionArgumentType.In : FunctionArgumentType.Out); + if (!int.TryParse(decl[i].Substring(1), out var idxType) || idxType < 0) arg.Type = null; + else arg.Type = script.Types[idxType]; + arg.Name = string.Format("Arg{0}", i); + ret.Arguments.Add(arg); + } + } + + return ret; + } + + internal void LoadInstructions(BinaryReader br, Script script) + { + br = br.Slice(CodeOffset, CodeLength); + + // Load all instructions + while (br.BaseStream.Position < br.BaseStream.Length) + { + Instructions.Add(Instruction.Load(br, script, this)); + } + + // Create a table of offset->instruction + var table = new Dictionary(); + foreach (var i in Instructions) table.Add(i.Offset, i); + + // Walk each instruction and fix up branch targets + foreach (var i in Instructions) + { + switch (i.OpCode.OperandType) + { + case OperandType.InlineBrTarget: + case OperandType.InlineBrTargetValue: + case OperandType.InlineEH: + i.FixUpBranchTargets(table); + break; + } + } + } + + public override string ToString() + { + var sb = new StringBuilder(".function"); + + if (Exported) sb.Append("(export)"); + sb.Append(' '); + + if (ReturnArgument == null) sb.Append("void "); + else sb.AppendFormat("{0} ", ReturnArgument.Name); + + sb.AppendFormat("{0}(", Name); + for (int i = 0; i < Arguments.Count; i++) + { + sb.Append(i == 0 ? "" : ","); + sb.Append(Arguments[i].ToString()); + } + sb.Append(')'); + + return sb.ToString(); + } + + + + private string ExportDeclString(Script.SaveContext ctx) + { + if (!Exported) return string.Empty; + int typeIdx; + if (ReturnArgument == null) typeIdx = -1; + else typeIdx = ctx.GetTypeIndex(ReturnArgument); + var sb = new StringBuilder(typeIdx.ToString()); + foreach (var arg in Arguments) + { + sb.Append(' '); + sb.Append(arg.ArgumentType == FunctionArgumentType.In ? ARGUMENT_TYPE_IN : ARGUMENT_TYPE_OUT); + sb.Append(ctx.GetTypeIndex(arg.Type)); + } + return sb.ToString(); + } + + /// + /// Gets the size of a not exported ScriptFunction. + /// + public override int Size => sizeof(uint) * 2; + + /// + /// Gets the size of all instructions. + /// + public int InstructionsSize => Instructions.Sum(x => x.Size); + + public ArgumentVariable CreateArgumentVariable(int index) + { + var isVoid = ReturnArgument == null; + var ret = ArgumentVariable.Create(index + (isVoid ? 0 : 1), isVoid); + ret.Name = Arguments[index].Name; + return ret; + } + + public ArgumentVariable CreateArgumentVariable(int index, string name) + { + var ret = CreateArgumentVariable(index); + ret.Name = name; + return ret; + } + + public ArgumentVariable CreateReturnVariable() + { + if (ReturnArgument == null) throw new InvalidOperationException(); + return ArgumentVariable.Create(0, false); + } + + internal override void Save(BinaryWriter bw, Script.SaveContext ctx) + { + FunctionFlags flags = 0; + var EDecl = ExportDeclString(ctx); + if (Exported) + { + flags |= FunctionFlags.Exported; + } + if (Attributes.Count != 0) flags |= FunctionFlags.HasAttributes; + bw.Write(flags); + + // save dummy offset and length, will be overwritten later. + bw.Write(0); + bw.Write(0); + + if (Exported) + { + bw.WriteAsciiString(Name.ToUpper(), true); + bw.WriteAsciiString(EDecl, true); + } + } + + public uint UpdateInstructionOffsets() + { + uint offset = 0; + var count = Instructions.Count; + for (int i = 0; i < count; i++) + { + Instructions[i].Offset = offset; + offset += (uint)Instructions[i].Size; + } + + return offset; + } + + private Dictionary UpdateInstructionCrossReferencesBeforeSave() + { + do + { + // Build the table. + var table = new Dictionary(); + var knownOffsets = new HashSet(); + foreach (var insn in Instructions) + { + if (!knownOffsets.Add(insn.Offset)) + { + UpdateInstructionOffsets(); + continue; + } + table.Add(insn, insn.Offset); + } + + // Fix up the targets. + foreach (var insn in Instructions) + insn.FixUpReferences(table); + + return table; + } while (true); + } + + public void UpdateInstructionCrossReferences() + { + UpdateInstructionCrossReferencesBeforeSave(); + } + + internal void SaveInstructions(BinaryWriter bw, Script.SaveContext ctx) + { + UpdateInstructionOffsets(); + var table = UpdateInstructionCrossReferencesBeforeSave(); + + var insnOffset = bw.BaseStream.Position; + if (insnOffset > uint.MaxValue) throw new InvalidOperationException("Instruction offset must be in the first 4GB"); + var insnOffset32 = (uint)insnOffset; + + foreach (var insn in Instructions) + { + insn.Save(bw, ctx, table); + } + + var insnAfter = bw.BaseStream.Position; + var insnsLength = insnAfter - insnOffset; + if (insnsLength > uint.MaxValue) throw new InvalidOperationException("Instructions length cannot be over 4GB"); + + bw.BaseStream.Position = ctx.GetFunctionOffset(this); + bw.Write(insnOffset32); + bw.Write((uint)insnsLength); + bw.BaseStream.Position = insnAfter; + } + } +} diff --git a/IFPSLib/Emit/SetFlagOpCode.cs b/IFPSLib/Emit/SetFlagOpCode.cs new file mode 100644 index 0000000..c1e645a --- /dev/null +++ b/IFPSLib/Emit/SetFlagOpCode.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Type of a set flag operation for the "setflag" instruction at the bytecode level. + /// + public enum SetFlagOpCode : byte + { + /// + /// Sets the jump flag if the variable is not zero. + /// + NotZero, + /// + /// Sets the jump flag if the variable is zero. + /// + Zero + } +} diff --git a/IFPSLib/Emit/StackBehaviour.cs b/IFPSLib/Emit/StackBehaviour.cs new file mode 100644 index 0000000..319ff08 --- /dev/null +++ b/IFPSLib/Emit/StackBehaviour.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Describes how values are pushed onto or popped off a stack. + /// + public enum StackBehaviour : byte + { + /// + /// No values are popped off the stack + /// + Pop0, + /// + /// One value is popped off the stack + /// + Pop1, + /// + /// Two values are popped off the stack + /// + Pop2, + /// + /// A variable number of values are popped off the stack + /// + Varpop, + + /// + /// No values are pushed onto the stack + /// + Push0, + /// + /// One value is pushed onto the stack + /// + Push1 + } +} diff --git a/IFPSLib/Emit/VariableType.cs b/IFPSLib/Emit/VariableType.cs new file mode 100644 index 0000000..372313a --- /dev/null +++ b/IFPSLib/Emit/VariableType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IFPSLib.Emit +{ + public enum VariableType + { + Global, + Local, + Argument + } +} diff --git a/IFPSLib/Emit/Variables.cs b/IFPSLib/Emit/Variables.cs new file mode 100644 index 0000000..b0f302f --- /dev/null +++ b/IFPSLib/Emit/Variables.cs @@ -0,0 +1,159 @@ +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IFPSLib.Emit +{ + /// + /// Represents a variable that can be represented in an operand. + /// + public interface IVariable + { + IType Type { get; } + string Name { get; set; } + VariableType VarType { get; } + int Index { get; } + + int Size { get; } + } + + public abstract class VariableBase : IVariable + { + public IType Type { get; internal set; } + public string Name { get; set; } + + public abstract VariableType VarType { get; } + public int Index { get; internal set; } + + public int Size => sizeof(uint); + + internal const int MAX_GLOBALS = 0x40000000; + internal const int MAX_ARGS = 0x20000000; + internal static VariableBase Load(BinaryReader reader, Script script, ScriptFunction function) + { + var idx = reader.Read(); + if (idx < MAX_GLOBALS) return script.GlobalVariables[(int)idx]; + int sIdx = ((int)idx - MAX_GLOBALS - MAX_ARGS); + if (sIdx >= 0) return new LocalVariable(sIdx); + return new ArgumentVariable((-sIdx) - 1, function.ReturnArgument == null); + } + + internal abstract void Save(BinaryWriter bw, Script.SaveContext ctx); + } + + + public sealed class LocalVariable : VariableBase + { + public override VariableType VarType => VariableType.Local; + + internal override void Save(BinaryWriter bw, Script.SaveContext ctx) + { + bw.Write((uint)(MAX_GLOBALS + MAX_ARGS + Index)); + } + + public static LocalVariable Create(int index) + { + if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); + return new LocalVariable(index + 1); + } + + internal LocalVariable(int index) + { + Index = index; + Name = string.Format("Var{0}", index); + } + } + + public sealed class ArgumentVariable : VariableBase + { + public override VariableType VarType => VariableType.Argument; + + internal static ArgumentVariable Create(int index, bool isVoid) + { + if (index < 0 || index >= MAX_ARGS) throw new ArgumentOutOfRangeException(nameof(index)); + return new ArgumentVariable(index, isVoid); + } + + internal override void Save(BinaryWriter bw, Script.SaveContext ctx) + { + bw.Write((uint)(MAX_GLOBALS + MAX_ARGS - Index - 1)); + } + + internal ArgumentVariable(int index, bool isVoid) + { + Index = index; + if (!isVoid && Index == 0) Name = "RetVal"; + else Name = string.Format("Arg{0}", index + (isVoid ? 1 : 0)); + } + } + + public sealed class GlobalVariable : VariableBase + { + public bool Exported { get; set; } + public override VariableType VarType => VariableType.Global; + + internal GlobalVariable(int index) + { + Index = index; + Name = string.Format("Global{0}", index); + } + + public static GlobalVariable Create(int index) + { + if (index < 0 || index >= MAX_GLOBALS) throw new ArgumentOutOfRangeException(nameof(index)); + return new GlobalVariable(index); + } + + public static GlobalVariable Create(int index, IType type) + { + var ret = Create(index); + ret.Type = type; + return ret; + } + + public static GlobalVariable Create(int index, IType type, string name) + { + var ret = Create(index, type); + ret.Name = name; + return ret; + } + + internal static GlobalVariable Load(BinaryReader br, Script script, int index) + { + var ret = new GlobalVariable(index); + ret.Type = script.Types[(int)br.Read()]; + var flags = br.ReadByte(); + if ((flags & 1) != 0) + { + var len = br.Read(); + ret.Name = br.ReadAsciiString(len); + ret.Exported = true; + } + return ret; + } + + public override string ToString() + { + return string.Format(".global{0} {1} {2}", Exported ? "(import)" : "", Type.Name, Name); + } + + internal override void Save(BinaryWriter bw, Script.SaveContext ctx) + { + bw.Write((uint)Index); + } + + internal void SaveHeader(BinaryWriter bw, Script.SaveContext ctx) + { + // type + bw.Write(ctx.GetTypeIndex(Type)); + // flags + bw.Write(Exported); + if (Exported) + { + bw.WriteAsciiString(Name.ToUpper(), true); + } + } + } +} diff --git a/IFPSLib/Function.cs b/IFPSLib/Function.cs new file mode 100644 index 0000000..9bb761f --- /dev/null +++ b/IFPSLib/Function.cs @@ -0,0 +1,79 @@ +using IFPSLib.Emit; +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IFPSLib +{ + [Flags] + public enum FunctionFlags : byte + { + External = (1 << 0), + ExternalWithDeclaration = External | (1 << 1), + Exported = (1 << 1), + HasAttributes = (1 << 2) + } + /// + /// Represents a function. + /// + public interface IFunction + { + IList Attributes { get; } + + string Name { get; set; } + + /// + /// If null, function returns void. + /// + IType ReturnArgument { get; set; } + + IList Arguments { get; set; } + bool Exported { get; set; } + + int Size { get; } + } + + /// + /// Base implementation of a function. + /// + public abstract class FunctionBase : IFunction + { + public IList Attributes { get; internal set; } = new List(); + public string Name { get; set; } + + public bool Exported { get; set; } + + public abstract IType ReturnArgument { get; set; } + + /// + /// If null, argument information is unknown. + /// + public abstract IList Arguments { get; set; } + + public abstract int Size { get; } + + internal static FunctionBase Load(BinaryReader br, Script script) + { + var flags = (FunctionFlags)br.ReadByte(); + + FunctionBase ret = null; + + var exported = (flags & FunctionFlags.Exported) != 0; + + if ((flags & FunctionFlags.External) != 0) + { + ret = ExternalFunction.Load(br, script, exported); + } else + { + ret = ScriptFunction.Load(br, script, exported); + } + if ((flags & FunctionFlags.HasAttributes) != 0) + ret.Attributes = CustomAttribute.LoadList(br, script); + return ret; + } + + internal abstract void Save(BinaryWriter bw, Script.SaveContext ctx); + } +} diff --git a/IFPSLib/FunctionArgument.cs b/IFPSLib/FunctionArgument.cs new file mode 100644 index 0000000..6a45d09 --- /dev/null +++ b/IFPSLib/FunctionArgument.cs @@ -0,0 +1,52 @@ +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IFPSLib +{ + public enum FunctionArgumentType : byte + { + Out, + In + } + public sealed class FunctionArgument + { + /// + /// If null, type is unknown. + /// + public IType Type; + + public FunctionArgumentType ArgumentType; + + public string Name; + + internal static IList LoadForExternal(BinaryReader br) + { + var count = (int)(br.BaseStream.Length - br.BaseStream.Position); + var ret = new List(count); + + for (int i = 0; i < count; i++) + { + ret.Add(new FunctionArgument() + { + Type = null, + ArgumentType = br.ReadByte() != 0 ? FunctionArgumentType.Out : FunctionArgumentType.In + }); + } + return ret; + } + + public override string ToString() + { + var noName = string.IsNullOrEmpty(Name); + return string.Format("{0} {1}{2}{3}", + ArgumentType == FunctionArgumentType.Out ? "__out" : "__in", + Type == null ? "__unknown" : Type.Name, + noName ? "" : " ", + noName ? "" : Name + ); + } + } +} diff --git a/IFPSLib/IFPSLib.csproj b/IFPSLib/IFPSLib.csproj new file mode 100644 index 0000000..3b69a4f --- /dev/null +++ b/IFPSLib/IFPSLib.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + true + + + + + + + + + + + + diff --git a/IFPSLib/Script.cs b/IFPSLib/Script.cs new file mode 100644 index 0000000..7fe6d62 --- /dev/null +++ b/IFPSLib/Script.cs @@ -0,0 +1,382 @@ +using IFPSLib.Types; +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Text; +using System.IO; +using System.Threading.Tasks; +using System.Linq; +using Cysharp.Collections; +using IFPSLib.Emit; +using System.Threading; + +namespace IFPSLib +{ + public sealed class Script + { + + private const int VERSION_LOWEST = 12; + public const int VERSION_HIGHEST = 23; + + internal const int VERSION_MIN_ATTRIBUTES = 21; + internal const int VERSION_MAX_SETSTACKTYPE = 22; // Is this correct? + internal const int VERSION_MIN_STATICARRAYSTART = 23; + + /// + /// Internal version of this file. + /// + public int FileVersion; + + /// + /// Entry point. Can be null. + /// + public IFunction EntryPoint = null; + + /// + /// List of types declared in this script by internal index. + /// + public IList Types = new List(); + /// + /// List of functions declared in this script by internal index. + /// + public IList Functions = new List(); + /// + /// List of global variables declared in this script by internal index. + /// + public IList GlobalVariables = new List(); + + private struct ScriptHeaderAfterMagic + { + // Fields get set directly by memcpy +#pragma warning disable CS0649 + internal int + Version, NumTypes, NumFuncs, NumVars, IdxEntryPoint, ImportSize; +#pragma warning restore CS0649 + } + + public Script(int version) + { + FileVersion = version; + } + + public Script() : this(VERSION_HIGHEST) { } + + private static Script LoadCore(Stream stream, bool leaveOpen = false) + { + using (var br = new BinaryReader(stream, Encoding.UTF8, leaveOpen)) + { + return LoadCore(br); + } + } + + private static Script LoadCore(BinaryReader br) + { + { + Span magic = stackalloc byte[4]; + br.Read(magic); + if (magic[0] != 'I' || magic[1] != 'F' || magic[2] != 'P' || magic[3] != 'S') throw new InvalidDataException(); + } + + var header = br.Read(); + if (header.Version < VERSION_LOWEST || header.Version > VERSION_HIGHEST) throw new InvalidDataException(string.Format("Invalid version: {0}", header.Version)); + var ret = new Script(header.Version); + + ret.Types = new List(header.NumTypes); + var typesToNames = new Dictionary(); + var samename = new Dictionary(); + for (int i = 0; i < header.NumTypes; i++) + { + var type = TypeBase.Load(br, ret); + if (string.IsNullOrEmpty(type.Name)) type.Name = string.Format("Type{0}", i); + if (typesToNames.ContainsKey(type.Name)) + { + int count = 0; + if (samename.TryGetValue(type.Name, out count)) count++; + else count = 1; + samename[type.Name] = count; + type.Name += "_" + (count + 1); + } + + typesToNames.Add(type.Name, type); + ret.Types.Add(type); + } + + ret.Functions = new List(header.NumFuncs); + samename = new Dictionary(); + var funcsToNames = new Dictionary(); + for (int i = 0; i < header.NumFuncs; i++) + { + var func = FunctionBase.Load(br, ret); + if (funcsToNames.ContainsKey(func.Name)) + { + int count = 0; + if (samename.TryGetValue(func.Name, out count)) count++; + else count = 1; + samename[func.Name] = count; + func.Name += "_" + (count + 1); + } + + funcsToNames.Add(func.Name, func); + ret.Functions.Add(func); + } + + ret.GlobalVariables = new List(header.NumVars); + for (int i = 0; i < header.NumVars; i++) + { + ret.GlobalVariables.Add(GlobalVariable.Load(br, ret, i)); + } + + foreach (var func in ret.Functions.OfType()) + { + func.LoadInstructions(br, ret); + } + + if (header.IdxEntryPoint >= 0 && header.IdxEntryPoint < header.NumFuncs) + ret.EntryPoint = ret.Functions[header.IdxEntryPoint]; + + return ret; + } + + public static Script Load(byte[] bytes) => LoadCore(new MemoryStream(bytes, 0, bytes.Length, false, true)); + + public static Script Load(MemoryStream stream) => LoadCore(stream, true); + public static Script Load(UnmanagedMemoryStream stream) => LoadCore(stream); + + public static Script Load(Stream stream) => LoadAsync(stream).GetAwaiter().GetResult(); + + public static async Task