Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spanify ABC tag parsing and writing #16

Merged
merged 2 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 106 additions & 61 deletions Flazzy/ABC/ABCFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Flazzy.ABC;

public class ABCFile : FlashItem, IDisposable
public class ABCFile : IFlashItem, IDisposable
{
private readonly FlashReader _input;
private readonly Dictionary<ASMultiname, List<ASClass>> _classByQNameCache;
private readonly Dictionary<string, ASInstance> _instanceByConstructorCache;

Expand All @@ -18,8 +17,6 @@ public class ABCFile : FlashItem, IDisposable
public ASConstantPool Pool { get; }
public Version Version { get; set; }

protected override string DebuggerDisplay => "Version: " + Version;

public ABCFile()
{
_classByQNameCache = new Dictionary<ASMultiname, List<ASClass>>();
Expand All @@ -32,28 +29,60 @@ public ABCFile()
Scripts = new List<ASScript>();
MethodBodies = new List<ASMethodBody>();
}
public ABCFile(byte[] data)
: this(new FlashReader(data))
{ }
public ABCFile(FlashReader input)
public ABCFile(ReadOnlySpan<byte> data)
: this()
{
_input = input;
var input = new SpanFlashReader(data);

Version = new Version(
minor: input.ReadUInt16(),
major: input.ReadUInt16());

Pool = new ASConstantPool(this, ref input);

Methods.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Methods.Capacity; i++)
{
Methods.Add(new ASMethod(this, ref input));
}

Metadata.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Metadata.Capacity; i++)
{
Metadata.Add(new ASMetadata(this, ref input));
}

ushort minor = input.ReadUInt16();
ushort major = input.ReadUInt16();
Version = new Version(major, minor);
Pool = new ASConstantPool(this, input);
Instances.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Instances.Capacity; i++)
{
Instances.Add(new ASInstance(this, ref input));
}

PopulateList(Methods, ReadMethod);
PopulateList(Metadata, ReadMetadata);
PopulateList(Instances, ReadInstance);
_classByQNameCache.EnsureCapacity(Instances.Count);
_instanceByConstructorCache.EnsureCapacity(Instances.Count);

PopulateList(Classes, ReadClass, Instances.Count);
PopulateList(Scripts, ReadScript);
PopulateList(MethodBodies, ReadMethodBody);
Classes.Capacity = Instances.Count;
for (int i = 0; i < Classes.Capacity; i++)
{
var @class = new ASClass(this, ref input)
{
InstanceIndex = i
};
CacheByNaming(@class);
Classes.Add(@class);
}

Scripts.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Scripts.Capacity; i++)
{
Scripts.Add(new ASScript(this, ref input));
}

MethodBodies.Capacity = input.ReadEncodedInt();
for (int i = 0; i < MethodBodies.Capacity; i++)
{
MethodBodies.Add(new ASMethodBody(this, ref input));
}

_classByQNameCache.TrimExcess();
_instanceByConstructorCache.TrimExcess();
Expand Down Expand Up @@ -147,21 +176,6 @@ public IEnumerable<ASClass> GetClasses(ASMultiname multiname)

public ASInstance GetInstanceByConstructor(string constructorName) => _instanceByConstructorCache.GetValueOrDefault(constructorName);

private ASMethod ReadMethod(int index) => new(this, _input);
private ASMetadata ReadMetadata(int index) => new(this, _input);
private ASInstance ReadInstance(int index) => new(this, _input);
private ASClass ReadClass(int index)
{
var @class = new ASClass(this, _input)
{
InstanceIndex = index
};
CacheByNaming(@class);
return @class;
}
private ASScript ReadScript(int index) => new(this, _input);
private ASMethodBody ReadMethodBody(int index) => new(this, _input);

private ASMultiname GetMultiname(string qualifiedName)
{
foreach (ASMultiname multiname in Pool.GetMultinames(qualifiedName))
Expand All @@ -170,42 +184,59 @@ private ASMultiname GetMultiname(string qualifiedName)
}
return null;
}
private void PopulateList<T>(List<T> list, Func<int, T> reader, int count = -1)

public int GetSize()
{
list.Capacity = count < 0 ? _input.ReadInt30() : count;
for (int i = 0; i < list.Capacity; i++)
int size = 0;
size += sizeof(ushort);
size += sizeof(ushort);
size += Pool.GetSize();

size += SpanFlashWriter.GetEncodedIntSize(Methods.Count);
for (int i = 0; i < Methods.Count; i++)
{
T value = reader(i);
list.Add(value);
size += Methods[i].GetSize();
}
}

