Add support for CrunchDisk disk images with reading, identification, and helper methods

This commit is contained in:
2026-04-16 16:31:43 +01:00
parent 2422e6a349
commit 3a7e588a70
13 changed files with 990 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Constants.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains constants for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
/// <summary>"CDF0" file header magic number</summary>
const uint HEADER_MAGIC = 0x43444630;
/// <summary>"CYL0" uncompressed cylinder marker</summary>
const uint CYL_UNCOMPRESSED = 0x43594C30;
/// <summary>"CYL1" compressed/interleaved cylinder marker</summary>
const uint CYL_COMPRESSED = 0x43594C31;
/// <summary>Size of the file header in bytes</summary>
const int HEADER_SIZE = 52;
/// <summary>Size of the per-cylinder header in bytes</summary>
const int CYLINDER_HEADER_SIZE = 8;
}

View File

@@ -0,0 +1,71 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : CrunchDisk.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
namespace Aaru.Images;
/// <inheritdoc />
/// <summary>Implements reading CrunchDisk disk images</summary>
public sealed partial class CrunchDisk : IMediaImage
{
const string MODULE_NAME = "CrunchDisk plugin";
byte[] _decompressedData;
Header _header;
ImageInfo _imageInfo;
public CrunchDisk() => _imageInfo = new ImageInfo
{
ReadableSectorTags = [],
ReadableMediaTags = [],
HasPartitions = false,
HasSessions = false,
Version = null,
Application = "CrunchDisk",
ApplicationVersion = null,
Creator = null,
Comments = null,
MediaManufacturer = null,
MediaModel = null,
MediaSerialNumber = null,
MediaBarcode = null,
MediaPartNumber = null,
MediaSequence = 0,
LastMediaSequence = 0,
DriveManufacturer = null,
DriveModel = null,
DriveSerialNumber = null,
DriveFirmwareRevision = null
};
}

View File

@@ -0,0 +1,46 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
enum PackerType : ushort
{
/// <summary>No compression, stored data only</summary>
Stored = 0,
/// <summary>PowerPacker compression</summary>
PowerPacker = 1,
/// <summary>XPK compression (not supported)</summary>
Xpk = 2
}
}

View File

