GDROMExplorer/SEGATools/Disc/DiscExtractor.cs

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);
}
}