mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-09 02:16:55 +00:00
Remove Extractor, finally
This commit is contained in:
@@ -1,232 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Compression.zlib;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using Header = SabreTools.Serialization.Wrappers.InstallShieldCabinet;
|
||||
|
||||
namespace UnshieldSharpInternal
|
||||
{
|
||||
// TODO: Figure out if individual parts of a split cab can be extracted separately
|
||||
internal class Extractor
|
||||
{
|
||||
/// <summary>
|
||||
/// Linked CAB headers
|
||||
/// </summary>
|
||||
public Header HeaderList { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default buffer size
|
||||
/// </summary>
|
||||
private const int BUFFER_SIZE = 64 * 1024;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Extractor(Header headerList)
|
||||
{
|
||||
HeaderList = headerList;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
|
||||
/// <summary>
|
||||
/// Save the file at the given index to the filename specified
|
||||
/// </summary>
|
||||
public bool FileSave(int index, string filename, bool useOld = false)
|
||||
{
|
||||
// Get the file descriptor
|
||||
if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
|
||||
return false;
|
||||
|
||||
// If the file is split
|
||||
if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
|
||||
return FileSave((int)fileDescriptor.LinkPrevious, filename, useOld);
|
||||
|
||||
// Get the reader at the index
|
||||
var reader = Reader.Create(this, index, fileDescriptor);
|
||||
if (reader == null)
|
||||
return false;
|
||||
|
||||
// Create the output file and hasher
|
||||
FileStream output = File.OpenWrite(filename);
|
||||
var md5 = new HashWrapper(HashType.MD5);
|
||||
|
||||
ulong bytesLeft = Header.GetReadableBytes(fileDescriptor);
|
||||
byte[] inputBuffer;
|
||||
byte[] outputBuffer = new byte[BUFFER_SIZE];
|
||||
ulong totalWritten = 0;
|
||||
|
||||
// Read while there are bytes remaining
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
ulong bytesToWrite = BUFFER_SIZE;
|
||||
int result;
|
||||
|
||||
// Handle compressed files
|
||||
#if NET20 || NET35
|
||||
if ((fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
|
||||
#else
|
||||
if (fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
|
||||
#endif
|
||||
{
|
||||
// Attempt to read the length value
|
||||
byte[] lengthArr = new byte[sizeof(ushort)];
|
||||
if (!reader.Read(lengthArr, 0, lengthArr.Length))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({HeaderList.GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the number of bytes to read
|
||||
ushort bytesToRead = BitConverter.ToUInt16(lengthArr, 0);
|
||||
if (bytesToRead == 0)
|
||||
{
|
||||
Console.Error.WriteLine("bytesToRead can't be zero");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to read the specified number of bytes
|
||||
inputBuffer = new byte[BUFFER_SIZE + 1];
|
||||
if (!reader.Read(inputBuffer, 0, bytesToRead))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({HeaderList.GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a null byte to make inflate happy
|
||||
inputBuffer[bytesToRead] = 0;
|
||||
ulong readBytes = (ulong)(bytesToRead + 1);
|
||||
|
||||
// Uncompress into a buffer
|
||||
if (useOld)
|
||||
result = Header.UncompressOld(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
|
||||
else
|
||||
result = Header.Uncompress(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
|
||||
|
||||
// If we didn't get a positive result that's not a data error (false positives)
|
||||
if (result != zlibConst.Z_OK && result != zlibConst.Z_DATA_ERROR)
|
||||
{
|
||||
Console.Error.WriteLine($"Decompression failed with code {result.ToZlibConstName()}. bytes_to_read={bytesToRead}, volume={fileDescriptor.Volume}, read_bytes={readBytes}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= 2;
|
||||
bytesLeft -= bytesToRead;
|
||||
}
|
||||
|
||||
// Handle uncompressed files
|
||||
else
|
||||
{
|
||||
bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
|
||||
if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to write {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= (uint)bytesToWrite;
|
||||
}
|
||||
|
||||
// Hash and write the next block
|
||||
md5.Process(outputBuffer, 0, (int)bytesToWrite);
|
||||
output?.Write(outputBuffer, 0, (int)bytesToWrite);
|
||||
totalWritten += bytesToWrite;
|
||||
}
|
||||
|
||||
// Validate the number of bytes written
|
||||
if (fileDescriptor.ExpandedSize != totalWritten)
|
||||
{
|
||||
Console.Error.WriteLine($"Expanded size expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finalize output values
|
||||
md5.Terminate();
|
||||
reader?.Dispose();
|
||||
output?.Close();
|
||||
|
||||
// Failing the file has been disabled because for a subset of CABs the values don't seem to match
|
||||
// TODO: Investigate what is causing this to fail and what data needs to be hashed
|
||||
|
||||
// // Validate the data written, if required
|
||||
// if (HeaderList!.MajorVersion >= 6)
|
||||
// {
|
||||
// string? md5result = md5.CurrentHashString;
|
||||
// if (md5result == null || md5result != BitConverter.ToString(fileDescriptor.MD5!))
|
||||
// {
|
||||
// Console.Error.WriteLine($"MD5 checksum failure for file {index} ({HeaderList.GetFileName(index)})");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the file at the given index to the filename specified as raw
|
||||
/// </summary>
|
||||
public bool FileSaveRaw(int index, string filename)
|
||||
{
|
||||
// Get the file descriptor
|
||||
if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
|
||||
return false;
|
||||
|
||||
// If the file is split
|
||||
if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
|
||||
return FileSaveRaw((int)fileDescriptor.LinkPrevious, filename);
|
||||
|
||||
// Get the reader at the index
|
||||
var reader = Reader.Create(this, index, fileDescriptor);
|
||||
if (reader == null)
|
||||
return false;
|
||||
|
||||
// Create the output file
|
||||
FileStream output = File.OpenWrite(filename);
|
||||
|
||||
ulong bytesLeft = Header.GetReadableBytes(fileDescriptor);
|
||||
byte[] outputBuffer = new byte[BUFFER_SIZE];
|
||||
|
||||
// Read while there are bytes remaining
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
ulong bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
|
||||
if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= (uint)bytesToWrite;
|
||||
|
||||
// Write the next block
|
||||
output.Write(outputBuffer, 0, (int)bytesToWrite);
|
||||
}
|
||||
|
||||
// Finalize output values
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using Header = SabreTools.Serialization.Wrappers.InstallShieldCabinet;
|
||||
|
||||
namespace UnshieldSharpInternal
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace UnshieldSharpInternal
|
||||
/// <summary>
|
||||
/// Cabinet file to read from
|
||||
/// </summary>
|
||||
private readonly Extractor _extractor;
|
||||
private readonly Header _cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected index
|
||||
@@ -57,9 +58,9 @@ namespace UnshieldSharpInternal
|
||||
|
||||
#region Constructors
|
||||
|
||||
private Reader(Extractor cabinet, uint index, FileDescriptor fileDescriptor)
|
||||
private Reader(Header cabinet, uint index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
_extractor = cabinet;
|
||||
_cabinet = cabinet;
|
||||
_index = index;
|
||||
_fileDescriptor = fileDescriptor;
|
||||
}
|
||||
@@ -69,7 +70,7 @@ namespace UnshieldSharpInternal
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Reader"> from an existing cabinet, index, and file descriptor
|
||||
/// </summary>
|
||||
public static Reader? Create(Extractor cabinet, int index, FileDescriptor fileDescriptor)
|
||||
public static Reader? Create(Header cabinet, int index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
var reader = new Reader(cabinet, (uint)index, fileDescriptor);
|
||||
for (; ; )
|
||||
@@ -87,7 +88,7 @@ namespace UnshieldSharpInternal
|
||||
}
|
||||
|
||||
// Start with the correct volume for IS5 cabinets
|
||||
if (reader._extractor.HeaderList.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
|
||||
if (reader._cabinet.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
|
||||
{
|
||||
// Normalize the volume ID for odd cases
|
||||
if (fileDescriptor.Volume == ushort.MinValue || fileDescriptor.Volume == ushort.MaxValue)
|
||||
@@ -147,7 +148,7 @@ namespace UnshieldSharpInternal
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED))
|
||||
#endif
|
||||
SabreTools.Serialization.Wrappers.InstallShieldCabinet.Deobfuscate(buffer, size, ref _obfuscationOffset);
|
||||
Header.Deobfuscate(buffer, size, ref _obfuscationOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -167,7 +168,7 @@ namespace UnshieldSharpInternal
|
||||
private bool OpenVolume(ushort volume)
|
||||
{
|
||||
// Read the volume from the cabinet set
|
||||
var next = _extractor.HeaderList.OpenVolume(volume, out var volumeStream);
|
||||
var next = _cabinet.OpenVolume(volume, out var volumeStream);
|
||||
if (next?.VolumeHeader == null || volumeStream == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to open input cabinet file {volume}");
|
||||
@@ -180,9 +181,9 @@ namespace UnshieldSharpInternal
|
||||
_volumeHeader = next.VolumeHeader;
|
||||
|
||||
// Enable support for split archives for IS5
|
||||
if (_extractor.HeaderList.MajorVersion == 5)
|
||||
if (_cabinet.MajorVersion == 5)
|
||||
{
|
||||
if (_index < (_extractor.HeaderList.FileCount - 1)
|
||||
if (_index < (_cabinet.FileCount - 1)
|
||||
&& _index == _volumeHeader.LastFileIndex
|
||||
&& _volumeHeader.LastFileSizeCompressed != _fileDescriptor.CompressedSize)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Compression.zlib;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
using UnshieldSharpInternal;
|
||||
using static SabreTools.Models.InstallShieldCabinet.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
@@ -404,7 +406,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
try
|
||||
{
|
||||
var cabfile = new UnshieldSharpInternal.Extractor(cabinet);
|
||||
for (int i = 0; i < cabinet.FileCount; i++)
|
||||
{
|
||||
try
|
||||
@@ -426,7 +427,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
cabfile.FileSave(i, filename);
|
||||
cabinet.FileSave(i, filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -443,10 +444,206 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the file at the given index to the filename specified
|
||||
/// </summary>
|
||||
public bool FileSave(int index, string filename, bool useOld = false)
|
||||
{
|
||||
// Get the file descriptor
|
||||
if (!TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
|
||||
return false;
|
||||
|
||||
// If the file is split
|
||||
if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
|
||||
return FileSave((int)fileDescriptor.LinkPrevious, filename, useOld);
|
||||
|
||||
// Get the reader at the index
|
||||
var reader = Reader.Create(this, index, fileDescriptor);
|
||||
if (reader == null)
|
||||
return false;
|
||||
|
||||
// Create the output file and hasher
|
||||
FileStream output = File.OpenWrite(filename);
|
||||
var md5 = new HashWrapper(HashType.MD5);
|
||||
|
||||
ulong bytesLeft = GetReadableBytes(fileDescriptor);
|
||||
byte[] inputBuffer;
|
||||
byte[] outputBuffer = new byte[BUFFER_SIZE];
|
||||
ulong totalWritten = 0;
|
||||
|
||||
// Read while there are bytes remaining
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
ulong bytesToWrite = BUFFER_SIZE;
|
||||
int result;
|
||||
|
||||
// Handle compressed files
|
||||
#if NET20 || NET35
|
||||
if ((fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
|
||||
#else
|
||||
if (fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
|
||||
#endif
|
||||
{
|
||||
// Attempt to read the length value
|
||||
byte[] lengthArr = new byte[sizeof(ushort)];
|
||||
if (!reader.Read(lengthArr, 0, lengthArr.Length))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the number of bytes to read
|
||||
ushort bytesToRead = BitConverter.ToUInt16(lengthArr, 0);
|
||||
if (bytesToRead == 0)
|
||||
{
|
||||
Console.Error.WriteLine("bytesToRead can't be zero");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to read the specified number of bytes
|
||||
inputBuffer = new byte[BUFFER_SIZE + 1];
|
||||
if (!reader.Read(inputBuffer, 0, bytesToRead))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a null byte to make inflate happy
|
||||
inputBuffer[bytesToRead] = 0;
|
||||
ulong readBytes = (ulong)(bytesToRead + 1);
|
||||
|
||||
// Uncompress into a buffer
|
||||
if (useOld)
|
||||
result = UncompressOld(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
|
||||
else
|
||||
result = Uncompress(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
|
||||
|
||||
// If we didn't get a positive result that's not a data error (false positives)
|
||||
if (result != zlibConst.Z_OK && result != zlibConst.Z_DATA_ERROR)
|
||||
{
|
||||
Console.Error.WriteLine($"Decompression failed with code {result.ToZlibConstName()}. bytes_to_read={bytesToRead}, volume={fileDescriptor.Volume}, read_bytes={readBytes}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= 2;
|
||||
bytesLeft -= bytesToRead;
|
||||
}
|
||||
|
||||
// Handle uncompressed files
|
||||
else
|
||||
{
|
||||
bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
|
||||
if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to write {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= (uint)bytesToWrite;
|
||||
}
|
||||
|
||||
// Hash and write the next block
|
||||
md5.Process(outputBuffer, 0, (int)bytesToWrite);
|
||||
output?.Write(outputBuffer, 0, (int)bytesToWrite);
|
||||
totalWritten += bytesToWrite;
|
||||
}
|
||||
|
||||
// Validate the number of bytes written
|
||||
if (fileDescriptor.ExpandedSize != totalWritten)
|
||||
{
|
||||
Console.Error.WriteLine($"Expanded size expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finalize output values
|
||||
md5.Terminate();
|
||||
reader?.Dispose();
|
||||
output?.Close();
|
||||
|
||||
// Failing the file has been disabled because for a subset of CABs the values don't seem to match
|
||||
// TODO: Investigate what is causing this to fail and what data needs to be hashed
|
||||
|
||||
// // Validate the data written, if required
|
||||
// if (HeaderList!.MajorVersion >= 6)
|
||||
// {
|
||||
// string? md5result = md5.CurrentHashString;
|
||||
// if (md5result == null || md5result != BitConverter.ToString(fileDescriptor.MD5!))
|
||||
// {
|
||||
// Console.Error.WriteLine($"MD5 checksum failure for file {index} ({HeaderList.GetFileName(index)})");
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the file at the given index to the filename specified as raw
|
||||
/// </summary>
|
||||
public bool FileSaveRaw(int index, string filename)
|
||||
{
|
||||
// Get the file descriptor
|
||||
if (!TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
|
||||
return false;
|
||||
|
||||
// If the file is split
|
||||
if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
|
||||
return FileSaveRaw((int)fileDescriptor.LinkPrevious, filename);
|
||||
|
||||
// Get the reader at the index
|
||||
var reader = Reader.Create(this, index, fileDescriptor);
|
||||
if (reader == null)
|
||||
return false;
|
||||
|
||||
// Create the output file
|
||||
FileStream output = File.OpenWrite(filename);
|
||||
|
||||
ulong bytesLeft = GetReadableBytes(fileDescriptor);
|
||||
byte[] outputBuffer = new byte[BUFFER_SIZE];
|
||||
|
||||
// Read while there are bytes remaining
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
ulong bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
|
||||
if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to read {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set remaining bytes
|
||||
bytesLeft -= (uint)bytesToWrite;
|
||||
|
||||
// Write the next block
|
||||
output.Write(outputBuffer, 0, (int)bytesToWrite);
|
||||
}
|
||||
|
||||
// Finalize output values
|
||||
reader.Dispose();
|
||||
output?.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a source byte array to a destination
|
||||
/// </summary>
|
||||
public unsafe static int Uncompress(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
|
||||
private unsafe static int Uncompress(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
|
||||
{
|
||||
fixed (byte* sourcePtr = source)
|
||||
fixed (byte* destPtr = dest)
|
||||
@@ -480,7 +677,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// Uncompress a source byte array to a destination (old version)
|
||||
/// </summary>
|
||||
public unsafe static int UncompressOld(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
|
||||
private unsafe static int UncompressOld(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
|
||||
{
|
||||
fixed (byte* sourcePtr = source)
|
||||
fixed (byte* destPtr = dest)
|
||||
|
||||
Reference in New Issue
Block a user