From 3bb501ce0c8329739cc9cc17d3e6b90134e37364 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 5 Nov 2025 09:14:37 -0500 Subject: [PATCH] Separate out some helper methods --- SabreTools.Serialization/Extensions/CDROM.cs | 240 ++++++++++-------- .../Models/CDROM/Constants.cs | 48 +++- SabreTools.Serialization/Wrappers/CDROM.cs | 2 +- 3 files changed, 186 insertions(+), 104 deletions(-) diff --git a/SabreTools.Serialization/Extensions/CDROM.cs b/SabreTools.Serialization/Extensions/CDROM.cs index bbe6e882..2e05b2e9 100644 --- a/SabreTools.Serialization/Extensions/CDROM.cs +++ b/SabreTools.Serialization/Extensions/CDROM.cs @@ -1,13 +1,106 @@ using System; using System.IO; using SabreTools.Data.Models.CDROM; -using SabreTools.IO; using SabreTools.IO.Extensions; namespace SabreTools.Data.Extensions { public static class CDROM { + /// + /// Get the sector mode for a CD-ROM stream + /// + /// Stream to derive the sector mode from + /// Sector mode from the stream on success, on error + public static SectorMode GetSectorMode(this Stream stream) + { + try + { + byte modeByte = stream.ReadByteValue(); + if (modeByte == 0) + { + return SectorMode.MODE0; + } + else if (modeByte == 1) + { + return SectorMode.MODE1; + } + else if (modeByte == 2) + { + stream.SeekIfPossible(2, SeekOrigin.Current); + byte submode = stream.ReadByteValue(); + if ((submode & 0x20) == 0x20) + return SectorMode.MODE2_FORM2; + else + return SectorMode.MODE2_FORM1; + } + else + { + return SectorMode.UNKNOWN; + } + } + catch + { + // Ignore the actual error + return SectorMode.UNKNOWN; + } + + } + + /// + /// Get the user data size for a sector mode + /// + /// Sector mode to get a value for + /// User data size, if possible + public static long GetUserDataSize(this SectorMode mode) + { + return mode switch + { + SectorMode.MODE0 => Constants.Mode0DataSize, + SectorMode.MODE1 => Constants.Mode1DataSize, + SectorMode.MODE2 => Constants.Mode0DataSize, + SectorMode.MODE2_FORM1 => Constants.Mode2Form1DataSize, + SectorMode.MODE2_FORM2 => Constants.Mode2Form2DataSize, + _ => Constants.Mode0DataSize, + }; + } + + /// + /// Get the user data end offset for a sector mode + /// + /// Sector mode to get a value for + /// User data end offset, if possible + public static long GetUserDataEnd(this SectorMode mode) + { + return mode switch + { + SectorMode.MODE0 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352) + SectorMode.MODE1 => Constants.Mode1UserDataEnd, + SectorMode.MODE2 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352) + SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataEnd, + SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataEnd, // TODO: Support flexible sector length (2348) + _ => Constants.Mode0UserDataEnd, + }; + } + + /// + /// Get the user data start offset for a sector mode + /// + /// Sector mode to get a value for + /// User data start offset, if possible + public static long GetUserDataStart(this SectorMode mode) + { + return mode switch + { + SectorMode.MODE0 => Constants.Mode0UserDataStart, + SectorMode.MODE1 => Constants.Mode1UserDataStart, + SectorMode.MODE2 => Constants.Mode0UserDataStart, + SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataStart, + SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataStart, + _ => Constants.Mode0UserDataStart, + }; + } + /// /// Creates a stream that provides only the user data of a CDROM stream /// @@ -19,26 +112,31 @@ namespace SabreTools.Data.Extensions // State variables private long _position = 0; private SectorMode _currentMode = SectorMode.UNKNOWN; - private long _userDataStart = 16; - private long _userDataEnd = 2064; + private long _userDataStart = Constants.Mode1UserDataStart; + private long _userDataEnd = Constants.Mode1UserDataEnd; private long _isoSectorSize = Constants.Mode1DataSize; public ISO9660Stream(Stream inputStream) { if (!inputStream.CanSeek || !inputStream.CanRead) throw new ArgumentException("Stream must be readable and seekable.", nameof(inputStream)); + _baseStream = inputStream; } - public override bool CanRead => true; - public override bool CanSeek => true; + /// + public override bool CanRead => _baseStream.CanRead; + + /// + public override bool CanSeek => _baseStream.CanSeek; + + /// public override bool CanWrite => false; - public override void Flush() - { - _baseStream.Flush(); - } + /// + public override void Flush() => _baseStream.Flush(); + /// public override long Length { get @@ -47,25 +145,28 @@ namespace SabreTools.Data.Extensions } } + /// public override void SetLength(long value) { throw new NotSupportedException("Setting the length of this stream is not supported."); } + /// public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("Writing to this stream is not supported."); } + /// protected override void Dispose(bool disposing) { if (disposing) - { _baseStream.Dispose(); - } + base.Dispose(disposing); } + /// public override long Position { // Get the position of the underlying ISO9660 stream @@ -93,16 +194,16 @@ namespace SabreTools.Data.Extensions } } + /// public override int Read(byte[] buffer, int offset, int count) { - bool readEntireSector = false; int totalRead = 0; int remaining = count; while (remaining > 0 && _position < _baseStream.Length) { // Determine location of current sector - long baseStreamOffset = (_position / Constants.CDROMSectorSize) * Constants.CDROMSectorSize; + long baseStreamOffset = _position - (_position % Constants.CDROMSectorSize); // Set the current sector's mode and user data location SetState(baseStreamOffset); @@ -123,16 +224,16 @@ namespace SabreTools.Data.Extensions _position += Constants.CDROMSectorSize - _userDataEnd + _userDataStart; } else + { baseStreamOffset += remainder; + } // Sanity check on read location before seeking if (baseStreamOffset < 0 || baseStreamOffset > _baseStream.Length) - { throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries."); - } // Seek to target position in base CDROM stream - _baseStream.Seek(baseStreamOffset, SeekOrigin.Begin); + _baseStream.SeekIfPossible(baseStreamOffset, SeekOrigin.Begin); // Read the remaining bytes, up to max of one ISO sector (2048 bytes) int bytesToRead = (int)Math.Min(remaining, _isoSectorSize - sectorOffset); @@ -140,11 +241,6 @@ namespace SabreTools.Data.Extensions // Don't overshoot end of stream bytesToRead = (int)Math.Min(bytesToRead, _baseStream.Length - _position); - if (bytesToRead == (_isoSectorSize - sectorOffset)) - readEntireSector = true; - else - readEntireSector = false; - // Finish reading if no more bytes to be read if (bytesToRead <= 0) break; @@ -154,7 +250,7 @@ namespace SabreTools.Data.Extensions // Update state for base stream _position = _baseStream.Position; - if (readEntireSector) + if (bytesToRead == (_isoSectorSize - sectorOffset)) _position += (Constants.CDROMSectorSize - _userDataEnd) + _userDataStart; // Update state for ISO stream @@ -168,24 +264,17 @@ namespace SabreTools.Data.Extensions return totalRead; } + /// public override long Seek(long offset, SeekOrigin origin) { // Get the intended position for the ISO9660 stream - long targetPosition; - switch (origin) + var targetPosition = origin switch { - case SeekOrigin.Begin: - targetPosition = offset; - break; - case SeekOrigin.Current: - targetPosition = Position + offset; - break; - case SeekOrigin.End: - targetPosition = Length + offset; - break; - default: - throw new ArgumentException("Invalid SeekOrigin.", nameof(origin)); - } + SeekOrigin.Begin => offset, + SeekOrigin.Current => Position + offset, + SeekOrigin.End => Length + offset, + _ => throw new ArgumentException("Invalid SeekOrigin.", nameof(origin)), + }; // Get the number of ISO sectors before current position long newPosition = (targetPosition / _isoSectorSize) * Constants.CDROMSectorSize; @@ -195,80 +284,33 @@ namespace SabreTools.Data.Extensions // Add the within-sector position newPosition += _userDataStart + (targetPosition % _isoSectorSize); - if (newPosition < 0 || newPosition > _baseStream.Length) - { throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries."); - } - _position = _baseStream.Seek(newPosition, SeekOrigin.Begin); + _position = _baseStream.SeekIfPossible(newPosition, SeekOrigin.Begin); return Position; } + /// + /// Update the current stream state based on the location + /// + /// Sector location to update from private void SetState(long sectorLocation) { - long oldPosition = _baseStream.Position; - long modePosition = (sectorLocation - sectorLocation % Constants.CDROMSectorSize) + 15; - _baseStream.Seek(modePosition, SeekOrigin.Begin); - byte modeByte = _baseStream.ReadByteValue(); - if (modeByte == 0) - _currentMode = SectorMode.MODE0; - else if (modeByte == 1) - _currentMode = SectorMode.MODE1; - else if (modeByte == 2) - { - _baseStream.Seek(modePosition + 3, SeekOrigin.Begin); - byte submode = _baseStream.ReadByteValue(); - if ((submode & 0x20) == 0x20) - _currentMode = SectorMode.MODE2_FORM2; - else - _currentMode = SectorMode.MODE2_FORM1; - } - else - _currentMode = SectorMode.UNKNOWN; + long current = _baseStream.Position; + long modePosition = sectorLocation - (sectorLocation % Constants.CDROMSectorSize) + 15; + + // Get the current sector mode + _baseStream.SeekIfPossible(modePosition, SeekOrigin.Begin); + _currentMode = _baseStream.GetSectorMode(); // Set the user data location variables - switch (_currentMode) - { - case SectorMode.MODE1: - _userDataStart = 16; - _userDataEnd = 2064; - //_isoSectorSize = Constants.Mode1DataSize; - break; + _userDataStart = _currentMode.GetUserDataStart(); + _userDataEnd = _currentMode.GetUserDataEnd(); + // _isoSectorSize = _currentMode.GetUserDataSize(); - case SectorMode.MODE2_FORM1: - _userDataStart = 24; - _userDataEnd = 2072; - //_isoSectorSize = Constants.Form1DataSize; - break; - - case SectorMode.MODE2_FORM2: - _userDataStart = 24; - _userDataEnd = 2072; - // TODO: Support flexible sector length - //_userDataEnd = 2348; - //_isoSectorSize = Constants.Form2DataSize; - break; - - case SectorMode.MODE0: - case SectorMode.MODE2: - _userDataStart = 16; - _userDataEnd = 2064; - // TODO: Support flexible sector length - //_userDataEnd = 2352; - //_isoSectorSize = Constants.Mode0DataSize; - break; - - case SectorMode.UNKNOWN: - _userDataStart = 16; - _userDataEnd = 2064; - //_isoSectorSize = Constants.Mode1DataSize; - break; - } - - _baseStream.Seek(oldPosition, SeekOrigin.Begin); - - return; + // Reset the stream position + _baseStream.SeekIfPossible(current, SeekOrigin.Begin); } } } diff --git a/SabreTools.Serialization/Models/CDROM/Constants.cs b/SabreTools.Serialization/Models/CDROM/Constants.cs index 3b3a677c..7d1724fd 100644 --- a/SabreTools.Serialization/Models/CDROM/Constants.cs +++ b/SabreTools.Serialization/Models/CDROM/Constants.cs @@ -17,18 +17,58 @@ namespace SabreTools.Data.Models.CDROM public const long Mode0DataSize = 2336; /// - /// Size of user data length for Mode1 / Mode2 Form1 + /// Offset in a Mode0 sector where user data starts + /// + public const long Mode0UserDataStart = 16; + + /// + /// Offset in a Mode0 sector where user data ends + /// + public const long Mode0UserDataEnd = 2064; + + /// + /// Size of user data length for Mode1 /// public const long Mode1DataSize = 2048; /// - /// Size of user data length for Mode1 / Mode2 Form1 + /// Offset in a Mode1 sector where user data starts /// - public const long Form1DataSize = 2048; + public const long Mode1UserDataStart = 16; + + /// + /// Offset in a Mode1 sector where user data ends + /// + public const long Mode1UserDataEnd = 2064; + + /// + /// Size of user data length for Mode2 Form1 + /// + public const long Mode2Form1DataSize = 2048; + + /// + /// Offset in a Mode2 Form1 sector where user data starts + /// + public const long Mode2Form1UserDataStart = 24; + + /// + /// Offset in a Mode2 Form1 sector where user data ends + /// + public const long Mode2Form1UserDataEnd = 2072; /// /// Size of user data length for Mode2 Form2 /// - public const long Form2DataSize = 2324; + public const long Mode2Form2DataSize = 2324; + + /// + /// Offset in a Mode2 Form2 sector where user data starts + /// + public const long Mode2Form2UserDataStart = 24; + + /// + /// Offset in a Mode2 Form2 sector where user data ends + /// + public const long Mode2Form2UserDataEnd = 2072; } } diff --git a/SabreTools.Serialization/Wrappers/CDROM.cs b/SabreTools.Serialization/Wrappers/CDROM.cs index 76070d47..d9a9c2dc 100644 --- a/SabreTools.Serialization/Wrappers/CDROM.cs +++ b/SabreTools.Serialization/Wrappers/CDROM.cs @@ -71,7 +71,7 @@ namespace SabreTools.Serialization.Wrappers try { // Create user data sub-stream - SabreTools.Data.Extensions.CDROM.ISO9660Stream userData = new(data); + var userData = new Data.Extensions.CDROM.ISO9660Stream(data); // Cache the current offset long currentOffset = userData.Position;