mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-04-05 21:44:17 +00:00
Add ability to merge flux
This commit is contained in:
@@ -9,6 +9,24 @@ namespace Aaru.Core.Image;
|
||||
|
||||
public sealed partial class Merger
|
||||
{
|
||||
/// <summary>
|
||||
/// Single extent covering sectors <c>0 .. sectorCount - 1</c>, or an empty list when
|
||||
/// <paramref name="sectorCount" /> is zero (avoids <c>0 - 1</c> unsigned underflow in callers).
|
||||
/// </summary>
|
||||
static List<Extent> CreateDefaultDumpExtents(ulong sectorCount)
|
||||
{
|
||||
if(sectorCount == 0) return new List<Extent>();
|
||||
|
||||
return new List<Extent>
|
||||
{
|
||||
new Extent
|
||||
{
|
||||
Start = 0,
|
||||
End = sectorCount - 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
List<ulong> CalculateSectorsToCopy(IMediaImage primaryImage, IMediaImage secondaryImage, Resume primaryResume,
|
||||
Resume secondaryResume, List<ulong> overrideSectorsList)
|
||||
{
|
||||
@@ -16,14 +34,7 @@ public sealed partial class Merger
|
||||
[
|
||||
new DumpHardware
|
||||
{
|
||||
Extents =
|
||||
[
|
||||
new Extent
|
||||
{
|
||||
Start = 0,
|
||||
End = primaryImage.Info.Sectors - 1
|
||||
}
|
||||
]
|
||||
Extents = CreateDefaultDumpExtents(primaryImage.Info.Sectors)
|
||||
}
|
||||
];
|
||||
|
||||
@@ -32,14 +43,7 @@ public sealed partial class Merger
|
||||
[
|
||||
new DumpHardware
|
||||
{
|
||||
Extents =
|
||||
[
|
||||
new Extent
|
||||
{
|
||||
Start = 0,
|
||||
End = secondaryImage.Info.Sectors - 1
|
||||
}
|
||||
]
|
||||
Extents = CreateDefaultDumpExtents(secondaryImage.Info.Sectors)
|
||||
}
|
||||
];
|
||||
|
||||
@@ -93,14 +97,7 @@ public sealed partial class Merger
|
||||
[
|
||||
new DumpHardware
|
||||
{
|
||||
Extents =
|
||||
[
|
||||
new Extent
|
||||
{
|
||||
Start = 0,
|
||||
End = primaryImage.Info.Sectors - 1
|
||||
}
|
||||
]
|
||||
Extents = CreateDefaultDumpExtents(primaryImage.Info.Sectors)
|
||||
}
|
||||
];
|
||||
|
||||
@@ -109,14 +106,7 @@ public sealed partial class Merger
|
||||
[
|
||||
new DumpHardware
|
||||
{
|
||||
Extents =
|
||||
[
|
||||
new Extent
|
||||
{
|
||||
Start = 0,
|
||||
End = secondaryImage.Info.Sectors - 1
|
||||
}
|
||||
]
|
||||
Extents = CreateDefaultDumpExtents(secondaryImage.Info.Sectors)
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,52 +1,140 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
|
||||
namespace Aaru.Core.Image;
|
||||
|
||||
public sealed partial class Merger
|
||||
{
|
||||
// TODO: Should we return error any time?
|
||||
// TODO: Add progress reporting
|
||||
ErrorNumber CopyFlux(IFluxImage inputFlux, IWritableFluxImage outputFlux)
|
||||
ErrorNumber MergeFlux(IFluxImage primaryFlux, IFluxImage secondaryFlux, IWritableFluxImage outputFlux)
|
||||
{
|
||||
for(ushort track = 0; track < inputFlux.Info.Cylinders; track++)
|
||||
ErrorNumber error = primaryFlux.GetAllFluxCaptures(out List<FluxCapture> primaryCaptures);
|
||||
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
if(primaryCaptures is null) primaryCaptures = new List<FluxCapture>();
|
||||
|
||||
List<FluxCapture> secondaryCaptures = new List<FluxCapture>();
|
||||
|
||||
if(secondaryFlux != null)
|
||||
{
|
||||
for(uint head = 0; head < inputFlux.Info.Heads; head++)
|
||||
error = secondaryFlux.GetAllFluxCaptures(out secondaryCaptures);
|
||||
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
if(secondaryCaptures is null) secondaryCaptures = new List<FluxCapture>();
|
||||
}
|
||||
|
||||
Dictionary<(uint Head, ushort Track, byte SubTrack), List<FluxCapture>> primaryByLocation =
|
||||
GroupFluxCapturesByLocation(primaryCaptures);
|
||||
|
||||
Dictionary<(uint Head, ushort Track, byte SubTrack), List<FluxCapture>> secondaryByLocation =
|
||||
GroupFluxCapturesByLocation(secondaryCaptures);
|
||||
|
||||
HashSet<(uint Head, ushort Track, byte SubTrack)> allKeys = new HashSet<(uint Head, ushort Track, byte SubTrack)>();
|
||||
|
||||
foreach((uint Head, ushort Track, byte SubTrack) key in primaryByLocation.Keys) allKeys.Add(key);
|
||||
|
||||
foreach((uint Head, ushort Track, byte SubTrack) key in secondaryByLocation.Keys) allKeys.Add(key);
|
||||
|
||||
List<(uint Head, ushort Track, byte SubTrack)> sortedKeys =
|
||||
allKeys.OrderBy(static k => k.Track).ThenBy(static k => k.Head).ThenBy(static k => k.SubTrack).ToList();
|
||||
|
||||
foreach((uint Head, ushort Track, byte SubTrack) key in sortedKeys)
|
||||
{
|
||||
List<FluxCapture> primaryGroup =
|
||||
primaryByLocation.TryGetValue(key, out List<FluxCapture> pg) ? pg : new List<FluxCapture>();
|
||||
|
||||
List<FluxCapture> secondaryGroup =
|
||||
secondaryByLocation.TryGetValue(key, out List<FluxCapture> sg) ? sg : new List<FluxCapture>();
|
||||
|
||||
primaryGroup.Sort((FluxCapture a, FluxCapture b) => a.CaptureIndex.CompareTo(b.CaptureIndex));
|
||||
secondaryGroup.Sort((FluxCapture a, FluxCapture b) => a.CaptureIndex.CompareTo(b.CaptureIndex));
|
||||
|
||||
uint outputIndex = 0;
|
||||
|
||||
foreach(FluxCapture capture in primaryGroup)
|
||||
{
|
||||
ErrorNumber error = inputFlux.SubTrackLength(head, track, out byte subTrackLen);
|
||||
error = primaryFlux.ReadFluxCapture(capture.Head,
|
||||
capture.Track,
|
||||
capture.SubTrack,
|
||||
capture.CaptureIndex,
|
||||
out ulong indexResolution,
|
||||
out ulong dataResolution,
|
||||
out byte[] indexBuffer,
|
||||
out byte[] dataBuffer);
|
||||
|
||||
if(error != ErrorNumber.NoError) continue;
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
for(byte subTrackIndex = 0; subTrackIndex < subTrackLen; subTrackIndex++)
|
||||
error = outputFlux.WriteFluxCapture(indexResolution,
|
||||
dataResolution,
|
||||
indexBuffer,
|
||||
dataBuffer,
|
||||
capture.Head,
|
||||
capture.Track,
|
||||
capture.SubTrack,
|
||||
outputIndex);
|
||||
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
outputIndex++;
|
||||
}
|
||||
|
||||
if(secondaryFlux != null)
|
||||
{
|
||||
foreach(FluxCapture capture in secondaryGroup)
|
||||
{
|
||||
error = inputFlux.CapturesLength(head, track, subTrackIndex, out uint capturesLen);
|
||||
error = secondaryFlux.ReadFluxCapture(capture.Head,
|
||||
capture.Track,
|
||||
capture.SubTrack,
|
||||
capture.CaptureIndex,
|
||||
out ulong indexResolution,
|
||||
out ulong dataResolution,
|
||||
out byte[] indexBuffer,
|
||||
out byte[] dataBuffer);
|
||||
|
||||
if(error != ErrorNumber.NoError) continue;
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
for(uint captureIndex = 0; captureIndex < capturesLen; captureIndex++)
|
||||
{
|
||||
inputFlux.ReadFluxCapture(head,
|
||||
track,
|
||||
subTrackIndex,
|
||||
captureIndex,
|
||||
out ulong indexResolution,
|
||||
out ulong dataResolution,
|
||||
out byte[] indexBuffer,
|
||||
out byte[] dataBuffer);
|
||||
error = outputFlux.WriteFluxCapture(indexResolution,
|
||||
dataResolution,
|
||||
indexBuffer,
|
||||
dataBuffer,
|
||||
capture.Head,
|
||||
capture.Track,
|
||||
capture.SubTrack,
|
||||
outputIndex);
|
||||
|
||||
outputFlux.WriteFluxCapture(indexResolution,
|
||||
dataResolution,
|
||||
indexBuffer,
|
||||
dataBuffer,
|
||||
head,
|
||||
track,
|
||||
subTrackIndex,
|
||||
captureIndex);
|
||||
}
|
||||
if(error != ErrorNumber.NoError) return error;
|
||||
|
||||
outputIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<(uint Head, ushort Track, byte SubTrack), List<FluxCapture>> GroupFluxCapturesByLocation(
|
||||
List<FluxCapture> captures)
|
||||
{
|
||||
Dictionary<(uint Head, ushort Track, byte SubTrack), List<FluxCapture>> result =
|
||||
new Dictionary<(uint Head, ushort Track, byte SubTrack), List<FluxCapture>>();
|
||||
|
||||
foreach(FluxCapture capture in captures)
|
||||
{
|
||||
(uint Head, ushort Track, byte SubTrack) key = (capture.Head, capture.Track, capture.SubTrack);
|
||||
|
||||
if(!result.TryGetValue(key, out List<FluxCapture> list))
|
||||
{
|
||||
list = new List<FluxCapture>();
|
||||
result[key] = list;
|
||||
}
|
||||
|
||||
list.Add(capture);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,12 +220,15 @@ public sealed partial class Merger
|
||||
InitProgress?.Invoke();
|
||||
PulseProgress?.Invoke(UI.Calculating_sectors_to_merge);
|
||||
|
||||
List<ulong> sectorsToCopyFromSecondImage =
|
||||
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
|
||||
List<ulong> sectorsToCopyFromSecondImage =
|
||||
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
|
||||
|
||||
EndProgress?.Invoke();
|
||||
|
||||
if(sectorsToCopyFromSecondImage.Count == 0)
|
||||
// Flux images might contain no decoded data, which results in a sector count of 0. We allow this if the image contains flux.
|
||||
var containsFlux = primaryImage is IFluxImage || secondaryImage is IFluxImage;
|
||||
|
||||
if(sectorsToCopyFromSecondImage.Count == 0 && !containsFlux)
|
||||
{
|
||||
StoppingErrorMessage
|
||||
?.Invoke(UI.No_sectors_to_merge__output_image_will_be_identical_to_primary_image_not_continuing);
|
||||
@@ -344,10 +347,15 @@ public sealed partial class Merger
|
||||
|
||||
if(errno != ErrorNumber.NoError) return errno;
|
||||
|
||||
if(primaryImage is IFluxImage inputFlux && outputFormat is IWritableFluxImage outputFlux)
|
||||
if(primaryImage is IFluxImage primaryFlux && outputFormat is IWritableFluxImage outputFlux)
|
||||
{
|
||||
UpdateStatus?.Invoke(UI.Flux_data_will_be_copied_as_is_from_primary_image);
|
||||
errno = CopyFlux(inputFlux, outputFlux);
|
||||
IFluxImage secondaryFlux = secondaryImage as IFluxImage;
|
||||
|
||||
UpdateStatus?.Invoke(secondaryFlux != null
|
||||
? UI.Flux_merge_primary_then_secondary_appended
|
||||
: UI.Flux_merge_primary_flux_only_secondary_not_flux_image);
|
||||
|
||||
errno = MergeFlux(primaryFlux, secondaryFlux, outputFlux);
|
||||
|
||||
if(errno != ErrorNumber.NoError) return errno;
|
||||
}
|
||||
|
||||
18
Aaru.Localization/UI.Designer.cs
generated
18
Aaru.Localization/UI.Designer.cs
generated
@@ -5115,6 +5115,24 @@ namespace Aaru.Localization {
|
||||
return ResourceManager.GetString("Flux_data_will_be_copied_as_is_from_primary_image", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up localized string similar to Merging flux: primary captures first, then secondary captures appended with renumbered indices...
|
||||
/// </summary>
|
||||
public static string Flux_merge_primary_then_secondary_appended {
|
||||
get {
|
||||
return ResourceManager.GetString("Flux_merge_primary_then_secondary_appended", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up localized string similar to Secondary image is not a flux image; copying flux captures from the primary image only...
|
||||
/// </summary>
|
||||
public static string Flux_merge_primary_flux_only_secondary_not_flux_image {
|
||||
get {
|
||||
return ResourceManager.GetString("Flux_merge_primary_flux_only_secondary_not_flux_image", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Force geometry, only supported in not tape block media. Specify as C/H/S..
|
||||
|
||||
@@ -5452,8 +5452,11 @@ Probadores:
|
||||
<data name="Will_copy_0_sectors_from_secondary_image" xml:space="preserve">
|
||||
<value>[slateblue1]Se copiarán [teal]{0}[/] sectores de la imagen secundaria.[/]</value>
|
||||
</data>
|
||||
<data name="Flux_data_will_be_copied_as_is_from_primary_image" xml:space="preserve">
|
||||
<value>[slateblue1]Los datos de flujo se copiarán tal cual de la imagen primaria...[/]</value>
|
||||
<data name="Flux_merge_primary_then_secondary_appended" xml:space="preserve">
|
||||
<value>[slateblue1]Combinando flujo: primero capturas de la imagen primaria, luego las de la secundaria con índices renumerados...[/]</value>
|
||||
</data>
|
||||
<data name="Flux_merge_primary_flux_only_secondary_not_flux_image" xml:space="preserve">
|
||||
<value>[slateblue1]La imagen secundaria no es de flujo; copiando solo las capturas de flujo de la imagen primaria...[/]</value>
|
||||
</data>
|
||||
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
||||
<value>[slateblue1]Copiando fichero [lime]{0}[/] de partición [teal]{1}[/]...[/]</value>
|
||||
|
||||
@@ -5536,8 +5536,11 @@ Do you want to continue?</value>
|
||||
<data name="Will_copy_0_sectors_from_secondary_image" xml:space="preserve">
|
||||
<value>[slateblue1]Will copy [teal]{0}[/] sectors from secondary image.[/]</value>
|
||||
</data>
|
||||
<data name="Flux_data_will_be_copied_as_is_from_primary_image" xml:space="preserve">
|
||||
<value>[slateblue1]Flux data will be copied as-is from primary image...[/]</value>
|
||||
<data name="Flux_merge_primary_then_secondary_appended" xml:space="preserve">
|
||||
<value>[slateblue1]Merging flux: primary captures first, then secondary captures appended with renumbered indices...[/]</value>
|
||||
</data>
|
||||
<data name="Flux_merge_primary_flux_only_secondary_not_flux_image" xml:space="preserve">
|
||||
<value>[slateblue1]Secondary image is not a flux image; copying flux captures from the primary image only...[/]</value>
|
||||
</data>
|
||||
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
||||
<value>[slateblue1]Copying file [lime]{0}[/] of partition [teal]{1}[/]...[/]</value>
|
||||
|
||||
Reference in New Issue
Block a user