2025-09-04 23:00:51 +02:00

209 lines
4.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Mpeg2.Psi;
namespace skyscraper8.Experimentals.OtvSsu
{
internal class OtvSsuParser : PrivateSectionParser
{
private readonly OtvSsuHandler _handler;
public OtvSsuParser(OtvSsuHandler handler)
{
_handler = handler;
}
private int errors;
protected override void HandleTable(byte tableId, int sourcePid, byte[] payload)
{
errors++;
}
private Dictionary<Coordinate, DataMap> _dataMaps;
protected override void HandleTable(byte tableId, int sourcePid, byte sectionNumber, byte lastSectionNumber, ushort tableIdExtension, byte[] payload)
{
MemoryStream ms = new MemoryStream(payload, false);
uint fileId = ms.ReadUInt32BE();
uint unknown1 = ms.ReadUInt32BE();
uint offset = ms.ReadUInt32BE();
uint length = ms.ReadUInt32BE();
if (offset > length)
{
_handler.OnOtvSsuError(sourcePid, OtvSsuError.OffsetOutOfRange);
return;
}
if (length > Int32.MaxValue)
{
_handler.OnOtvSsuError(sourcePid, OtvSsuError.FileTooLarge);
return;
}
if (tableIdExtension < 10 && (fileId & 0x00ff0000) == 0x00ff0000)
{
_handler.OnOtvSsuError(sourcePid, OtvSsuError.NdsOverlap);
return;
}
Coordinate coords = new Coordinate(tableIdExtension, fileId, unknown1, length);
if (_dataMaps == null)
_dataMaps = new Dictionary<Coordinate, DataMap>();
DataMap map;
if (_dataMaps.ContainsKey(coords))
{
map = _dataMaps[coords];
_handler.OnOtvSsuBlock(sourcePid, tableIdExtension, fileId, unknown1, length);
}
else
{
map = new DataMap(length);
if (_handler.OnOtvCheckFileAlreadyKnown(sourcePid, tableIdExtension, fileId, unknown1, length))
{
map.Dispose();
_dataMaps.Add(coords, map);
return;
}
else
{
_handler.OnOtvSsuFileAnnouncement(sourcePid, tableIdExtension, fileId, unknown1, length);
_dataMaps.Add(coords, map);
}
}
if (map.IsComplete)
{
return;
}
if (map.WasWrittenBefore(offset))
{
return;
}
map.PushPacket(payload,offset);
if (map.IsErronous)
{
_handler.OnOtvSsuError(sourcePid, OtvSsuError.PacketDeliveredTooMuchData);
_dataMaps.Remove(coords);
map.Dispose();
return;
}
if (map.IsComplete)
{
_handler.OnOtvSsuComplete(sourcePid, map.GetStream(), tableIdExtension, fileId, unknown1, length);
map.Dispose();
}
}
class Coordinate
{
private readonly ushort _tableIdExtension;
private readonly uint _fileId;
private readonly uint _unknown1;
private readonly uint _length;
public Coordinate(ushort tableIdExtension, uint fileId, uint unknown1, uint length)
{
_tableIdExtension = tableIdExtension;
_fileId = fileId;
_unknown1 = unknown1;
_length = length;
}
protected bool Equals(Coordinate other)
{
return _tableIdExtension == other._tableIdExtension && _fileId == other._fileId && _unknown1 == other._unknown1 && _length == other._length;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((Coordinate)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(_tableIdExtension, _fileId, _unknown1, _length);
}
}
class DataMap : IDisposable
{
private MemoryStream ms;
private uint length;
private uint needed;
private List<uint> knownOffsets;
private bool disposed;
public DataMap(uint length)
{
ms = new MemoryStream((int)length);
this.length = length;
this.needed = length;
this.knownOffsets = new List<uint>();
}
public void PushPacket(byte[] payload, uint offset)
{
if (disposed)
throw new ObjectDisposedException(this.ToString());
ms.Position = offset;
ms.Write(payload, 16, payload.Length - 16);
needed -= (uint)(payload.Length - 16);
knownOffsets.Add(offset);
}
public bool WasWrittenBefore(uint offset)
{
if (disposed)
return true;
if (knownOffsets == null)
return false;
return knownOffsets.Contains(offset);
}
public bool IsComplete
{
get
{
if (disposed)
return false;
return needed == 0;
}
}
public bool IsErronous
{
get
{
if (disposed)
throw new ObjectDisposedException(this.ToString());
return length < needed;
}
}
public Stream GetStream()
{
if (disposed)
throw new ObjectDisposedException(this.ToString());
return ms;
}
public void Dispose()
{
ms = null;
needed = 0;
knownOffsets = null;
disposed = true;
}
}
}
}