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_DLL_LOAD_FLAGS = 23;
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