diff --git a/Aaru.CommonTypes/Enums/Images.cs b/Aaru.CommonTypes/Enums/Images.cs
index 5981ccfe0..e71ed9ac4 100644
--- a/Aaru.CommonTypes/Enums/Images.cs
+++ b/Aaru.CommonTypes/Enums/Images.cs
@@ -387,7 +387,10 @@ public enum MediaTagType
DVD_DiscKey_Decrypted = 73,
/// Physical Format Information for the 2nd layer of dual-layer DVDs
[Description("Physical Format Information (2nd Layer)")]
- DVD_PFI_2ndLayer = 74
+ DVD_PFI_2ndLayer = 74,
+ /// Floppy write protection status
+ [Description("Write protection status")]
+ Floppy_WriteProtection = 75
}
/// Enumeration of media types defined in metadata
diff --git a/Aaru.Images/A2R/A2R.cs b/Aaru.Images/A2R/A2R.cs
index 0a0cafc93..e2e1d2f1a 100644
--- a/Aaru.Images/A2R/A2R.cs
+++ b/Aaru.Images/A2R/A2R.cs
@@ -40,6 +40,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
@@ -67,12 +68,13 @@ public sealed partial class A2R : IWritableFluxImage
InfoChunkV2 _infoChunkV2;
InfoChunkV3 _infoChunkV3;
Dictionary _meta;
+ Dictionary _mediaTags;
FileStream _writingStream;
public A2R() => _imageInfo = new ImageInfo
{
ReadableSectorTags = [],
- ReadableMediaTags = [],
+ ReadableMediaTags = [MediaTagType.Floppy_WriteProtection],
HasPartitions = false,
HasSessions = false,
Version = null,
diff --git a/Aaru.Images/A2R/Properties.cs b/Aaru.Images/A2R/Properties.cs
index 907658ef9..4d70da231 100644
--- a/Aaru.Images/A2R/Properties.cs
+++ b/Aaru.Images/A2R/Properties.cs
@@ -78,14 +78,72 @@ public sealed partial class A2R
public IEnumerable KnownExtensions => [".a2r"];
///
- public IEnumerable SupportedMediaTags => null;
+ public IEnumerable SupportedMediaTags => [MediaTagType.Floppy_WriteProtection];
///
public IEnumerable SupportedMediaTypes =>
[
- // TODO: A2R supports a lot more formats, please add more whence tested.
- MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD,
- MediaType.Apple32SS, MediaType.AppleSonyDS, MediaType.Unknown
+ // Apple formats
+ MediaType.Apple32SS, MediaType.Apple32DS, MediaType.Apple33SS, MediaType.Apple33DS,
+ MediaType.AppleSonySS, MediaType.AppleSonyDS, MediaType.AppleFileWare,
+
+ // IBM PC/DOS formats - 5.25"
+ MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
+ MediaType.DOS_525_DS_DD_8, MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD,
+
+ // IBM PC/DOS formats - 3.5"
+ MediaType.DOS_35_SS_DD_8, MediaType.DOS_35_SS_DD_9,
+ MediaType.DOS_35_DS_DD_8, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, MediaType.DOS_35_ED,
+
+ // Microsoft formats
+ MediaType.DMF, MediaType.DMF_82, MediaType.XDF_525, MediaType.XDF_35,
+
+ // Atari formats
+ MediaType.ATARI_525_SD, MediaType.ATARI_525_DD, MediaType.ATARI_525_ED,
+ MediaType.ATARI_35_SS_DD, MediaType.ATARI_35_DS_DD,
+ MediaType.ATARI_35_SS_DD_11, MediaType.ATARI_35_DS_DD_11,
+
+ // Commodore/Amiga formats
+ MediaType.CBM_35_DD, MediaType.CBM_AMIGA_35_DD, MediaType.CBM_AMIGA_35_HD,
+ MediaType.CBM_1540, MediaType.CBM_1540_Ext, MediaType.CBM_1571,
+
+ // NEC/Sharp formats
+ MediaType.NEC_525_SS, MediaType.NEC_525_DS, MediaType.NEC_525_HD,
+ MediaType.NEC_35_HD_8, MediaType.NEC_35_HD_15, MediaType.NEC_35_TD,
+ MediaType.SHARP_525, MediaType.SHARP_525_9, MediaType.SHARP_35, MediaType.SHARP_35_9,
+
+ // 8" formats
+ MediaType.NEC_8_SD, MediaType.NEC_8_DD,
+ MediaType.ECMA_99_8, MediaType.ECMA_69_8,
+
+ // IBM formats
+ MediaType.IBM23FD, MediaType.IBM33FD_128, MediaType.IBM33FD_256, MediaType.IBM33FD_512,
+ MediaType.IBM43FD_128, MediaType.IBM43FD_256,
+ MediaType.IBM53FD_256, MediaType.IBM53FD_512, MediaType.IBM53FD_1024,
+
+ // DEC formats
+ MediaType.RX01, MediaType.RX02, MediaType.RX03, MediaType.RX50,
+
+ // Acorn formats
+ MediaType.ACORN_525_SS_SD_40, MediaType.ACORN_525_SS_SD_80,
+ MediaType.ACORN_525_SS_DD_40, MediaType.ACORN_525_SS_DD_80, MediaType.ACORN_525_DS_DD,
+ MediaType.ACORN_35_DS_DD, MediaType.ACORN_35_DS_HD,
+
+ // ECMA standard formats
+ MediaType.ECMA_54, MediaType.ECMA_59, MediaType.ECMA_66, MediaType.ECMA_69_8,
+ MediaType.ECMA_69_15, MediaType.ECMA_69_26, MediaType.ECMA_70, MediaType.ECMA_78,
+ MediaType.ECMA_78_2, MediaType.ECMA_99_15, MediaType.ECMA_99_26, MediaType.ECMA_100,
+ MediaType.ECMA_125, MediaType.ECMA_147,
+
+ // FDFORMAT formats
+ MediaType.FDFORMAT_525_DD, MediaType.FDFORMAT_525_HD,
+ MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_35_HD,
+
+ // Other formats
+ MediaType.Apricot_35, MediaType.MetaFloppy_Mod_I, MediaType.MetaFloppy_Mod_II,
+
+ // Unknown/fallback
+ MediaType.Unknown
];
///
diff --git a/Aaru.Images/A2R/Read.cs b/Aaru.Images/A2R/Read.cs
index af1d21821..79e660562 100644
--- a/Aaru.Images/A2R/Read.cs
+++ b/Aaru.Images/A2R/Read.cs
@@ -57,6 +57,7 @@ public sealed partial class A2R
_a2RStream.Seek(0, SeekOrigin.Begin);
_a2RFilter = imageFilter;
+ _mediaTags = [];
// Per A2R spec: Read 8-byte header
// Bytes 0-2: Signature ("A2R")
@@ -118,6 +119,11 @@ public sealed partial class A2R
_imageInfo.Creator = Encoding.ASCII.GetString(_infoChunkV2.creator).TrimEnd();
+ // Store write protection status as media tag
+ // Boolean value: 1 byte where 0 = false (not write protected), 1 = true (write protected)
+ _mediaTags[MediaTagType.Floppy_WriteProtection] = _infoChunkV2.writeProtected == 1 ? [1] : [0];
+ _imageInfo.ReadableMediaTags.Add(MediaTagType.Floppy_WriteProtection);
+
// The disk type actually only denotes the form factor of the media, and not the media type.
switch(_infoChunkV2.diskType)
{
@@ -576,7 +582,16 @@ public sealed partial class A2R
}
///
- public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer) => throw new NotImplementedException();
+ public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
+ {
+ buffer = null;
+
+ if(!_mediaTags.TryGetValue(tag, out byte[] data)) return ErrorNumber.NoData;
+
+ buffer = data?.Clone() as byte[];
+
+ return buffer == null ? ErrorNumber.NoData : ErrorNumber.NoError;
+ }
///
public ErrorNumber
diff --git a/Aaru.Images/A2R/Write.cs b/Aaru.Images/A2R/Write.cs
index 46d6e7324..895159341 100644
--- a/Aaru.Images/A2R/Write.cs
+++ b/Aaru.Images/A2R/Write.cs
@@ -235,8 +235,20 @@ public sealed partial class A2R
Encoding.UTF8.GetBytes($"Aaru v{typeof(A2R).Assembly.GetName().Version?.ToString()}".PadRight(32, ' '));
// Per A2R 3.x spec: writeProtected indicates if floppy is write protected (1 = protected)
- // Default to 1 (write protected) as a safe default for archival images
- _infoChunkV3.writeProtected = 1;
+ // Check if Floppy_WriteProtection media tag is available, otherwise default to 1 (write protected)
+ // as a safe default for archival images
+ // Initialize _mediaTags if not already initialized
+ _mediaTags ??= [];
+
+ if(_mediaTags.TryGetValue(MediaTagType.Floppy_WriteProtection, out byte[] writeProtectionTag))
+ {
+ // Boolean value: non-zero byte = true (write protected), 0 = false (not write protected)
+ _infoChunkV3.writeProtected = writeProtectionTag is { Length: > 0 } && writeProtectionTag[0] != 0 ? (byte)1 : (byte)0;
+ }
+ else
+ {
+ _infoChunkV3.writeProtected = 1; // Default to write protected
+ }
// Per A2R 3.x spec: synchronized indicates if cross-track sync/index was used during imaging (1 = synchronized)
// Will be set based on first capture's index signals in WriteFluxCapture
@@ -285,7 +297,30 @@ public sealed partial class A2R
public bool SetMetadata(Metadata metadata) => false;
///
- public bool WriteMediaTag(byte[] data, MediaTagType tag) => false;
+ public bool WriteMediaTag(byte[] data, MediaTagType tag)
+ {
+ if(!SupportedMediaTags.Contains(tag))
+ {
+ ErrorMessage = $"Tried to write unsupported media tag {tag}.";
+
+ return false;
+ }
+
+ _mediaTags ??= [];
+
+ if(_mediaTags.ContainsKey(tag)) _mediaTags.Remove(tag);
+
+ _mediaTags.Add(tag, data);
+
+ // If this is the write protection tag, update the INFO chunk value
+ if(tag == MediaTagType.Floppy_WriteProtection)
+ {
+ // Boolean value: non-zero byte = true (write protected), 0 = false (not write protected)
+ _infoChunkV3.writeProtected = data is { Length: > 0 } && data[0] != 0 ? (byte)1 : (byte)0;
+ }
+
+ return true;
+ }
///
public bool WriteSector(byte[] data, ulong sectorAddress, bool negative, SectorStatus sectorStatus) =>
diff --git a/Aaru.Images/HxCStream/HxCStream.cs b/Aaru.Images/HxCStream/HxCStream.cs
index c1f47cdbb..80429938d 100644
--- a/Aaru.Images/HxCStream/HxCStream.cs
+++ b/Aaru.Images/HxCStream/HxCStream.cs
@@ -31,7 +31,7 @@
// ****************************************************************************/
using System.Collections.Generic;
-using System.IO;
+using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
@@ -49,10 +49,12 @@ public sealed partial class HxCStream : IFluxImage, IVerifiableImage
List _trackFilePaths;
+ Dictionary _mediaTags;
+
public HxCStream() => _imageInfo = new ImageInfo
{
ReadableSectorTags = [],
- ReadableMediaTags = [],
+ ReadableMediaTags = [MediaTagType.Floppy_WriteProtection],
HasPartitions = false,
HasSessions = false,
Version = null,
diff --git a/Aaru.Images/HxCStream/Properties.cs b/Aaru.Images/HxCStream/Properties.cs
index 6689f9188..d62b7f022 100644
--- a/Aaru.Images/HxCStream/Properties.cs
+++ b/Aaru.Images/HxCStream/Properties.cs
@@ -80,13 +80,74 @@ public sealed partial class HxCStream
};
///
- public IEnumerable SupportedMediaTags => null;
+ public IEnumerable SupportedMediaTags => new[]
+ {
+ MediaTagType.Floppy_WriteProtection
+ };
///
public IEnumerable SupportedMediaTypes => new[]
{
- // TODO: HxCStream supports a lot more formats, please add more whence tested.
- MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD,
+ // Apple formats
+ MediaType.Apple32SS, MediaType.Apple32DS, MediaType.Apple33SS, MediaType.Apple33DS,
+ MediaType.AppleSonySS, MediaType.AppleSonyDS, MediaType.AppleFileWare,
+
+ // IBM PC/DOS formats - 5.25"
+ MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9,
+ MediaType.DOS_525_DS_DD_8, MediaType.DOS_525_DS_DD_9, MediaType.DOS_525_HD,
+
+ // IBM PC/DOS formats - 3.5"
+ MediaType.DOS_35_SS_DD_8, MediaType.DOS_35_SS_DD_9,
+ MediaType.DOS_35_DS_DD_8, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, MediaType.DOS_35_ED,
+
+ // Microsoft formats
+ MediaType.DMF, MediaType.DMF_82, MediaType.XDF_525, MediaType.XDF_35,
+
+ // Atari formats
+ MediaType.ATARI_525_SD, MediaType.ATARI_525_DD, MediaType.ATARI_525_ED,
+ MediaType.ATARI_35_SS_DD, MediaType.ATARI_35_DS_DD,
+ MediaType.ATARI_35_SS_DD_11, MediaType.ATARI_35_DS_DD_11,
+
+ // Commodore/Amiga formats
+ MediaType.CBM_35_DD, MediaType.CBM_AMIGA_35_DD, MediaType.CBM_AMIGA_35_HD,
+ MediaType.CBM_1540, MediaType.CBM_1540_Ext, MediaType.CBM_1571,
+
+ // NEC/Sharp formats
+ MediaType.NEC_525_SS, MediaType.NEC_525_DS, MediaType.NEC_525_HD,
+ MediaType.NEC_35_HD_8, MediaType.NEC_35_HD_15, MediaType.NEC_35_TD,
+ MediaType.SHARP_525, MediaType.SHARP_525_9, MediaType.SHARP_35, MediaType.SHARP_35_9,
+
+ // 8" formats
+ MediaType.NEC_8_SD, MediaType.NEC_8_DD,
+ MediaType.ECMA_99_8, MediaType.ECMA_69_8,
+
+ // IBM formats
+ MediaType.IBM23FD, MediaType.IBM33FD_128, MediaType.IBM33FD_256, MediaType.IBM33FD_512,
+ MediaType.IBM43FD_128, MediaType.IBM43FD_256,
+ MediaType.IBM53FD_256, MediaType.IBM53FD_512, MediaType.IBM53FD_1024,
+
+ // DEC formats
+ MediaType.RX01, MediaType.RX02, MediaType.RX03, MediaType.RX50,
+
+ // Acorn formats
+ MediaType.ACORN_525_SS_SD_40, MediaType.ACORN_525_SS_SD_80,
+ MediaType.ACORN_525_SS_DD_40, MediaType.ACORN_525_SS_DD_80, MediaType.ACORN_525_DS_DD,
+ MediaType.ACORN_35_DS_DD, MediaType.ACORN_35_DS_HD,
+
+ // ECMA standard formats
+ MediaType.ECMA_54, MediaType.ECMA_59, MediaType.ECMA_66, MediaType.ECMA_69_8,
+ MediaType.ECMA_69_15, MediaType.ECMA_69_26, MediaType.ECMA_70, MediaType.ECMA_78,
+ MediaType.ECMA_78_2, MediaType.ECMA_99_15, MediaType.ECMA_99_26, MediaType.ECMA_100,
+ MediaType.ECMA_125, MediaType.ECMA_147,
+
+ // FDFORMAT formats
+ MediaType.FDFORMAT_525_DD, MediaType.FDFORMAT_525_HD,
+ MediaType.FDFORMAT_35_DD, MediaType.FDFORMAT_35_HD,
+
+ // Other formats
+ MediaType.Apricot_35, MediaType.MetaFloppy_Mod_I, MediaType.MetaFloppy_Mod_II,
+
+ // Unknown/fallback
MediaType.Unknown
};
diff --git a/Aaru.Images/HxCStream/Read.cs b/Aaru.Images/HxCStream/Read.cs
index b64b6d211..dad5b36c3 100644
--- a/Aaru.Images/HxCStream/Read.cs
+++ b/Aaru.Images/HxCStream/Read.cs
@@ -53,6 +53,7 @@ public sealed partial class HxCStream
{
_trackCaptures = [];
_trackFilePaths = [];
+ _mediaTags = [];
_imageInfo.Heads = 0;
_imageInfo.Cylinders = 0;
@@ -109,7 +110,7 @@ public sealed partial class HxCStream
// Process each track file
int trackIndex = 0;
int totalTracks = trackFiles.Count;
-
+
AaruLogging.Debug(MODULE_NAME, "Processing {0} track files...", totalTracks);
foreach((int cylinder, int head) key in trackFiles.Keys.OrderBy(k => k.cylinder).ThenBy(k => k.head).ToList())
@@ -117,15 +118,15 @@ public sealed partial class HxCStream
trackIndex++;
string trackfile = trackFiles[key];
_trackFilePaths.Add(trackfile);
-
+
AaruLogging.Debug(MODULE_NAME, "Processing track {0}/{1}: cylinder {2}, head {3}",
trackIndex, totalTracks, key.cylinder, key.head);
-
+
ErrorNumber error = ProcessTrackFile(trackfile, (uint)key.head, (ushort)key.cylinder);
if(error != ErrorNumber.NoError) return error;
}
-
+
AaruLogging.Debug(MODULE_NAME, "Successfully processed all {0} track files", totalTracks);
_imageInfo.MetadataMediaType = MetadataMediaType.BlockMedia;
@@ -173,7 +174,7 @@ public sealed partial class HxCStream
// Verify CRC32 - calculate CRC of chunk data (excluding the CRC itself)
byte[] chunkData = new byte[chunkHeader.size - 4];
Array.Copy(fileData, (int)fileOffset, chunkData, 0, (int)(chunkHeader.size - 4));
-
+
uint storedCrc = BitConverter.ToUInt32(fileData, (int)(fileOffset + chunkHeader.size - 4));
if(!VerifyChunkCrc32(chunkData, storedCrc))
@@ -360,6 +361,19 @@ public sealed partial class HxCStream
AaruLogging.Debug(MODULE_NAME, "Finished processing chunks. Total flux pulses: {0}, IO stream values: {1}",
fluxPulses.Count, ioStream.Count);
+ // Extract write protect status from IO stream
+ // The write protect flag should be constant for a capture, so we only need to check the first value
+ if(ioStream.Count > 0 && !_mediaTags.ContainsKey(MediaTagType.Floppy_WriteProtection))
+ {
+ IoStreamState firstState = DecodeIoStreamValue(ioStream[0]);
+ bool writeProtected = firstState.WriteProtect;
+
+ // Store as boolean: 1 byte where 0 = false (not write protected), 1 = true (write protected)
+ _mediaTags[MediaTagType.Floppy_WriteProtection] = writeProtected ? [1] : [0];
+
+ AaruLogging.Debug(MODULE_NAME, "Write protect status from IO stream: {0}", writeProtected ? "write protected" : "not write protected");
+ }
+
// Extract index signals from IO stream
var indexPositions = new List();
@@ -646,7 +660,21 @@ public sealed partial class HxCStream
#region IMediaImage Members
///
- public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer) => throw new NotImplementedException();
+ public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
+ {
+ buffer = null;
+
+ if(_mediaTags == null) return ErrorNumber.NotOpened;
+
+ if(_mediaTags.TryGetValue(tag, out byte[] tagData))
+ {
+ buffer = new byte[tagData.Length];
+ Array.Copy(tagData, buffer, tagData.Length);
+ return ErrorNumber.NoError;
+ }
+
+ return ErrorNumber.NoData;
+ }
///
public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)