Skip to content

Commit 3a6293e

Browse files
committed
Port some code from UnshieldSharp
1 parent 3210b86 commit 3a6293e

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.IO;
33
using System.Text.RegularExpressions;
4+
using SabreTools.IO.Compression.zlib;
45
using SabreTools.Models.InstallShieldCabinet;
6+
using static SabreTools.Models.InstallShieldCabinet.Constants;
57

68
namespace SabreTools.Serialization.Wrappers
79
{
@@ -72,6 +74,15 @@ public int MajorVersion
7274

7375
#endregion
7476

77+
#region Constants
78+
79+
/// <summary>
80+
/// Maximum size of the window in bits
81+
/// </summary>
82+
private const int MAX_WBITS = 15;
83+
84+
#endregion
85+
7586
#region Constructors
7687

7788
/// <inheritdoc/>
@@ -157,6 +168,73 @@ public InstallShieldCabinet(Cabinet? model, Stream? data)
157168
return new Regex(@"\d+$").Replace(pattern, string.Empty);
158169
}
159170

171+
/// <summary>
172+
/// Open a cabinet set for reading, if possible
173+
/// </summary>
174+
/// <param name="pattern">Filename pattern for matching cabinet files</param>
175+
/// <returns></returns>
176+
public static InstallShieldCabinet? OpenSet(string? pattern)
177+
{
178+
// An invalid pattern means no cabinet files
179+
if (string.IsNullOrEmpty(pattern))
180+
return null;
181+
182+
// Create a placeholder wrapper for output
183+
InstallShieldCabinet? set = null;
184+
185+
// Loop until there are no parts left
186+
bool iterate = true;
187+
InstallShieldCabinet? previous = null;
188+
for (int i = 1; iterate; i++)
189+
{
190+
var file = OpenFileForReading(pattern, i, HEADER_SUFFIX);
191+
if (file != null)
192+
iterate = false;
193+
else
194+
file = OpenFileForReading(pattern, i, CABINET_SUFFIX);
195+
196+
if (file == null)
197+
break;
198+
199+
var header = Create(file);
200+
if (header == null)
201+
break;
202+
203+
if (previous != null)
204+
previous.Next = header;
205+
else
206+
previous = set = header;
207+
}
208+
209+
return set;
210+
}
211+
212+
/// <summary>
213+
/// Open a cabinet file for reading
214+
/// </summary>
215+
/// <param name="pattern">Filename pattern for matching cabinet files</param>
216+
/// <param name="index">Cabinet part index to be opened</param>
217+
/// <param name="suffix">Cabinet files suffix (e.g. `.cab`)</param>
218+
/// <returns>A Stream representing the cabinet part, null on error</returns>
219+
private static Stream? OpenFileForReading(string? pattern, int index, string suffix)
220+
{
221+
// An invalid pattern means no cabinet files
222+
if (string.IsNullOrEmpty(pattern))
223+
return null;
224+
225+
// Attempt lower-case extension
226+
string filename = $"{pattern}{index}.{suffix}";
227+
if (File.Exists(filename))
228+
return File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
229+
230+
// Attempt upper-case extension
231+
filename = $"{pattern}{index}.{suffix.ToUpperInvariant()}";
232+
if (File.Exists(filename))
233+
return File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
234+
235+
return null;
236+
}
237+
160238
#endregion
161239

162240
#region Component
@@ -271,6 +349,33 @@ public ulong GetExpandedFileSize(int index)
271349
return Model.FileDescriptors[index];
272350
}
273351

