Skip to content

Commit

Permalink
fix: Restore some Lingo parsing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulusParssinen committed Jun 18, 2024
1 parent 426b218 commit 62955b5
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Shockky.SourceGeneration/InstructionGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

writer.WriteLine($"namespace {InstructionNamespace};");
writer.WriteLine();

WriteInstructionReadSyntax(writer, instructions);

context.AddSource($"{InstructionNamespace}.IInstruction.Read.g.cs", writer.ToString());
Expand Down
2 changes: 1 addition & 1 deletion Shockky/IO/Compression/ZLibShockwaveReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Shockky.IO;

public sealed class ZLibShockwaveReader(Stream innerStream, bool isBigEndian, bool leaveOpen)
public sealed class ZLibShockwaveReader(Stream innerStream, bool isBigEndian, bool leaveOpen)
: BinaryReader(new ZLibStream(innerStream, CompressionMode.Decompress, leaveOpen))
{
private readonly bool _isBigEndian = isBigEndian;
Expand Down
3 changes: 2 additions & 1 deletion Shockky/IO/ShockwaveReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using Shockky.Resources;
using System.Buffers;
using System.Numerics;

namespace Shockky.IO;

Expand Down Expand Up @@ -236,7 +237,7 @@ public unsafe IResource ReadCompressedResource(AfterburnerMapEntry entry, Reader
try
{
Span<byte> decompressedData = entry.DecompressedLength <= StackallocThreshold ?
stackalloc byte[StackallocThreshold] :
stackalloc byte[StackallocThreshold] :
(rentedBuffer = ArrayPool<byte>.Shared.Rent(entry.DecompressedLength));

decompressedData = decompressedData.Slice(0, entry.DecompressedLength);
Expand Down
2 changes: 1 addition & 1 deletion Shockky/Lingo/Instructions/IInstruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public partial interface IInstruction
/// The <see cref="OPCode"/> value representing the instruction.
/// </summary>
OPCode OP { get; }

/// <summary>
/// The immediate operand encoded in the instruction.
/// </summary>
Expand Down
19 changes: 13 additions & 6 deletions Shockky/Lingo/LingoFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@ public class LingoFunction : IShockwaveItem

public LingoFunction()
{
Bytecode = [];
Arguments = new List<short>();
Locals = new List<short>();
BytesPerLine = Array.Empty<byte>();
BytesPerLine = [];
}
public LingoFunction(ref ShockwaveReader input, ReaderContext context)
public LingoFunction(ref ShockwaveReader input)
{
EnvironmentIndex = input.ReadInt16LittleEndian();
EventKind = (LingoEventFlags)input.ReadInt16LittleEndian();

Bytecode = new byte[input.ReadInt32LittleEndian()];
int bytecodeOffset = input.ReadInt32LittleEndian();

Arguments.Capacity = input.ReadInt16LittleEndian();
Arguments = new List<short>(input.ReadInt16LittleEndian());
int argumentsOffset = input.ReadInt32LittleEndian();

Locals.Capacity = input.ReadInt16LittleEndian();
Locals = new List<short>(input.ReadInt16LittleEndian());
int localsOffset = input.ReadInt32LittleEndian();

short globalsCount = input.ReadInt16LittleEndian(); //v5
Expand All @@ -46,7 +47,7 @@ public LingoFunction(ref ShockwaveReader input, ReaderContext context)
//if version > 0x800
StackHeight = input.ReadInt32LittleEndian();

int handlerEndOffset = input.Position;
int bodyEndOffset = input.Position;

input.Position = bytecodeOffset;
input.ReadBytes(Bytecode);
Expand All @@ -63,7 +64,13 @@ public LingoFunction(ref ShockwaveReader input, ReaderContext context)
Locals.Add(input.ReadInt16LittleEndian());
}

throw new NotImplementedException(nameof(LingoFunction));
input.Position = lineOffset;
for (int i = 0; i < BytesPerLine.Length; i++)
{
BytesPerLine[i] = input.ReadByte();
}

input.Position = bodyEndOffset;
}

public int GetBodySize(WriterOptions options)
Expand Down
6 changes: 3 additions & 3 deletions Shockky/Lingo/LingoLiteral.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace Shockky.Lingo;
// TODO: Attempt to rewrite this again, not happy.
public sealed class LingoLiteral : IShockwaveItem, IEquatable<LingoLiteral>
{
public VariantKind Kind { get; set; }
public object Value { get; set; }
public VariantKind Kind { get; }
public object Value { get; }

public LingoLiteral(VariantKind kind, object value)
{
Expand Down Expand Up @@ -66,6 +66,6 @@ public static LingoLiteral Read(ref ShockwaveReader input, VariantKind entryKind

public override int GetHashCode()
{
throw new NotImplementedException();
return HashCode.Combine(Kind, Value);
}
}
8 changes: 4 additions & 4 deletions Shockky/Resources/AfterBurner/FileGzipEmbeddedImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public static class FileGzipEmbeddedImage
{
// TODO: Tidy up more.
public static IDictionary<int, IResource> ReadResources(
ref ShockwaveReader input, ReaderContext context,
ref ShockwaveReader input, ReaderContext context,
AfterburnerMap afterburnerMap, FileCompressionTypes compressionTypes)
{
int chunkStart = input.Position;
Expand All @@ -22,8 +22,8 @@ public static IDictionary<int, IResource> ReadResources(
input.Position = chunkStart + entry.Offset;

// TODO: Support more compression types: font maps, sounds.
IResource resource = compressionTypes.CompressionTypes[entry.CompressionTypeIndex].Id.Equals(ZLib.MoaId) ?
input.ReadCompressedResource(entry, context)
IResource resource = compressionTypes.CompressionTypes[entry.CompressionTypeIndex].Id.Equals(ZLib.MoaId) ?
input.ReadCompressedResource(entry, context)
: IResource.Read(ref input, context, entry.Kind, entry.Length);

resources.Add(index, resource);
Expand All @@ -32,7 +32,7 @@ public static IDictionary<int, IResource> ReadResources(
}

private static bool TryReadInitialLoadSegment(
ref ShockwaveReader input, ReaderContext context,
ref ShockwaveReader input, ReaderContext context,
AfterburnerMap afterburnerMap, Dictionary<int, IResource> resources)
{
// First entry in the AfterburnerMap must be ILS.
Expand Down
2 changes: 1 addition & 1 deletion Shockky/Resources/AfterBurner/FileVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed class FileVersion : IResource, IShockwaveItem
public DirectorVersion Version { get; set; }
public string VersionString { get; set; }

public FileVersion(ref ShockwaveReader input, ReaderContext context)
public FileVersion(scoped ref ShockwaveReader input, ReaderContext context)
{
int versionMaybeTooForgot = input.Read7BitEncodedInt();
if (versionMaybeTooForgot < 0x401) return;
Expand Down
2 changes: 1 addition & 1 deletion Shockky/Resources/Cast/CastMemberMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private void ReadProperty(ref ShockwaveReader input, int index, int length)
}
}



public int GetBodySize(WriterOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion Shockky/Resources/Cast/CastMemberProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private IMemberProperties ReadTypeProperties(ref ShockwaveReader input, ReaderCo
MemberKind.Script => new ScriptCastProperties(ref input, context),
MemberKind.RichText => new RichTextCastProperties(ref input),
MemberKind.Transition => new TransitionCastProperties(ref input, context),
MemberKind.Xtra => new XtraCastProperties(ref input, context),
// TODO: MemberKind.Xtra => new XtraCastProperties(ref input, context),

_ => new UnknownCastProperties(ref input, dataLength)
};
Expand Down
6 changes: 3 additions & 3 deletions Shockky/Resources/IResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ namespace Shockky.Resources;

public interface IResource
{
abstract OsType Kind { get; }
OsType Kind { get; }

public static IResource Read(ref ShockwaveReader input, ReaderContext context)
public static IResource Read(scoped ref ShockwaveReader input, ReaderContext context)
{
var header = new ResourceHeader(ref input);
return Read(ref input, context, header.Kind, header.Length);
}
public static IResource Read(ref ShockwaveReader input, ReaderContext context, OsType kind, int length)
public static IResource Read(scoped ref ShockwaveReader input, ReaderContext context, OsType kind, int length)
{
ReadOnlySpan<byte> chunkSpan = input.ReadBytes(length);
var chunkInput = new ShockwaveReader(chunkSpan, input.ReverseEndianness);
Expand Down
76 changes: 69 additions & 7 deletions Shockky/Resources/Lingo/LingoScript.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Shockky.IO;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using Shockky.IO;
using Shockky.Lingo;

namespace Shockky.Resources;
Expand Down Expand Up @@ -32,8 +35,25 @@ public sealed class LingoScript : IShockwaveItem, IResource
/// </summary>
public LingoEventFlags EventFlags { get; set; }

public List<short> EventHandlerIndices { get; set; }
public List<short> Properties { get; set; }
public List<short> Globals { get; set; }

/// <summary>
/// Represents all lingo handlers in this script.
/// </summary>
public List<LingoFunction> Functions { get; set; }

public List<LingoLiteral> Literals { get; }

public LingoScript()
{ }
{
EventHandlerIndices = new List<short>();
Properties = new List<short>();
Globals = new List<short>();
Functions = new List<LingoFunction>();
Literals = new List<LingoLiteral>();
}
public LingoScript(ref ShockwaveReader input)
{
input.ReverseEndianness = true;
Expand Down Expand Up @@ -62,25 +82,67 @@ public LingoScript(ref ShockwaveReader input)

FactoryNameIndex = input.ReadInt16LittleEndian();

int eventHandlerCount = input.ReadInt16LittleEndian();
EventHandlerIndices = new List<short>(input.ReadInt16LittleEndian());
int eventHandlerIndexOffset = input.ReadInt32LittleEndian();

EventFlags = (LingoEventFlags)input.ReadInt32LittleEndian();

short propertiesCount = input.ReadInt16LittleEndian();
Properties = new List<short>(input.ReadInt16LittleEndian());
int propertiesOffset = input.ReadInt32LittleEndian();

short globalsCount = input.ReadInt16LittleEndian();
Globals = new List<short>(input.ReadInt16LittleEndian());
int globalsOffset = input.ReadInt32LittleEndian();

short functionsCount = input.ReadInt16LittleEndian();
Functions = new List<LingoFunction>(input.ReadInt16LittleEndian());
int functionsOffset = input.ReadInt32LittleEndian();

short literalsCount = input.ReadInt16LittleEndian();
Literals = new List<LingoLiteral>(input.ReadInt16LittleEndian());
int literalsOffset = input.ReadInt32LittleEndian();

int literalDataLength = input.ReadInt32LittleEndian();
int literalDataOffset = input.ReadInt32LittleEndian();

input.Position = propertiesOffset;
for (int i = 0; i < Properties.Capacity; i++)
{
Properties.Add(input.ReadInt16LittleEndian());
}

input.Position = globalsOffset;
for (int i = 0; i < Globals.Capacity; i++)
{
Globals.Add(input.ReadInt16LittleEndian());
}

input.Position = functionsOffset;
for (int i = 0; i < Functions.Capacity; i++)
{
Functions.Add(new LingoFunction(ref input));
}

input.Position = literalsOffset;
var literalEntries = new (VariantKind Kind, int Offset)[Literals.Capacity]; // TODO: Stackalloc/ArrayPool
for (int i = 0; i < Literals.Capacity; i++)
{
literalEntries[i].Kind = (VariantKind)input.ReadInt32LittleEndian();
literalEntries[i].Offset = input.ReadInt32LittleEndian();
}

input.Position = literalDataOffset;
for (int i = 0; i < Literals.Capacity; i++)
{
(VariantKind kind, int offset) = literalEntries[i];

Literals.Add(LingoLiteral.Read(ref input, kind, literalDataOffset + offset));

Debug.Assert(literalDataOffset + literalDataLength >= input.Position);
}

input.Position = eventHandlerIndexOffset;
for (int i = 0; i < EventHandlerIndices.Capacity; i++)
{
EventHandlerIndices.Add(input.ReadInt16LittleEndian());
}
}

public static int GetHeaderSize()
Expand Down
30 changes: 13 additions & 17 deletions Shockky/ShockwaveFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@ public ShockwaveFile()
Resources = new Dictionary<int, IResource>();
}

public void Load(ReadOnlySpan<byte> data)
public static ShockwaveFile Read(string path) => Read(File.ReadAllBytes(path));
public static ShockwaveFile Read(ReadOnlySpan<byte> data)
{
var file = new ShockwaveFile();
var input = new ShockwaveReader(data);
Metadata = new FileMetadata(ref input);
input.ReverseEndianness = Metadata.IsBigEndian;

if (Metadata.Codec is CodecKind.FGDM or CodecKind.FGDC)
file.Metadata = new FileMetadata(ref input);
input.ReverseEndianness = file.Metadata.IsBigEndian;

if (file.Metadata.Codec is CodecKind.FGDM or CodecKind.FGDC)
{
if (IResource.Read(ref input, default) is not FileVersion fileVersion)
throw new InvalidDataException();

ReaderContext readerContext = new(fileVersion.Version);

if (IResource.Read(ref input, readerContext) is not FileCompressionTypes compressionTypes)
throw new InvalidDataException();

if (IResource.Read(ref input, readerContext) is not AfterburnerMap afterburnerMap)
throw new InvalidDataException();

Expand All @@ -41,9 +44,9 @@ public void Load(ReadOnlySpan<byte> data)
if (fgeiHeader.Kind is not OsType.FGEI)
throw new InvalidDataException();

Resources = FileGzipEmbeddedImage.ReadResources(ref input, readerContext, afterburnerMap, compressionTypes);
file.Resources = FileGzipEmbeddedImage.ReadResources(ref input, readerContext, afterburnerMap, compressionTypes);
}
else if (Metadata.Codec is CodecKind.MV93)
else if (file.Metadata.Codec is CodecKind.MV93)
{
if (IResource.Read(ref input, default) is not IndexMap initialMap)
throw new InvalidDataException($"Failed to read {nameof(IndexMap)}");
Expand All @@ -63,17 +66,10 @@ public void Load(ReadOnlySpan<byte> data)
continue;

input.Position = entry.Offset;
Resources.Add(i, IResource.Read(ref input, readerContext));
file.Resources.Add(i, IResource.Read(ref input, readerContext));
}
}
}

public static ShockwaveFile Load(string path)
{
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
var shockwaveFile = new ShockwaveFile();
shockwaveFile.Load(data);

return shockwaveFile;
return file;
}
}

0 comments on commit 62955b5

Please sign in to comment.