348 lines
13 KiB
C#
348 lines
13 KiB
C#
// Decompiled with JetBrains decompiler
|
|
// Type: SEGATools.Disc.DiscExtractor
|
|
// Assembly: SEGATools, Version=1.0.3.0, Culture=neutral, PublicKeyToken=611be24fdeb07e08
|
|
// MVID: D631183F-57B1-40A1-B502-5364D288307A
|
|
// Assembly location: SEGATools.dll
|
|
|
|
using ImageReader.ISO9660.DirectoryRecords;
|
|
using ImageReader.Stream;
|
|
using SEGATools.DiscFileSystem;
|
|
using SEGATools.Security;
|
|
using SEGATools.UserProcess;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
|
|
namespace SEGATools.Disc
|
|
{
|
|
public class DiscExtractor : UserProcessBase
|
|
{
|
|
private static readonly int OutputFileStreamBuffer = 524288;
|
|
|
|
public event AsyncOperationProgressChangedEventHandler ExtractionProgressChanged
|
|
{
|
|
add => this.AsyncOperationProgressChanged += value;
|
|
remove => this.AsyncOperationProgressChanged -= value;
|
|
}
|
|
|
|
public event AsyncOperationCompletedEventHandler ExtractionCompleted
|
|
{
|
|
add => this.AsyncOperationCompleted += value;
|
|
remove => this.AsyncOperationCompleted -= value;
|
|
}
|
|
|
|
public IDiscFileSystem DiscFileSystem { get; set; }
|
|
|
|
public DiscExtractor()
|
|
{
|
|
}
|
|
|
|
public DiscExtractor(IContainer container)
|
|
: base(container)
|
|
{
|
|
}
|
|
|
|
public void ExtractPathsAsync(
|
|
List<DirectoryRecord> directoryRecords,
|
|
string outputPath,
|
|
object taskId)
|
|
{
|
|
this.ExtractPathsAsync(directoryRecords, outputPath, (SendOrPostCallback) null, taskId);
|
|
}
|
|
|
|
public void ExtractDirectoryRecordsAsync(
|
|
DirectoryRecord directoryRecord,
|
|
string outputPath,
|
|
object taskId)
|
|
{
|
|
this.ExtractPathsAsync(directoryRecord, outputPath, (SendOrPostCallback) null, taskId);
|
|
}
|
|
|
|
public void ExtractDiscTracksAsync(IDiscTrack track, string outputFileName, object taskId) => this.ExtractDiscTracksAsync(new List<IDiscTrack>()
|
|
{
|
|
track
|
|
}, outputFileName, taskId);
|
|
|
|
public void ExtractBootStrap(InitialProgram ip, string outputFilename)
|
|
{
|
|
if (ip == null)
|
|
throw new ArgumentNullException(nameof (ip));
|
|
if (string.IsNullOrEmpty(outputFilename))
|
|
throw new ArgumentNullException(nameof (outputFilename));
|
|
using (System.IO.Stream stream = ip.Stream)
|
|
{
|
|
byte[] buffer = new byte[stream.Length];
|
|
using (FileStream fileStream = new FileStream(outputFilename, FileMode.OpenOrCreate))
|
|
{
|
|
stream.Read(buffer, 0, buffer.Length);
|
|
fileStream.Write(buffer, 0, buffer.Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ExtractPathsAsync(
|
|
DirectoryRecord directoryRecord,
|
|
string outputPath,
|
|
SendOrPostCallback callBack,
|
|
object taskId)
|
|
{
|
|
this.ExtractPathsAsync(new List<DirectoryRecord>()
|
|
{
|
|
directoryRecord
|
|
}, outputPath, callBack, taskId);
|
|
}
|
|
|
|
private void ExtractPathsAsync(
|
|
List<DirectoryRecord> directoryRecords,
|
|
string outputPath,
|
|
SendOrPostCallback callBack,
|
|
object taskId)
|
|
{
|
|
AsyncOperation asyncOperation = this.CreateAsyncOperation(taskId);
|
|
new DiscExtractor.FileExtractorWorkerEventHandler(this.FileExtractorWorker).BeginInvoke(directoryRecords, outputPath, asyncOperation, (AsyncCallback) null, (object) null);
|
|
}
|
|
|
|
private void ExtractDiscTracksAsync(List<IDiscTrack> tracks, string outputPath, object taskId)
|
|
{
|
|
AsyncOperation asyncOperation = this.CreateAsyncOperation(taskId);
|
|
new DiscExtractor.TrackExtractorWorkerEventHandler(this.TrackExtractorWorker).BeginInvoke(tracks, outputPath, asyncOperation, (AsyncCallback) null, (object) null);
|
|
}
|
|
|
|
private void FileExtractorWorker(
|
|
List<DirectoryRecord> directoryRecords,
|
|
string outputPath,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
string lastFileName = string.Empty;
|
|
string Filename = string.Empty;
|
|
Exception exception = (Exception) null;
|
|
long num1 = 0;
|
|
List<DirectoryRecord> source = new List<DirectoryRecord>();
|
|
foreach (DirectoryRecord directoryRecord in directoryRecords)
|
|
{
|
|
num1 += (long) directoryRecord.UsedSpace;
|
|
source.Add(directoryRecord);
|
|
source.AddRange((IEnumerable<DirectoryRecord>) directoryRecord.GetAllSubDirectories());
|
|
}
|
|
List<DirectoryRecord> list = source.Distinct<DirectoryRecord>().ToList<DirectoryRecord>();
|
|
long num2 = num1;
|
|
string commonPathPrefix = DirectoryRecord.FindCommonPathPrefix(directoryRecords);
|
|
UserProcessBase.logger.DebugFormat("The longest path prefix for all {0} files is {1}", (object) list.Count, (object) commonPathPrefix);
|
|
try
|
|
{
|
|
this.CheckForEnoughFreeDiscSpace(num1, outputPath);
|
|
foreach (DirectoryRecord directoryRecord in list)
|
|
{
|
|
if (!this.TaskCanceled(asyncOp))
|
|
{
|
|
Filename = directoryRecord.FullPath;
|
|
string str1;
|
|
if (list.Count == 1 && !directoryRecord.IsDirectory)
|
|
{
|
|
str1 = outputPath;
|
|
}
|
|
else
|
|
{
|
|
if (!directoryRecord.FullPath.StartsWith(commonPathPrefix))
|
|
throw new DiscExtractorException(string.Format("The path \"{0}\" was expected to have the prefix \"{1}\"", (object) directoryRecord.FullPath, (object) commonPathPrefix));
|
|
string str2 = directoryRecord.FullPath.Remove(0, commonPathPrefix.Length);
|
|
if (Path.IsPathRooted(str2))
|
|
str2 = str2.Substring(1);
|
|
str1 = Path.Combine(outputPath, str2);
|
|
}
|
|
UserProcessBase.logger.DebugFormat("Extract {0} to {1}", (object) Filename, (object) str1);
|
|
if (directoryRecord.IsDirectory)
|
|
{
|
|
UserProcessBase.logger.DebugFormat("Create output directory {0}", (object) str1);
|
|
DirectoryInfo directoryInfo = new DirectoryInfo(str1);
|
|
if (!directoryInfo.Exists)
|
|
{
|
|
Directory.CreateDirectory(directoryInfo.FullName);
|
|
DateTime dateTime = directoryRecord.RecordingDateTime.HasValue ? directoryRecord.RecordingDateTime.Value : DateTime.Now;
|
|
directoryInfo.CreationTime = dateTime;
|
|
directoryInfo.LastWriteTime = dateTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lastFileName = str1;
|
|
num2 = this.ExtractDirectoryRecord(directoryRecord, str1, num1, num2, asyncOp);
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
exception = ex;
|
|
UserProcessBase.logger.ErrorFormat("Unable to extract the data: {0}", (object) exception);
|
|
}
|
|
this.DoExtractionCleanupIfNeeded(lastFileName, num2, exception, asyncOp);
|
|
this.ReportCompletion(Filename, exception, asyncOp);
|
|
}
|
|
|
|
private long ExtractDirectoryRecord(
|
|
DirectoryRecord directoryRecord,
|
|
string FileOutputPath,
|
|
long totalNumberOfBytesToExtract,
|
|
long totalNumberOfBytesRemaining,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
using (DiscSectorStream forDirectoryRecord = this.DiscFileSystem.GetDiscStreamForDirectoryRecord(directoryRecord))
|
|
{
|
|
using (FileStream fileStream = new FileStream(FileOutputPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, DiscExtractor.OutputFileStreamBuffer))
|
|
{
|
|
long length = forDirectoryRecord.Length;
|
|
byte[] buffer = new byte[DiscExtractor.OutputFileStreamBuffer];
|
|
while (length > 0L)
|
|
{
|
|
if (!this.TaskCanceled(asyncOp))
|
|
{
|
|
int count = forDirectoryRecord.Read(buffer, 0, buffer.Length);
|
|
length -= (long) count;
|
|
totalNumberOfBytesRemaining -= (long) count;
|
|
fileStream.Write(buffer, 0, count);
|
|
this.ReportProgress(this.CreateProgressChangedEventArgs(directoryRecord.FullPath, FileOutputPath, forDirectoryRecord.Length, length, totalNumberOfBytesToExtract, totalNumberOfBytesRemaining, asyncOp), asyncOp);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
DateTime dateTime = directoryRecord.RecordingDateTime.HasValue ? directoryRecord.RecordingDateTime.Value : DateTime.Now;
|
|
File.SetCreationTime(FileOutputPath, dateTime);
|
|
File.SetLastWriteTime(FileOutputPath, dateTime);
|
|
return totalNumberOfBytesRemaining;
|
|
}
|
|
|
|
private void TrackExtractorWorker(
|
|
List<IDiscTrack> tracks,
|
|
string outputPath,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
Exception exception = (Exception) null;
|
|
string lastFileName = string.Empty;
|
|
long ofBytesToExtract = this.ComputeNumberOfBytesToExtract(tracks);
|
|
this.CheckForEnoughFreeDiscSpace(ofBytesToExtract, outputPath);
|
|
long num = ofBytesToExtract;
|
|
try
|
|
{
|
|
foreach (IDiscTrack track in tracks)
|
|
{
|
|
if (!this.TaskCanceled(asyncOp))
|
|
{
|
|
string outputFileName = tracks.Count > 1 ? Path.Combine(outputPath, Path.GetFileName(track.VirtualName)) : outputPath;
|
|
lastFileName = outputFileName;
|
|
num = this.ExtractDiscTrack(track, outputFileName, ofBytesToExtract, num, asyncOp);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
exception = ex;
|
|
UserProcessBase.logger.ErrorFormat("Unable to extract the track: {0}", (object) exception);
|
|
}
|
|
this.DoExtractionCleanupIfNeeded(lastFileName, num, exception, asyncOp);
|
|
this.ReportCompletion(outputPath, exception, asyncOp);
|
|
}
|
|
|
|
private long ExtractDiscTrack(
|
|
IDiscTrack track,
|
|
string outputFileName,
|
|
long totalNumberOfBytesToExtract,
|
|
long totalNumberOfBytesRemaining,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
using (System.IO.Stream fileInputStream = track.FileInputStream)
|
|
{
|
|
using (FileStream fileStream = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, DiscExtractor.OutputFileStreamBuffer))
|
|
{
|
|
long length = fileInputStream.Length;
|
|
byte[] buffer = new byte[DiscExtractor.OutputFileStreamBuffer];
|
|
while (length > 0L)
|
|
{
|
|
if (!this.TaskCanceled(asyncOp))
|
|
{
|
|
int count = fileInputStream.Read(buffer, 0, buffer.Length);
|
|
length -= (long) count;
|
|
totalNumberOfBytesRemaining -= (long) count;
|
|
fileStream.Write(buffer, 0, count);
|
|
this.ReportProgress(this.CreateProgressChangedEventArgs(track.FileName, outputFileName, fileInputStream.Length, length, totalNumberOfBytesToExtract, totalNumberOfBytesRemaining, asyncOp), asyncOp);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return totalNumberOfBytesRemaining;
|
|
}
|
|
|
|
private long ComputeNumberOfBytesToExtract(List<IDiscTrack> tracks)
|
|
{
|
|
long num = 0;
|
|
foreach (IDiscTrack track in tracks)
|
|
num += track.FileInputStream.Length;
|
|
return num;
|
|
}
|
|
|
|
private void CheckForEnoughFreeDiscSpace(long requiredSpaceInBytes, string outputPath)
|
|
{
|
|
if (requiredSpaceInBytes > new DriveInfo(outputPath.Substring(0, 3)).AvailableFreeSpace)
|
|
{
|
|
UserProcessBase.logger.ErrorFormat("Extraction requires {0} bytes of free disc space in drive {1}", (object) requiredSpaceInBytes, (object) outputPath.Substring(0, 3));
|
|
throw new IOException("Not enough free disc space!");
|
|
}
|
|
}
|
|
|
|
private UserProcessProgressChangedEventArgs CreateProgressChangedEventArgs(
|
|
string input,
|
|
string output,
|
|
long numberOfBytesToExtract,
|
|
long remainingBytesToExtract,
|
|
long totalNumberOfBytesToExtract,
|
|
long totalNumberOfBytesRemaining,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
int progressPercentage = (int) ((double) (numberOfBytesToExtract - remainingBytesToExtract) / (double) numberOfBytesToExtract * 100.0);
|
|
int totalProgressPercentage = (int) ((double) (totalNumberOfBytesToExtract - totalNumberOfBytesRemaining) / (double) totalNumberOfBytesToExtract * 100.0);
|
|
return new UserProcessProgressChangedEventArgs(input, output, progressPercentage, totalProgressPercentage, asyncOp.UserSuppliedState);
|
|
}
|
|
|
|
private void DoExtractionCleanupIfNeeded(
|
|
string lastFileName,
|
|
long remainingBytes,
|
|
Exception exception,
|
|
AsyncOperation asyncOp)
|
|
{
|
|
if (!this.TaskCanceled(asyncOp) && exception == null || remainingBytes <= 0L)
|
|
return;
|
|
if (!File.Exists(lastFileName))
|
|
return;
|
|
try
|
|
{
|
|
File.Delete(lastFileName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
UserProcessBase.logger.ErrorFormat("Unable to delete the file {0}: {1}", (object) lastFileName, (object) ex.Message);
|
|
}
|
|
}
|
|
|
|
private delegate void FileExtractorWorkerEventHandler(
|
|
List<DirectoryRecord> directoryRecords,
|
|
string outputPath,
|
|
AsyncOperation asyncOp);
|
|
|
|
private delegate void TrackExtractorWorkerEventHandler(
|
|
List<IDiscTrack> tracks,
|
|
string outputPath,
|
|
AsyncOperation asyncOp);
|
|
}
|
|
}
|