mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
can now verify/decode files directly from rar archive
This commit is contained in:
@@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioCodecsDotNet", "..\Aud
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ALACDotNet", "..\ALACDotNet\ALACDotNet.csproj", "{F2EC7193-D5E5-4252-9803-5CEB407E910F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ALACDotNet", "..\ALACDotNet\ALACDotNet.csproj", "{F2EC7193-D5E5-4252-9803-5CEB407E910F}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnRarDotNet", "..\UnRarDotNet\UnRarDotNet.csproj", "{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -200,6 +202,16 @@ Global
|
|||||||
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x64.Build.0 = Release|x64
|
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x64.Build.0 = Release|x64
|
||||||
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x86.ActiveCfg = Release|x86
|
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x86.ActiveCfg = Release|x86
|
||||||
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x86.Build.0 = Release|x86
|
{F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|x86.Build.0 = Release|x86
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Release|x64.Build.0 = Release|x64
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ namespace CUEToolsLib {
|
|||||||
throw new Exception("Unsupported audio type.");
|
throw new Exception("Unsupported audio type.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static IAudioSource GetAudioSource(string path, Stream IO)
|
||||||
|
{
|
||||||
|
switch (Path.GetExtension(path).ToLower())
|
||||||
|
{
|
||||||
|
#if !MONO
|
||||||
|
case ".flac":
|
||||||
|
return new FLACReader(path, IO);
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
throw new Exception("Unsupported audio type in archive.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static IAudioDest GetAudioDest(string path, int bitsPerSample, int channelCount, int sampleRate, long finalSampleCount) {
|
public static IAudioDest GetAudioDest(string path, int bitsPerSample, int channelCount, int sampleRate, long finalSampleCount) {
|
||||||
IAudioDest dest;
|
IAudioDest dest;
|
||||||
@@ -59,7 +71,14 @@ namespace CUEToolsLib {
|
|||||||
uint _bufferOffset, _bufferLength;
|
uint _bufferOffset, _bufferLength;
|
||||||
|
|
||||||
public FLACReader(string path) {
|
public FLACReader(string path) {
|
||||||
_flacReader = new FLACDotNet.FLACReader(path);
|
_flacReader = new FLACDotNet.FLACReader(path, null);
|
||||||
|
_bufferOffset = 0;
|
||||||
|
_bufferLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FLACReader(string path, Stream IO)
|
||||||
|
{
|
||||||
|
_flacReader = new FLACDotNet.FLACReader(path, IO);
|
||||||
_bufferOffset = 0;
|
_bufferOffset = 0;
|
||||||
_bufferLength = 0;
|
_bufferLength = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,10 @@
|
|||||||
<Project>{32338A04-5B6B-4C63-8EE7-C6400F73B5D7}</Project>
|
<Project>{32338A04-5B6B-4C63-8EE7-C6400F73B5D7}</Project>
|
||||||
<Name>HDCDDotNet</Name>
|
<Name>HDCDDotNet</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\UnRarDotNet\UnRarDotNet.csproj">
|
||||||
|
<Project>{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}</Project>
|
||||||
|
<Name>UnRarDotNet</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\WavPackDotNet\WavPackDotNet.vcproj">
|
<ProjectReference Include="..\WavPackDotNet\WavPackDotNet.vcproj">
|
||||||
<Project>{CC2E74B6-534A-43D8-9F16-AC03FE955000}</Project>
|
<Project>{CC2E74B6-534A-43D8-9F16-AC03FE955000}</Project>
|
||||||
<Name>WavPackDotNet</Name>
|
<Name>WavPackDotNet</Name>
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ using System.Net;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using AudioCodecsDotNet;
|
using AudioCodecsDotNet;
|
||||||
using HDCDDotNet;
|
using HDCDDotNet;
|
||||||
|
#if !MONO
|
||||||
|
using UnRarDotNet;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace CUEToolsLib
|
namespace CUEToolsLib
|
||||||
{
|
{
|
||||||
@@ -415,6 +418,8 @@ namespace CUEToolsLib
|
|||||||
private HDCDDotNet.HDCDDotNet hdcdDecoder;
|
private HDCDDotNet.HDCDDotNet hdcdDecoder;
|
||||||
CUEConfig _config;
|
CUEConfig _config;
|
||||||
string _cddbDiscIdTag;
|
string _cddbDiscIdTag;
|
||||||
|
private bool _isArchive;
|
||||||
|
private string _archivePath;
|
||||||
|
|
||||||
public CUESheet(string pathIn, CUEConfig config)
|
public CUESheet(string pathIn, CUEConfig config)
|
||||||
{
|
{
|
||||||
@@ -466,6 +471,7 @@ namespace CUEToolsLib
|
|||||||
seenDataTrack = false;
|
seenDataTrack = false;
|
||||||
accDisks = new List<AccDisk>();
|
accDisks = new List<AccDisk>();
|
||||||
_hasEmbeddedCUESheet = false;
|
_hasEmbeddedCUESheet = false;
|
||||||
|
_isArchive = false;
|
||||||
|
|
||||||
TextReader sr;
|
TextReader sr;
|
||||||
|
|
||||||
@@ -480,8 +486,56 @@ namespace CUEToolsLib
|
|||||||
if (cueSheet == null)
|
if (cueSheet == null)
|
||||||
throw new Exception("Input directory doesn't contain supported audio files.");
|
throw new Exception("Input directory doesn't contain supported audio files.");
|
||||||
sr = new StringReader(cueSheet);
|
sr = new StringReader(cueSheet);
|
||||||
|
}
|
||||||
|
#if !MONO
|
||||||
|
else if (Path.GetExtension(pathIn).ToLower() == ".rar")
|
||||||
|
{
|
||||||
|
Unrar _unrar = new Unrar();
|
||||||
|
string cueName = null, cueText = null;
|
||||||
|
_unrar.Open(pathIn, Unrar.OpenMode.List);
|
||||||
|
while (_unrar.ReadHeader())
|
||||||
|
{
|
||||||
|
if (!_unrar.CurrentFile.IsDirectory && Path.GetExtension(_unrar.CurrentFile.FileName).ToLower() == ".cue")
|
||||||
|
{
|
||||||
|
cueName = _unrar.CurrentFile.FileName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_unrar.Skip();
|
||||||
|
}
|
||||||
|
_unrar.Close();
|
||||||
|
if (cueName != null)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RarStream rarStream = new RarStream(pathIn, cueName);
|
||||||
|
StreamReader cueReader = new StreamReader(rarStream, CUESheet.Encoding);
|
||||||
|
cueText = cueReader.ReadToEnd();
|
||||||
|
cueReader.Close();
|
||||||
|
rarStream.Close();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
if (cueText == null)
|
||||||
|
throw new Exception("Input archive doesn't contain a cue sheet.");
|
||||||
|
sr = new StringReader(cueText);
|
||||||
|
_isArchive = true;
|
||||||
|
_archivePath = pathIn;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (Path.GetExtension(pathIn).ToLower() == ".cue")
|
||||||
|
{
|
||||||
|
if (_config.autoCorrectFilenames)
|
||||||
|
sr = new StringReader (CorrectAudioFilenames(pathIn, false));
|
||||||
|
else
|
||||||
|
sr = new StreamReader (pathIn, CUESheet.Encoding);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StreamReader logReader = new StreamReader(Path.ChangeExtension(pathIn, ".log"), CUESheet.Encoding);
|
||||||
|
_eacLog = logReader.ReadToEnd();
|
||||||
|
logReader.Close();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
} else
|
} else
|
||||||
if (Path.GetExtension(pathIn).ToLower() != ".cue")
|
|
||||||
{
|
{
|
||||||
IAudioSource audioSource;
|
IAudioSource audioSource;
|
||||||
NameValueCollection tags;
|
NameValueCollection tags;
|
||||||
@@ -500,20 +554,6 @@ namespace CUEToolsLib
|
|||||||
sr = new StringReader (cuesheetTag);
|
sr = new StringReader (cuesheetTag);
|
||||||
pathAudio = pathIn;
|
pathAudio = pathIn;
|
||||||
_hasEmbeddedCUESheet = true;
|
_hasEmbeddedCUESheet = true;
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (_config.autoCorrectFilenames)
|
|
||||||
sr = new StringReader (CorrectAudioFilenames(pathIn, false));
|
|
||||||
else
|
|
||||||
sr = new StreamReader (pathIn, CUESheet.Encoding);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StreamReader logReader = new StreamReader(Path.ChangeExtension(pathIn, ".log"), CUESheet.Encoding);
|
|
||||||
_eacLog = logReader.ReadToEnd();
|
|
||||||
logReader.Close();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using (sr) {
|
using (sr) {
|
||||||
@@ -533,10 +573,17 @@ namespace CUEToolsLib
|
|||||||
else {
|
else {
|
||||||
if (!_hasEmbeddedCUESheet)
|
if (!_hasEmbeddedCUESheet)
|
||||||
{
|
{
|
||||||
pathAudio = LocateFile(cueDir, line.Params[1]);
|
#if !MONO
|
||||||
if (pathAudio == null)
|
if (_isArchive)
|
||||||
|
pathAudio = line.Params[1];
|
||||||
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Unable to locate file \"" + line.Params[1] + "\".");
|
#endif
|
||||||
|
pathAudio = LocateFile(cueDir, line.Params[1]);
|
||||||
|
if (pathAudio == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to locate file \"" + line.Params[1] + "\".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
@@ -645,6 +692,18 @@ namespace CUEToolsLib
|
|||||||
{
|
{
|
||||||
_accurateRipId = line.Params[2];
|
_accurateRipId = line.Params[2];
|
||||||
}
|
}
|
||||||
|
//else if ((command == "REM") &&
|
||||||
|
// (line.Params.Count == 3) &&
|
||||||
|
// (line.Params[1].ToUpper() == "SHORTEN"))
|
||||||
|
//{
|
||||||
|
// fileTimeLengthFrames -= General.TimeFromString(line.Params[2]);
|
||||||
|
//}
|
||||||
|
//else if ((command == "REM") &&
|
||||||
|
// (line.Params.Count == 3) &&
|
||||||
|
// (line.Params[1].ToUpper() == "LENGTHEN"))
|
||||||
|
//{
|
||||||
|
// fileTimeLengthFrames += General.TimeFromString(line.Params[2]);
|
||||||
|
//}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (trackInfo != null)
|
if (trackInfo != null)
|
||||||
@@ -951,7 +1010,14 @@ namespace CUEToolsLib
|
|||||||
{
|
{
|
||||||
IAudioSource audioSource;
|
IAudioSource audioSource;
|
||||||
|
|
||||||
audioSource = AudioReadWrite.GetAudioSource(path);
|
#if !MONO
|
||||||
|
if (_isArchive)
|
||||||
|
{
|
||||||
|
RarStream IO = new RarStream(_archivePath, path);
|
||||||
|
audioSource = AudioReadWrite.GetAudioSource(path, IO);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
audioSource = AudioReadWrite.GetAudioSource(path);
|
||||||
|
|
||||||
if ((audioSource.BitsPerSample != 16) ||
|
if ((audioSource.BitsPerSample != 16) ||
|
||||||
(audioSource.ChannelCount != 2) ||
|
(audioSource.ChannelCount != 2) ||
|
||||||
@@ -1686,7 +1752,7 @@ namespace CUEToolsLib
|
|||||||
if (_accurateRip)
|
if (_accurateRip)
|
||||||
{
|
{
|
||||||
statusDel((string)"Generating AccurateRip report...", 0, 0, null, null);
|
statusDel((string)"Generating AccurateRip report...", 0, 0, null, null);
|
||||||
if (!_accurateOffset && _config.writeArTagsOnVerify && _writeOffset == 0)
|
if (!_accurateOffset && _config.writeArTagsOnVerify && _writeOffset == 0 && !_isArchive)
|
||||||
{
|
{
|
||||||
uint tracksMatch;
|
uint tracksMatch;
|
||||||
int bestOffset;
|
int bestOffset;
|
||||||
@@ -2141,7 +2207,15 @@ namespace CUEToolsLib
|
|||||||
return sw.ToString();
|
return sw.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string CorrectAudioFilenames(string path, bool always) {
|
public static string CorrectAudioFilenames(string path, bool always)
|
||||||
|
{
|
||||||
|
StreamReader sr = new StreamReader(path, CUESheet.Encoding);
|
||||||
|
string cue = sr.ReadToEnd();
|
||||||
|
sr.Close();
|
||||||
|
return CorrectAudioFilenames(Path.GetDirectoryName(path), cue, always);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CorrectAudioFilenames(string dir, string cue, bool always) {
|
||||||
string[] audioExts = new string[] { "*.wav", "*.flac", "*.wv", "*.ape", "*.m4a" };
|
string[] audioExts = new string[] { "*.wav", "*.flac", "*.wv", "*.ape", "*.m4a" };
|
||||||
List<string> lines = new List<string>();
|
List<string> lines = new List<string>();
|
||||||
List<int> filePos = new List<int>();
|
List<int> filePos = new List<int>();
|
||||||
@@ -2150,12 +2224,9 @@ namespace CUEToolsLib
|
|||||||
string[] audioFiles = null;
|
string[] audioFiles = null;
|
||||||
string lineStr;
|
string lineStr;
|
||||||
CUELine line;
|
CUELine line;
|
||||||
string dir;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dir = Path.GetDirectoryName(path);
|
using (StringReader sr = new StringReader(cue)) {
|
||||||
|
|
||||||
using (StreamReader sr = new StreamReader(path, CUESheet.Encoding)) {
|
|
||||||
while ((lineStr = sr.ReadLine()) != null) {
|
while ((lineStr = sr.ReadLine()) != null) {
|
||||||
lines.Add(lineStr);
|
lines.Add(lineStr);
|
||||||
line = new CUELine(lineStr);
|
line = new CUELine(lineStr);
|
||||||
@@ -2335,10 +2406,19 @@ namespace CUEToolsLib
|
|||||||
audioSource = new SilenceGenerator(sourceInfo.Offset + sourceInfo.Length);
|
audioSource = new SilenceGenerator(sourceInfo.Offset + sourceInfo.Length);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path);
|
#if !MONO
|
||||||
|
if (_isArchive)
|
||||||
|
{
|
||||||
|
RarStream IO = new RarStream(_archivePath, sourceInfo.Path);
|
||||||
|
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path, IO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
audioSource = AudioReadWrite.GetAudioSource(sourceInfo.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioSource.Position = sourceInfo.Offset;
|
if (sourceInfo.Offset != 0)
|
||||||
|
audioSource.Position = sourceInfo.Offset;
|
||||||
|
|
||||||
return audioSource;
|
return audioSource;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace FLACDotNet {
|
|||||||
|
|
||||||
public ref class FLACReader {
|
public ref class FLACReader {
|
||||||
public:
|
public:
|
||||||
FLACReader(String^ path) {
|
FLACReader(String^ path, Stream^ IO) {
|
||||||
_tags = gcnew NameValueCollection();
|
_tags = gcnew NameValueCollection();
|
||||||
|
|
||||||
_writeDel = gcnew DecoderWriteDelegate(this, &FLACReader::WriteCallback);
|
_writeDel = gcnew DecoderWriteDelegate(this, &FLACReader::WriteCallback);
|
||||||
@@ -88,7 +88,10 @@ namespace FLACDotNet {
|
|||||||
_sampleBuffer = nullptr;
|
_sampleBuffer = nullptr;
|
||||||
_path = path;
|
_path = path;
|
||||||
|
|
||||||
_IO = gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
|
if (IO)
|
||||||
|
_IO = IO;
|
||||||
|
else
|
||||||
|
_IO = gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
|
||||||
|
|
||||||
_decoder = FLAC__stream_decoder_new();
|
_decoder = FLAC__stream_decoder_new();
|
||||||
|
|
||||||
@@ -252,7 +255,10 @@ namespace FLACDotNet {
|
|||||||
_decoderActive = false;
|
_decoderActive = false;
|
||||||
}
|
}
|
||||||
if (_IO != nullptr)
|
if (_IO != nullptr)
|
||||||
|
{
|
||||||
_IO->Close ();
|
_IO->Close ();
|
||||||
|
_IO = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Int32 Read([Out] array<Int32, 2>^% sampleBuffer)
|
Int32 Read([Out] array<Int32, 2>^% sampleBuffer)
|
||||||
@@ -288,7 +294,7 @@ namespace FLACDotNet {
|
|||||||
NameValueCollection^ _tags;
|
NameValueCollection^ _tags;
|
||||||
String^ _path;
|
String^ _path;
|
||||||
bool _decoderActive;
|
bool _decoderActive;
|
||||||
FileStream^ _IO;
|
Stream^ _IO;
|
||||||
|
|
||||||
FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder,
|
FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder,
|
||||||
const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
|
const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
|
||||||
|
|||||||
35
UnRarDotNet/Properties/AssemblyInfo.cs
Normal file
35
UnRarDotNet/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("UnRarDotNet")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("UnRarDotNet")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Gregory S. Chudov, Michael A. McCloskey")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("87d4581b-faf5-4b85-999c-247f0a3eb64a")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Revision and Build Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
170
UnRarDotNet/RarStream.cs
Normal file
170
UnRarDotNet/RarStream.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
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 archive, string fileName)
|
||||||
|
{
|
||||||
|
_stop = false;
|
||||||
|
_unrar = new Unrar();
|
||||||
|
_buffer = null;
|
||||||
|
_offset = 0;
|
||||||
|
_length = 0;
|
||||||
|
_unrar.PasswordRequired += new PasswordRequiredHandler(unrar_PasswordRequired);
|
||||||
|
_unrar.DataAvailable += new DataAvailableHandler(unrar_DataAvailable);
|
||||||
|
_unrar.Open(archive, Unrar.OpenMode.Extract);
|
||||||
|
_fileName = fileName;
|
||||||
|
_workThread = new Thread(Decompress);
|
||||||
|
_workThread.Priority = ThreadPriority.BelowNormal;
|
||||||
|
_workThread.IsBackground = true;
|
||||||
|
_workThread.Start(null);
|
||||||
|
}
|
||||||
|
public override bool CanRead
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
public override bool CanSeek
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
public override bool CanWrite
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
public override long Length
|
||||||
|
{
|
||||||
|
get { throw new NotSupportedException(); }
|
||||||
|
}
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get { throw new NotSupportedException(); }
|
||||||
|
set { Seek(value, SeekOrigin.Begin); }
|
||||||
|
}
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
_stop = true;
|
||||||
|
Monitor.Pulse(this);
|
||||||
|
}
|
||||||
|
_workThread.Join();
|
||||||
|
_workThread = null;
|
||||||
|
_unrar.Close();
|
||||||
|
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;
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
while (_buffer == null && !_stop)
|
||||||
|
Monitor.Wait(this);
|
||||||
|
if (_buffer == null)
|
||||||
|
return total;
|
||||||
|
if (_length > count)
|
||||||
|
{
|
||||||
|
Array.Copy(_buffer, _offset, array, offset, count);
|
||||||
|
total += count;
|
||||||
|
_offset += count;
|
||||||
|
_length -= count;
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
Array.Copy(_buffer, _offset, array, offset, _length);
|
||||||
|
total += _length;
|
||||||
|
offset += _length;
|
||||||
|
count -= _length;
|
||||||
|
_buffer = null;
|
||||||
|
Monitor.Pulse(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
public override void Write(byte[] array, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Unrar _unrar;
|
||||||
|
private string _fileName;
|
||||||
|
private Thread _workThread;
|
||||||
|
private bool _stop;
|
||||||
|
private byte[] _buffer;
|
||||||
|
int _offset, _length;
|
||||||
|
|
||||||
|
private void unrar_PasswordRequired(object sender, PasswordRequiredEventArgs e)
|
||||||
|
{
|
||||||
|
e.Password = "PARS";
|
||||||
|
e.ContinueOperation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unrar_DataAvailable(object sender, DataAvailableEventArgs e)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
while (_buffer != null && !_stop)
|
||||||
|
Monitor.Wait(this);
|
||||||
|
if (_stop)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//try
|
||||||
|
{
|
||||||
|
while (_unrar.ReadHeader())
|
||||||
|
{
|
||||||
|
if (_unrar.CurrentFile.FileName == _fileName)
|
||||||
|
{
|
||||||
|
// unrar.CurrentFile.UnpackedSize;
|
||||||
|
_unrar.Test();
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
_stop = true;
|
||||||
|
Monitor.Pulse(this);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_unrar.Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//catch (StopExtractionException)
|
||||||
|
//{
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
UnRarDotNet/UnRarDotNet.csproj
Normal file
92
UnRarDotNet/UnRarDotNet.csproj
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>8.0.50727</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{8427CAA5-80B8-4952-9A68-5F3DFCFBDF40}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>UnRarDotNet</RootNamespace>
|
||||||
|
<AssemblyName>UnRarDotNet</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>..\bin\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||||
|
<OutputPath>..\bin\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>..\bin\Win32\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<OutputPath>..\bin\Win32\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<CodeAnalysisRuleAssemblies>C:\Program Files (x86)\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
|
||||||
|
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
|
||||||
|
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="RarStream.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Unrar.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
969
UnRarDotNet/Unrar.cs
Normal file
969
UnRarDotNet/Unrar.cs
Normal file
@@ -0,0 +1,969 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
|
||||||
|
/* Author: Michael A. McCloskey
|
||||||
|
* Company: Schematrix
|
||||||
|
* Version: 20040714
|
||||||
|
*
|
||||||
|
* Personal Comments:
|
||||||
|
* I created this unrar wrapper class for personal use
|
||||||
|
* after running into a number of issues trying to use
|
||||||
|
* another COM unrar product via COM interop. I hope it
|
||||||
|
* proves as useful to you as it has to me and saves you
|
||||||
|
* some time in building your own products.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UnRarDotNet
|
||||||
|
{
|
||||||
|
#region Event Delegate Definitions
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle data available events
|
||||||
|
/// </summary>
|
||||||
|
public delegate void DataAvailableHandler(object sender, DataAvailableEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle extraction progress events
|
||||||
|
/// </summary>
|
||||||
|
public delegate void ExtractionProgressHandler(object sender, ExtractionProgressEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle missing archive volume events
|
||||||
|
/// </summary>
|
||||||
|
public delegate void MissingVolumeHandler(object sender, MissingVolumeEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle new volume events
|
||||||
|
/// </summary>
|
||||||
|
public delegate void NewVolumeHandler(object sender, NewVolumeEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle new file notifications
|
||||||
|
/// </summary>
|
||||||
|
public delegate void NewFileHandler(object sender, NewFileEventArgs e);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the method that will handle password required events
|
||||||
|
/// </summary>
|
||||||
|
public delegate void PasswordRequiredHandler(object sender, PasswordRequiredEventArgs e);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapper class for unrar DLL supplied by RARSoft.
|
||||||
|
/// Calls unrar DLL via platform invocation services (pinvoke).
|
||||||
|
/// DLL is available at http://www.rarlab.com/rar/UnRARDLL.exe
|
||||||
|
/// </summary>
|
||||||
|
public class Unrar : IDisposable
|
||||||
|
{
|
||||||
|
#region Unrar DLL enumerations
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mode in which archive is to be opened for processing.
|
||||||
|
/// </summary>
|
||||||
|
public enum OpenMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Open archive for listing contents only
|
||||||
|
/// </summary>
|
||||||
|
List=0,
|
||||||
|
/// <summary>
|
||||||
|
/// Open archive for testing or extracting contents
|
||||||
|
/// </summary>
|
||||||
|
Extract=1
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum RarError : uint
|
||||||
|
{
|
||||||
|
EndOfArchive=10,
|
||||||
|
InsufficientMemory=11,
|
||||||
|
BadData=12,
|
||||||
|
BadArchive=13,
|
||||||
|
UnknownFormat=14,
|
||||||
|
OpenError=15,
|
||||||
|
CreateError=16,
|
||||||
|
CloseError=17,
|
||||||
|
ReadError=18,
|
||||||
|
WriteError=19,
|
||||||
|
BufferTooSmall=20,
|
||||||
|
UnknownError=21
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Operation : uint
|
||||||
|
{
|
||||||
|
Skip=0,
|
||||||
|
Test=1,
|
||||||
|
Extract=2
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum VolumeMessage : uint
|
||||||
|
{
|
||||||
|
Ask=0,
|
||||||
|
Notify=1
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum ArchiveFlags : uint
|
||||||
|
{
|
||||||
|
Volume=0x1, // Volume attribute (archive volume)
|
||||||
|
CommentPresent=0x2, // Archive comment present
|
||||||
|
Lock=0x4, // Archive lock attribute
|
||||||
|
SolidArchive=0x8, // Solid attribute (solid archive)
|
||||||
|
NewNamingScheme=0x10, // New volume naming scheme ('volname.partN.rar')
|
||||||
|
AuthenticityPresent=0x20, // Authenticity information present
|
||||||
|
RecoveryRecordPresent=0x40, // Recovery record present
|
||||||
|
EncryptedHeaders=0x80, // Block headers are encrypted
|
||||||
|
FirstVolume=0x100 // 0x0100 - First volume (set only by RAR 3.0 and later)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CallbackMessages : uint
|
||||||
|
{
|
||||||
|
VolumeChange=0,
|
||||||
|
ProcessData=1,
|
||||||
|
NeedPassword=2
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unrar DLL structure definitions
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
|
||||||
|
private struct RARHeaderData
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
|
||||||
|
public string ArcName;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
|
||||||
|
public string FileName;
|
||||||
|
public uint Flags;
|
||||||
|
public uint PackSize;
|
||||||
|
public uint UnpSize;
|
||||||
|
public uint HostOS;
|
||||||
|
public uint FileCRC;
|
||||||
|
public uint FileTime;
|
||||||
|
public uint UnpVer;
|
||||||
|
public uint Method;
|
||||||
|
public uint FileAttr;
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)]
|
||||||
|
public string CmtBuf;
|
||||||
|
public uint CmtBufSize;
|
||||||
|
public uint CmtSize;
|
||||||
|
public uint CmtState;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.CmtBuf=new string((char)0, 65536);
|
||||||
|
this.CmtBufSize=65536;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
||||||
|
public struct RARHeaderDataEx
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=512)]
|
||||||
|
public string ArcName;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)]
|
||||||
|
public string ArcNameW;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=512)]
|
||||||
|
public string FileName;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=1024)]
|
||||||
|
public string FileNameW;
|
||||||
|
public uint Flags;
|
||||||
|
public uint PackSize;
|
||||||
|
public uint PackSizeHigh;
|
||||||
|
public uint UnpSize;
|
||||||
|
public uint UnpSizeHigh;
|
||||||
|
public uint HostOS;
|
||||||
|
public uint FileCRC;
|
||||||
|
public uint FileTime;
|
||||||
|
public uint UnpVer;
|
||||||
|
public uint Method;
|
||||||
|
public uint FileAttr;
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)]
|
||||||
|
public string CmtBuf;
|
||||||
|
public uint CmtBufSize;
|
||||||
|
public uint CmtSize;
|
||||||
|
public uint CmtState;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1024)]
|
||||||
|
public uint[] Reserved;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.CmtBuf=new string((char)0, 65536);
|
||||||
|
this.CmtBufSize=65536;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
|
||||||
|
public struct RAROpenArchiveData
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
|
||||||
|
public string ArcName;
|
||||||
|
public uint OpenMode;
|
||||||
|
public uint OpenResult;
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)]
|
||||||
|
public string CmtBuf;
|
||||||
|
public uint CmtBufSize;
|
||||||
|
public uint CmtSize;
|
||||||
|
public uint CmtState;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.CmtBuf=new string((char)0,65536);
|
||||||
|
this.CmtBufSize=65536;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RAROpenArchiveDataEx
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)]
|
||||||
|
public string ArcName;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
||||||
|
public string ArcNameW;
|
||||||
|
public uint OpenMode;
|
||||||
|
public uint OpenResult;
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)]
|
||||||
|
public string CmtBuf;
|
||||||
|
public uint CmtBufSize;
|
||||||
|
public uint CmtSize;
|
||||||
|
public uint CmtState;
|
||||||
|
public uint Flags;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
|
||||||
|
public uint[] Reserved;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
this.CmtBuf=new string((char)0,65536);
|
||||||
|
this.CmtBufSize=65536;
|
||||||
|
this.Reserved=new uint[32];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unrar function declarations
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern IntPtr RAROpenArchive(ref RAROpenArchiveData archiveData);
|
||||||
|
|
||||||
|
[DllImport("UNRAR.DLL")]
|
||||||
|
private static extern IntPtr RAROpenArchiveEx(ref RAROpenArchiveDataEx archiveData);
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern int RARCloseArchive(IntPtr hArcData);
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern int RARReadHeader(IntPtr hArcData, ref RARHeaderData headerData);
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern int RARReadHeaderEx(IntPtr hArcData, ref RARHeaderDataEx headerData);
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern int RARProcessFile(IntPtr hArcData, int operation,
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)] string destPath,
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)] string destName );
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern void RARSetCallback(IntPtr hArcData, UNRARCallback callback, int userData);
|
||||||
|
|
||||||
|
[DllImport("unrar.dll")]
|
||||||
|
private static extern void RARSetPassword(IntPtr hArcData,
|
||||||
|
[MarshalAs(UnmanagedType.LPStr)] string password);
|
||||||
|
|
||||||
|
// Unrar callback delegate signature
|
||||||
|
private delegate int UNRARCallback(uint msg, int UserData, IntPtr p1, int p2);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public event declarations
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised when a new chunk of data has been extracted
|
||||||
|
/// </summary>
|
||||||
|
public event DataAvailableHandler DataAvailable;
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised to indicate extraction progress
|
||||||
|
/// </summary>
|
||||||
|
public event ExtractionProgressHandler ExtractionProgress;
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised when a required archive volume is missing
|
||||||
|
/// </summary>
|
||||||
|
public event MissingVolumeHandler MissingVolume;
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised when a new file is encountered during processing
|
||||||
|
/// </summary>
|
||||||
|
public event NewFileHandler NewFile;
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised when a new archive volume is opened for processing
|
||||||
|
/// </summary>
|
||||||
|
public event NewVolumeHandler NewVolume;
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised when a password is required before continuing
|
||||||
|
/// </summary>
|
||||||
|
public event PasswordRequiredHandler PasswordRequired;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private fields
|
||||||
|
|
||||||
|
private string archivePathName=string.Empty;
|
||||||
|
private IntPtr archiveHandle=new IntPtr(0);
|
||||||
|
private bool retrieveComment=true;
|
||||||
|
private string password=string.Empty;
|
||||||
|
private string comment=string.Empty;
|
||||||
|
private ArchiveFlags archiveFlags=0;
|
||||||
|
private RARHeaderDataEx header=new RARHeaderDataEx();
|
||||||
|
private string destinationPath=string.Empty;
|
||||||
|
private RARFileInfo currentFile=null;
|
||||||
|
private UNRARCallback callback=null;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Object lifetime procedures
|
||||||
|
|
||||||
|
public Unrar()
|
||||||
|
{
|
||||||
|
this.callback=new UNRARCallback(RARCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Unrar(string archivePathName) : this()
|
||||||
|
{
|
||||||
|
this.archivePathName=archivePathName;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Unrar()
|
||||||
|
{
|
||||||
|
if(this.archiveHandle!=IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Unrar.RARCloseArchive(this.archiveHandle);
|
||||||
|
this.archiveHandle=IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if(this.archiveHandle!=IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Unrar.RARCloseArchive(this.archiveHandle);
|
||||||
|
this.archiveHandle=IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path and name of RAR archive to open
|
||||||
|
/// </summary>
|
||||||
|
public string ArchivePathName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.archivePathName;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.archivePathName=value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Archive comment
|
||||||
|
/// </summary>
|
||||||
|
public string Comment
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return(this.comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current file being processed
|
||||||
|
/// </summary>
|
||||||
|
public RARFileInfo CurrentFile
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return(this.currentFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default destination path for extraction
|
||||||
|
/// </summary>
|
||||||
|
public string DestinationPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.destinationPath;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.destinationPath=value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Password for opening encrypted archive
|
||||||
|
/// </summary>
|
||||||
|
public string Password
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return(this.password);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.password=value;
|
||||||
|
if(this.archiveHandle!=IntPtr.Zero)
|
||||||
|
RARSetPassword(this.archiveHandle, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close the currently open archive
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
// Exit without exception if no archive is open
|
||||||
|
if(this.archiveHandle==IntPtr.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Close archive
|
||||||
|
int result=Unrar.RARCloseArchive(this.archiveHandle);
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
ProcessFileError(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.archiveHandle=IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens archive specified by the ArchivePathName property for testing or extraction
|
||||||
|
/// </summary>
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
if(this.ArchivePathName.Length==0)
|
||||||
|
throw new IOException("Archive name has not been set.");
|
||||||
|
this.Open(this.ArchivePathName, OpenMode.Extract);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens archive specified by the ArchivePathName property with a specified mode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="openMode">Mode in which archive should be opened</param>
|
||||||
|
public void Open(OpenMode openMode)
|
||||||
|
{
|
||||||
|
if(this.ArchivePathName.Length==0)
|
||||||
|
throw new IOException("Archive name has not been set.");
|
||||||
|
this.Open(this.ArchivePathName, openMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens specified archive using the specified mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="archivePathName">Path of archive to open</param>
|
||||||
|
/// <param name="openMode">Mode in which to open archive</param>
|
||||||
|
public void Open(string archivePathName, OpenMode openMode)
|
||||||
|
{
|
||||||
|
IntPtr handle=IntPtr.Zero;
|
||||||
|
|
||||||
|
// Close any previously open archives
|
||||||
|
if(this.archiveHandle!=IntPtr.Zero)
|
||||||
|
this.Close();
|
||||||
|
|
||||||
|
// Prepare extended open archive struct
|
||||||
|
this.ArchivePathName=archivePathName;
|
||||||
|
RAROpenArchiveDataEx openStruct=new RAROpenArchiveDataEx();
|
||||||
|
openStruct.Initialize();
|
||||||
|
openStruct.ArcName=this.archivePathName+"\0";
|
||||||
|
openStruct.ArcNameW=this.archivePathName+"\0";
|
||||||
|
openStruct.OpenMode=(uint)openMode;
|
||||||
|
if(this.retrieveComment)
|
||||||
|
{
|
||||||
|
openStruct.CmtBuf=new string((char)0,65536);
|
||||||
|
openStruct.CmtBufSize=65536;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
openStruct.CmtBuf=null;
|
||||||
|
openStruct.CmtBufSize=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open archive
|
||||||
|
handle=Unrar.RAROpenArchiveEx(ref openStruct);
|
||||||
|
|
||||||
|
// Check for success
|
||||||
|
if(openStruct.OpenResult!=0)
|
||||||
|
{
|
||||||
|
switch((RarError)openStruct.OpenResult)
|
||||||
|
{
|
||||||
|
case RarError.InsufficientMemory:
|
||||||
|
throw new OutOfMemoryException("Insufficient memory to perform operation.");
|
||||||
|
|
||||||
|
case RarError.BadData:
|
||||||
|
throw new IOException("Archive header broken");
|
||||||
|
|
||||||
|
case RarError.BadArchive:
|
||||||
|
throw new IOException("File is not a valid archive.");
|
||||||
|
|
||||||
|
case RarError.OpenError:
|
||||||
|
throw new IOException("File could not be opened.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save handle and flags
|
||||||
|
this.archiveHandle=handle;
|
||||||
|
this.archiveFlags=(ArchiveFlags)openStruct.Flags;
|
||||||
|
|
||||||
|
// Set callback
|
||||||
|
Unrar.RARSetCallback(this.archiveHandle, this.callback, this.GetHashCode());
|
||||||
|
|
||||||
|
// If comment retrieved, save it
|
||||||
|
if(openStruct.CmtState==1)
|
||||||
|
this.comment=openStruct.CmtBuf.ToString();
|
||||||
|
|
||||||
|
// If password supplied, set it
|
||||||
|
if(this.password.Length!=0)
|
||||||
|
Unrar.RARSetPassword(this.archiveHandle, this.password);
|
||||||
|
|
||||||
|
// Fire NewVolume event for first volume
|
||||||
|
this.OnNewVolume(this.archivePathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the next archive header and populates CurrentFile property data
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool ReadHeader()
|
||||||
|
{
|
||||||
|
// Throw exception if archive not open
|
||||||
|
if(this.archiveHandle==IntPtr.Zero)
|
||||||
|
throw new IOException("Archive is not open.");
|
||||||
|
|
||||||
|
// Initialize header struct
|
||||||
|
this.header=new RARHeaderDataEx();
|
||||||
|
header.Initialize();
|
||||||
|
|
||||||
|
// Read next entry
|
||||||
|
currentFile=null;
|
||||||
|
int result=Unrar.RARReadHeaderEx(this.archiveHandle, ref this.header);
|
||||||
|
|
||||||
|
// Check for error or end of archive
|
||||||
|
if((RarError)result==RarError.EndOfArchive)
|
||||||
|
return false;
|
||||||
|
else if((RarError)result==RarError.BadData)
|
||||||
|
throw new IOException("Archive data is corrupt.");
|
||||||
|
|
||||||
|
// Determine if new file
|
||||||
|
if(((header.Flags & 0x01) != 0) && currentFile!=null)
|
||||||
|
currentFile.ContinuedFromPrevious=true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// New file, prepare header
|
||||||
|
currentFile=new RARFileInfo();
|
||||||
|
currentFile.FileName=header.FileNameW.ToString();
|
||||||
|
if((header.Flags & 0x02) != 0)
|
||||||
|
currentFile.ContinuedOnNext=true;
|
||||||
|
if(header.PackSizeHigh != 0)
|
||||||
|
currentFile.PackedSize=(header.PackSizeHigh * 0x100000000) + header.PackSize;
|
||||||
|
else
|
||||||
|
currentFile.PackedSize=header.PackSize;
|
||||||
|
if(header.UnpSizeHigh != 0)
|
||||||
|
currentFile.UnpackedSize=(header.UnpSizeHigh * 0x100000000) + header.UnpSize;
|
||||||
|
else
|
||||||
|
currentFile.UnpackedSize=header.UnpSize;
|
||||||
|
currentFile.HostOS=(int)header.HostOS;
|
||||||
|
currentFile.FileCRC=header.FileCRC;
|
||||||
|
currentFile.FileTime=FromMSDOSTime(header.FileTime);
|
||||||
|
currentFile.VersionToUnpack=(int)header.UnpVer;
|
||||||
|
currentFile.Method=(int)header.Method;
|
||||||
|
currentFile.FileAttributes=(int)header.FileAttr;
|
||||||
|
currentFile.BytesExtracted=0;
|
||||||
|
if((header.Flags & 0xE0) == 0xE0)
|
||||||
|
currentFile.IsDirectory=true;
|
||||||
|
this.OnNewFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns array of file names contained in archive
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string[] ListFiles()
|
||||||
|
{
|
||||||
|
ArrayList fileNames=new ArrayList();
|
||||||
|
while(this.ReadHeader())
|
||||||
|
{
|
||||||
|
if(!currentFile.IsDirectory)
|
||||||
|
fileNames.Add(currentFile.FileName);
|
||||||
|
this.Skip();
|
||||||
|
}
|
||||||
|
string[] files=new string[fileNames.Count];
|
||||||
|
fileNames.CopyTo(files);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the current archive position to the next available header
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void Skip()
|
||||||
|
{
|
||||||
|
int result=Unrar.RARProcessFile(this.archiveHandle, (int)Operation.Skip, string.Empty, string.Empty);
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
ProcessFileError(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the ability to extract the current file without saving extracted data to disk
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void Test()
|
||||||
|
{
|
||||||
|
int result=Unrar.RARProcessFile(this.archiveHandle, (int)Operation.Test, string.Empty, string.Empty);
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
ProcessFileError(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the current file to the default destination path
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void Extract()
|
||||||
|
{
|
||||||
|
this.Extract(this.destinationPath, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the current file to a specified destination path and filename
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destinationName">Path and name of extracted file</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void Extract(string destinationName)
|
||||||
|
{
|
||||||
|
this.Extract(string.Empty, destinationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the current file to a specified directory without renaming file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destinationPath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public void ExtractToDirectory(string destinationPath)
|
||||||
|
{
|
||||||
|
this.Extract(destinationPath, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
|
||||||
|
private void Extract(string destinationPath, string destinationName)
|
||||||
|
{
|
||||||
|
int result=Unrar.RARProcessFile(this.archiveHandle, (int)Operation.Extract, destinationPath, destinationName);
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if(result!=0)
|
||||||
|
{
|
||||||
|
ProcessFileError(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime FromMSDOSTime(uint dosTime)
|
||||||
|
{
|
||||||
|
int day=0;
|
||||||
|
int month=0;
|
||||||
|
int year=0;
|
||||||
|
int second=0;
|
||||||
|
int hour=0;
|
||||||
|
int minute=0;
|
||||||
|
ushort hiWord;
|
||||||
|
ushort loWord;
|
||||||
|
hiWord=(ushort)((dosTime & 0xFFFF0000) >> 16);
|
||||||
|
loWord=(ushort)(dosTime & 0xFFFF);
|
||||||
|
year=((hiWord & 0xFE00) >> 9)+1980;
|
||||||
|
month=(hiWord & 0x01E0) >> 5;
|
||||||
|
day=hiWord & 0x1F;
|
||||||
|
hour=(loWord & 0xF800) >> 11;
|
||||||
|
minute=(loWord & 0x07E0) >> 5;
|
||||||
|
second=(loWord & 0x1F) << 1;
|
||||||
|
return new DateTime(year, month, day, hour, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessFileError(int result)
|
||||||
|
{
|
||||||
|
switch((RarError)result)
|
||||||
|
{
|
||||||
|
case RarError.UnknownFormat:
|
||||||
|
throw new OutOfMemoryException("Unknown archive format.");
|
||||||
|
|
||||||
|
case RarError.BadData:
|
||||||
|
throw new IOException("File CRC Error");
|
||||||
|
|
||||||
|
case RarError.BadArchive:
|
||||||
|
throw new IOException("File is not a valid archive.");
|
||||||
|
|
||||||
|
case RarError.OpenError:
|
||||||
|
throw new IOException("File could not be opened.");
|
||||||
|
|
||||||
|
case RarError.CreateError:
|
||||||
|
throw new IOException("File could not be created.");
|
||||||
|
|
||||||
|
case RarError.CloseError:
|
||||||
|
throw new IOException("File close error.");
|
||||||
|
|
||||||
|
case RarError.ReadError:
|
||||||
|
throw new IOException("File read error.");
|
||||||
|
|
||||||
|
case RarError.WriteError:
|
||||||
|
throw new IOException("File write error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int RARCallback(uint msg, int UserData, IntPtr p1, int p2)
|
||||||
|
{
|
||||||
|
string volume=string.Empty;
|
||||||
|
string newVolume=string.Empty;
|
||||||
|
int result=-1;
|
||||||
|
|
||||||
|
switch((CallbackMessages)msg)
|
||||||
|
{
|
||||||
|
case CallbackMessages.VolumeChange:
|
||||||
|
volume=Marshal.PtrToStringAnsi(p1);
|
||||||
|
if((VolumeMessage)p2==VolumeMessage.Notify)
|
||||||
|
result=OnNewVolume(volume);
|
||||||
|
else if((VolumeMessage)p2==VolumeMessage.Ask)
|
||||||
|
{
|
||||||
|
newVolume=OnMissingVolume(volume);
|
||||||
|
if(newVolume.Length==0)
|
||||||
|
result=-1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(newVolume!=volume)
|
||||||
|
{
|
||||||
|
for(int i=0; i<newVolume.Length; i++)
|
||||||
|
{
|
||||||
|
Marshal.WriteByte(p1, i, (byte)newVolume[i]);
|
||||||
|
}
|
||||||
|
Marshal.WriteByte(p1, newVolume.Length, (byte)0);
|
||||||
|
}
|
||||||
|
result=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CallbackMessages.ProcessData:
|
||||||
|
result=OnDataAvailable(p1, p2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CallbackMessages.NeedPassword:
|
||||||
|
result=OnPasswordRequired(p1, p2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Protected Virtual (Overridable) Methods
|
||||||
|
|
||||||
|
protected virtual void OnNewFile()
|
||||||
|
{
|
||||||
|
if(this.NewFile!=null)
|
||||||
|
{
|
||||||
|
NewFileEventArgs e = new NewFileEventArgs(this.currentFile);
|
||||||
|
this.NewFile(this, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual int OnPasswordRequired(IntPtr p1, int p2)
|
||||||
|
{
|
||||||
|
int result=-1;
|
||||||
|
if(this.PasswordRequired!=null)
|
||||||
|
{
|
||||||
|
PasswordRequiredEventArgs e=new PasswordRequiredEventArgs();
|
||||||
|
this.PasswordRequired(this, e);
|
||||||
|
if(e.ContinueOperation && e.Password.Length>0)
|
||||||
|
{
|
||||||
|
for(int i=0; (i<e.Password.Length) && (i<p2); i++)
|
||||||
|
Marshal.WriteByte(p1, i, (byte)e.Password[i]);
|
||||||
|
Marshal.WriteByte(p1, e.Password.Length, (byte)0);
|
||||||
|
result=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IOException("Password is required for extraction.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual int OnDataAvailable(IntPtr p1, int p2)
|
||||||
|
{
|
||||||
|
int result=1;
|
||||||
|
if(this.currentFile!=null)
|
||||||
|
this.currentFile.BytesExtracted+=p2;
|
||||||
|
if(this.DataAvailable!=null)
|
||||||
|
{
|
||||||
|
byte[] data=new byte[p2];
|
||||||
|
Marshal.Copy(p1, data, 0, p2);
|
||||||
|
DataAvailableEventArgs e=new DataAvailableEventArgs(data);
|
||||||
|
this.DataAvailable(this, e);
|
||||||
|
if(!e.ContinueOperation)
|
||||||
|
result=-1;
|
||||||
|
}
|
||||||
|
if((this.ExtractionProgress!=null) && (this.currentFile!=null))
|
||||||
|
{
|
||||||
|
ExtractionProgressEventArgs e = new ExtractionProgressEventArgs();
|
||||||
|
e.FileName=this.currentFile.FileName;
|
||||||
|
e.FileSize=this.currentFile.UnpackedSize;
|
||||||
|
e.BytesExtracted=this.currentFile.BytesExtracted;
|
||||||
|
e.PercentComplete=this.currentFile.PercentComplete;
|
||||||
|
this.ExtractionProgress(this, e);
|
||||||
|
if(!e.ContinueOperation)
|
||||||
|
result=-1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual int OnNewVolume(string volume)
|
||||||
|
{
|
||||||
|
int result=1;
|
||||||
|
if(this.NewVolume!=null)
|
||||||
|
{
|
||||||
|
NewVolumeEventArgs e=new NewVolumeEventArgs(volume);
|
||||||
|
this.NewVolume(this, e);
|
||||||
|
if(!e.ContinueOperation)
|
||||||
|
result=-1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string OnMissingVolume(string volume)
|
||||||
|
{
|
||||||
|
string result=string.Empty;
|
||||||
|
if(this.MissingVolume!=null)
|
||||||
|
{
|
||||||
|
MissingVolumeEventArgs e=new MissingVolumeEventArgs(volume);
|
||||||
|
this.MissingVolume(this, e);
|
||||||
|
if(e.ContinueOperation)
|
||||||
|
result=e.VolumeName;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Event Argument Classes
|
||||||
|
|
||||||
|
public class NewVolumeEventArgs
|
||||||
|
{
|
||||||
|
public string VolumeName;
|
||||||
|
public bool ContinueOperation=true;
|
||||||
|
|
||||||
|
public NewVolumeEventArgs(string volumeName)
|
||||||
|
{
|
||||||
|
this.VolumeName=volumeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MissingVolumeEventArgs
|
||||||
|
{
|
||||||
|
public string VolumeName;
|
||||||
|
public bool ContinueOperation=false;
|
||||||
|
|
||||||
|
public MissingVolumeEventArgs(string volumeName)
|
||||||
|
{
|
||||||
|
this.VolumeName=volumeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DataAvailableEventArgs
|
||||||
|
{
|
||||||
|
public readonly byte[] Data;
|
||||||
|
public bool ContinueOperation=true;
|
||||||
|
|
||||||
|
public DataAvailableEventArgs(byte[] data)
|
||||||
|
{
|
||||||
|
this.Data=data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PasswordRequiredEventArgs
|
||||||
|
{
|
||||||
|
public string Password=string.Empty;
|
||||||
|
public bool ContinueOperation=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NewFileEventArgs
|
||||||
|
{
|
||||||
|
public RARFileInfo fileInfo;
|
||||||
|
public NewFileEventArgs(RARFileInfo fileInfo)
|
||||||
|
{
|
||||||
|
this.fileInfo=fileInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExtractionProgressEventArgs
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public long FileSize;
|
||||||
|
public long BytesExtracted;
|
||||||
|
public double PercentComplete;
|
||||||
|
public bool ContinueOperation=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RARFileInfo
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public bool ContinuedFromPrevious=false;
|
||||||
|
public bool ContinuedOnNext=false;
|
||||||
|
public bool IsDirectory=false;
|
||||||
|
public long PackedSize=0;
|
||||||
|
public long UnpackedSize=0;
|
||||||
|
public int HostOS=0;
|
||||||
|
public long FileCRC=0;
|
||||||
|
public DateTime FileTime;
|
||||||
|
public int VersionToUnpack=0;
|
||||||
|
public int Method=0;
|
||||||
|
public int FileAttributes=0;
|
||||||
|
public long BytesExtracted=0;
|
||||||
|
|
||||||
|
public double PercentComplete
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(this.UnpackedSize!=0)
|
||||||
|
return(((double)this.BytesExtracted/(double)this.UnpackedSize) * (double)100.0);
|
||||||
|
else
|
||||||
|
return (double)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user