352+
/// <summary>
353+
/// Get the file descriptor at a given index, if possible
354+
/// </summary>
355+
/// <remarks>Verifies the file descriptor flags before returning</remarks>
356+
public FileDescriptor? GetFileDescriptorWithVerification(int index, out string? error)
357+
{
358+
var fileDescriptor = GetFileDescriptor(index);
359+
if (fileDescriptor == null)
360+
{
361+
error = $"Failed to get file descriptor for file {index}";
362+
return null;
363+
}
364+
365+
#if NET20 || NET35
366+
if ((fileDescriptor.Flags & FileFlags.FILE_INVALID) != 0 || fileDescriptor.DataOffset == 0)
367+
#else
368+
if (fileDescriptor.Flags.HasFlag(FileFlags.FILE_INVALID) || fileDescriptor.DataOffset == 0)
369+
#endif
370+
{
371+
error = $"File at {index} is marked as invalid";
372+
return null;
373+
}
374+
375+
error = null;
376+
return fileDescriptor;
377+
}
378+
274379
/// <summary>
275380
/// Get the file name at a given index, if possible
276381
/// </summary>
@@ -287,6 +392,24 @@ public ulong GetExpandedFileSize(int index)
287392
return descriptor.Name;
288393
}
289394

395+
/// <summary>
396+
/// Get the packed size of a file, if possible
397+
/// </summary>
398+
public static ulong GetReadableBytes(FileDescriptor? descriptor)
399+
{
400+
if (descriptor == null)
401+
return 0;
402+
403+
#if NET20 || NET35
404+
if ((descriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
405+
#else
406+
if (descriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
407+
#endif
408+
return descriptor.CompressedSize;
409+
else
410+
return descriptor.ExpandedSize;
411+
}
412+
290413
#endregion
291414

292415
#region File Group
@@ -355,5 +478,83 @@ public ulong GetExpandedFileSize(int index)
355478
=> GetFileGroupFromFile(index)?.Name;
356479

357480
#endregion
481+
482+
#region Extraction
483+
484+
/// <summary>
485+
/// Uncompress a source byte array to a destination
486+
/// </summary>
487+
public unsafe static int Uncompress(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
488+
{
489+
fixed (byte* sourcePtr = source)
490+
fixed (byte* destPtr = dest)
491+
{
492+
var stream = new ZLib.z_stream_s
493+
{
494+
next_in = sourcePtr,
495+
avail_in = (uint)sourceLen,
496+
next_out = destPtr,
497+
avail_out = (uint)destLen,
498+
};
499+
500+
// make second parameter negative to disable checksum verification
501+
int err = ZLib.inflateInit2_(stream, -MAX_WBITS, ZLib.zlibVersion(), source.Length);
502+
if (err != zlibConst.Z_OK)
503+
return err;
504+
505+
err = ZLib.inflate(stream, 1);
506+
if (err != zlibConst.Z_STREAM_END)
507+
{
508+
ZLib.inflateEnd(stream);
509+
return err;
510+
}
511+
512+
destLen = stream.total_out;
513+
sourceLen = stream.total_in;
514+
return ZLib.inflateEnd(stream);
515+
}
516+
}
517+
518+
/// <summary>
519+
/// Uncompress a source byte array to a destination (old version)
520+
/// </summary>
521+
public unsafe static int UncompressOld(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
522+
{
523+
fixed (byte* sourcePtr = source)
524+
fixed (byte* destPtr = dest)
525+
{
526+
var stream = new ZLib.z_stream_s
527+
{
528+
next_in = sourcePtr,
529+
avail_in = (uint)sourceLen,
530+
next_out = destPtr,
531+
avail_out = (uint)destLen,
532+
};
533+
534+
destLen = 0;
535+
sourceLen = 0;
536+
537+
// make second parameter negative to disable checksum verification
538+
int err = ZLib.inflateInit2_(stream, -MAX_WBITS, ZLib.zlibVersion(), source.Length);
539+
if (err != zlibConst.Z_OK)
540+
return err;
541+
542+
while (stream.avail_in > 1)
543+
{
544+
err = ZLib.inflate(stream, 1);
545+
if (err != zlibConst.Z_OK)
546+
{
547+
ZLib.inflateEnd(stream);
548+
return err;
549+
}
550+
}
551+
552+
destLen = stream.total_out;
553+
sourceLen = stream.total_in;
554+
return ZLib.inflateEnd(stream);
555+
}
556+
}
557+
558+
#endregion
358559
}
359560
}

0 commit comments

Comments
 (0)