@@ -0,0 +1,216 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Helpers.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains helper methods for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
/// <summary>Decompresses PowerPacker compressed data</summary>
/// <param name="source">Compressed data</param>
/// <param name="packedLength">Length of compressed data</param>
/// <param name="unpackedLength">Expected length of decompressed data</param>
/// <param name="offsetSizes">Bit widths for offset encoding per index (4 entries)</param>
/// <returns>Decompressed data</returns>
static byte[] PowerPackerDecompress(byte[] source, int packedLength, int unpackedLength, byte[] offsetSizes)
{
var output = new byte[unpackedLength];
int srcPos = packedLength - 4;
int dstPos = unpackedLength;
uint shiftReg = 1u << 8;
// Skip the number of bits indicated by the last byte of compressed data
SkipBits(source, ref srcPos, ref shiftReg, source[packedLength - 1]);
while(dstPos > 0)
{
// Determine whether we need to copy literal bytes
uint flag = ReadBits(source, ref srcPos, ref shiftReg, 1);
if(flag == 0)
{
// Read run of literal bytes: at least 1, extended in groups of 3
uint count = 1;
uint extra;
do
{
extra = ReadBits(source, ref srcPos, ref shiftReg, 2);
count += extra;
} while(extra == 3);
// Copy literal bytes from the bitstream
while(count-- > 0)
{
if(--dstPos < 0) return output;
output[dstPos] = (byte)ReadBits(source, ref srcPos, ref shiftReg, 8);
}
}
// Decode a back-reference from already-decompressed data
uint idx = ReadBits(source, ref srcPos, ref shiftReg, 2);
int nBits = offsetSizes[idx];
uint length = idx + 2;
uint offset;
if(idx == 3)
{
// Extended match: variable offset width and extended length
uint useFullBits = ReadBits(source, ref srcPos, ref shiftReg, 1);
offset = ReadBits(source, ref srcPos, ref shiftReg, useFullBits != 0 ? nBits : 7);
// Read additional length in groups of 7
uint lengthExtra;
do
{
lengthExtra = ReadBits(source, ref srcPos, ref shiftReg, 3);
length += lengthExtra;
} while(lengthExtra == 7);
}
else
offset = ReadBits(source, ref srcPos, ref shiftReg, nBits);
offset++;
// Copy bytes from previous output
while(length-- > 0)
{
if(--dstPos < 0) return output;
output[dstPos] = output[dstPos + offset];
}
}
return output;
}
/// <summary>Reads a number of bits from the PowerPacker bitstream (backward, LSB-first)</summary>
static uint ReadBits(byte[] source, ref int srcPos, ref uint shiftReg, int count)
{
uint result = 0;
for(var i = 0; i < count; i++)
{
if((shiftReg & 1u << 8) != 0) shiftReg = 1u << 16 | source[--srcPos];
result = result << 1 | shiftReg & 1;
shiftReg >>= 1;
}
return result;
}
/// <summary>Skips a number of bits in the PowerPacker bitstream</summary>
static void SkipBits(byte[] source, ref int srcPos, ref uint shiftReg, int count)
{
for(var i = 0; i < count; i++)
{
if((shiftReg & 1u << 8) != 0) shiftReg = 1u << 16 | source[--srcPos];
shiftReg >>= 1;
}
}
/// <summary>
/// De-interleaves cylinder data from CrunchDisk's 4-pass byte ordering back to sequential sector data.
/// In CrunchDisk format, each sector's bytes are stored as all first bytes, then all second bytes,
/// then third bytes, then fourth bytes (interleaved in 4-byte strides).
/// </summary>
/// <param name="source">Interleaved source data</param>
/// <param name="destination">Output buffer for de-interleaved data</param>
/// <param name="blockSize">Size of each sector in bytes</param>
/// <param name="numBlocks">Number of sectors in the cylinder</param>
/// <param name="sourceSize">Actual size of source data available</param>
static void ResortCylinderData(byte[] source, byte[] destination, uint blockSize, uint numBlocks, uint sourceSize)
{
var srcIdx = 0;
var dstBase = 0;
for(uint block = 0; block < numBlocks; block++)
{
int blockEnd = dstBase + (int)blockSize;
if(sourceSize >= blockSize)
{
for(var pass = 0; pass < 4; pass++)
{
for(int p = dstBase + pass; p < blockEnd; p += 4) destination[p] = source[srcIdx++];
}
sourceSize -= blockSize;
}
else
Array.Clear(destination, dstBase, (int)blockSize);
dstBase = blockEnd;
}
}
/// <summary>Computes PowerPacker offset bit widths from the efficiency parameter</summary>
/// <param name="efficiency">Efficiency level 1-4</param>
/// <returns>Array of 4 bit widths for offset encoding</returns>
static byte[] ComputeOffsetSizes(ushort efficiency)
{
byte[] sizes = [9, 9, 9, 9];
// Each efficiency level progressively adds bits to the offset sizes
// Higher levels use wider offsets for better compression of larger data
switch(efficiency)
{
case >= 4:
sizes[3]++;
goto case 3;
case 3:
sizes[3]++;
sizes[2]++;
goto case 2;
case 2:
sizes[3]++;
sizes[2]++;
goto case 1;
case 1:
sizes[3]++;
sizes[2]++;
sizes[1]++;
break;
}
return sizes;
}
}

View File

@@ -0,0 +1,73 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Identify.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System.IO;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
#region IMediaImage Members
/// <inheritdoc />
public bool Identify(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
if(stream.Length < HEADER_SIZE) return false;
stream.Seek(0, SeekOrigin.Begin);
var buffer = new byte[HEADER_SIZE];
stream.EnsureRead(buffer, 0, HEADER_SIZE);
Header header = Marshal.ByteArrayToStructureBigEndian<Header>(buffer);
if(header.Id != HEADER_MAGIC) return false;
if(header.BlockSize == 0) return false;
if(header.Heads == 0) return false;
if(header.BlocksPerTrack == 0) return false;
if(header.HighCyl < header.LowCyl) return false;
if(header.PackerType > 2) return false;
return true;
}
#endregion
}

View File

@@ -0,0 +1,68 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Properties.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains properties for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using Aaru.CommonTypes.AaruMetadata;
using Aaru.CommonTypes.Structs;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
#region IMediaImage Members
/// <inheritdoc />
public string Name => Localization.CrunchDisk_Name;
/// <inheritdoc />
public Guid Id => new("A6D26D02-8D5A-4E4B-A542-E34E8F0052E9");
/// <inheritdoc />
public string Author => Authors.NataliaPortillo;
/// <inheritdoc />
public string Format => "CrunchDisk";
/// <inheritdoc />
// ReSharper disable once ConvertToAutoProperty
public ImageInfo Info => _imageInfo;
/// <inheritdoc />
public List<DumpHardware> DumpHardware => null;
/// <inheritdoc />
public Metadata AaruMetadata => null;
#endregion
}

