Files

163 lines
5.2 KiB
C#
Raw Permalink Normal View History

using System.Collections.Generic;
using System.IO;
2025-09-26 13:06:18 -04:00
using SabreTools.Data.Models.ASN1;
using SabreTools.IO.Extensions;
2026-03-24 19:17:25 -04:00
using SabreTools.Numerics.Extensions;
2026-01-27 12:03:01 -05:00
#pragma warning disable CA1822 // Mark members as static
#pragma warning disable IDE0017 // Simplify object initialization
2025-09-26 14:57:20 -04:00
namespace SabreTools.Serialization.Readers
{
public class AbstractSyntaxNotationOne : BaseBinaryReader<TypeLengthValue[]>
{
/// <inheritdoc/>
public override TypeLengthValue[]? Deserialize(Stream? data)
{
// If the data is invalid
2026-01-25 14:30:18 -05:00
if (data is null || !data.CanRead)
return null;
try
{
// Cache the current offset
long initialOffset = data.Position;
2025-09-24 09:19:46 -04:00
// Loop through the data and return all top-level values
var topLevelValues = new List<TypeLengthValue>();
2025-09-24 09:19:46 -04:00
while (data.Position < data.Length)
{
var topLevelValue = ParseTypeLengthValue(data);
2026-01-25 14:30:18 -05:00
if (topLevelValue is null)
2025-09-24 09:19:46 -04:00
break;
2025-09-24 09:19:46 -04:00
topLevelValues.Add(topLevelValue);
}
// Return null instead of empty
if (topLevelValues.Count == 0)
return null;
2025-09-24 09:19:46 -04:00
// Return the top-level values
return [.. topLevelValues];
}
catch
{
// Ignore the actual error
return null;
}
}
/// <summary>
/// Parse a Stream into a TypeLengthValue
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled TypeLengthValue on success, null on error</returns>
public TypeLengthValue? ParseTypeLengthValue(Stream data)
{
var obj = new TypeLengthValue();
// Get the type and modifiers
obj.Type = (ASN1Type)data.ReadByteValue();
// If we have an end indicator, we just return
if (obj.Type == ASN1Type.V_ASN1_EOC)
return obj;
// Get the length of the value
ulong? length = ReadLength(data);
2026-01-25 14:30:18 -05:00
if (length is null)
return null;
// Set the length
obj.Length = length.Value;
// Read the value
#if NET20 || NET35
if ((obj.Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
#else
if (obj.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
var valueList = new List<TypeLengthValue>();
long currentIndex = data.Position;
while (data.Position < currentIndex + (long)obj.Length)
{
var value = ParseTypeLengthValue(data);
2026-01-25 14:32:49 -05:00
if (value is not null)
valueList.Add(value);
}
obj.Value = valueList.ToArray();
}
else
{
// TODO: Get more granular based on type
obj.Value = data.ReadBytes((int)obj.Length);
}
return obj;
}
/// <summary>
/// Reads the length field for a type
/// </summary>
/// <param name="data">Stream representing data to read</param>
/// <returns>The length value read from the array</returns>
private static ulong? ReadLength(Stream data)
{
// Read the first byte, assuming it's the length
byte length = data.ReadByteValue();
// If the bit 7 is not set, then use the value as it is
if ((length & 0x80) == 0)
return length;
// Otherwise, use the value as the number of remaining bytes to read
int bytesToRead = length & ~0x80;
// Assemble the length based on byte count
ulong fullLength = 0;
switch (bytesToRead)
{
case 8:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 7;
case 7:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 6;
case 6:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 5;
case 5:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 4;
case 4:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 3;
case 3:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 2;
case 2:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 1;
case 1:
fullLength |= data.ReadByteValue();
break;
default:
return null;
}
return fullLength;
}
}
}