mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-04-06 05:54:06 +00:00
Update Kryoflux
This commit is contained in:
@@ -31,156 +31,13 @@
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.Helpers;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global")]
|
||||
public sealed partial class KryoFlux
|
||||
{
|
||||
/// <summary>
|
||||
/// Decodes KryoFlux stream format into cell values (flux transition timings).
|
||||
/// Per KryoFlux spec: The stream uses encoding markers to efficiently encode flux transition timings.
|
||||
/// </summary>
|
||||
/// <param name="streamData">The raw stream data to decode</param>
|
||||
/// <param name="streamLength">Length of the stream data</param>
|
||||
/// <param name="cellValues">Output list of decoded cell values (flux transition timings in clock cycles)</param>
|
||||
/// <returns>Error number indicating success or failure</returns>
|
||||
static ErrorNumber DecodeKryoFluxStream(byte[] streamData, int streamLength, out List<uint> cellValues)
|
||||
{
|
||||
cellValues = new List<uint>();
|
||||
|
||||
if(streamData == null || streamLength <= 0) return ErrorNumber.InvalidArgument;
|
||||
|
||||
int streamPosition = 0;
|
||||
|
||||
while(streamPosition < streamLength)
|
||||
{
|
||||
byte encodingMarker = streamData[streamPosition++];
|
||||
|
||||
if(encodingMarker <= 0x07)
|
||||
{
|
||||
// Value: 16-bit value where upper 8 bits are the marker, lower 8 bits are next byte
|
||||
if(streamPosition >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte lowerByte = streamData[streamPosition++];
|
||||
uint cellValue = (uint)((encodingMarker << 8) | lowerByte);
|
||||
|
||||
cellValues.Add(cellValue);
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Nop1)
|
||||
{
|
||||
// Nop1: Skip 1 byte
|
||||
streamPosition++;
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Nop2)
|
||||
{
|
||||
// Nop2: Skip 2 bytes
|
||||
streamPosition += 2;
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Nop3)
|
||||
{
|
||||
// Nop3: Skip 3 bytes
|
||||
streamPosition += 3;
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Ovl16)
|
||||
{
|
||||
// Overflow16: Next cell value is increased by 0x10000
|
||||
// Continue decoding at next stream position
|
||||
// The actual value will be determined by the next encoding marker
|
||||
if(streamPosition >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte nextMarker = streamData[streamPosition++];
|
||||
uint cellValue = 0x10000;
|
||||
|
||||
if(nextMarker <= 0x07)
|
||||
{
|
||||
// Value16 after overflow
|
||||
if(streamPosition >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte lowerByte = streamData[streamPosition++];
|
||||
cellValue += (uint)((nextMarker << 8) | lowerByte);
|
||||
}
|
||||
else if(nextMarker == (byte)BlockIds.Flux3)
|
||||
{
|
||||
// Value16: 16-bit value from next 2 bytes
|
||||
if(streamPosition + 1 >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte byte1 = streamData[streamPosition++];
|
||||
byte byte2 = streamData[streamPosition++];
|
||||
cellValue += (uint)((byte2 << 8) | byte1);
|
||||
}
|
||||
else if(nextMarker >= 0x0E)
|
||||
{
|
||||
// Sample: direct value
|
||||
cellValue += (uint)(nextMarker - 0x0D);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid encoding after overflow
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
cellValues.Add(cellValue);
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Flux3)
|
||||
{
|
||||
// Value16: 16-bit value from next 2 bytes
|
||||
if(streamPosition + 1 >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte byte1 = streamData[streamPosition++];
|
||||
byte byte2 = streamData[streamPosition++];
|
||||
uint cellValue = (uint)((byte2 << 8) | byte1);
|
||||
|
||||
cellValues.Add(cellValue);
|
||||
}
|
||||
else if(encodingMarker == (byte)BlockIds.Oob)
|
||||
{
|
||||
// OOB header: This should be handled by the caller, but if we encounter it here,
|
||||
// we need to skip past the OOB block. The OOB block structure is:
|
||||
// byte 0: 0x0D (already read)
|
||||
// byte 1: OOB type
|
||||
// bytes 2-3: length (little-endian)
|
||||
// bytes 4+: data
|
||||
if(streamPosition + 2 >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte oobType = streamData[streamPosition++];
|
||||
ushort oobLength = (ushort)(streamData[streamPosition] | (streamData[streamPosition + 1] << 8));
|
||||
streamPosition += 2;
|
||||
|
||||
// Skip OOB data
|
||||
streamPosition += oobLength;
|
||||
}
|
||||
else if(encodingMarker >= 0x0E)
|
||||
{
|
||||
// Sample: direct value (marker - 0x0D)
|
||||
uint cellValue = (uint)(encodingMarker - 0x0D);
|
||||
cellValues.Add(cellValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flux2 variants (0x00-0x07 already handled above)
|
||||
// These are legacy encodings, treat as regular values
|
||||
// Actually, 0x00-0x07 are already handled, so this shouldn't happen
|
||||
// But handle Flux2_1 through Flux2_7 just in case
|
||||
if(encodingMarker >= (byte)BlockIds.Flux2_1 && encodingMarker <= (byte)BlockIds.Flux2_7)
|
||||
{
|
||||
// These are 2-byte values where the marker indicates the high byte
|
||||
if(streamPosition >= streamLength) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte lowerByte = streamData[streamPosition++];
|
||||
uint cellValue = (uint)((encodingMarker << 8) | lowerByte);
|
||||
cellValues.Add(cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a uint32 cell value to Aaru's flux representation format.
|
||||
/// Format: byte array where 255 = overflow, remainder = value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// /***************************************************************************
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
@@ -41,6 +41,7 @@ using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Filters;
|
||||
using Aaru.Helpers;
|
||||
using Aaru.Logging;
|
||||
using Aaru.CommonTypes;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
@@ -142,6 +143,8 @@ public sealed partial class KryoFlux
|
||||
|
||||
_imageInfo.Heads = heads;
|
||||
_imageInfo.Cylinders = (uint)(tracks.Count / heads);
|
||||
// TODO: Find a way to determine the media type from the track data.
|
||||
_imageInfo.MediaType = MediaType.DOS_35_HD;
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
@@ -155,186 +158,165 @@ public sealed partial class KryoFlux
|
||||
Stream trackStream = trackFilter.GetDataForkStream();
|
||||
trackStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var fluxPulses = new List<uint>();
|
||||
var indexPositions = new List<uint>();
|
||||
double sck = SCK; // Default sample clock frequency
|
||||
double ick = ICK; // Default index clock frequency
|
||||
bool inStreamRead = false;
|
||||
|
||||
// Read entire file into memory for processing
|
||||
var fileData = new byte[trackStream.Length];
|
||||
byte[] fileData = new byte[trackStream.Length];
|
||||
trackStream.EnsureRead(fileData, 0, (int)trackStream.Length);
|
||||
|
||||
int streamPosition = 0;
|
||||
var streamData = new List<byte>();
|
||||
int fileSize = fileData.Length;
|
||||
|
||||
while(streamPosition < fileData.Length)
|
||||
uint[] cellValues = new uint[fileSize];
|
||||
uint[] cellStreamPositions = new uint[fileSize];
|
||||
|
||||
uint cellAccumulator = 0;
|
||||
int streamOfs = 0;
|
||||
uint streamPos = 0;
|
||||
int cellPos = 0;
|
||||
bool oobEnd = false;
|
||||
|
||||
double sck = SCK;
|
||||
double ick = ICK;
|
||||
|
||||
List<(uint streamPosition, uint timer, uint sysTime)> indexEvents = [];
|
||||
|
||||
int oobHeaderSize = Marshal.SizeOf<OobBlock>();
|
||||
|
||||
while(streamOfs < fileSize && !oobEnd)
|
||||
{
|
||||
byte blockId = fileData[streamPosition++];
|
||||
byte curOp = fileData[streamOfs];
|
||||
int curOpLen;
|
||||
|
||||
switch(blockId)
|
||||
if(curOp == (byte)BlockIds.Oob)
|
||||
{
|
||||
case (byte)BlockIds.Oob:
|
||||
if(fileSize - streamOfs < oobHeaderSize)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte[] oobBytes = new byte[oobHeaderSize];
|
||||
Array.Copy(fileData, streamOfs, oobBytes, 0, oobHeaderSize);
|
||||
OobBlock oobBlk = Marshal.ByteArrayToStructureLittleEndian<OobBlock>(oobBytes);
|
||||
|
||||
if(oobBlk.blockType == OobTypes.EOF)
|
||||
{
|
||||
streamPosition--;
|
||||
oobEnd = true;
|
||||
|
||||
if(streamPosition + Marshal.SizeOf<OobBlock>() > fileData.Length)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
break;
|
||||
}
|
||||
|
||||
var oob = new byte[Marshal.SizeOf<OobBlock>()];
|
||||
Array.Copy(fileData, streamPosition, oob, 0, Marshal.SizeOf<OobBlock>());
|
||||
streamPosition += Marshal.SizeOf<OobBlock>();
|
||||
curOpLen = oobHeaderSize + oobBlk.length;
|
||||
|
||||
OobBlock oobBlk = Marshal.ByteArrayToStructureLittleEndian<OobBlock>(oob);
|
||||
if(fileSize - streamOfs < curOpLen)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
if(oobBlk.blockType == OobTypes.EOF)
|
||||
int oobDataStart = streamOfs + oobHeaderSize;
|
||||
|
||||
switch(oobBlk.blockType)
|
||||
{
|
||||
case OobTypes.KFInfo:
|
||||
{
|
||||
// Process any remaining stream data
|
||||
if(streamData.Count > 0 && inStreamRead)
|
||||
byte[] kfinfo = new byte[oobBlk.length];
|
||||
Array.Copy(fileData, oobDataStart, kfinfo, 0, oobBlk.length);
|
||||
|
||||
string kfinfoStr = StringHandlers.CToString(kfinfo);
|
||||
string[] lines = kfinfoStr.Split([','], StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
DateTime blockDate = DateTime.Now;
|
||||
DateTime blockTime = DateTime.Now;
|
||||
bool foundDate = false;
|
||||
|
||||
foreach(string[] kvp in lines.Select(static line => line.Split('='))
|
||||
.Where(static kvp => kvp.Length == 2))
|
||||
{
|
||||
ErrorNumber decodeError = DecodeKryoFluxStream(streamData.ToArray(), streamData.Count,
|
||||
out List<uint> cells);
|
||||
kvp[0] = kvp[0].Trim();
|
||||
kvp[1] = kvp[1].Trim();
|
||||
AaruLogging.Debug(MODULE_NAME, "\"{0}\" = \"{1}\"", kvp[0], kvp[1]);
|
||||
|
||||
if(decodeError != ErrorNumber.NoError) return decodeError;
|
||||
switch(kvp[0])
|
||||
{
|
||||
case HOST_DATE:
|
||||
if(DateTime.TryParseExact(kvp[1],
|
||||
"yyyy.MM.dd",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeLocal,
|
||||
out blockDate))
|
||||
foundDate = true;
|
||||
|
||||
fluxPulses.AddRange(cells);
|
||||
streamData.Clear();
|
||||
inStreamRead = false;
|
||||
break;
|
||||
case HOST_TIME:
|
||||
DateTime.TryParseExact(kvp[1],
|
||||
"HH:mm:ss",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeLocal,
|
||||
out blockTime);
|
||||
|
||||
break;
|
||||
case KF_NAME:
|
||||
_imageInfo.Application = kvp[1];
|
||||
|
||||
break;
|
||||
case KF_VERSION:
|
||||
_imageInfo.ApplicationVersion = kvp[1];
|
||||
|
||||
break;
|
||||
case KF_SCK:
|
||||
if(double.TryParse(kvp[1], NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||
out double parsedSck))
|
||||
sck = parsedSck;
|
||||
|
||||
break;
|
||||
case KF_ICK:
|
||||
if(double.TryParse(kvp[1], NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||
out double parsedIck))
|
||||
ick = parsedIck;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
streamPosition = fileData.Length;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(streamPosition + oobBlk.length > fileData.Length) return ErrorNumber.InvalidArgument;
|
||||
|
||||
switch(oobBlk.blockType)
|
||||
{
|
||||
case OobTypes.KFInfo:
|
||||
if(foundDate)
|
||||
{
|
||||
var kfinfo = new byte[oobBlk.length];
|
||||
Array.Copy(fileData, streamPosition, kfinfo, 0, oobBlk.length);
|
||||
streamPosition += oobBlk.length;
|
||||
|
||||
string kfinfoStr = StringHandlers.CToString(kfinfo);
|
||||
string[] lines = kfinfoStr.Split([','], StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
DateTime blockDate = DateTime.Now;
|
||||
DateTime blockTime = DateTime.Now;
|
||||
var foundDate = false;
|
||||
|
||||
foreach(string[] kvp in lines.Select(static line => line.Split('='))
|
||||
.Where(static kvp => kvp.Length == 2))
|
||||
{
|
||||
kvp[0] = kvp[0].Trim();
|
||||
kvp[1] = kvp[1].Trim();
|
||||
AaruLogging.Debug(MODULE_NAME, "\"{0}\" = \"{1}\"", kvp[0], kvp[1]);
|
||||
|
||||
switch(kvp[0])
|
||||
{
|
||||
case HOST_DATE:
|
||||
if(DateTime.TryParseExact(kvp[1],
|
||||
"yyyy.MM.dd",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeLocal,
|
||||
out blockDate))
|
||||
foundDate = true;
|
||||
|
||||
break;
|
||||
case HOST_TIME:
|
||||
DateTime.TryParseExact(kvp[1],
|
||||
"HH:mm:ss",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeLocal,
|
||||
out blockTime);
|
||||
|
||||
break;
|
||||
case KF_NAME:
|
||||
_imageInfo.Application = kvp[1];
|
||||
|
||||
break;
|
||||
case KF_VERSION:
|
||||
_imageInfo.ApplicationVersion = kvp[1];
|
||||
|
||||
break;
|
||||
case KF_SCK:
|
||||
if(double.TryParse(kvp[1], NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||
out double parsedSck))
|
||||
sck = parsedSck;
|
||||
|
||||
break;
|
||||
case KF_ICK:
|
||||
if(double.TryParse(kvp[1], NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||
out double parsedIck))
|
||||
ick = parsedIck;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundDate)
|
||||
{
|
||||
var blockTimestamp = new DateTime(blockDate.Year,
|
||||
DateTime blockTimestamp = new DateTime(blockDate.Year,
|
||||
blockDate.Month,
|
||||
blockDate.Day,
|
||||
blockTime.Hour,
|
||||
blockTime.Minute,
|
||||
blockTime.Second);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME, Localization.Found_timestamp_0, blockTimestamp);
|
||||
AaruLogging.Debug(MODULE_NAME, Localization.Found_timestamp_0, blockTimestamp);
|
||||
|
||||
if(blockTimestamp < Info.CreationTime) _imageInfo.CreationTime = blockTimestamp;
|
||||
if(blockTimestamp < Info.CreationTime) _imageInfo.CreationTime = blockTimestamp;
|
||||
|
||||
if(blockTimestamp > Info.LastModificationTime)
|
||||
_imageInfo.LastModificationTime = blockTimestamp;
|
||||
}
|
||||
|
||||
break;
|
||||
if(blockTimestamp > Info.LastModificationTime)
|
||||
_imageInfo.LastModificationTime = blockTimestamp;
|
||||
}
|
||||
case OobTypes.StreamInfo:
|
||||
|
||||
break;
|
||||
}
|
||||
case OobTypes.StreamInfo:
|
||||
{
|
||||
if(oobDataStart + Marshal.SizeOf<OobStreamRead>() <= fileSize)
|
||||
{
|
||||
// Process any previous stream data
|
||||
if(streamData.Count > 0 && inStreamRead)
|
||||
{
|
||||
ErrorNumber decodeError = DecodeKryoFluxStream(streamData.ToArray(), streamData.Count,
|
||||
out List<uint> cells);
|
||||
byte[] streamReadBytes = new byte[Marshal.SizeOf<OobStreamRead>()];
|
||||
|
||||
if(decodeError != ErrorNumber.NoError) return decodeError;
|
||||
|
||||
fluxPulses.AddRange(cells);
|
||||
streamData.Clear();
|
||||
}
|
||||
|
||||
if(streamPosition + Marshal.SizeOf<OobStreamRead>() > fileData.Length)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
var streamRead = new byte[Marshal.SizeOf<OobStreamRead>()];
|
||||
Array.Copy(fileData, streamPosition, streamRead, 0, Marshal.SizeOf<OobStreamRead>());
|
||||
streamPosition += Marshal.SizeOf<OobStreamRead>();
|
||||
Array.Copy(fileData, oobDataStart, streamReadBytes, 0,
|
||||
Marshal.SizeOf<OobStreamRead>());
|
||||
|
||||
OobStreamRead oobStreamRead =
|
||||
Marshal.ByteArrayToStructureLittleEndian<OobStreamRead>(streamRead);
|
||||
Marshal.ByteArrayToStructureLittleEndian<OobStreamRead>(streamReadBytes);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"Stream Read at position {0}, elapsed time {1} ms",
|
||||
oobStreamRead.streamPosition,
|
||||
oobStreamRead.trTime);
|
||||
|
||||
inStreamRead = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case OobTypes.Index:
|
||||
|
||||
break;
|
||||
}
|
||||
case OobTypes.Index:
|
||||
{
|
||||
if(oobDataStart + Marshal.SizeOf<OobIndex>() <= fileSize)
|
||||
{
|
||||
if(streamPosition + Marshal.SizeOf<OobIndex>() > fileData.Length)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
byte[] indexBytes = new byte[Marshal.SizeOf<OobIndex>()];
|
||||
Array.Copy(fileData, oobDataStart, indexBytes, 0, Marshal.SizeOf<OobIndex>());
|
||||
|
||||
var index = new byte[Marshal.SizeOf<OobIndex>()];
|
||||
Array.Copy(fileData, streamPosition, index, 0, Marshal.SizeOf<OobIndex>());
|
||||
streamPosition += Marshal.SizeOf<OobIndex>();
|
||||
|
||||
OobIndex oobIndex = Marshal.ByteArrayToStructureLittleEndian<OobIndex>(index);
|
||||
OobIndex oobIndex = Marshal.ByteArrayToStructureLittleEndian<OobIndex>(indexBytes);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"Index signal at stream position {0}, timer {1}, sysTime {2}",
|
||||
@@ -342,115 +324,158 @@ public sealed partial class KryoFlux
|
||||
oobIndex.timer,
|
||||
oobIndex.sysTime);
|
||||
|
||||
// Store index position in cell buffer
|
||||
// The index position is the current number of flux pulses decoded
|
||||
indexPositions.Add((uint)fluxPulses.Count);
|
||||
|
||||
break;
|
||||
indexEvents.Add((oobIndex.streamPosition, oobIndex.timer, oobIndex.sysTime));
|
||||
}
|
||||
case OobTypes.StreamEnd:
|
||||
{
|
||||
if(streamPosition + Marshal.SizeOf<OobStreamEnd>() > fileData.Length)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
var streamEnd = new byte[Marshal.SizeOf<OobStreamEnd>()];
|
||||
Array.Copy(fileData, streamPosition, streamEnd, 0, Marshal.SizeOf<OobStreamEnd>());
|
||||
streamPosition += Marshal.SizeOf<OobStreamEnd>();
|
||||
break;
|
||||
}
|
||||
case OobTypes.StreamEnd:
|
||||
{
|
||||
if(oobDataStart + Marshal.SizeOf<OobStreamEnd>() <= fileSize)
|
||||
{
|
||||
byte[] streamEndBytes = new byte[Marshal.SizeOf<OobStreamEnd>()];
|
||||
|
||||
Array.Copy(fileData, oobDataStart, streamEndBytes, 0,
|
||||
Marshal.SizeOf<OobStreamEnd>());
|
||||
|
||||
OobStreamEnd oobStreamEnd =
|
||||
Marshal.ByteArrayToStructureLittleEndian<OobStreamEnd>(streamEnd);
|
||||
Marshal.ByteArrayToStructureLittleEndian<OobStreamEnd>(streamEndBytes);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"Stream End at position {0}, result {1}",
|
||||
oobStreamEnd.streamPosition,
|
||||
oobStreamEnd.result);
|
||||
|
||||
// Process any remaining stream data
|
||||
if(streamData.Count > 0 && inStreamRead)
|
||||
{
|
||||
ErrorNumber decodeError = DecodeKryoFluxStream(streamData.ToArray(), streamData.Count,
|
||||
out List<uint> cells);
|
||||
|
||||
if(decodeError != ErrorNumber.NoError) return decodeError;
|
||||
|
||||
fluxPulses.AddRange(cells);
|
||||
streamData.Clear();
|
||||
}
|
||||
|
||||
inStreamRead = false;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Skip unknown OOB types
|
||||
streamPosition += oobBlk.length;
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
streamOfs += curOpLen;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-OOB: determine operation length and decode flux data
|
||||
bool newCell = false;
|
||||
|
||||
switch(curOp)
|
||||
{
|
||||
case (byte)BlockIds.Nop1:
|
||||
curOpLen = 1;
|
||||
|
||||
break;
|
||||
case (byte)BlockIds.Nop2:
|
||||
curOpLen = 2;
|
||||
|
||||
break;
|
||||
case (byte)BlockIds.Nop3:
|
||||
curOpLen = 3;
|
||||
|
||||
break;
|
||||
case (byte)BlockIds.Ovl16:
|
||||
curOpLen = 1;
|
||||
cellAccumulator += 0x10000;
|
||||
|
||||
break;
|
||||
case (byte)BlockIds.Flux3:
|
||||
curOpLen = 3;
|
||||
cellAccumulator += (uint)((fileData[streamOfs + 1] << 8) | fileData[streamOfs + 2]);
|
||||
newCell = true;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Collect stream data for decoding when in Stream Read block
|
||||
if(inStreamRead)
|
||||
if(curOp >= 0x0E)
|
||||
{
|
||||
// Check if this is an OOB marker - if so, decode what we have and handle OOB
|
||||
if(blockId == (byte)BlockIds.Oob)
|
||||
{
|
||||
// Decode collected stream data before handling OOB block
|
||||
if(streamData.Count > 0)
|
||||
{
|
||||
ErrorNumber decodeError = DecodeKryoFluxStream(streamData.ToArray(), streamData.Count,
|
||||
out List<uint> cells);
|
||||
|
||||
if(decodeError != ErrorNumber.NoError) return decodeError;
|
||||
|
||||
fluxPulses.AddRange(cells);
|
||||
streamData.Clear();
|
||||
}
|
||||
|
||||
// Back up to process OOB block
|
||||
streamPosition--;
|
||||
}
|
||||
else
|
||||
{
|
||||
streamData.Add(blockId);
|
||||
}
|
||||
curOpLen = 1;
|
||||
cellAccumulator += curOp;
|
||||
newCell = true;
|
||||
}
|
||||
// If not in Stream Read block, skip non-stream data
|
||||
// streamPosition already incremented
|
||||
else if((curOp & 0xF8) == 0)
|
||||
{
|
||||
curOpLen = 2;
|
||||
cellAccumulator += ((uint)curOp << 8) | fileData[streamOfs + 1];
|
||||
newCell = true;
|
||||
}
|
||||
else
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(fileSize - streamOfs < curOpLen)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
if(newCell)
|
||||
{
|
||||
cellValues[cellPos] = cellAccumulator;
|
||||
cellStreamPositions[cellPos] = streamPos;
|
||||
cellPos++;
|
||||
cellAccumulator = 0;
|
||||
}
|
||||
|
||||
streamPos += (uint)curOpLen;
|
||||
streamOfs += curOpLen;
|
||||
}
|
||||
|
||||
// Process any remaining stream data
|
||||
if(streamData.Count > 0 && inStreamRead)
|
||||
// Store final partial cell for index resolution boundary
|
||||
cellValues[cellPos] = cellAccumulator;
|
||||
cellStreamPositions[cellPos] = streamPos;
|
||||
|
||||
int totalCells = cellPos;
|
||||
|
||||
// Resolve index stream positions to cell positions
|
||||
List<uint> indexPositions = [];
|
||||
|
||||
if(indexEvents.Count > 0)
|
||||
{
|
||||
ErrorNumber decodeError = DecodeKryoFluxStream(streamData.ToArray(), streamData.Count,
|
||||
out List<uint> cells);
|
||||
int nextIndex = 0;
|
||||
uint nextIndexStreamPos = indexEvents[nextIndex].streamPosition;
|
||||
|
||||
if(decodeError != ErrorNumber.NoError) return decodeError;
|
||||
for(int i = 0; i < totalCells; i++)
|
||||
{
|
||||
if(nextIndex >= indexEvents.Count)
|
||||
break;
|
||||
|
||||
fluxPulses.AddRange(cells);
|
||||
int nextCellPos = i + 1;
|
||||
|
||||
if(nextIndexStreamPos <= cellStreamPositions[nextCellPos])
|
||||
{
|
||||
if(i == 0 && cellStreamPositions[0] >= nextIndexStreamPos)
|
||||
nextCellPos = 0;
|
||||
|
||||
indexPositions.Add((uint)nextCellPos);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"Index {0} resolved to cell position {1}",
|
||||
nextIndex, nextCellPos);
|
||||
|
||||
nextIndex++;
|
||||
|
||||
if(nextIndex < indexEvents.Count)
|
||||
nextIndexStreamPos = indexEvents[nextIndex].streamPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate resolution from sample clock frequency
|
||||
// Build flux pulses array
|
||||
uint[] fluxPulses = new uint[totalCells];
|
||||
Array.Copy(cellValues, fluxPulses, totalCells);
|
||||
|
||||
ulong resolution = CalculateResolution(sck);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"Decoded {0} flux pulses, {1} index signals, resolution {2} ps",
|
||||
fluxPulses.Count,
|
||||
fluxPulses.Length,
|
||||
indexPositions.Count,
|
||||
resolution);
|
||||
|
||||
// Create track capture
|
||||
var capture = new TrackCapture
|
||||
TrackCapture capture = new TrackCapture
|
||||
{
|
||||
head = head,
|
||||
track = track,
|
||||
resolution = resolution,
|
||||
fluxPulses = fluxPulses.ToArray(),
|
||||
fluxPulses = fluxPulses,
|
||||
indexPositions = indexPositions.ToArray()
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,8 @@ public sealed partial class KryoFlux
|
||||
readonly struct OobStreamEnd
|
||||
{
|
||||
public readonly uint streamPosition;
|
||||
public readonly ulong result;
|
||||
// The specification says it's a ulong, but the actual data is a uint
|
||||
public readonly uint result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user