public override void WriteTo(FlashWriter output)
{
output.Write((ushort)Version.Minor);
output.Write((ushort)Version.Major);
size += SpanFlashWriter.GetEncodedIntSize(Metadata.Count);
for (int i = 0; i < Metadata.Count; i++)
{
size += Metadata[i].GetSize();
}

Pool.WriteTo(output);
size += SpanFlashWriter.GetEncodedIntSize(Instances.Count);
for (int i = 0; i < Instances.Count; i++)
{
size += Instances[i].GetSize();
size += Classes[i].GetSize();
}

WriteTo(output, Methods);
WriteTo(output, Metadata);
WriteTo(output, Instances);
WriteTo(output, Classes, false);
WriteTo(output, Scripts);
WriteTo(output, MethodBodies);
}
private void WriteTo<T>(FlashWriter output, List<T> list, bool writeCount = true)
where T : FlashItem
{
if (writeCount)
size += SpanFlashWriter.GetEncodedIntSize(Scripts.Count);
for (int i = 0; i < Scripts.Count; i++)
{
output.WriteInt30(list.Count);
size += Scripts[i].GetSize();
}
for (int i = 0; i < list.Count; i++)

size += SpanFlashWriter.GetEncodedIntSize(MethodBodies.Count);
for (int i = 0; i < MethodBodies.Count; i++)
{
FlashItem item = list[i];
item.WriteTo(output);
size += MethodBodies[i].GetSize();
}
return size;
}
public void WriteTo(ref SpanFlashWriter output)
{
output.Write((ushort)Version.Minor);
output.Write((ushort)Version.Major);

Pool.WriteTo(ref output);

WriteTo(ref output, Methods);
WriteTo(ref output, Metadata);
WriteTo(ref output, Instances);
WriteTo(ref output, Classes, false);
WriteTo(ref output, Scripts);
WriteTo(ref output, MethodBodies);
}

public void Dispose()
Expand All @@ -216,7 +247,6 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_input.Dispose();
_classByQNameCache.Clear();
_instanceByConstructorCache.Clear();

Expand All @@ -236,4 +266,19 @@ protected virtual void Dispose(bool disposing)
Pool.Multinames.Clear();
}
}

public override string ToString() => "Version: " + Version;

private static void WriteTo<T>(ref SpanFlashWriter output, List<T> list, bool writeCount = true)
where T : IFlashItem
{
if (writeCount)
{
output.WriteEncodedInt(list.Count);
}
for (int i = 0; i < list.Count; i++)
{
list[i].WriteTo(ref output);
}
}
}
14 changes: 2 additions & 12 deletions Flazzy/ABC/AS3Item.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
namespace Flazzy.ABC;

public abstract class AS3Item : FlashItem
public interface IAS3Item
{
public ABCFile ABC { get; }

public AS3Item(ABCFile abc)
{
ABC = abc;
}

public virtual string ToAS3()
{
throw new NotSupportedException();
}
string ToAS3();
}
22 changes: 12 additions & 10 deletions Flazzy/ABC/ASClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Flazzy.ABC;

public class ASClass : ASContainer
public class ASClass : ASContainer, IAS3Item
{
internal int InstanceIndex { get; set; }
public ASInstance Instance => ABC.Instances[InstanceIndex];
Expand All @@ -12,28 +12,30 @@ public class ASClass : ASContainer

public override bool IsStatic => true;
public override ASMultiname QName => Instance.QName;
protected override string DebuggerDisplay => ToAS3();

public ASClass(ABCFile abc)
: base(abc)
{ }
public ASClass(ABCFile abc, FlashReader input)
public ASClass(ABCFile abc, ref SpanFlashReader input)
: base(abc)
{
ConstructorIndex = input.ReadInt30();
ConstructorIndex = input.ReadEncodedInt();
Constructor.IsConstructor = true;
Constructor.Container = this;

PopulateTraits(input);
PopulateTraits(ref input);
}

public override string ToAS3()
public override int GetSize()
{
return Instance.ToAS3();
return SpanFlashWriter.GetEncodedIntSize(ConstructorIndex) + base.GetSize();
}
public override void WriteTo(FlashWriter output)
public override void WriteTo(ref SpanFlashWriter output)
{
output.WriteInt30(ConstructorIndex);
base.WriteTo(output);
output.WriteEncodedInt(ConstructorIndex);
base.WriteTo(ref output);
}

public string ToAS3() => Instance.ToAS3();
public override string ToString() => ToAS3();
}
Loading
Loading