2022-04-30 19:35:40 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using SharpCompress.Readers;
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
namespace SharpCompress.IO;
|
|
|
|
|
|
|
|
|
|
public class SourceStream : Stream
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
private long _prevSize;
|
2023-03-21 13:41:36 +00:00
|
|
|
private readonly List<FileInfo> _files;
|
|
|
|
|
private readonly List<Stream> _streams;
|
2024-04-18 13:24:03 +01:00
|
|
|
private readonly Func<int, FileInfo?>? _getFilePart;
|
|
|
|
|
private readonly Func<int, Stream?>? _getStreamPart;
|
2022-12-20 15:06:44 +00:00
|
|
|
private int _stream;
|
|
|
|
|
|
|
|
|
|
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options)
|
|
|
|
|
: this(null, null, file, getPart, options) { }
|
|
|
|
|
|
|
|
|
|
public SourceStream(Stream stream, Func<int, Stream?> getPart, ReaderOptions options)
|
|
|
|
|
: this(stream, getPart, null, null, options) { }
|
|
|
|
|
|
|
|
|
|
private SourceStream(
|
|
|
|
|
Stream? stream,
|
|
|
|
|
Func<int, Stream?>? getStreamPart,
|
|
|
|
|
FileInfo? file,
|
|
|
|
|
Func<int, FileInfo?>? getFilePart,
|
|
|
|
|
ReaderOptions options
|
|
|
|
|
)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
ReaderOptions = options;
|
|
|
|
|
_files = new List<FileInfo>();
|
|
|
|
|
_streams = new List<Stream>();
|
|
|
|
|
IsFileMode = file != null;
|
|
|
|
|
IsVolumes = false;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
if (!IsFileMode)
|
|
|
|
|
{
|
|
|
|
|
_streams.Add(stream!);
|
2024-04-18 13:24:03 +01:00
|
|
|
_getStreamPart = getStreamPart;
|
|
|
|
|
_getFilePart = _ => null;
|
2023-03-21 13:41:36 +00:00
|
|
|
if (stream is FileStream fileStream)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2023-03-21 13:41:36 +00:00
|
|
|
_files.Add(new FileInfo(fileStream.Name));
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
else
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
_files.Add(file!);
|
|
|
|
|
_streams.Add(_files[0].OpenRead());
|
2024-04-18 13:24:03 +01:00
|
|
|
_getFilePart = getFilePart;
|
|
|
|
|
_getStreamPart = _ => null;
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
_stream = 0;
|
|
|
|
|
_prevSize = 0;
|
|
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public void LoadAllParts()
|
|
|
|
|
{
|
2022-12-20 15:14:22 +00:00
|
|
|
for (var i = 1; SetStream(i); i++) { }
|
2022-12-20 15:06:44 +00:00
|
|
|
SetStream(0);
|
|
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public bool IsVolumes { get; set; }
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public ReaderOptions ReaderOptions { get; }
|
|
|
|
|
public bool IsFileMode { get; }
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public IEnumerable<FileInfo> Files => _files;
|
|
|
|
|
public IEnumerable<Stream> Streams => _streams;
|
2022-12-20 13:45:47 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
private Stream Current => _streams[_stream];
|
|
|
|
|
|
|
|
|
|
public bool LoadStream(int index) //ensure all parts to id are loaded
|
|
|
|
|
{
|
|
|
|
|
while (_streams.Count <= index)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
if (IsFileMode)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2024-04-18 13:24:03 +01:00
|
|
|
var f = _getFilePart.NotNull("GetFilePart is null")(_streams.Count);
|
2022-12-20 15:06:44 +00:00
|
|
|
if (f == null)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
_stream = _streams.Count - 1;
|
|
|
|
|
return false;
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
//throw new Exception($"File part {idx} not available.");
|
|
|
|
|
_files.Add(f);
|
|
|
|
|
_streams.Add(_files.Last().OpenRead());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-04-18 13:24:03 +01:00
|
|
|
var s = _getStreamPart.NotNull("GetStreamPart is null")(_streams.Count);
|
2022-12-20 15:06:44 +00:00
|
|
|
if (s == null)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
_stream = _streams.Count - 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//throw new Exception($"Stream part {idx} not available.");
|
|
|
|
|
_streams.Add(s);
|
2023-03-21 13:41:36 +00:00
|
|
|
if (s is FileStream stream)
|
2022-12-20 15:06:44 +00:00
|
|
|
{
|
2023-03-21 13:41:36 +00:00
|
|
|
_files.Add(new FileInfo(stream.Name));
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2022-12-20 13:45:47 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public bool SetStream(int idx) //allow caller to switch part in multipart
|
|
|
|
|
{
|
|
|
|
|
if (LoadStream(idx))
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
_stream = idx;
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
return _stream == idx;
|
|
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public override bool CanRead => true;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public override bool CanSeek => true;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public override bool CanWrite => false;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2023-03-21 13:41:36 +00:00
|
|
|
public override long Length => !IsVolumes ? _streams.Sum(a => a.Length) : Current.Length;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public override long Position
|
|
|
|
|
{
|
2023-03-21 13:41:36 +00:00
|
|
|
get => _prevSize + Current.Position; //_prevSize is 0 for multi-volume
|
2022-12-20 15:06:44 +00:00
|
|
|
set => Seek(value, SeekOrigin.Begin);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:20:49 +00:00
|
|
|
public override void Flush() => Current.Flush();
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
|
|
|
{
|
|
|
|
|
if (count <= 0)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
var total = count;
|
|
|
|
|
var r = -1;
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
while (count != 0 && r != 0)
|
|
|
|
|
{
|
|
|
|
|
r = Current.Read(
|
|
|
|
|
buffer,
|
|
|
|
|
offset,
|
|
|
|
|
(int)Math.Min(count, Current.Length - Current.Position)
|
|
|
|
|
);
|
|
|
|
|
count -= r;
|
|
|
|
|
offset += r;
|
|
|
|
|
|
|
|
|
|
if (!IsVolumes && count != 0 && Current.Position == Current.Length)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
var length = Current.Length;
|
|
|
|
|
|
|
|
|
|
// Load next file if present
|
|
|
|
|
if (!SetStream(_stream + 1))
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
break;
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
// Current stream switched
|
|
|
|
|
// Add length of previous stream
|
|
|
|
|
_prevSize += length;
|
|
|
|
|
Current.Seek(0, SeekOrigin.Begin);
|
2023-03-21 13:14:08 +00:00
|
|
|
r = -1; //BugFix: reset to allow loop if count is still not 0 - was breaking split zipx (lzma xz etc)
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
return total - count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
|
|
|
{
|
|
|
|
|
var pos = Position;
|
|
|
|
|
switch (origin)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
case SeekOrigin.Begin:
|
|
|
|
|
pos = offset;
|
|
|
|
|
break;
|
|
|
|
|
case SeekOrigin.Current:
|
|
|
|
|
pos += offset;
|
|
|
|
|
break;
|
|
|
|
|
case SeekOrigin.End:
|
|
|
|
|
pos = Length + offset;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
_prevSize = 0;
|
|
|
|
|
if (!IsVolumes)
|
|
|
|
|
{
|
|
|
|
|
SetStream(0);
|
|
|
|
|
while (_prevSize + Current.Length < pos)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
_prevSize += Current.Length;
|
|
|
|
|
SetStream(_stream + 1);
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
if (pos != _prevSize + Current.Position)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
Current.Seek(pos - _prevSize, SeekOrigin.Begin);
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:20:49 +00:00
|
|
|
public override void SetLength(long value) => throw new NotImplementedException();
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:20:49 +00:00
|
|
|
public override void Write(byte[] buffer, int offset, int count) =>
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
|
|
|
|
public override void Close()
|
|
|
|
|
{
|
|
|
|
|
if (IsFileMode || !ReaderOptions.LeaveStreamOpen) //close if file mode or options specify it
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
foreach (var stream in _streams)
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
try
|
2022-04-30 19:35:40 +01:00
|
|
|
{
|
2023-03-21 13:41:36 +00:00
|
|
|
stream.Dispose();
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// ignored
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
_streams.Clear();
|
|
|
|
|
_files.Clear();
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
2022-04-30 19:35:40 +01:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
protected override void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
Close();
|
|
|
|
|
base.Dispose(disposing);
|
2022-04-30 19:35:40 +01:00
|
|
|
}
|
|
|
|
|
}
|