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