View File

@@ -0,0 +1,241 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System;
using System.IO;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
using Aaru.Logging;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
#region IMediaImage Members
/// <inheritdoc />
public ErrorNumber Open(IFilter imageFilter)
{
Stream stream = imageFilter.GetDataForkStream();
if(stream.Length < HEADER_SIZE) return ErrorNumber.InvalidArgument;
stream.Seek(0, SeekOrigin.Begin);
var headerBytes = new byte[HEADER_SIZE];
stream.EnsureRead(headerBytes, 0, HEADER_SIZE);
_header = Marshal.ByteArrayToStructureBigEndian<Header>(headerBytes);
if(_header.Id != HEADER_MAGIC) return ErrorNumber.InvalidArgument;
if(_header.IsPassword != 0)
{
AaruLogging.Debug(MODULE_NAME, Localization.CrunchDisk_image_is_password_protected);
return ErrorNumber.NotSupported;
}
if(_header.PackerType > (ushort)PackerType.PowerPacker)
{
AaruLogging.Debug(MODULE_NAME, Localization.CrunchDisk_unsupported_packer_type_0, _header.PackerType);
return ErrorNumber.NotSupported;
}
uint cylinders = _header.HighCyl - _header.LowCyl + 1;
uint sectorsPerCyl = _header.BlocksPerTrack * _header.Heads;
uint cylinderSize = sectorsPerCyl * _header.BlockSize;
uint totalSectors = cylinders * sectorsPerCyl;
AaruLogging.Debug(MODULE_NAME,
Localization.CrunchDisk_0_cylinders_1_heads_2_sectors_per_track_3_bytes_per_sector,
cylinders,
_header.Heads,
_header.BlocksPerTrack,
_header.BlockSize);
// Compute PowerPacker offset sizes if needed
byte[] offsetSizes = null;
if(_header.PackerType == (ushort)PackerType.PowerPacker) offsetSizes = ComputeOffsetSizes(_header.Efficiency);
// Allocate buffer for the full decompressed disk data
_decompressedData = new byte[totalSectors * _header.BlockSize];
var cylHeaderBytes = new byte[CYLINDER_HEADER_SIZE];
var outputOffset = 0;
for(uint cyl = _header.LowCyl; cyl <= _header.HighCyl; cyl++)
{
stream.EnsureRead(cylHeaderBytes, 0, CYLINDER_HEADER_SIZE);
var cylMagic = BigEndianBitConverter.ToUInt32(cylHeaderBytes, 0);
var cylDataLen = BigEndianBitConverter.ToUInt32(cylHeaderBytes, 4);
switch(cylMagic)
{
case CYL_UNCOMPRESSED:
{
// Raw cylinder data, read directly into output
stream.EnsureRead(_decompressedData, outputOffset, (int)cylDataLen);
break;
}
case CYL_COMPRESSED:
{
var packedData = new byte[cylDataLen];
stream.EnsureRead(packedData, 0, (int)cylDataLen);
switch((PackerType)_header.PackerType)
{
case PackerType.Stored:
{
// Data is byte-interleaved only, no compression
var resorted = new byte[cylinderSize];
ResortCylinderData(packedData, resorted, _header.BlockSize, sectorsPerCyl, cylDataLen);
Buffer.BlockCopy(resorted, 0, _decompressedData, outputOffset, (int)cylinderSize);
break;
}
case PackerType.PowerPacker:
{
// Decompress with PowerPacker, then de-interleave
byte[] decompressed =
PowerPackerDecompress(packedData, (int)cylDataLen, (int)cylinderSize, offsetSizes);
var resorted = new byte[cylinderSize];
ResortCylinderData(decompressed, resorted, _header.BlockSize, sectorsPerCyl, cylinderSize);
Buffer.BlockCopy(resorted, 0, _decompressedData, outputOffset, (int)cylinderSize);
break;
}
default:
return ErrorNumber.NotSupported;
}
break;
}
default:
AaruLogging.Debug(MODULE_NAME, Localization.CrunchDisk_unexpected_cylinder_magic_0, cylMagic);
return ErrorNumber.InvalidArgument;
}
outputOffset += (int)cylinderSize;
}
_imageInfo.Cylinders = cylinders;
_imageInfo.Heads = _header.Heads;
_imageInfo.SectorsPerTrack = _header.BlocksPerTrack;
_imageInfo.SectorSize = _header.BlockSize;
_imageInfo.Sectors = totalSectors;
_imageInfo.ImageSize = (ulong)_decompressedData.Length;
_imageInfo.MetadataMediaType = MetadataMediaType.BlockMedia;
_imageInfo.CreationTime = imageFilter.CreationTime;
_imageInfo.LastModificationTime = imageFilter.LastWriteTime;
_imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.Filename);
_imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, (byte)_imageInfo.Heads,
(ushort)_imageInfo.SectorsPerTrack, (ushort)_imageInfo.SectorSize,
MediaEncoding.MFM, false));
if(_imageInfo.MediaType == MediaType.Unknown)
AaruLogging.Debug(MODULE_NAME, Localization.CrunchDisk_unknown_media_type);
return ErrorNumber.NoError;
}
/// <inheritdoc />
public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)
{
buffer = null;
sectorStatus = SectorStatus.NotDumped;
if(negative) return ErrorNumber.NotSupported;
if(sectorAddress >= _imageInfo.Sectors) return ErrorNumber.OutOfRange;
buffer = new byte[_imageInfo.SectorSize];
Array.Copy(_decompressedData, (long)sectorAddress * _imageInfo.SectorSize, buffer, 0, _imageInfo.SectorSize);
sectorStatus = SectorStatus.Dumped;
return ErrorNumber.NoError;
}
/// <inheritdoc />
public ErrorNumber ReadSectors(ulong sectorAddress, bool negative, uint length, out byte[] buffer,
out SectorStatus[] sectorStatus)
{
buffer = null;
sectorStatus = null;
if(negative) return ErrorNumber.NotSupported;
if(sectorAddress >= _imageInfo.Sectors) return ErrorNumber.OutOfRange;
if(sectorAddress + length > _imageInfo.Sectors) return ErrorNumber.OutOfRange;
var ms = new MemoryStream();
sectorStatus = new SectorStatus[length];
for(uint i = 0; i < length; i++)
{
ErrorNumber errno = ReadSector(sectorAddress + i, false, out byte[] sector, out SectorStatus status);
if(errno != ErrorNumber.NoError) return errno;
ms.Write(sector, 0, sector.Length);
sectorStatus[i] = status;
}
buffer = ms.ToArray();
return ErrorNumber.NoError;
}
#endregion
}

