mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 18:16:24 +00:00
Add support for CrunchDisk disk images with reading, identification, and helper methods
This commit is contained in:
47
Aaru.Images/CrunchDisk/Constants.cs
Normal file
47
Aaru.Images/CrunchDisk/Constants.cs
Normal 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;
|
||||
}
|
||||
71
Aaru.Images/CrunchDisk/CrunchDisk.cs
Normal file
71
Aaru.Images/CrunchDisk/CrunchDisk.cs
Normal 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
|
||||
};
|
||||
}
|
||||
46
Aaru.Images/CrunchDisk/Enums.cs
Normal file
46
Aaru.Images/CrunchDisk/Enums.cs
Normal 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
|
||||
}
|
||||
}
|
||||
216
Aaru.Images/CrunchDisk/Helpers.cs
Normal file
216
Aaru.Images/CrunchDisk/Helpers.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
73
Aaru.Images/CrunchDisk/Identify.cs
Normal file
73
Aaru.Images/CrunchDisk/Identify.cs
Normal 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
|
||||
}
|
||||
68
Aaru.Images/CrunchDisk/Properties.cs
Normal file
68
Aaru.Images/CrunchDisk/Properties.cs
Normal 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
|
||||
}
|
||||
241
Aaru.Images/CrunchDisk/Read.cs
Normal file
241
Aaru.Images/CrunchDisk/Read.cs
Normal 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
|
||||
}
|
||||
68
Aaru.Images/CrunchDisk/Structs.cs
Normal file
68
Aaru.Images/CrunchDisk/Structs.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
87
Aaru.Images/CrunchDisk/Unsupported.cs
Normal file
87
Aaru.Images/CrunchDisk/Unsupported.cs
Normal 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
|
||||
}
|
||||
36
Aaru.Images/Localization/Localization.Designer.cs
generated
36
Aaru.Images/Localization/Localization.Designer.cs
generated
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user