11using System ;
22using System . IO ;
33using SabreTools . Data . Models . CDROM ;
4- using SabreTools . IO ;
54using SabreTools . IO . Extensions ;
65
76namespace SabreTools . Data . Extensions
87{
98 public static class CDROM
109 {
10+ /// <summary>
11+ /// Get the sector mode for a CD-ROM stream
12+ /// </summary>
13+ /// <param name="stream">Stream to derive the sector mode from</param>
14+ /// <returns>Sector mode from the stream on success, <see cref="SectorMode.UNKNOWN"/> on error</returns>
15+ public static SectorMode GetSectorMode ( this Stream stream )
16+ {
17+ try
18+ {
19+ byte modeByte = stream . ReadByteValue ( ) ;
20+ if ( modeByte == 0 )
21+ {
22+ return SectorMode . MODE0 ;
23+ }
24+ else if ( modeByte == 1 )
25+ {
26+ return SectorMode . MODE1 ;
27+ }
28+ else if ( modeByte == 2 )
29+ {
30+ stream . SeekIfPossible ( 2 , SeekOrigin . Current ) ;
31+ byte submode = stream . ReadByteValue ( ) ;
32+ if ( ( submode & 0x20 ) == 0x20 )
33+ return SectorMode . MODE2_FORM2 ;
34+ else
35+ return SectorMode . MODE2_FORM1 ;
36+ }
37+ else
38+ {
39+ return SectorMode . UNKNOWN ;
40+ }
41+ }
42+ catch
43+ {
44+ // Ignore the actual error
45+ return SectorMode . UNKNOWN ;
46+ }
47+
48+ }
49+
50+ /// <summary>
51+ /// Get the user data size for a sector mode
52+ /// </summary>
53+ /// <param name="mode">Sector mode to get a value for</param>
54+ /// <returns>User data size, if possible</returns>
55+ public static long GetUserDataSize ( this SectorMode mode )
56+ {
57+ return mode switch
58+ {
59+ SectorMode . MODE0 => Constants . Mode0DataSize ,
60+ SectorMode . MODE1 => Constants . Mode1DataSize ,
61+ SectorMode . MODE2 => Constants . Mode0DataSize ,
62+ SectorMode . MODE2_FORM1 => Constants . Mode2Form1DataSize ,
63+ SectorMode . MODE2_FORM2 => Constants . Mode2Form2DataSize ,
64+ _ => Constants . Mode0DataSize ,
65+ } ;
66+ }
67+
68+ /// <summary>
69+ /// Get the user data end offset for a sector mode
70+ /// </summary>
71+ /// <param name="mode">Sector mode to get a value for</param>
72+ /// <returns>User data end offset, if possible</returns>
73+ public static long GetUserDataEnd ( this SectorMode mode )
74+ {
75+ return mode switch
76+ {
77+ SectorMode . MODE0 => Constants . Mode0UserDataEnd , // TODO: Support flexible sector length (2352)
78+ SectorMode . MODE1 => Constants . Mode1UserDataEnd ,
79+ SectorMode . MODE2 => Constants . Mode0UserDataEnd , // TODO: Support flexible sector length (2352)
80+ SectorMode . MODE2_FORM1 => Constants . Mode2Form1UserDataEnd ,
81+ SectorMode . MODE2_FORM2 => Constants . Mode2Form2UserDataEnd , // TODO: Support flexible sector length (2348)
82+ _ => Constants . Mode0UserDataEnd ,
83+ } ;
84+ }
85+
86+ /// <summary>
87+ /// Get the user data start offset for a sector mode
88+ /// </summary>
89+ /// <param name="mode">Sector mode to get a value for</param>
90+ /// <returns>User data start offset, if possible</returns>
91+ public static long GetUserDataStart ( this SectorMode mode )
92+ {
93+ return mode switch
94+ {
95+ SectorMode . MODE0 => Constants . Mode0UserDataStart ,
96+ SectorMode . MODE1 => Constants . Mode1UserDataStart ,
97+ SectorMode . MODE2 => Constants . Mode0UserDataStart ,
98+ SectorMode . MODE2_FORM1 => Constants . Mode2Form1UserDataStart ,
99+ SectorMode . MODE2_FORM2 => Constants . Mode2Form2UserDataStart ,
100+ _ => Constants . Mode0UserDataStart ,
101+ } ;
102+ }
103+
11104 /// <summary>
12105 /// Creates a stream that provides only the user data of a CDROM stream
13106 /// </summary>
@@ -19,26 +112,31 @@ public class ISO9660Stream : Stream
19112 // State variables
20113 private long _position = 0 ;
21114 private SectorMode _currentMode = SectorMode . UNKNOWN ;
22- private long _userDataStart = 16 ;
23- private long _userDataEnd = 2064 ;
115+ private long _userDataStart = Constants . Mode1UserDataStart ;
116+ private long _userDataEnd = Constants . Mode1UserDataEnd ;
24117 private long _isoSectorSize = Constants . Mode1DataSize ;
25118
26119 public ISO9660Stream ( Stream inputStream )
27120 {
28121 if ( ! inputStream . CanSeek || ! inputStream . CanRead )
29122 throw new ArgumentException ( "Stream must be readable and seekable." , nameof ( inputStream ) ) ;
123+
30124 _baseStream = inputStream ;
31125 }
32126
33- public override bool CanRead => true ;
34- public override bool CanSeek => true ;
127+ /// <inheritdoc/>
128+ public override bool CanRead => _baseStream . CanRead ;
129+
130+ /// <inheritdoc/>
131+ public override bool CanSeek => _baseStream . CanSeek ;
132+
133+ /// <inheritdoc/>
35134 public override bool CanWrite => false ;
36135
37- public override void Flush ( )
38- {
39- _baseStream . Flush ( ) ;
40- }
136+ /// <inheritdoc/>
137+ public override void Flush ( ) => _baseStream . Flush ( ) ;
41138
139+ /// <inheritdoc/>
42140 public override long Length
43141 {
44142 get
@@ -47,25 +145,28 @@ public override long Length
47145 }
48146 }
49147
148+ /// <inheritdoc/>
50149 public override void SetLength ( long value )
51150 {
52151 throw new NotSupportedException ( "Setting the length of this stream is not supported." ) ;
53152 }
54153
154+ /// <inheritdoc/>
55155 public override void Write ( byte [ ] buffer , int offset , int count )
56156 {
57157 throw new NotSupportedException ( "Writing to this stream is not supported." ) ;
58158 }
59159
160+ /// <inheritdoc/>
60161 protected override void Dispose ( bool disposing )
61162 {
62163 if ( disposing )
63- {
64164 _baseStream . Dispose ( ) ;
65- }
165+
66166 base . Dispose ( disposing ) ;
67167 }
68168
169+ /// <inheritdoc/>
69170 public override long Position
70171 {
71172 // Get the position of the underlying ISO9660 stream
@@ -93,16 +194,16 @@ public override long Position
93194 }
94195 }
95196
197+ /// <inheritdoc/>
96198 public override int Read ( byte [ ] buffer , int offset , int count )
97199 {
98- bool readEntireSector = false ;
99200 int totalRead = 0 ;
100201 int remaining = count ;
101202
102203 while ( remaining > 0 && _position < _baseStream . Length )
103204 {
104205 // Determine location of current sector
105- long baseStreamOffset = ( _position / Constants . CDROMSectorSize ) * Constants . CDROMSectorSize ;
206+ long baseStreamOffset = _position - ( _position % Constants . CDROMSectorSize ) ;
106207
107208 // Set the current sector's mode and user data location
108209 SetState ( baseStreamOffset ) ;
@@ -123,28 +224,23 @@ public override int Read(byte[] buffer, int offset, int count)
123224 _position += Constants . CDROMSectorSize - _userDataEnd + _userDataStart ;
124225 }
125226 else
227+ {
126228 baseStreamOffset += remainder ;
229+ }
127230
128231 // Sanity check on read location before seeking
129232 if ( baseStreamOffset < 0 || baseStreamOffset > _baseStream . Length )
130- {
131233 throw new ArgumentOutOfRangeException ( nameof ( offset ) , "Attempted to seek outside the stream boundaries." ) ;
132- }
133234
134235 // Seek to target position in base CDROM stream
135- _baseStream . Seek ( baseStreamOffset , SeekOrigin . Begin ) ;
236+ _baseStream . SeekIfPossible ( baseStreamOffset , SeekOrigin . Begin ) ;
136237
137238 // Read the remaining bytes, up to max of one ISO sector (2048 bytes)
138239 int bytesToRead = ( int ) Math . Min ( remaining , _isoSectorSize - sectorOffset ) ;
139240
140241 // Don't overshoot end of stream
141242 bytesToRead = ( int ) Math . Min ( bytesToRead , _baseStream . Length - _position ) ;
142243
143- if ( bytesToRead == ( _isoSectorSize - sectorOffset ) )
144- readEntireSector = true ;
145- else
146- readEntireSector = false ;
147-
148244 // Finish reading if no more bytes to be read
149245 if ( bytesToRead <= 0 )
150246 break ;
@@ -154,7 +250,7 @@ public override int Read(byte[] buffer, int offset, int count)
154250
155251 // Update state for base stream
156252 _position = _baseStream . Position ;
157- if ( readEntireSector )
253+ if ( bytesToRead == ( _isoSectorSize - sectorOffset ) )
158254 _position += ( Constants . CDROMSectorSize - _userDataEnd ) + _userDataStart ;
159255
160256 // Update state for ISO stream
@@ -168,24 +264,17 @@ public override int Read(byte[] buffer, int offset, int count)
168264 return totalRead ;
169265 }
170266
267+ /// <inheritdoc/>
171268 public override long Seek ( long offset , SeekOrigin origin )
172269 {
173270 // Get the intended position for the ISO9660 stream
174- long targetPosition ;
175- switch ( origin )
271+ var targetPosition = origin switch
176272 {
177- case SeekOrigin . Begin :
178- targetPosition = offset ;
179- break ;
180- case SeekOrigin . Current :
181- targetPosition = Position + offset ;
182- break ;
183- case SeekOrigin . End :
184- targetPosition = Length + offset ;
185- break ;
186- default :
187- throw new ArgumentException ( "Invalid SeekOrigin." , nameof ( origin ) ) ;
188- }
273+ SeekOrigin . Begin => offset ,
274+ SeekOrigin . Current => Position + offset ,
275+ SeekOrigin . End => Length + offset ,
276+ _ => throw new ArgumentException ( "Invalid SeekOrigin." , nameof ( origin ) ) ,
277+ } ;
189278
190279 // Get the number of ISO sectors before current position
191280 long newPosition = ( targetPosition / _isoSectorSize ) * Constants . CDROMSectorSize ;
@@ -195,80 +284,33 @@ public override long Seek(long offset, SeekOrigin origin)
195284
196285 // Add the within-sector position
197286 newPosition += _userDataStart + ( targetPosition % _isoSectorSize ) ;
198-
199287 if ( newPosition < 0 || newPosition > _baseStream . Length )
200- {
201288 throw new ArgumentOutOfRangeException ( nameof ( offset ) , "Attempted to seek outside the stream boundaries." ) ;
202- }
203289
204- _position = _baseStream . Seek ( newPosition , SeekOrigin . Begin ) ;
290+ _position = _baseStream . SeekIfPossible ( newPosition , SeekOrigin . Begin ) ;
205291 return Position ;
206292 }
207293
294+ /// <summary>
295+ /// Update the current stream state based on the location
296+ /// </summary>
297+ /// <param name="sectorLocation">Sector location to update from</param>
208298 private void SetState ( long sectorLocation )
209299 {
210- long oldPosition = _baseStream . Position ;
211- long modePosition = ( sectorLocation - sectorLocation % Constants . CDROMSectorSize ) + 15 ;
212- _baseStream . Seek ( modePosition , SeekOrigin . Begin ) ;
213- byte modeByte = _baseStream . ReadByteValue ( ) ;
214- if ( modeByte == 0 )
215- _currentMode = SectorMode . MODE0 ;
216- else if ( modeByte == 1 )
217- _currentMode = SectorMode . MODE1 ;
218- else if ( modeByte == 2 )
219- {
220- _baseStream . Seek ( modePosition + 3 , SeekOrigin . Begin ) ;
221- byte submode = _baseStream . ReadByteValue ( ) ;
222- if ( ( submode & 0x20 ) == 0x20 )
223- _currentMode = SectorMode . MODE2_FORM2 ;
224- else
225- _currentMode = SectorMode . MODE2_FORM1 ;
226- }
227- else
228- _currentMode = SectorMode . UNKNOWN ;
300+ long current = _baseStream . Position ;
301+ long modePosition = sectorLocation - ( sectorLocation % Constants . CDROMSectorSize ) + 15 ;
229302
230- // Set the user data location variables
231- switch ( _currentMode )
232- {
233- case SectorMode . MODE1 :
234- _userDataStart = 16 ;
235- _userDataEnd = 2064 ;
236- //_isoSectorSize = Constants.Mode1DataSize;
237- break ;
303+ // Get the current sector mode
304+ _baseStream . SeekIfPossible ( modePosition , SeekOrigin . Begin ) ;
305+ _currentMode = _baseStream . GetSectorMode ( ) ;
238306
239- case SectorMode . MODE2_FORM1 :
240- _userDataStart = 24 ;
241- _userDataEnd = 2072 ;
242- //_isoSectorSize = Constants.Form1DataSize;
243- break ;
244-
245- case SectorMode . MODE2_FORM2 :
246- _userDataStart = 24 ;
247- _userDataEnd = 2072 ;
248- // TODO: Support flexible sector length
249- //_userDataEnd = 2348;
250- //_isoSectorSize = Constants.Form2DataSize;
251- break ;
252-
253- case SectorMode . MODE0 :
254- case SectorMode . MODE2 :
255- _userDataStart = 16 ;
256- _userDataEnd = 2064 ;
257- // TODO: Support flexible sector length
258- //_userDataEnd = 2352;
259- //_isoSectorSize = Constants.Mode0DataSize;
260- break ;
261-
262- case SectorMode . UNKNOWN :
263- _userDataStart = 16 ;
264- _userDataEnd = 2064 ;
265- //_isoSectorSize = Constants.Mode1DataSize;
266- break ;
267- }
268-
269- _baseStream . Seek ( oldPosition , SeekOrigin . Begin ) ;
307+ // Set the user data location variables
308+ _userDataStart = _currentMode . GetUserDataStart ( ) ;
309+ _userDataEnd = _currentMode . GetUserDataEnd ( ) ;
310+ // _isoSectorSize = _currentMode.GetUserDataSize();
270311
271- return ;
312+ // Reset the stream position
313+ _baseStream . SeekIfPossible ( current , SeekOrigin . Begin ) ;
272314 }
273315 }
274316 }
0 commit comments