Files
cuetools.net/UnRarDotNet/RarStream.cs
chudov c2fc51b6b9 1) Better log file/freedb entry selection dialog interface
2) 'Stop' button works when analyzing archive
3) application icon added
4) two verification modes: AR only/AR+CRCs
2009-02-22 07:47:56 +00:00

297 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
/* Author: Gregory S. Chudov
*
*/
namespace UnRarDotNet
{
public class RarStream : Stream
{
public RarStream(string path, string fileName)
{
_close = false;
_eof = false;
_rewind = false;
_unrar = new Unrar();
_buffer = null;
_offset = 0;
_length = 0;
_pos = 0;
_path = path;
_fileName = fileName;
_workThread = null;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get
{
Go();
lock (this)
{
while (_size == null && !_close)
Monitor.Wait(this);
}
if (_close)
throw new IOException("Decompression failed", _ex);
return _size.Value;
}
}
public override long Position
{
get { return _seek_to == null ? _pos : _seek_to.Value; }
set { Seek(value, SeekOrigin.Begin); }
}
public override void Close()
{
lock (this)
{
_close = true;
Monitor.Pulse(this);
}
if (_workThread != null)
{
_workThread.Join();
_workThread = null;
}
if (_unrar != null)
{
_unrar.Close();
_unrar = null;
}
base.Close();
}
public override void Flush()
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] array, int offset, int count)
{
int total = 0;
Go();
while (count > 0)
{
lock (this)
{
while (_buffer == null && !_eof && !_close)
Monitor.Wait(this);
if (_close)
throw _ex ?? new IOException("Decompression failed");
if (_buffer == null)
return total;
if (_seek_to != null)
{
if (_seek_to.Value < _pos)
throw new NotSupportedException();
if (_length <= _seek_to.Value - _pos)
{
_pos += _length;
_buffer = null;
Monitor.Pulse(this);
continue;
}
_offset += (int)(_seek_to.Value - _pos);
_length -= (int)(_seek_to.Value - _pos);
_pos = _seek_to.Value;
_seek_to = null;
}
if (_length > count)
{
Array.Copy(_buffer, _offset, array, offset, count);
total += count;
_pos += count;
_offset += count;
_length -= count;
return total;
}
Array.Copy(_buffer, _offset, array, offset, _length);
total += _length;
_pos += _length;
offset += _length;
count -= _length;
_buffer = null;
Monitor.Pulse(this);
}
}
return total;
}
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
_seek_to = offset;
break;
case SeekOrigin.Current:
_seek_to = Position + offset;
break;
case SeekOrigin.End:
_seek_to = Length + offset;
break;
}
if (_seek_to.Value > Length)
{
_seek_to = null;
throw new IOException("Invalid seek");
}
if (_seek_to.Value == _pos)
{
_seek_to = null;
return _pos;
}
if (_seek_to.Value < _pos)
{
lock (this)
{
_pos = 0;
_rewind = true;
_buffer = null;
Monitor.Pulse(this);
}
}
return _seek_to.Value;
}
public override void Write(byte[] array, int offset, int count)
{
throw new NotSupportedException();
}
public event PasswordRequiredHandler PasswordRequired;
public event ExtractionProgressHandler ExtractionProgress;
private Unrar _unrar;
private string _fileName;
private Thread _workThread;
private bool _close, _rewind, _eof;
private byte[] _buffer;
private Exception _ex;
int _offset, _length;
long? _size;
long? _seek_to;
long _pos;
string _path;
private void Go()
{
if (_workThread != null) return;
_workThread = new Thread(Decompress);
_workThread.Priority = ThreadPriority.BelowNormal;
_workThread.IsBackground = true;
_workThread.Start(null);
}
private void unrar_DataAvailable(object sender, DataAvailableEventArgs e)
{
lock (this)
{
while (_buffer != null && !_close)
Monitor.Wait(this);
if (_close)
{
e.ContinueOperation = false;
Monitor.Pulse(this);
return;
}
if (_rewind)
{
e.ContinueOperation = false;
Monitor.Pulse(this);
return;
}
_buffer = e.Data;
_length = _buffer.Length;
_offset = 0;
e.ContinueOperation = true;
Monitor.Pulse(this);
}
}
private void Decompress(object o)
{
_unrar.DataAvailable += new DataAvailableHandler(unrar_DataAvailable);
_unrar.PasswordRequired += PasswordRequired;
_unrar.ExtractionProgress += ExtractionProgress;
#if !DEBUG
try
#endif
{
do
{
bool foundFile = false;
_unrar.Open(_path, Unrar.OpenMode.Extract);
while (_unrar.ReadHeader())
{
if (_unrar.CurrentFile.FileName == _fileName)
{
lock (this)
{
if (_size == null)
{
_size = _unrar.CurrentFile.UnpackedSize;
Monitor.Pulse(this);
}
}
_unrar.Test();
foundFile = true;
break;
}
else
_unrar.Skip();
}
_unrar.Close();
lock (this)
{
if (!foundFile)
{
_ex = new FileNotFoundException();
break;
}
else
{
_eof = true;
Monitor.Pulse(this);
while (!_rewind && !_close)
Monitor.Wait(this);
if (_close)
break;
_rewind = false;
_eof = false;
}
}
} while (true);
}
#if !DEBUG
catch (Exception ex)
{
_ex = ex;
}
#endif
lock (this)
{
_close = true;
Monitor.Pulse(this);
}
}
}
}