mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-02-04 00:44:39 +00:00
Add Floppy_WriteProtection media tag
This commit is contained in:
@@ -387,7 +387,10 @@ public enum MediaTagType
|
||||
DVD_DiscKey_Decrypted = 73,
|
||||
/// <summary>Physical Format Information for the 2nd layer of dual-layer DVDs</summary>
|
||||
[Description("Physical Format Information (2nd Layer)")]
|
||||
DVD_PFI_2ndLayer = 74
|
||||
DVD_PFI_2ndLayer = 74,
|
||||
/// <summary>Floppy write protection status</summary>
|
||||
[Description("Write protection status")]
|
||||
Floppy_WriteProtection = 75
|
||||
}
|
||||
|
||||
/// <summary>Enumeration of media types defined in metadata</summary>
|
||||
|
||||
@@ -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<string, string> _meta;
|
||||
Dictionary<MediaTagType, byte[]> _mediaTags;
|
||||
FileStream _writingStream;
|
||||
|
||||
public A2R() => _imageInfo = new ImageInfo
|
||||
{
|
||||
ReadableSectorTags = [],
|
||||
ReadableMediaTags = [],
|
||||
ReadableMediaTags = [MediaTagType.Floppy_WriteProtection],
|
||||
HasPartitions = false,
|
||||
HasSessions = false,
|
||||
Version = null,
|
||||
|
||||
@@ -78,14 +78,72 @@ public sealed partial class A2R
|
||||
public IEnumerable<string> KnownExtensions => [".a2r"];
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<MediaTagType> SupportedMediaTags => null;
|
||||
public IEnumerable<MediaTagType> SupportedMediaTags => [MediaTagType.Floppy_WriteProtection];
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<MediaType> 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
|
||||
];
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool WriteSector(byte[] data, ulong sectorAddress, bool negative, SectorStatus sectorStatus) =>
|
||||
|
||||
@@ -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<string> _trackFilePaths;
|
||||
|
||||
Dictionary<MediaTagType, byte[]> _mediaTags;
|
||||
|
||||
public HxCStream() => _imageInfo = new ImageInfo
|
||||
{
|
||||
ReadableSectorTags = [],
|
||||
ReadableMediaTags = [],
|
||||
ReadableMediaTags = [MediaTagType.Floppy_WriteProtection],
|
||||
HasPartitions = false,
|
||||
HasSessions = false,
|
||||
Version = null,
|
||||
|
||||
@@ -80,13 +80,74 @@ public sealed partial class HxCStream
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<MediaTagType> SupportedMediaTags => null;
|
||||
public IEnumerable<MediaTagType> SupportedMediaTags => new[]
|
||||
{
|
||||
MediaTagType.Floppy_WriteProtection
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<MediaType> 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
|
||||
};
|
||||
|
||||
|
||||
@@ -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<uint>();
|
||||
|
||||
@@ -646,7 +660,21 @@ public sealed partial class HxCStream
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)
|
||||
|
||||
Reference in New Issue
Block a user