Skip to content

Commit

Permalink
make the UI more responsive on disc scan and fs analysis stages
Browse files Browse the repository at this point in the history
  • Loading branch information
13xforever committed Jun 8, 2024
1 parent ec09106 commit a73ad0c
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 38 deletions.
21 changes: 16 additions & 5 deletions IrdLibraryClient/IrdFormat/IsoHeaderParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ namespace IrdLibraryClient.IrdFormat;

public static class IsoHeaderParser
{
public static (List<FileRecord> files, List<DirRecord> dirs) GetFilesystemStructure(this CDReader reader)
public static async Task<(List<FileRecord> files, List<DirRecord> dirs)> GetFilesystemStructureAsync(this CDReader reader, CancellationToken cancellationToken)
{
Log.Debug("Scanning filesystem structure…");
var fsObjects = reader.GetFileSystemEntries(reader.Root.FullName).ToList();
var nextLevel = new List<string>();
var filePaths = new List<string>();
var filePaths = new List<string>(20_000);
var dirPaths = new List<string>();
var cnt = 0;
while (fsObjects.Any())
{
foreach (var path in fsObjects)
Expand All @@ -24,6 +26,11 @@ public static (List<FileRecord> files, List<DirRecord> dirs) GetFilesystemStruct
}
else
Log.Warn($"Unknown filesystem object: {path}");
if (++cnt <= 200)
continue;

await Task.Yield();
cnt = 0;
}
(fsObjects, nextLevel) = (nextLevel, fsObjects);
nextLevel.Clear();
Expand All @@ -38,11 +45,12 @@ public static (List<FileRecord> files, List<DirRecord> dirs) GetFilesystemStruct
.Select(di => new DirRecord(di.dir, new(di.info.CreationTimeUtc, di.info.LastWriteTimeUtc)))
.ToList();

Log.Debug("Building file cluster map…");
var fileList = new List<FileRecord>();
foreach (var filename in filenames)
{
var targetFilename = filename.TrimStart('\\');
if (targetFilename.EndsWith("."))
if (targetFilename.EndsWith('.'))
{
Log.Warn($"Fixing potential mastering error in {filename}");
targetFilename = targetFilename.TrimEnd('.');
Expand All @@ -59,10 +67,13 @@ public static (List<FileRecord> files, List<DirRecord> dirs) GetFilesystemStruct
var parent = fileInfo.Parent;
var parentInfo = new DirRecord(parent.FullName.TrimStart('\\').Replace('\\', Path.DirectorySeparatorChar), new(parent.CreationTimeUtc, parent.LastWriteTimeUtc));
fileList.Add(new(targetFilename, startSector, lengthInSectors, length, recordInfo, parentInfo));
if (++cnt <= 200)
continue;

await Task.Yield();
cnt = 0;
}
fileList = fileList.OrderBy(r => r.StartSector).ToList();


return (files: fileList, dirs: dirList);
}

Expand Down
2 changes: 1 addition & 1 deletion Ps3DiscDumper/Decrypter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public override int Read(byte[] buffer, int offset, int count)
Log.Debug($"Block has only {(readCount % 16) * 8} bits of data, reading raw sector...");
discStream.Seek(SectorPosition * sectorSize, SeekOrigin.Begin);
var newTmpSector = new byte[sectorSize];
discStream.ReadExact(newTmpSector, 0, sectorSize);
discStream.ReadExactly(newTmpSector, 0, sectorSize);
if (!newTmpSector.Take(readCount).SequenceEqual(tmpSector.Take(readCount)))
Log.Warn($"Filesystem data and raw data do not match for sector 0x{SectorPosition:x8}");
tmpSector = newTmpSector;
Expand Down
10 changes: 6 additions & 4 deletions Ps3DiscDumper/DiscInfo/DiscInfoConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DiscUtils.Iso9660;
using IrdLibraryClient;
using IrdLibraryClient.IrdFormat;
Expand All @@ -11,18 +13,18 @@ namespace Ps3DiscDumper.DiscInfo;

public static class DiscInfoConverter
{
public static DiscInfo ToDiscInfo(this Ird ird)
public static async Task<DiscInfo> ToDiscInfoAsync(this Ird ird, CancellationToken cancellationToken)
{
List<FileRecord> fsInfo;
var sectorSize = 2048L;
using (var stream = new MemoryStream())
{
using (var headerStream = new MemoryStream(ird.Header))
using (var gzipStream = new GZipStream(headerStream, CompressionMode.Decompress))
gzipStream.CopyTo(stream);
await using (var gzipStream = new GZipStream(headerStream, CompressionMode.Decompress))
await gzipStream.CopyToAsync(stream, cancellationToken).ConfigureAwait(false);
stream.Seek(0, SeekOrigin.Begin);
var reader = new CDReader(stream, true, true);
(fsInfo, _) = reader.GetFilesystemStructure();
(fsInfo, _) = await reader.GetFilesystemStructureAsync(cancellationToken).ConfigureAwait(false);
sectorSize = reader.ClusterSize;
}
var checksums = new Dictionary<long, List<string>>(ird.FileCount);
Expand Down
47 changes: 29 additions & 18 deletions Ps3DiscDumper/Dumper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
Log.Trace($"Getting keys from {keyProvider.GetType().Name}...");
var newKeys = await keyProvider.EnumerateAsync(discKeyCachePath, ProductCode, Cts.Token).ConfigureAwait(false);
Log.Trace($"Got {newKeys.Count} keys from {keyProvider.GetType().Name}");
await Task.Yield();
lock (AllKnownDiscKeys)
{
foreach (var keyInfo in newKeys)
Expand Down Expand Up @@ -427,6 +428,7 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
throw;
}
Log.Debug($"Found {physicalDrives.Count} physical drives");
await Task.Yield();

if (physicalDrives.Count == 0)
throw new InvalidOperationException("No optical drives were found");
Expand All @@ -453,7 +455,7 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
var sector = tmpDiscReader.PathToClusters(discSfbInfo.FullName).First().Offset;
Log.Trace($"PS3_DISC.SFB sector number is {sector}, reading content...");
discStream.Seek(sector * tmpDiscReader.ClusterSize, SeekOrigin.Begin);
discStream.ReadExact(buf, 0, buf.Length);
await discStream.ReadExactlyAsync(buf, 0, buf.Length).ConfigureAwait(false);
if (buf.SequenceEqual(discSfbData))
{
SelectedPhysicalDevice = drive;
Expand All @@ -467,6 +469,7 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
{
Log.Debug($"Skipping drive {drive}: {e.Message}");
}
await Task.Yield();
}
if (SelectedPhysicalDevice == null)
throw new AccessViolationException("Direct disk access to the drive was denied");
Expand Down Expand Up @@ -498,6 +501,7 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
Log.Debug($"Using {path} for disc key detection");
break;
}
await Task.Yield();
}
catch (Exception e)
{
Expand All @@ -517,11 +521,12 @@ public async Task FindDiscKeyAsync(string discKeyCachePath)
detectionBytesExpected = expectedBytes;
sectorIV = Decrypter.GetSectorIV(detectionRecord.StartSector);
Log.Debug($"Initialized {nameof(sectorIV)} ({sectorIV?.Length * 8} bit) for sector {detectionRecord.StartSector}: {sectorIV?.ToHexString()}");
driveStream.ReadExact(detectionSector, 0, detectionSector.Length);
await driveStream.ReadExactlyAsync(detectionSector, 0, detectionSector.Length).ConfigureAwait(false);
string discKey = null;
try
{
var validKeys = untestedKeys.AsParallel().Where(k => !Cts.IsCancellationRequested && IsValidDiscKey(k)).Distinct().ToList();
await Task.Yield();
if (validKeys.Count > 1)
{
Log.Warn($"Expected only one valid decryption key, but found {validKeys.Count}:");
Expand Down Expand Up @@ -566,29 +571,30 @@ public async Task DumpAsync(string output)
while (!string.IsNullOrEmpty(dumpPath) && !Directory.Exists(dumpPath))
{
var parent = Path.GetDirectoryName(dumpPath);
if (parent == null || parent == dumpPath)
if (parent is null || parent == dumpPath)
dumpPath = null;
else
dumpPath = parent;
}
if (filesystemStructure is null)
{
(filesystemStructure, emptyDirStructure) = GetFilesystemStructure();
(filesystemStructure, emptyDirStructure) = await GetFilesystemStructureAsync(Cts.Token).ConfigureAwait(false);
var filterDirList = SettingsProvider.Settings.FilterDirList;
var prefixList = filterDirList.Select(f => f + Path.DirectorySeparatorChar).ToArray();
if (SettingsProvider.Settings.FilterRequired)
{
filesystemStructure = filesystemStructure
.Where(r => !filterDirList.Any(f => r.TargetFileName == f) &&
!prefixList.Any(p => r.TargetFileName.StartsWith(p)))
.ToList();
.Where(r => !filterDirList.Any(f => r.TargetFileName == f)
&& !prefixList.Any(p => r.TargetFileName.StartsWith(p))
).ToList();
emptyDirStructure = emptyDirStructure
.Where(r => !filterDirList.Any(f => r.TargetDirName == f) &&
!prefixList.Any(p => r.TargetDirName.StartsWith(p)))
.ToList();
.Where(r => !filterDirList.Any(f => r.TargetDirName == f)
&& !prefixList.Any(p => r.TargetDirName.StartsWith(p))
).ToList();
}
}
var validators = GetValidationInfo();
await Task.Yield();
var validators = await GetValidationInfoAsync().ConfigureAwait(false);
if (!string.IsNullOrEmpty(dumpPath))
{
var fullOutputPath = Path.GetFullPath(output);
Expand All @@ -606,11 +612,14 @@ public async Task DumpAsync(string output)
Log.Warn($"Target drive might require {diff.AsStorageUnit()} of additional free space");
}
}
await Task.Yield();

foreach (var dir in emptyDirStructure)
Log.Trace($"Empty dir: {dir}");
await Task.Yield();
foreach (var file in filesystemStructure)
Log.Trace($"0x{file.StartSector:x8}: {file.TargetFileName} ({file.SizeInBytes}, {file.FileInfo.CreationTimeUtc:u})");
await Task.Yield();
var outputPathBase = Path.Combine(output, OutputDir);
Log.Debug($"Output path: {outputPathBase}");
if (!Directory.Exists(outputPathBase))
Expand Down Expand Up @@ -648,6 +657,7 @@ public async Task DumpAsync(string output)
BrokenFiles.Add((dir.TargetDirName, "Unexpected error: " + ex.Message));
}
}
await Task.Yield();

foreach (var file in filesystemStructure)
{
Expand Down Expand Up @@ -733,6 +743,7 @@ select v.Files[file.TargetFileName].Hashes
ValidationStatus = false;
}
}
await Task.Yield();
} while (error && tries > 0 && !Cts.IsCancellationRequested);

