mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-09-22 23:05:12 +00:00
Libraries
This change looks dramatic, but it's just separating out the already-split namespaces into separate top-level folders. In theory, every single one could be built into their own Nuget package. `SabreTools.Serialization` still builds the normal Nuget package that is used by all other projects and includes all namespaces.
This commit is contained in:
161
SabreTools.Serialization.Readers/AbstractSyntaxNotationOne.cs
Normal file
161
SabreTools.Serialization.Readers/AbstractSyntaxNotationOne.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.ASN1;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
#pragma warning disable CA1822 // Mark members as static
|
||||
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
namespace SabreTools.Serialization.Readers
|
||||
{
|
||||
public class AbstractSyntaxNotationOne : BaseBinaryReader<TypeLengthValue[]>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TypeLengthValue[]? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data is null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Loop through the data and return all top-level values
|
||||
var topLevelValues = new List<TypeLengthValue>();
|
||||
while (data.Position < data.Length)
|
||||
{
|
||||
var topLevelValue = ParseTypeLengthValue(data);
|
||||
if (topLevelValue is null)
|
||||
break;
|
||||
|
||||
topLevelValues.Add(topLevelValue);
|
||||
}
|
||||
|
||||
// Return null instead of empty
|
||||
if (topLevelValues.Count == 0)
|
||||
return null;
|
||||
|
||||
// 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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user