View File

@@ -0,0 +1,68 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
using Aaru.CommonTypes.Attributes;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
/// <summary>CrunchDisk file header, 52 bytes, big-endian</summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[SwapEndian]
partial struct Header
{
/// <summary>Magic number, "CDF0" (0x43444630)</summary>
public uint Id;
/// <summary>Bytes per sector</summary>
public uint BlockSize;
/// <summary>Sectors per track</summary>
public uint BlocksPerTrack;
/// <summary>Number of heads</summary>
public uint Heads;
/// <summary>First cylinder in image</summary>
public uint LowCyl;
/// <summary>Last cylinder in image</summary>
public uint HighCyl;
/// <summary>Non-zero if password protected</summary>
public byte IsPassword;
/// <summary>Padding byte</summary>
public byte Pad0;
/// <summary>PX20 password checksum</summary>
public ushort PasswordChecksum;
/// <summary>PowerPacker efficiency level (1-4)</summary>
public ushort Efficiency;
/// <summary>Compression type</summary>
public ushort PackerType;
}
}

View File

@@ -0,0 +1,87 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Unsupported.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains unsupported features for CrunchDisk disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using Aaru.CommonTypes.Enums;
namespace Aaru.Images;
public sealed partial class CrunchDisk
{
#region IMediaImage Members
/// <inheritdoc />
public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
{
buffer = null;
return ErrorNumber.NotSupported;
}
/// <inheritdoc />
public ErrorNumber ReadSectorTag(ulong sectorAddress, bool negative, SectorTagType tag, out byte[] buffer)
{
buffer = null;
return ErrorNumber.NotSupported;
}
/// <inheritdoc />
public ErrorNumber ReadSectorsTag(ulong sectorAddress, bool negative, uint length, SectorTagType tag,
out byte[] buffer)
{
buffer = null;
return ErrorNumber.NotSupported;
}
/// <inheritdoc />
public ErrorNumber ReadSectorLong(ulong sectorAddress, bool negative, out byte[] buffer,
out SectorStatus sectorStatus)
{
buffer = null;
sectorStatus = SectorStatus.NotDumped;
return ErrorNumber.NotSupported;
}
/// <inheritdoc />
public ErrorNumber ReadSectorsLong(ulong sectorAddress, bool negative, uint length, out byte[] buffer,
out SectorStatus[] sectorStatus)
{
buffer = null;
sectorStatus = null;
return ErrorNumber.NotSupported;
}
#endregion
}

