mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
260 lines
11 KiB
C#
260 lines
11 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Diagnostics;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.IO.Compression;
|
|||
|
|
using Compress.SevenZip.Compress.BZip2;
|
|||
|
|
using Compress.SevenZip.Compress.LZMA;
|
|||
|
|
using Compress.SevenZip.Compress.PPmd;
|
|||
|
|
using Compress.SevenZip.Filters;
|
|||
|
|
using Compress.SevenZip.Structure;
|
|||
|
|
using Zstandard.Net;
|
|||
|
|
using FileStream = RVIO.FileStream;
|
|||
|
|
|
|||
|
|
namespace Compress.SevenZip
|
|||
|
|
{
|
|||
|
|
public partial class SevenZ
|
|||
|
|
{
|
|||
|
|
private int _streamIndex = -1;
|
|||
|
|
private Stream _stream;
|
|||
|
|
|
|||
|
|
public ZipReturn ZipFileOpenReadStream(int index, out Stream stream, out ulong unCompressedSize)
|
|||
|
|
{
|
|||
|
|
Debug.WriteLine("Opening File " + _localFiles[index].FileName);
|
|||
|
|
stream = null;
|
|||
|
|
unCompressedSize = 0;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (ZipOpen != ZipOpenType.OpenRead)
|
|||
|
|
{
|
|||
|
|
return ZipReturn.ZipErrorGettingDataStream;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (IsDirectory(index))
|
|||
|
|
{
|
|||
|
|
return ZipReturn.ZipTryingToAccessADirectory;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
unCompressedSize = _localFiles[index].UncompressedSize;
|
|||
|
|
int thisStreamIndex = _localFiles[index].StreamIndex;
|
|||
|
|
ulong streamOffset = _localFiles[index].StreamOffset;
|
|||
|
|
|
|||
|
|
if ((thisStreamIndex == _streamIndex) && (streamOffset >= (ulong)_stream.Position))
|
|||
|
|
{
|
|||
|
|
stream = _stream;
|
|||
|
|
stream.Seek((long)_localFiles[index].StreamOffset - _stream.Position, SeekOrigin.Current);
|
|||
|
|
return ZipReturn.ZipGood;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ZipFileCloseReadStream();
|
|||
|
|
_streamIndex = thisStreamIndex;
|
|||
|
|
|
|||
|
|
|
|||
|
|
Folder folder = _header.StreamsInfo.Folders[_streamIndex];
|
|||
|
|
|
|||
|
|
// first make the List of Decompressors streams
|
|||
|
|
int codersNeeded = folder.Coders.Length;
|
|||
|
|
|
|||
|
|
List<InStreamSourceInfo> allInputStreams = new List<InStreamSourceInfo>();
|
|||
|
|
for (int i = 0; i < codersNeeded; i++)
|
|||
|
|
{
|
|||
|
|
folder.Coders[i].DecoderStream = null;
|
|||
|
|
allInputStreams.AddRange(folder.Coders[i].InputStreamsSourceInfo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// now use the binding pairs to links the outputs to the inputs
|
|||
|
|
int bindPairsCount = folder.BindPairs.Length;
|
|||
|
|
for (int i = 0; i < bindPairsCount; i++)
|
|||
|
|
{
|
|||
|
|
allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamSource = InStreamSource.CompStreamOutput;
|
|||
|
|
allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamIndex = folder.BindPairs[i].OutIndex;
|
|||
|
|
folder.Coders[(int)folder.BindPairs[i].OutIndex].OutputUsedInternally = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// next use the stream indises to connect the remaining input streams from the sourcefile
|
|||
|
|
int packedStreamsCount = folder.PackedStreamIndices.Length;
|
|||
|
|
for (int i = 0; i < packedStreamsCount; i++)
|
|||
|
|
{
|
|||
|
|
ulong packedStreamIndex = (ulong)i + folder.PackedStreamIndexBase;
|
|||
|
|
|
|||
|
|
// create and open the source file stream if needed
|
|||
|
|
if (_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream == null)
|
|||
|
|
{
|
|||
|
|
_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream = CloneStream(_zipFs);
|
|||
|
|
}
|
|||
|
|
_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream.Seek(
|
|||
|
|
_baseOffset + (long)_header.StreamsInfo.PackedStreams[packedStreamIndex].StreamPosition, SeekOrigin.Begin);
|
|||
|
|
|
|||
|
|
|
|||
|
|
allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamSource = InStreamSource.FileStream;
|
|||
|
|
allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamIndex = packedStreamIndex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Stream> inputCoders = new List<Stream>();
|
|||
|
|
|
|||
|
|
bool allCodersComplete = false;
|
|||
|
|
while (!allCodersComplete)
|
|||
|
|
{
|
|||
|
|
allCodersComplete = true;
|
|||
|
|
for (int i = 0; i < codersNeeded; i++)
|
|||
|
|
{
|
|||
|
|
Coder coder = folder.Coders[i];
|
|||
|
|
|
|||
|
|
// check is decoder already processed
|
|||
|
|
if (coder.DecoderStream != null)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inputCoders.Clear();
|
|||
|
|
for (int j = 0; j < (int)coder.NumInStreams; j++)
|
|||
|
|
{
|
|||
|
|
if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.FileStream)
|
|||
|
|
{
|
|||
|
|
inputCoders.Add(_header.StreamsInfo.PackedStreams[coder.InputStreamsSourceInfo[j].InStreamIndex].PackedStream);
|
|||
|
|
}
|
|||
|
|
else if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.CompStreamOutput)
|
|||
|
|
{
|
|||
|
|
if (folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream == null)
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
inputCoders.Add(folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// unknown input type so error
|
|||
|
|
return ZipReturn.ZipDecodeError;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (inputCoders.Count == (int)coder.NumInStreams)
|
|||
|
|
{
|
|||
|
|
// all inputs streams are available to make the decoder stream
|
|||
|
|
switch (coder.DecoderType)
|
|||
|
|
{
|
|||
|
|
case DecompressType.Stored:
|
|||
|
|
coder.DecoderStream = inputCoders[0];
|
|||
|
|
break;
|
|||
|
|
case DecompressType.Delta:
|
|||
|
|
coder.DecoderStream = new Delta(folder.Coders[i].Properties, inputCoders[0]);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.LZMA:
|
|||
|
|
coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.LZMA2:
|
|||
|
|
coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.PPMd:
|
|||
|
|
coder.DecoderStream = new PpmdStream(new PpmdProperties(folder.Coders[i].Properties), inputCoders[0], false);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.BZip2:
|
|||
|
|
coder.DecoderStream = new CBZip2InputStream(inputCoders[0], false);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.BCJ:
|
|||
|
|
coder.DecoderStream = new BCJFilter(false, inputCoders[0]);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.BCJ2:
|
|||
|
|
coder.DecoderStream = new BCJ2Filter(inputCoders[0], inputCoders[1], inputCoders[2], inputCoders[3]);
|
|||
|
|
break;
|
|||
|
|
case DecompressType.ZSTD:
|
|||
|
|
coder.DecoderStream = new ZstandardStream(inputCoders[0], CompressionMode.Decompress, true);
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
return ZipReturn.ZipDecodeError;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// if skipped a coder need to loop round again
|
|||
|
|
if (coder.DecoderStream == null)
|
|||
|
|
{
|
|||
|
|
allCodersComplete = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// find the final output stream and return it.
|
|||
|
|
int outputStream = -1;
|
|||
|
|
for (int i = 0; i < codersNeeded; i++)
|
|||
|
|
{
|
|||
|
|
Coder coder = folder.Coders[i];
|
|||
|
|
if (!coder.OutputUsedInternally)
|
|||
|
|
{
|
|||
|
|
outputStream = i;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stream = folder.Coders[outputStream].DecoderStream;
|
|||
|
|
stream.Seek((long)_localFiles[index].StreamOffset, SeekOrigin.Current);
|
|||
|
|
|
|||
|
|
_stream = stream;
|
|||
|
|
|
|||
|
|
return ZipReturn.ZipGood;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
return ZipReturn.ZipErrorGettingDataStream;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Stream CloneStream(Stream s)
|
|||
|
|
{
|
|||
|
|
switch (s)
|
|||
|
|
{
|
|||
|
|
case System.IO.FileStream _:
|
|||
|
|
int errorCode = FileStream.OpenFileRead(ZipFilename, out Stream streamOut);
|
|||
|
|
return errorCode != 0 ? null : streamOut;
|
|||
|
|
|
|||
|
|
case MemoryStream memStream:
|
|||
|
|
long pos = memStream.Position;
|
|||
|
|
memStream.Position = 0;
|
|||
|
|
byte[] newStream = new byte[memStream.Length];
|
|||
|
|
memStream.Read(newStream, 0, (int)memStream.Length);
|
|||
|
|
MemoryStream ret = new MemoryStream(newStream, false);
|
|||
|
|
memStream.Position = pos;
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public ZipReturn ZipFileCloseReadStream()
|
|||
|
|
{
|
|||
|
|
if (_streamIndex != -1)
|
|||
|
|
{
|
|||
|
|
Folder folder = _header.StreamsInfo.Folders[_streamIndex];
|
|||
|
|
|
|||
|
|
foreach (Coder c in folder.Coders)
|
|||
|
|
{
|
|||
|
|
Stream ds = c?.DecoderStream;
|
|||
|
|
if (ds == null)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
ds.Close();
|
|||
|
|
ds.Dispose();
|
|||
|
|
c.DecoderStream = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_streamIndex = -1;
|
|||
|
|
|
|||
|
|
if (_header?.StreamsInfo != null)
|
|||
|
|
{
|
|||
|
|
foreach (PackedStreamInfo psi in _header.StreamsInfo.PackedStreams)
|
|||
|
|
{
|
|||
|
|
if (psi?.PackedStream == null)
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
psi.PackedStream.Close();
|
|||
|
|
psi.PackedStream.Dispose();
|
|||
|
|
psi.PackedStream = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ZipReturn.ZipGood;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|