_ = new FileInfo(outputFilename)
Expand Down Expand Up @@ -820,36 +831,36 @@ select v.Files[file.TargetFileName].Hashes
}


private (List<FileRecord> files, List<DirRecord> dirs) GetFilesystemStructure()
private async Task<(List<FileRecord> files, List<DirRecord> dirs)> GetFilesystemStructureAsync(CancellationToken cancellationToken)
{
var pos = driveStream.Position;
var buf = new byte[64 * 1024 * 1024];
driveStream.Seek(0, SeekOrigin.Begin);
driveStream.ReadExact(buf, 0, buf.Length);
await driveStream.ReadExactlyAsync(buf, 0, buf.Length, cancellationToken).ConfigureAwait(false);
driveStream.Seek(pos, SeekOrigin.Begin);
try
{
using var memStream = new MemoryStream(buf, false);
var reader = new CDReader(memStream, true, true);
return reader.GetFilesystemStructure();
return await reader.GetFilesystemStructureAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Error(e, "Failed to buffer TOC");
}
return discReader.GetFilesystemStructure();
return await discReader.GetFilesystemStructureAsync(cancellationToken).ConfigureAwait(false);
}

private List<DiscInfo.DiscInfo> GetValidationInfo()
private async Task<List<DiscInfo.DiscInfo>> GetValidationInfoAsync()
{
var discInfoList = new List<DiscInfo.DiscInfo>();
foreach (var discKeyInfo in allMatchingKeys.Where(ki => ki.KeyType == KeyType.Ird))
{
var ird = IrdParser.Parse(File.ReadAllBytes(discKeyInfo.FullPath));
var ird = IrdParser.Parse(await File.ReadAllBytesAsync(discKeyInfo.FullPath).ConfigureAwait(false));
if (!DiscVersion.Equals(ird.GameVersion))
continue;

discInfoList.Add(ird.ToDiscInfo());
discInfoList.Add(await ird.ToDiscInfoAsync(Cts.Token).ConfigureAwait(false));
}
return discInfoList;
}
Expand Down
4 changes: 3 additions & 1 deletion Ps3DiscDumper/Utils/StreamEx.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.IO;
using System;
using System.IO;

namespace Ps3DiscDumper.Utils;

public static class StreamEx
{
[Obsolete]
public static int ReadExact(this Stream input, byte[] buffer, int offset, int count)
{
var result = 0;
Expand Down
2 changes: 1 addition & 1 deletion Tests/IrdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public async Task FileStructureParseTest(string productCode, int expectedFileCou

await using var decompressedStream = GetDecompressHeader(ird);
var reader = new CDReader(decompressedStream, true, true);
var (files, _) = reader.GetFilesystemStructure();
var (files, _) = await reader.GetFilesystemStructureAsync(CancellationToken.None).ConfigureAwait(false);
Assert.That(files, Has.Count.EqualTo(expectedFileCount));
}

Expand Down
4 changes: 2 additions & 2 deletions UI.Avalonia/UI.Avalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release;MacOS;Linux</Configurations>
<Platforms>AnyCPU</Platforms>
<VersionPrefix>4.2.1</VersionPrefix>
<Version>4.2.1-pre1</Version>
<VersionPrefix>4.2.2</VersionPrefix>
<Version>$(VersionPrefix)-pre1</Version>
<DebugType>PdbOnly</DebugType>
</PropertyGroup>

Expand Down
Loading

0 comments on commit a73ad0c

Please sign in to comment.