2024-04-03 20:55:02 -04:00
|
|
|
using System.Collections.Generic;
|
2023-09-08 16:55:35 -04:00
|
|
|
using System.IO;
|
2024-04-03 20:55:02 -04:00
|
|
|
using System.Text;
|
2025-09-26 13:06:18 -04:00
|
|
|
using SabreTools.Data.Models.AACS;
|
2024-04-17 11:52:22 -04:00
|
|
|
using SabreTools.IO.Extensions;
|
2026-03-24 19:17:25 -04:00
|
|
|
using SabreTools.Numerics.Extensions;
|
2023-09-08 16:55:35 -04:00
|
|
|
|
2026-01-27 12:03:01 -05:00
|
|
|
#pragma warning disable IDE0017 // Simplify object initialization
|
2025-09-26 14:57:20 -04:00
|
|
|
namespace SabreTools.Serialization.Readers
|
2023-09-08 16:55:35 -04:00
|
|
|
{
|
2025-09-26 15:02:43 -04:00
|
|
|
public class AACS : BaseBinaryReader<MediaKeyBlock>
|
2023-09-08 16:55:35 -04:00
|
|
|
{
|
2024-04-03 20:55:02 -04:00
|
|
|
/// <inheritdoc/>
|
2024-04-04 01:55:05 -04:00
|
|
|
public override MediaKeyBlock? Deserialize(Stream? data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
|
|
|
|
// If the data is invalid
|
2026-01-25 14:30:18 -05:00
|
|
|
if (data is null || !data.CanRead)
|
2024-04-03 20:55:02 -04:00
|
|
|
return null;
|
|
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
#region Records
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
// Create the records list
|
|
|
|
|
var records = new List<Record>();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
// Try to parse the records
|
|
|
|
|
while (data.Position < data.Length)
|
|
|
|
|
{
|
|
|
|
|
// Try to parse the record
|
|
|
|
|
var record = ParseRecord(data);
|
2026-01-25 14:30:18 -05:00
|
|
|
if (record is null)
|
2025-09-16 20:11:41 -04:00
|
|
|
continue;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
// Add the record
|
|
|
|
|
records.Add(record);
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
// If we have an end of media key block record
|
|
|
|
|
if (record.RecordType == RecordType.EndOfMediaKeyBlock)
|
|
|
|
|
break;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
// Align to the 4-byte boundary if we're not at the end
|
2024-11-29 20:48:50 -05:00
|
|
|
if (!data.AlignToBoundary(4))
|
2024-11-28 22:57:12 -05:00
|
|
|
break;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-28 22:57:12 -05:00
|
|
|
#endregion
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-11-28 23:06:09 -05:00
|
|
|
// Set the records
|
|
|
|
|
if (records.Count > 0)
|
2024-12-16 23:08:45 -05:00
|
|
|
return new MediaKeyBlock { Records = [.. records] };
|
2024-11-28 23:06:09 -05:00
|
|
|
|
|
|
|
|
return null;
|
2024-11-28 22:57:12 -05:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// Ignore the actual error
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a Record
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled Record on success, null on error</returns>
|
2024-04-03 20:55:02 -04:00
|
|
|
private static Record? ParseRecord(Stream data)
|
|
|
|
|
{
|
2025-09-16 20:11:41 -04:00
|
|
|
// The first 4 bytes are the type and length
|
2025-11-14 14:06:43 -05:00
|
|
|
RecordType type = (RecordType)data.PeekByteValue();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
|
|
|
|
// Create a record based on the type
|
2025-09-22 20:07:18 -04:00
|
|
|
return type switch
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-11-19 12:40:32 -05:00
|
|
|
// Known record types
|
2025-09-22 20:07:18 -04:00
|
|
|
RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data),
|
|
|
|
|
RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data),
|
|
|
|
|
RecordType.MediaKeyData => ParseMediaKeyDataRecord(data),
|
|
|
|
|
RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data),
|
|
|
|
|
RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data),
|
|
|
|
|
RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data),
|
|
|
|
|
RecordType.HostRevocationList => ParseHostRevocationListRecord(data),
|
|
|
|
|
RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data),
|
|
|
|
|
RecordType.Copyright => ParseCopyrightRecord(data),
|
2024-11-28 22:57:12 -05:00
|
|
|
|
2026-01-25 16:15:05 -05:00
|
|
|
// Unimplemented
|
|
|
|
|
RecordType.MediaKeyVariantData => ParseGenericRecord(data),
|
|
|
|
|
RecordType.Unknown0x28_AACS2 => ParseGenericRecord(data),
|
|
|
|
|
RecordType.DriveRevocationList_AACS2 => ParseGenericRecord(data),
|
|
|
|
|
RecordType.HostRevocationList_AACS2 => ParseGenericRecord(data),
|
|
|
|
|
RecordType.VerifyMediaKey_AACS2 => ParseGenericRecord(data),
|
|
|
|
|
RecordType.EmptyRecord0xF8_AACS2 => ParseGenericRecord(data),
|
|
|
|
|
|
2024-11-19 12:40:32 -05:00
|
|
|
// Unknown record type
|
2025-09-22 20:07:18 -04:00
|
|
|
_ => ParseGenericRecord(data),
|
|
|
|
|
};
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a CopyrightRecord
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled CopyrightRecord on success, null on error</returns>
|
|
|
|
|
public static CopyrightRecord ParseCopyrightRecord(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new CopyrightRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2026-03-24 19:17:25 -04:00
|
|
|
if ((uint)obj.RecordLength > 4)
|
2024-12-16 23:08:45 -05:00
|
|
|
{
|
2026-03-24 19:17:25 -04:00
|
|
|
byte[] copyright = data.ReadBytes((int)((uint)obj.RecordLength - 4));
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0');
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
2025-07-24 09:31:28 -04:00
|
|
|
|
2024-04-03 20:55:02 -04:00
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a DriveRevocationListEntry
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled DriveRevocationListEntry on success, null on error</returns>
|
|
|
|
|
public static DriveRevocationListEntry ParseDriveRevocationListEntry(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new DriveRevocationListEntry();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.Range = data.ReadUInt16BigEndian();
|
|
|
|
|
obj.DriveID = data.ReadBytes(6);
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a DriveRevocationListRecord
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled DriveRevocationListRecord on success, null on error</returns>
|
|
|
|
|
public static DriveRevocationListRecord ParseDriveRevocationListRecord(Stream data)
|
|
|
|
|
{
|
2024-04-03 20:55:02 -04:00
|
|
|
// Cache the current offset
|
2024-12-16 23:08:45 -05:00
|
|
|
long initialOffset = data.Position;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new DriveRevocationListRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Try to parse the signature blocks
|
|
|
|
|
var blocks = new List<DriveRevocationSignatureBlock>();
|
|
|
|
|
uint entryCount = 0;
|
2026-03-24 19:17:25 -04:00
|
|
|
while (entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + (uint)obj.RecordLength)
|
2024-12-16 23:08:45 -05:00
|
|
|
{
|
|
|
|
|
var block = ParseDriveRevocationSignatureBlock(data);
|
|
|
|
|
entryCount += block.NumberOfEntries;
|
|
|
|
|
blocks.Add(block);
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// If we have an empty block
|
|
|
|
|
if (block.NumberOfEntries == 0)
|
|
|
|
|
break;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Set the signature blocks
|
|
|
|
|
obj.SignatureBlocks = [.. blocks];
|
2024-04-03 20:55:02 -04:00
|
|
|
|
|
|
|
|
// If there's any data left, discard it
|
2026-03-24 19:17:25 -04:00
|
|
|
if (data.Position < initialOffset + (uint)obj.RecordLength)
|
|
|
|
|
_ = data.ReadBytes((int)(initialOffset + (uint)obj.RecordLength - data.Position));
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a DriveRevocationSignatureBlock
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled DriveRevocationSignatureBlock on success, null on error</returns>
|
|
|
|
|
public static DriveRevocationSignatureBlock ParseDriveRevocationSignatureBlock(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new DriveRevocationSignatureBlock();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.NumberOfEntries = data.ReadUInt32BigEndian();
|
|
|
|
|
obj.EntryFields = new DriveRevocationListEntry[obj.NumberOfEntries];
|
|
|
|
|
for (int i = 0; i < obj.EntryFields.Length; i++)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.EntryFields[i] = ParseDriveRevocationListEntry(data);
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a EndOfMediaKeyBlockRecord
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled EndOfMediaKeyBlockRecord on success, null on error</returns>
|
|
|
|
|
public static EndOfMediaKeyBlockRecord ParseEndOfMediaKeyBlockRecord(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new EndOfMediaKeyBlockRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2026-03-24 19:17:25 -04:00
|
|
|
if ((uint)obj.RecordLength > 4)
|
|
|
|
|
obj.SignatureData = data.ReadBytes((int)((uint)obj.RecordLength - 4));
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a ExplicitSubsetDifferenceRecord
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled ExplicitSubsetDifferenceRecord on success, null on error</returns>
|
|
|
|
|
public static ExplicitSubsetDifferenceRecord ParseExplicitSubsetDifferenceRecord(Stream data)
|
|
|
|
|
{
|
2024-04-03 20:55:02 -04:00
|
|
|
// Cache the current offset
|
2024-12-16 23:08:45 -05:00
|
|
|
long initialOffset = data.Position;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new ExplicitSubsetDifferenceRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Try to parse the subset differences
|
|
|
|
|
var subsetDifferences = new List<SubsetDifference>();
|
2026-03-24 19:17:25 -04:00
|
|
|
while (data.Position < initialOffset + (uint)obj.RecordLength - 5)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var subsetDifference = ParseSubsetDifference(data);
|
|
|
|
|
subsetDifferences.Add(subsetDifference);
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Set the subset differences
|
|
|
|
|
obj.SubsetDifferences = [.. subsetDifferences];
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// If there's any data left, discard it
|
2026-03-24 19:17:25 -04:00
|
|
|
if (data.Position < initialOffset + (uint)obj.RecordLength)
|
|
|
|
|
_ = data.ReadBytes((int)(initialOffset + (uint)obj.RecordLength - data.Position));
|
2024-12-16 23:08:45 -05:00
|
|
|
|
|
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
2025-09-22 20:07:18 -04:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a GenericRecord
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled GenericRecord on success, null on error</returns>
|
|
|
|
|
public static GenericRecord ParseGenericRecord(Stream data)
|
|
|
|
|
{
|
|
|
|
|
var obj = new GenericRecord();
|
|
|
|
|
|
|
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
|
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
|
|
|
|
obj.Data = data.ReadBytes(0x10);
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-03 20:55:02 -04:00
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a HostRevocationListEntry
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled HostRevocationListEntry on success, null on error</returns>
|
|
|
|
|
public static HostRevocationListEntry ParseHostRevocationListEntry(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new HostRevocationListEntry();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.Range = data.ReadUInt16BigEndian();
|
|
|
|
|
obj.HostID = data.ReadBytes(6);
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a HostRevocationListRecord
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled HostRevocationListRecord on success, null on error</returns>
|
|
|
|
|
public static HostRevocationListRecord ParseHostRevocationListRecord(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
|
|
|
|
// Cache the current offset
|
2024-12-16 23:08:45 -05:00
|
|
|
long initialOffset = data.Position;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new HostRevocationListRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
|
|
|
|
// Try to parse the signature blocks
|
2024-12-16 23:08:45 -05:00
|
|
|
var blocks = new List<HostRevocationSignatureBlock>();
|
2026-03-24 19:17:25 -04:00
|
|
|
for (uint entryCount = 0; entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + (uint)obj.RecordLength;)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var block = ParseHostRevocationSignatureBlock(data);
|
|
|
|
|
entryCount += block.NumberOfEntries;
|
2024-04-03 20:55:02 -04:00
|
|
|
blocks.Add(block);
|
|
|
|
|
|
|
|
|
|
// If we have an empty block
|
|
|
|
|
if (block.NumberOfEntries == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the signature blocks
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.SignatureBlocks = [.. blocks];
|
2024-04-03 20:55:02 -04:00
|
|
|
|
|
|
|
|
// If there's any data left, discard it
|
2026-03-24 19:17:25 -04:00
|
|
|
if (data.Position < initialOffset + (uint)obj.RecordLength)
|
|
|
|
|
_ = data.ReadBytes((int)(initialOffset + (uint)obj.RecordLength - data.Position));
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a HostRevocationSignatureBlock
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled HostRevocationSignatureBlock on success, null on error</returns>
|
|
|
|
|
public static HostRevocationSignatureBlock ParseHostRevocationSignatureBlock(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new HostRevocationSignatureBlock();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.NumberOfEntries = data.ReadUInt32BigEndian();
|
|
|
|
|
obj.EntryFields = new HostRevocationListEntry[obj.NumberOfEntries];
|
|
|
|
|
for (int i = 0; i < obj.EntryFields.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
obj.EntryFields[i] = ParseHostRevocationListEntry(data);
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a MediaKeyDataRecord
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled MediaKeyDataRecord on success, null on error</returns>
|
|
|
|
|
public static MediaKeyDataRecord ParseMediaKeyDataRecord(Stream data)
|
|
|
|
|
{
|
2024-04-03 20:55:02 -04:00
|
|
|
// Cache the current offset
|
2024-12-16 23:08:45 -05:00
|
|
|
long initialOffset = data.Position;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new MediaKeyDataRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Try to parse the media keys
|
|
|
|
|
var mediaKeys = new List<byte[]>();
|
2026-03-24 19:17:25 -04:00
|
|
|
while (data.Position < initialOffset + (uint)obj.RecordLength)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
byte[] mediaKey = data.ReadBytes(0x10);
|
|
|
|
|
mediaKeys.Add(mediaKey);
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Set the media keys
|
|
|
|
|
obj.MediaKeyData = [.. mediaKeys];
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a SubsetDifference
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled SubsetDifference on success, null on error</returns>
|
|
|
|
|
public static SubsetDifference ParseSubsetDifference(Stream data)
|
|
|
|
|
{
|
|
|
|
|
var obj = new SubsetDifference();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.Mask = data.ReadByteValue();
|
|
|
|
|
obj.Number = data.ReadUInt32BigEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
|
|
|
|
}
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Parse a Stream into a SubsetDifferenceIndexRecord
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
|
|
|
|
/// <returns>Filled SubsetDifferenceIndexRecord on success, null on error</returns>
|
|
|
|
|
public static SubsetDifferenceIndexRecord ParseSubsetDifferenceIndexRecord(Stream data)
|
|
|
|
|
{
|
|
|
|
|
// Cache the current offset
|
|
|
|
|
long initialOffset = data.Position;
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new SubsetDifferenceIndexRecord();
|
|
|
|
|
|
|
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.Span = data.ReadUInt32BigEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
// Try to parse the offsets
|
|
|
|
|
var offsets = new List<uint>();
|
2026-03-24 19:17:25 -04:00
|
|
|
while (data.Position < initialOffset + (uint)obj.RecordLength)
|
2024-12-16 23:08:45 -05:00
|
|
|
{
|
|
|
|
|
uint offset = data.ReadUInt32BigEndian();
|
|
|
|
|
offsets.Add(offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the offsets
|
|
|
|
|
obj.Offsets = [.. offsets];
|
|
|
|
|
|
|
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a TypeAndVersionRecord
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled TypeAndVersionRecord on success, null on error</returns>
|
|
|
|
|
public static TypeAndVersionRecord ParseTypeAndVersionRecord(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new TypeAndVersionRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BigEndian();
|
|
|
|
|
obj.VersionNumber = data.ReadUInt32BigEndian();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// Parse a Stream into a VerifyMediaKeyRecord
|
2024-04-03 20:55:02 -04:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">Stream to parse</param>
|
2024-12-16 23:08:45 -05:00
|
|
|
/// <returns>Filled VerifyMediaKeyRecord on success, null on error</returns>
|
|
|
|
|
public static VerifyMediaKeyRecord ParseVerifyMediaKeyRecord(Stream data)
|
2024-04-03 20:55:02 -04:00
|
|
|
{
|
2024-12-16 23:08:45 -05:00
|
|
|
var obj = new VerifyMediaKeyRecord();
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.RecordType = (RecordType)data.ReadByteValue();
|
2024-12-17 22:38:57 -05:00
|
|
|
obj.RecordLength = data.ReadUInt24LittleEndian();
|
2024-12-16 23:08:45 -05:00
|
|
|
obj.CiphertextValue = data.ReadBytes(0x10);
|
2024-04-03 20:55:02 -04:00
|
|
|
|
2024-12-16 23:08:45 -05:00
|
|
|
return obj;
|
2024-04-03 17:27:08 -04:00
|
|
|
}
|
2023-09-08 16:55:35 -04:00
|
|
|
}
|
2025-07-24 09:31:28 -04:00
|
|
|
}
|