209 lines
4.7 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|