mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-04-05 22:01:33 +00:00
523 lines
25 KiB
C#
523 lines
25 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using SabreTools.Data.Models.ISO9660;
|
|
using SabreTools.Numerics.Extensions;
|
|
using SabreTools.Text.Extensions;
|
|
|
|
namespace SabreTools.Wrappers
|
|
{
|
|
public partial class ISO9660 : IPrintable
|
|
{
|
|
#if NETCOREAPP
|
|
/// <inheritdoc/>
|
|
public string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions);
|
|
#endif
|
|
|
|
/// <inheritdoc/>
|
|
public void PrintInformation(StringBuilder builder)
|
|
{
|
|
builder.AppendLine("ISO 9660 Information:");
|
|
builder.AppendLine("-------------------------");
|
|
builder.AppendLine();
|
|
|
|
Print(builder, Model.SystemArea);
|
|
|
|
Print(builder, Model.VolumeDescriptorSet);
|
|
|
|
// TODO: Parse the volume descriptors to print the Path Table Groups and Directory Descriptors with proper encoding
|
|
Encoding encoding = Encoding.UTF8;
|
|
Print(builder, Model.PathTableGroups);
|
|
Print(builder, Model.DirectoryDescriptors, encoding);
|
|
}
|
|
|
|
protected static void Print(StringBuilder builder, byte[] systemArea)
|
|
{
|
|
if (systemArea.Length == 0)
|
|
builder.AppendLine(systemArea, " System Area");
|
|
else if (Array.TrueForAll(systemArea, b => b == 0))
|
|
builder.AppendLine("Zeroed", " System Area");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " System Area");
|
|
builder.AppendLine();
|
|
}
|
|
|
|
#region Volume Descriptors
|
|
|
|
protected static void Print(StringBuilder builder, VolumeDescriptor[] vdSet)
|
|
{
|
|
builder.AppendLine(" Volume Descriptors:");
|
|
builder.AppendLine(" -------------------------");
|
|
if (vdSet.Length == 0)
|
|
{
|
|
builder.AppendLine(" No volume descriptor set");
|
|
builder.AppendLine();
|
|
return;
|
|
}
|
|
|
|
foreach (var vd in vdSet)
|
|
{
|
|
if (vd is BootRecordVolumeDescriptor brvd)
|
|
Print(builder, brvd);
|
|
else if (vd is PrimaryVolumeDescriptor pvd)
|
|
Print(builder, pvd);
|
|
else if (vd is SupplementaryVolumeDescriptor svd)
|
|
Print(builder, svd);
|
|
else if (vd is VolumePartitionDescriptor vpd)
|
|
Print(builder, vpd);
|
|
else if (vd is VolumeDescriptorSetTerminator vdst)
|
|
Print(builder, vdst);
|
|
else if (vd is GenericVolumeDescriptor gvd)
|
|
Print(builder, gvd);
|
|
else
|
|
{
|
|
builder.AppendLine(" Unknown Volume Descriptor");
|
|
builder.AppendLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, BootRecordVolumeDescriptor vd)
|
|
{
|
|
builder.AppendLine(" Boot Record Volume Descriptor:");
|
|
builder.AppendLine(" -------------------------");
|
|
|
|
builder.AppendLine(vd.BootSystemIdentifier, " Boot System Identifier");
|
|
builder.AppendLine(vd.BootSystemIdentifier, " Boot Identifier");
|
|
|
|
if (vd.BootSystemUse.Length == 0)
|
|
builder.AppendLine(vd.BootSystemUse, " Boot System Use");
|
|
else if (Array.TrueForAll(vd.BootSystemUse, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Boot System Use");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " Boot System Use");
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, BaseVolumeDescriptor vd)
|
|
{
|
|
// TOOD: Determine encoding based on vd.Type, svd.EscapeSequence (and manual detection?)
|
|
|
|
if (vd.Type == VolumeDescriptorType.PRIMARY_VOLUME_DESCRIPTOR)
|
|
builder.AppendLine(" Primary Volume Descriptor:");
|
|
else if (vd.Type == VolumeDescriptorType.SUPPLEMENTARY_VOLUME_DESCRIPTOR)
|
|
builder.AppendLine(" Supplementary Volume Descriptor:");
|
|
else
|
|
builder.AppendLine(" Unidentified Base Volume Descriptor:");
|
|
builder.AppendLine(" -------------------------");
|
|
|
|
// Default to UTF-8 deocding (note: Spec says PVD uses subset of ASCII, but UTF-8 is used here as it is a strict superset)
|
|
Encoding encoding = Encoding.UTF8;
|
|
if (vd is PrimaryVolumeDescriptor pvd)
|
|
{
|
|
builder.AppendLine(pvd.UnusedByte, " Unused Byte");
|
|
}
|
|
else if (vd is SupplementaryVolumeDescriptor svd)
|
|
{
|
|
// Decode strings using UTF-16 BigEndian (note: Spec says SVD uses UCS-2, but UTF-16 is used here as it is a strict superset)
|
|
encoding = Encoding.BigEndianUnicode;
|
|
builder.AppendLine(" Volume Flags:");
|
|
#if NET20 || NET35
|
|
builder.AppendLine((svd.VolumeFlags & VolumeFlags.UNREGISTERED_ESCAPE_SEQUENCES) != 0, " Unregistered Escape Sequences");
|
|
#else
|
|
builder.AppendLine(svd.VolumeFlags.HasFlag(VolumeFlags.UNREGISTERED_ESCAPE_SEQUENCES), " Unregistered Escape Sequences");
|
|
#endif
|
|
if ((byte)svd.VolumeFlags > 1)
|
|
builder.AppendLine("Not Zeroed", " Reserved Flags");
|
|
else
|
|
builder.AppendLine("Zeroed", " Reserved Flags");
|
|
}
|
|
|
|
// TODO: Better string decoding (based on spec, EscapeSequences, and detection)
|
|
|
|
builder.AppendLine(encoding.GetString(vd.SystemIdentifier), " System Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.VolumeIdentifier), " Volume Identifier");
|
|
|
|
if (vd.Unused8Bytes is not null && Array.TrueForAll(vd.Unused8Bytes, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Unused 8 Bytes");
|
|
else
|
|
builder.AppendLine(vd.Unused8Bytes, " Unused 8 Bytes");
|
|
|
|
builder.AppendLineBothEndian(vd.VolumeSpaceSize, " Volume Space Size");
|
|
|
|
if (vd is PrimaryVolumeDescriptor pvd2)
|
|
{
|
|
if (pvd2.Unused32Bytes is not null && Array.TrueForAll(pvd2.Unused32Bytes, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Unused 32 Bytes");
|
|
else
|
|
builder.AppendLine(pvd2.Unused32Bytes, " Unused 32 Bytes");
|
|
}
|
|
|
|
if (vd is SupplementaryVolumeDescriptor svd2)
|
|
{
|
|
// TODO: Trim trailing 0x00 and split array into characters (multi-byte encoding detection)
|
|
builder.AppendLine(svd2.EscapeSequences, " Escape Sequences");
|
|
}
|
|
|
|
builder.AppendLineBothEndian(vd.VolumeSetSize, " Volume Set Size");
|
|
builder.AppendLineBothEndian(vd.VolumeSequenceNumber, " Volume Sequence Number");
|
|
builder.AppendLineBothEndian(vd.LogicalBlockSize, " Logical Block Size");
|
|
builder.AppendLineBothEndian(vd.PathTableSize, " Path Table Size");
|
|
builder.AppendLine(vd.PathTableLocationL, " Type-L Path Table Location");
|
|
builder.AppendLine(vd.OptionalPathTableLocationL, " Optional Type-L Path Table Location");
|
|
builder.AppendLine(vd.PathTableLocationM, " Type-M Path Table Location");
|
|
builder.AppendLine(vd.OptionalPathTableLocationM, " Optional Type-M Path Table Location");
|
|
|
|
builder.AppendLine(" Root Directory Record:");
|
|
Print(builder, vd.RootDirectoryRecord);
|
|
|
|
builder.AppendLine(encoding.GetString(vd.VolumeSetIdentifier), " Volume Set Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.PublisherIdentifier), " Publisher Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.DataPreparerIdentifier), " Data Preparer Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.ApplicationIdentifier), " Application Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.CopyrightFileIdentifier), " Copyright Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.AbstractFileIdentifier), " Abstract Identifier");
|
|
builder.AppendLine(encoding.GetString(vd.BibliographicFileIdentifier), " Bibliographic Identifier");
|
|
|
|
builder.AppendLine(Format(vd.VolumeCreationDateTime), " Volume Creation Date Time:");
|
|
builder.AppendLine(Format(vd.VolumeModificationDateTime), " Volume Modification Date Time:");
|
|
builder.AppendLine(Format(vd.VolumeExpirationDateTime), " Volume Expiration Date Time:");
|
|
builder.AppendLine(Format(vd.VolumeEffectiveDateTime), " Volume Effective Date Time:");
|
|
|
|
builder.AppendLine(vd.FileStructureVersion, " File Structure Version");
|
|
|
|
builder.AppendLine(vd.ReservedByte, " Reserved Byte");
|
|
|
|
if (vd.ApplicationUse.Length == 0)
|
|
builder.AppendLine(vd.ApplicationUse, " Application Use");
|
|
else if (Array.TrueForAll(vd.ApplicationUse, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Application Use");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " Application Use");
|
|
|
|
if (vd.Reserved653Bytes.Length == 0)
|
|
builder.AppendLine(vd.Reserved653Bytes, " Reserved 653 Bytes");
|
|
else if (Array.TrueForAll(vd.Reserved653Bytes, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Reserved 653 Bytes");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " Reserved 653 Bytes");
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, VolumePartitionDescriptor vd)
|
|
{
|
|
builder.AppendLine(" Volume Partition Descriptor:");
|
|
builder.AppendLine(" -------------------------");
|
|
|
|
builder.AppendLine(vd.SystemIdentifier, " System Identifier");
|
|
builder.AppendLine(vd.VolumePartitionIdentifier, " Volume Partition Identifier");
|
|
builder.AppendLineBothEndian(vd.VolumePartitionLocation, " Volume Partition Location");
|
|
builder.AppendLineBothEndian(vd.VolumePartitionSize, " Volume Partition Size");
|
|
|
|
if (vd.SystemUse.Length == 0)
|
|
builder.AppendLine(vd.SystemUse, " System Use");
|
|
else if (Array.TrueForAll(vd.SystemUse, b => b == 0))
|
|
builder.AppendLine("Zeroed", " System Use");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " System Use");
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, VolumeDescriptorSetTerminator vd)
|
|
{
|
|
builder.AppendLine(" Volume Descriptor Set Terminator:");
|
|
builder.AppendLine(" -------------------------");
|
|
|
|
if (vd.Reserved2041Bytes.Length == 0)
|
|
builder.AppendLine(vd.Reserved2041Bytes, " Reserved Bytes");
|
|
else if (Array.TrueForAll(vd.Reserved2041Bytes, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Reserved Bytes");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " Reserved Bytes");
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, GenericVolumeDescriptor vd)
|
|
{
|
|
builder.AppendLine(" Unidentified Volume Descriptor:");
|
|
builder.AppendLine(" -------------------------");
|
|
|
|
if (vd.Data.Length == 0)
|
|
builder.AppendLine(vd.Data, " Data");
|
|
else if (Array.TrueForAll(vd.Data, b => b == 0))
|
|
builder.AppendLine("Zeroed", " Data");
|
|
else
|
|
builder.AppendLine("Not Zeroed", " Data");
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Path Tables
|
|
|
|
protected static void Print(StringBuilder builder, PathTableGroup[] ptgs)
|
|
{
|
|
builder.AppendLine(" Path Table Group(s):");
|
|
builder.AppendLine(" -------------------------");
|
|
if (ptgs.Length == 0)
|
|
{
|
|
builder.AppendLine(" No path table groups");
|
|
builder.AppendLine();
|
|
return;
|
|
}
|
|
|
|
for (int tableNum = 0; tableNum < ptgs.Length; tableNum++)
|
|
{
|
|
if (ptgs[tableNum].PathTableL is not null)
|
|
{
|
|
builder.AppendLine($" Type-L Path Table {tableNum}:");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, ptgs[tableNum].PathTableL);
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine($" No Type-L Path Table {tableNum}:");
|
|
builder.AppendLine();
|
|
}
|
|
|
|
if (ptgs[tableNum].OptionalPathTableL is not null)
|
|
{
|
|
builder.AppendLine($" Optional Type-L Path Table {tableNum}:");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, ptgs[tableNum].OptionalPathTableL);
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine($" No Optional Type-L Path Table {tableNum}:");
|
|
builder.AppendLine();
|
|
}
|
|
|
|
if (ptgs[tableNum].PathTableM is not null)
|
|
{
|
|
builder.AppendLine($" Type-M Path Table {tableNum}:");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, ptgs[tableNum].PathTableM);
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine($" No Type-M Path Table {tableNum}:");
|
|
builder.AppendLine();
|
|
}
|
|
|
|
if (ptgs[tableNum].OptionalPathTableM is not null)
|
|
{
|
|
builder.AppendLine($" Optional Type-M Path Table {tableNum}:");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, ptgs[tableNum].OptionalPathTableM);
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine($" No Optional Type-M Path Table {tableNum}:");
|
|
builder.AppendLine();
|
|
}
|
|
}
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, PathTableRecord[]? records)
|
|
{
|
|
if (records is null || records.Length == 0)
|
|
{
|
|
builder.AppendLine(" No records");
|
|
builder.AppendLine();
|
|
return;
|
|
}
|
|
|
|
for (int recordNum = 0; recordNum < records.Length; recordNum++)
|
|
{
|
|
builder.AppendLine($" Path Table Record {recordNum}");
|
|
builder.AppendLine(records[recordNum].DirectoryIdentifierLength, " Directory Identifier Length");
|
|
builder.AppendLine(records[recordNum].ExtendedAttributeRecordLength, " Extended Attribute Record Length");
|
|
builder.AppendLine(records[recordNum].ExtentLocation, " Extent Location");
|
|
builder.AppendLine(records[recordNum].DirectoryIdentifier, " Directory Identifier");
|
|
if (records[recordNum].PaddingField is not null)
|
|
builder.AppendLine(records[recordNum].PaddingField, " Padding Field");
|
|
}
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Directories
|
|
|
|
protected static void Print(StringBuilder builder, Dictionary<int, FileExtent> dirs, Encoding encoding)
|
|
{
|
|
builder.AppendLine(" Directory Descriptors Information:");
|
|
builder.AppendLine(" -------------------------");
|
|
if (dirs.Count == 0)
|
|
{
|
|
builder.AppendLine(" No directory descriptors");
|
|
builder.AppendLine();
|
|
return;
|
|
}
|
|
|
|
foreach (var kvp in dirs)
|
|
{
|
|
builder.AppendLine($" Directory at Sector {kvp.Key}");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, kvp.Value, encoding);
|
|
}
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, FileExtent extent, Encoding encoding)
|
|
{
|
|
if (extent is DirectoryExtent dir)
|
|
{
|
|
if (dir.DirectoryRecords.Length == 0)
|
|
{
|
|
builder.AppendLine(" No directory records");
|
|
builder.AppendLine();
|
|
return;
|
|
}
|
|
|
|
// File extent is a directory, print all directory records
|
|
for (int recordNum = 0; recordNum < dir.DirectoryRecords.Length; recordNum++)
|
|
{
|
|
builder.AppendLine($" Directory Record {recordNum}:");
|
|
builder.AppendLine(" -------------------------");
|
|
Print(builder, dir.DirectoryRecords[recordNum]);
|
|
builder.AppendLine();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// File extent is a file, print the file's Extended Attribute Record
|
|
Print(builder, extent.ExtendedAttributeRecord, encoding);
|
|
}
|
|
|
|
builder.AppendLine();
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, DirectoryRecord dr)
|
|
{
|
|
builder.AppendLine(dr.DirectoryRecordLength, " Directory Record Length");
|
|
builder.AppendLine(dr.ExtendedAttributeRecordLength, " Extended Attribute Record Length");
|
|
|
|
builder.AppendLineBothEndian(dr.ExtentLocation, " Extent Location");
|
|
builder.AppendLineBothEndian(dr.ExtentLength, " Extent Length");
|
|
|
|
Print(builder, dr.RecordingDateTime);
|
|
|
|
builder.AppendLine(" File Flags:");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.EXISTENCE) == FileFlags.EXISTENCE, " Existence");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.DIRECTORY) == FileFlags.DIRECTORY, " Directory");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.ASSOCIATED_FILE) == FileFlags.ASSOCIATED_FILE, " Associated File");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.RECORD) == FileFlags.RECORD, " Record");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.PROTECTION) == FileFlags.PROTECTION, " Protection");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.RESERVED_BIT5) == FileFlags.RESERVED_BIT5, " Reserved Flag (Bit 5)");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.RESERVED_BIT6) == FileFlags.RESERVED_BIT6, " Reserved Flag (Bit 6)");
|
|
builder.AppendLine((dr.FileFlags & FileFlags.MULTI_EXTENT) == FileFlags.MULTI_EXTENT, " Multi-Extent");
|
|
|
|
builder.AppendLine(dr.FileUnitSize, " File Unit Size");
|
|
builder.AppendLine(dr.InterleaveGapSize, " Interleave Gap Size");
|
|
|
|
builder.AppendLineBothEndian(dr.VolumeSequenceNumber, " Volume Sequence Number");
|
|
|
|
builder.AppendLine(dr.FileIdentifierLength, " File Identifier Length");
|
|
builder.AppendLine(dr.FileIdentifier, " File Identifier");
|
|
builder.AppendLine(dr.PaddingField, " Padding Field");
|
|
|
|
if (dr.SystemUse.Length == 0)
|
|
builder.AppendLine(dr.SystemUse, " System Use");
|
|
else if (Array.TrueForAll(dr.SystemUse, b => b == 0))
|
|
builder.AppendLine($"Zeroed ({dr.SystemUse.Length} bytes)", " System Use");
|
|
else
|
|
builder.AppendLine($"Not Zeroed ({dr.SystemUse.Length} bytes)", " System Use");
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, DirectoryRecordDateTime drdt)
|
|
{
|
|
builder.AppendLine(" Directory Record Date Time:");
|
|
|
|
builder.AppendLine(drdt.YearsSince1990, " Years Since 1900");
|
|
builder.AppendLine(drdt.Month, " Month");
|
|
builder.AppendLine(drdt.Day, " Day");
|
|
builder.AppendLine(drdt.Hour, " Hour");
|
|
builder.AppendLine(drdt.Minute, " Minute");
|
|
builder.AppendLine(drdt.Second, " Second");
|
|
string tz = $"{(drdt.TimezoneOffset - 48) * 15 / 60:+0;-0}:{(((drdt.TimezoneOffset - 48) * 15 % 60) + 60) % 60:00} (0x{drdt.TimezoneOffset:X2})";
|
|
builder.AppendLine(tz, " Timezone Offset");
|
|
}
|
|
|
|
private static void Print(StringBuilder builder, ExtendedAttributeRecord? ear, Encoding encoding)
|
|
{
|
|
builder.AppendLine(" File Extent");
|
|
if (ear is null)
|
|
return;
|
|
|
|
builder.AppendLineBothEndian(ear.OwnerIdentification, " Owner Identification");
|
|
builder.AppendLineBothEndian(ear.OwnerIdentification, " Group Identification");
|
|
|
|
builder.AppendLine(" Permissions:");
|
|
builder.AppendLine((ear.Permissions & Permissions.SYSTEM_USER_CANNOT_READ) != 0, " System User Cannot Read");
|
|
builder.AppendLine((ear.Permissions & Permissions.SYSTEM_USER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.OWNER_CANNOT_READ) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.OWNER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.GROUP_MEMBER_CANNOT_READ) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.GROUP_MEMBER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.NON_GROUP_MEMBER_CANNOT_READ) != 0, " System User Cannot Execute");
|
|
builder.AppendLine((ear.Permissions & Permissions.NON_GROUP_MEMBER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
|
if ((ear.Permissions & Permissions.PERMISSIONS_MASK) == Permissions.PERMISSIONS_MASK)
|
|
builder.AppendLine(" Fixed Bits: All Set");
|
|
else
|
|
builder.AppendLine(" Fixed Bits: Not All Set");
|
|
|
|
builder.AppendLine(Format(ear.FileCreationDateTime), " File Creation Date Time");
|
|
builder.AppendLine(Format(ear.FileModificationDateTime), " File Modification Date Time");
|
|
builder.AppendLine(Format(ear.FileExpirationDateTime), " File Expiration Date Time");
|
|
builder.AppendLine(Format(ear.FileEffectiveDateTime), " File Effective Date Time");
|
|
|
|
builder.AppendLine((byte)ear.RecordFormat, " Record Format:");
|
|
builder.AppendLine((byte)ear.RecordAttributes, " Record Attributes");
|
|
builder.AppendLineBothEndian(ear.RecordLength, " Record Length");
|
|
builder.AppendLine(encoding.GetString(ear.SystemIdentifier), " System Identifier");
|
|
builder.AppendLine(ear.SystemUse, " System Use");
|
|
builder.AppendLine(ear.ExtendedAttributeRecordVersion, " Extended Attribute Record Version");
|
|
builder.AppendLine(ear.EscapeSequencesLength, " Escape Sequences Length");
|
|
builder.AppendLine(ear.Reserved64Bytes, " Reserved 64 Bytes");
|
|
builder.AppendLineBothEndian(ear.ApplicationLength, " Application Length");
|
|
builder.AppendLine(ear.ApplicationUse, " Application Use");
|
|
builder.AppendLine(ear.EscapeSequences, " Escape Sequences");
|
|
}
|
|
|
|
#endregion
|
|
|
|
private static string Format(DecDateTime dt)
|
|
{
|
|
string year = dt.Year.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Year)
|
|
: BitConverter.ToString(dt.Year).Replace('-', ' ');
|
|
string month = dt.Month.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Month)
|
|
: BitConverter.ToString(dt.Month).Replace('-', ' ');
|
|
string day = dt.Day.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Day)
|
|
: BitConverter.ToString(dt.Day).Replace('-', ' ');
|
|
|
|
string hour = dt.Hour.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Hour)
|
|
: BitConverter.ToString(dt.Hour).Replace('-', ' ');
|
|
string minute = dt.Minute.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Minute)
|
|
: BitConverter.ToString(dt.Minute).Replace('-', ' ');
|
|
string second = dt.Second.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Second)
|
|
: BitConverter.ToString(dt.Second).Replace('-', ' ');
|
|
string csecond = dt.Centisecond.IsNumericArray()
|
|
? Encoding.ASCII.GetString(dt.Centisecond)
|
|
: BitConverter.ToString(dt.Centisecond).Replace('-', ' ');
|
|
|
|
string tz = $"{(dt.TimezoneOffset - 48) * 15 / 60:+0;-0}:{(((dt.TimezoneOffset - 48) * 15 % 60) + 60) % 60:00} (0x{dt.TimezoneOffset:X2})";
|
|
|
|
return $"{year}-{month}-{day} {hour}:{minute}:{second}.{csecond} [{tz}]";
|
|
}
|
|
}
|
|
}
|