Skip to content

Commit ee924e3

Browse files
committed
Backport some CFB work
1 parent 9e5085b commit ee924e3

File tree

1 file changed

+82
-30
lines changed
  • SabreTools.Serialization/Wrappers

1 file changed

+82
-30
lines changed

SabreTools.Serialization/Wrappers/CFB.cs

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using SabreTools.IO.Extensions;
5+
using SabreTools.Models.CFB;
46

57
namespace 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

Comments
 (0)