11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using SabreTools . IO . Extensions ;
5+ using SabreTools . Models . CFB ;
46
57namespace SabreTools . Serialization . Wrappers
68{
7- public class CFB : WrapperBase < Models . CFB . Binary >
9+ public class CFB : WrapperBase < Binary >
810 {
911 #region Descriptive Properties
1012
@@ -15,8 +17,39 @@ public class CFB : WrapperBase<Models.CFB.Binary>
1517
1618 #region Extension Properties
1719
18- /// <inheritdoc cref="Models.CFB.Binary.DirectoryEntries"/>
19- public Models . CFB . DirectoryEntry [ ] ? DirectoryEntries => Model . DirectoryEntries ;
20+ /// <inheritdoc cref="Binary.DirectoryEntries"/>
21+ public DirectoryEntry [ ] ? DirectoryEntries => Model . DirectoryEntries ;
22+
23+ /// <inheritdoc cref="Binary.FATSectorNumbers"/>
24+ public SectorNumber [ ] ? FATSectorNumbers => Model . FATSectorNumbers ;
25+
26+ /// <inheritdoc cref="Binary.MiniFATSectorNumbers"/>
27+ public SectorNumber [ ] ? MiniFATSectorNumbers => Model . MiniFATSectorNumbers ;
28+
29+ /// <summary>
30+ /// Byte array representing the mini stream
31+ /// </summary>
32+ public byte [ ] MiniStream
33+ {
34+ get
35+ {
36+ // Use the cached value, if it exists
37+ if ( _miniStream != null )
38+ return _miniStream ;
39+
40+ // If there are no directory entries
41+ if ( DirectoryEntries == null || DirectoryEntries . Length == 0 )
42+ return [ ] ;
43+
44+ // Get the mini stream offset from root object
45+ var startingSector = ( SectorNumber ) DirectoryEntries [ 0 ] . StartingSectorLocation ;
46+
47+ // Get the mini stream data
48+ _miniStream = GetFATSectorChainData ( startingSector ) ;
49+ return _miniStream ?? [ ] ;
50+ }
51+ }
52+ private byte [ ] ? _miniStream ;
2053
2154 /// <summary>
2255 /// Normal sector size in bytes
@@ -33,14 +66,14 @@ public class CFB : WrapperBase<Models.CFB.Binary>
3366 #region Constructors
3467
3568 /// <inheritdoc/>
36- public CFB ( Models . CFB . Binary ? model , byte [ ] ? data , int offset )
69+ public CFB ( Binary ? model , byte [ ] ? data , int offset )
3770 : base ( model , data , offset )
3871 {
3972 // All logic is handled by the base class
4073 }
4174
4275 /// <inheritdoc/>
43- public CFB ( Models . CFB . Binary ? model , Stream ? data )
76+ public CFB ( Binary ? model , Stream ? data )
4477 : base ( model , data )
4578 {
4679 // All logic is handled by the base class
@@ -96,7 +129,7 @@ public CFB(Models.CFB.Binary? model, Stream? data)
96129
97130 #region Extraction
98131
99- /// <summary>
132+ /// <summary>
100133 /// Extract all files from the CFB to an output directory
101134 /// </summary>
102135 /// <param name="outputDirectory">Output directory to write to</param>
@@ -125,27 +158,34 @@ public bool ExtractAll(string outputDirectory)
125158 /// <returns>True if the file extracted, false otherwise</returns>
126159 public bool ExtractEntry ( int index , string outputDirectory )
127160 {
128- // If we have no files
161+ // If we have no entries
129162 if ( DirectoryEntries == null || DirectoryEntries . Length == 0 )
130163 return false ;
131164
132165 // If we have an invalid index
133166 if ( index < 0 || index >= DirectoryEntries . Length )
134167 return false ;
135168
136- // Get the file information
169+ // Get the entry information
137170 var entry = DirectoryEntries [ index ] ;
138171 if ( entry == null )
139172 return false ;
140173
141- // TODO: Determine how to read the data for a single directory entry
174+ // Only try to extract stream objects
175+ if ( entry . ObjectType != ObjectType . StreamObject )
176+ return true ;
177+
178+ // Get the entry data
179+ byte [ ] ? data = GetDirectoryEntryData ( entry ) ;
180+ if ( data == null )
181+ return false ;
142182
143183 // If we have an invalid output directory
144184 if ( string . IsNullOrEmpty ( outputDirectory ) )
145185 return false ;
146186
147187 // Ensure directory separators are consistent
148- string filename = entry . Name ?? $ "file { index } ";
188+ string filename = entry . Name ?? $ "entry { index } ";
149189 if ( Path . DirectorySeparatorChar == '\\ ' )
150190 filename = filename . Replace ( '/' , '\\ ' ) ;
151191 else if ( Path . DirectorySeparatorChar == '/' )
@@ -162,7 +202,7 @@ public bool ExtractEntry(int index, string outputDirectory)
162202 {
163203 // Open the output file for writing
164204 using FileStream fs = File . OpenWrite ( filename ) ;
165- // TODO: Write out the data
205+ fs . Write ( data ) ;
166206 fs . Flush ( ) ;
167207 }
168208 catch
@@ -173,6 +213,17 @@ public bool ExtractEntry(int index, string outputDirectory)
173213 return true ;
174214 }
175215
216+ /// <summary>
217+ /// Read the entry data for a single directory entry, if possible
218+ /// </summary>
219+ /// <param name="entry">Entry to try to retrieve data for</param>
220+ /// <returns>Byte array representing the entry data on success, null otherwise</returns>
221+ private byte [ ] ? GetDirectoryEntryData ( DirectoryEntry entry )
222+ {
223+ // TODO: Determine how to read the data for a single directory entry
224+ return null ;
225+ }
226+
176227 #endregion
177228
178229 #region FAT Sector Data
@@ -182,14 +233,14 @@ public bool ExtractEntry(int index, string outputDirectory)
182233 /// </summary>
183234 /// <param name="startingSector">Initial FAT sector</param>
184235 /// <returns>Ordered list of sector numbers, null on error</returns>
185- public List < Models . CFB . SectorNumber > ? GetFATSectorChain ( Models . CFB . SectorNumber ? startingSector )
236+ public List < SectorNumber > ? GetFATSectorChain ( SectorNumber ? startingSector )
186237 {
187238 // If we have an invalid sector
188- if ( startingSector == null || startingSector < 0 || Model . FATSectorNumbers == null || ( long ) startingSector >= Model . FATSectorNumbers . Length )
239+ if ( startingSector == null || startingSector < 0 || FATSectorNumbers == null || ( long ) startingSector >= FATSectorNumbers . Length )
189240 return null ;
190241
191242 // Setup the returned list
192- var sectors = new List < Models . CFB . SectorNumber > { startingSector . Value } ;
243+ var sectors = new List < SectorNumber > { startingSector . Value } ;
193244
194245 var lastSector = startingSector ;
195246 while ( true )
@@ -198,10 +249,10 @@ public bool ExtractEntry(int index, string outputDirectory)
198249 break ;
199250
200251 // Get the next sector from the lookup table
201- var nextSector = Model . FATSectorNumbers [ ( uint ) lastSector ! . Value ] ;
252+ var nextSector = FATSectorNumbers [ ( uint ) lastSector ! . Value ] ;
202253
203254 // If we have an end of chain or free sector
204- if ( nextSector == Models . CFB . SectorNumber . ENDOFCHAIN || nextSector == Models . CFB . SectorNumber . FREESECT )
255+ if ( nextSector == SectorNumber . ENDOFCHAIN || nextSector == SectorNumber . FREESECT )
205256 break ;
206257
207258 // Add the next sector to the list and replace the last sector
@@ -217,7 +268,7 @@ public bool ExtractEntry(int index, string outputDirectory)
217268 /// </summary>
218269 /// <param name="startingSector">Initial FAT sector</param>
219270 /// <returns>Ordered list of sector numbers, null on error</returns>
220- public byte [ ] ? GetFATSectorChainData ( Models . CFB . SectorNumber startingSector )
271+ public byte [ ] ? GetFATSectorChainData ( SectorNumber startingSector )
221272 {
222273 // Get the sector chain first
223274 var sectorChain = GetFATSectorChain ( startingSector ) ;
@@ -250,10 +301,10 @@ public bool ExtractEntry(int index, string outputDirectory)
250301 /// </summary>
251302 /// <param name="sector">Sector to convert</param>
252303 /// <returns>File offset in bytes, -1 on error</returns>
253- public long FATSectorToFileOffset ( Models . CFB . SectorNumber ? sector )
304+ public long FATSectorToFileOffset ( SectorNumber ? sector )
254305 {
255306 // If we have an invalid sector number
256- if ( sector == null || sector > Models . CFB . SectorNumber . MAXREGSECT )
307+ if ( sector == null || sector > SectorNumber . MAXREGSECT )
257308 return - 1 ;
258309
259310 // Convert based on the sector shift value
@@ -269,14 +320,14 @@ public long FATSectorToFileOffset(Models.CFB.SectorNumber? sector)
269320 /// </summary>
270321 /// <param name="startingSector">Initial Mini FAT sector</param>
271322 /// <returns>Ordered list of sector numbers, null on error</returns>
272- public List < Models . CFB . SectorNumber > ? GetMiniFATSectorChain ( Models . CFB . SectorNumber ? startingSector )
323+ public List < SectorNumber > ? GetMiniFATSectorChain ( SectorNumber ? startingSector )
273324 {
274325 // If we have an invalid sector
275- if ( startingSector == null || startingSector < 0 || Model . MiniFATSectorNumbers == null || ( long ) startingSector >= Model . MiniFATSectorNumbers . Length )
326+ if ( startingSector == null || startingSector < 0 || MiniFATSectorNumbers == null || ( long ) startingSector >= MiniFATSectorNumbers . Length )
276327 return null ;
277328
278329 // Setup the returned list
279- var sectors = new List < Models . CFB . SectorNumber > { startingSector . Value } ;
330+ var sectors = new List < SectorNumber > { startingSector . Value } ;
280331
281332 var lastSector = startingSector ;
282333 while ( true )
@@ -285,10 +336,10 @@ public long FATSectorToFileOffset(Models.CFB.SectorNumber? sector)
285336 break ;
286337
287338 // Get the next sector from the lookup table
288- var nextSector = Model . MiniFATSectorNumbers [ ( uint ) lastSector ! . Value ] ;
339+ var nextSector = MiniFATSectorNumbers [ ( uint ) lastSector ! . Value ] ;
289340
290341 // If we have an end of chain or free sector
291- if ( nextSector == Models . CFB . SectorNumber . ENDOFCHAIN || nextSector == Models . CFB . SectorNumber . FREESECT )
342+ if ( nextSector == SectorNumber . ENDOFCHAIN || nextSector == SectorNumber . FREESECT )
292343 break ;
293344
294345 // Add the next sector to the list and replace the last sector
@@ -304,7 +355,7 @@ public long FATSectorToFileOffset(Models.CFB.SectorNumber? sector)
304355 /// </summary>
305356 /// <param name="startingSector">Initial Mini FAT sector</param>
306357 /// <returns>Ordered list of sector numbers, null on error</returns>
307- public byte [ ] ? GetMiniFATSectorChainData ( Models . CFB . SectorNumber startingSector )
358+ public byte [ ] ? GetMiniFATSectorChainData ( SectorNumber startingSector )
308359 {
309360 // Get the sector chain first
310361 var sectorChain = GetMiniFATSectorChain ( startingSector ) ;
@@ -316,7 +367,7 @@ public long FATSectorToFileOffset(Models.CFB.SectorNumber? sector)
316367 for ( int i = 0 ; i < sectorChain . Count ; i ++ )
317368 {
318369 // Try to get the sector data offset
319- int sectorDataOffset = ( int ) MiniFATSectorToFileOffset ( sectorChain [ i ] ) ;
370+ int sectorDataOffset = ( int ) MiniFATSectorToMiniStreamOffset ( sectorChain [ i ] ) ;
320371 if ( sectorDataOffset < 0 || sectorDataOffset >= GetEndOfFile ( ) )
321372 return null ;
322373
@@ -336,15 +387,16 @@ public long FATSectorToFileOffset(Models.CFB.SectorNumber? sector)
336387 /// Convert a Mini FAT sector value to a byte offset
337388 /// </summary>
338389 /// <param name="sector">Sector to convert</param>
339- /// <returns>File offset in bytes, -1 on error</returns>
340- public long MiniFATSectorToFileOffset ( Models . CFB . SectorNumber ? sector )
390+ /// <returns>Stream offset in bytes, -1 on error</returns>
391+ /// <remarks>Offset is within the mini stream, not the full file</remarks>
392+ public long MiniFATSectorToMiniStreamOffset ( SectorNumber ? sector )
341393 {
342394 // If we have an invalid sector number
343- if ( sector == null || sector > Models . CFB . SectorNumber . MAXREGSECT )
395+ if ( sector == null || sector > SectorNumber . MAXREGSECT )
344396 return - 1 ;
345397
346398 // Convert based on the sector shift value
347- return ( long ) ( sector + 1 ) * MiniSectorSize ;
399+ return ( long ) sector * MiniSectorSize ;
348400 }
349401
350402 #endregion
0 commit comments