View File

@@ -3669,6 +3669,42 @@ namespace Aaru.Images {
}
}
internal static string CrunchDisk_Name {
get {
return ResourceManager.GetString("CrunchDisk_Name", resourceCulture);
}
}
internal static string CrunchDisk_image_is_password_protected {
get {
return ResourceManager.GetString("CrunchDisk_image_is_password_protected", resourceCulture);
}
}
internal static string CrunchDisk_unsupported_packer_type_0 {
get {
return ResourceManager.GetString("CrunchDisk_unsupported_packer_type_0", resourceCulture);
}
}
internal static string CrunchDisk_0_cylinders_1_heads_2_sectors_per_track_3_bytes_per_sector {
get {
return ResourceManager.GetString("CrunchDisk_0_cylinders_1_heads_2_sectors_per_track_3_bytes_per_sector", resourceCulture);
}
}
internal static string CrunchDisk_unexpected_cylinder_magic_0 {
get {
return ResourceManager.GetString("CrunchDisk_unexpected_cylinder_magic_0", resourceCulture);
}
}
internal static string CrunchDisk_unknown_media_type {
get {
return ResourceManager.GetString("CrunchDisk_unknown_media_type", resourceCulture);
}
}
internal static string Incorrect_disk_type_0 {
get {
return ResourceManager.GetString("Incorrect_disk_type_0", resourceCulture);

View File

@@ -364,6 +364,24 @@
</data>
<data name="CisCopy_Name" xml:space="preserve">
<value>Imagen de disco CisCopy (DC-File)</value>
</data>
<data name="CrunchDisk_Name" xml:space="preserve">
<value>Imagen de disco CrunchDisk</value>
</data>
<data name="CrunchDisk_image_is_password_protected" xml:space="preserve">
<value>La imagen CrunchDisk está protegida con contraseña.</value>
</data>
<data name="CrunchDisk_unsupported_packer_type_0" xml:space="preserve">
<value>La imagen CrunchDisk usa un tipo de compresión no soportado {0}.</value>
</data>
<data name="CrunchDisk_0_cylinders_1_heads_2_sectors_per_track_3_bytes_per_sector" xml:space="preserve">
<value>Imagen CrunchDisk: {0} cilindros, {1} cabezas, {2} sectores por pista, {3} bytes por sector.</value>
</data>
<data name="CrunchDisk_unexpected_cylinder_magic_0" xml:space="preserve">
<value>La imagen CrunchDisk tiene un valor mágico de cilindro inesperado 0x{0:X8}.</value>
</data>
<data name="CrunchDisk_unknown_media_type" xml:space="preserve">
<value>La geometría de la imagen CrunchDisk no corresponde a un tipo de medio conocido.</value>
</data>
<data name="CloneCd_Name" xml:space="preserve">
<value>CloneCD</value>

View File

@@ -1844,6 +1844,24 @@
</data>
<data name="CisCopy_Name" xml:space="preserve">
<value>CisCopy Disk Image (DC-File)</value>
</data>
<data name="CrunchDisk_Name" xml:space="preserve">
<value>CrunchDisk disk image</value>
</data>
<data name="CrunchDisk_image_is_password_protected" xml:space="preserve">
<value>CrunchDisk image is password protected.</value>
</data>
<data name="CrunchDisk_unsupported_packer_type_0" xml:space="preserve">
<value>CrunchDisk image uses unsupported packer type {0}.</value>
</data>
<data name="CrunchDisk_0_cylinders_1_heads_2_sectors_per_track_3_bytes_per_sector" xml:space="preserve">
<value>CrunchDisk image: {0} cylinders, {1} heads, {2} sectors per track, {3} bytes per sector.</value>
</data>
<data name="CrunchDisk_unexpected_cylinder_magic_0" xml:space="preserve">
<value>CrunchDisk image has unexpected cylinder magic 0x{0:X8}.</value>
</data>
<data name="CrunchDisk_unknown_media_type" xml:space="preserve">
<value>CrunchDisk image geometry does not correspond to a known media type.</value>
</data>
<data name="Incorrect_disk_type_0" xml:space="preserve">
<value>Incorrect disk type {0}</value>

View File

@@ -94,6 +94,7 @@ Media image formats we can read
* BlindWrite 5/6 TOC files (.B5T/.B5I and .B6T/.B6I)
* CopyQM
* CPCEMU Disk file and Extended Disk File
* Crunchdisk
* Dave Dunfield IMD
* DiscJuggler images
* Dreamcast GDI