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
|
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,
|
List<ulong> CalculateSectorsToCopy(IMediaImage primaryImage, IMediaImage secondaryImage, Resume primaryResume,
|
||||||
Resume secondaryResume, List<ulong> overrideSectorsList)
|
Resume secondaryResume, List<ulong> overrideSectorsList)
|
||||||
{
|
{
|
||||||
@@ -16,14 +34,7 @@ public sealed partial class Merger
|
|||||||
[
|
[
|
||||||
new DumpHardware
|
new DumpHardware
|
||||||
{
|
{
|
||||||
Extents =
|
Extents = CreateDefaultDumpExtents(primaryImage.Info.Sectors)
|
||||||
[
|
|
||||||
new Extent
|
|
||||||
{
|
|
||||||
Start = 0,
|
|
||||||
End = primaryImage.Info.Sectors - 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -32,14 +43,7 @@ public sealed partial class Merger
|
|||||||
[
|
[
|
||||||
new DumpHardware
|
new DumpHardware
|
||||||
{
|
{
|
||||||
Extents =
|
Extents = CreateDefaultDumpExtents(secondaryImage.Info.Sectors)
|
||||||
[
|
|
||||||
new Extent
|
|
||||||
{
|
|
||||||
Start = 0,
|
|
||||||
End = secondaryImage.Info.Sectors - 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -93,14 +97,7 @@ public sealed partial class Merger
|
|||||||
[
|
[
|
||||||
new DumpHardware
|
new DumpHardware
|
||||||
{
|
{
|
||||||
Extents =
|
Extents = CreateDefaultDumpExtents(primaryImage.Info.Sectors)
|
||||||
[
|
|
||||||
new Extent
|
|
||||||
{
|
|
||||||
Start = 0,
|
|
||||||
End = primaryImage.Info.Sectors - 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -109,14 +106,7 @@ public sealed partial class Merger
|
|||||||
[
|
[
|
||||||
new DumpHardware
|
new DumpHardware
|
||||||
{
|
{
|
||||||
Extents =
|
Extents = CreateDefaultDumpExtents(secondaryImage.Info.Sectors)
|
||||||
[
|
|
||||||
new Extent
|
|
||||||
{
|
|
||||||
Start = 0,
|
|
||||||
End = secondaryImage.Info.Sectors - 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +1,140 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Aaru.CommonTypes.Enums;
|
using Aaru.CommonTypes.Enums;
|
||||||
using Aaru.CommonTypes.Interfaces;
|
using Aaru.CommonTypes.Interfaces;
|
||||||
|
using Aaru.CommonTypes.Structs;
|
||||||
|
|
||||||
namespace Aaru.Core.Image;
|
namespace Aaru.Core.Image;
|
||||||
|
|
||||||
public sealed partial class Merger
|
public sealed partial class Merger
|
||||||
{
|
{
|
||||||
// TODO: Should we return error any time?
|
ErrorNumber MergeFlux(IFluxImage primaryFlux, IFluxImage secondaryFlux, IWritableFluxImage outputFlux)
|
||||||
// TODO: Add progress reporting
|
|
||||||
ErrorNumber CopyFlux(IFluxImage inputFlux, 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++)
|
error = outputFlux.WriteFluxCapture(indexResolution,
|
||||||
{
|
dataResolution,
|
||||||
inputFlux.ReadFluxCapture(head,
|
indexBuffer,
|
||||||
track,
|
dataBuffer,
|
||||||
subTrackIndex,
|
capture.Head,
|
||||||
captureIndex,
|
capture.Track,
|
||||||
out ulong indexResolution,
|
capture.SubTrack,
|
||||||
out ulong dataResolution,
|
outputIndex);
|
||||||
out byte[] indexBuffer,
|
|
||||||
out byte[] dataBuffer);
|
|
||||||
|
|
||||||
outputFlux.WriteFluxCapture(indexResolution,
|
if(error != ErrorNumber.NoError) return error;
|
||||||
dataResolution,
|
|
||||||
indexBuffer,
|
outputIndex++;
|
||||||
dataBuffer,
|
|
||||||
head,
|
|
||||||
track,
|
|
||||||
subTrackIndex,
|
|
||||||
captureIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorNumber.NoError;
|
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();
|
InitProgress?.Invoke();
|
||||||
PulseProgress?.Invoke(UI.Calculating_sectors_to_merge);
|
PulseProgress?.Invoke(UI.Calculating_sectors_to_merge);
|
||||||
|
|
||||||
List<ulong> sectorsToCopyFromSecondImage =
|
List<ulong> sectorsToCopyFromSecondImage =
|
||||||
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
|
CalculateSectorsToCopy(primaryImage, secondaryImage, primaryResume, secondaryResume, overrideSectorsList);
|
||||||
|
|
||||||
EndProgress?.Invoke();
|
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
|
StoppingErrorMessage
|
||||||
?.Invoke(UI.No_sectors_to_merge__output_image_will_be_identical_to_primary_image_not_continuing);
|
?.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(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);
|
IFluxImage secondaryFlux = secondaryImage as IFluxImage;
|
||||||
errno = CopyFlux(inputFlux, outputFlux);
|
|
||||||
|
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;
|
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);
|
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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Force geometry, only supported in not tape block media. Specify as C/H/S..
|
/// 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">
|
<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>
|
<value>[slateblue1]Se copiarán [teal]{0}[/] sectores de la imagen secundaria.[/]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Flux_data_will_be_copied_as_is_from_primary_image" xml:space="preserve">
|
<data name="Flux_merge_primary_then_secondary_appended" xml:space="preserve">
|
||||||
<value>[slateblue1]Los datos de flujo se copiarán tal cual de la imagen primaria...[/]</value>
|
<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>
|
||||||
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
||||||
<value>[slateblue1]Copiando fichero [lime]{0}[/] de partición [teal]{1}[/]...[/]</value>
|
<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">
|
<data name="Will_copy_0_sectors_from_secondary_image" xml:space="preserve">
|
||||||
<value>[slateblue1]Will copy [teal]{0}[/] sectors from secondary image.[/]</value>
|
<value>[slateblue1]Will copy [teal]{0}[/] sectors from secondary image.[/]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Flux_data_will_be_copied_as_is_from_primary_image" xml:space="preserve">
|
<data name="Flux_merge_primary_then_secondary_appended" xml:space="preserve">
|
||||||
<value>[slateblue1]Flux data will be copied as-is from primary image...[/]</value>
|
<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>
|
||||||
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
<data name="Copying_file_0_of_partition_1" xml:space="preserve">
|
||||||
<value>[slateblue1]Copying file [lime]{0}[/] of partition [teal]{1}[/]...[/]</value>
|
<value>[slateblue1]Copying file [lime]{0}[/] of partition [teal]{1}[/]...[/]</value>
|
||||||
|
|||||||
Reference in New Issue
Block a user