mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-05-21 20:17:37 +00:00
Add support for Sydex CopyQM+ Self-eXtracting Disk (SXD) images
This commit is contained in:
66
Aaru.Images/Localization/Localization.Designer.cs
generated
66
Aaru.Images/Localization/Localization.Designer.cs
generated
@@ -3987,6 +3987,72 @@ namespace Aaru.Images {
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_header_CRC_mismatch {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_header_CRC_mismatch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_image_is_password_protected_and_unsupported {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_image_is_password_protected_and_unsupported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_image_has_invalid_geometry {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_image_has_invalid_geometry", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_inconsistent_track_length {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_inconsistent_track_length", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_comment_CRC_mismatch_discarded {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_comment_CRC_mismatch_discarded", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_track_decompression_failed_0 {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_track_decompression_failed_0", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_track_compressed_length_out_of_range_0 {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_track_compressed_length_out_of_range_0", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_image_contains_a_disk_of_type_0 {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_image_contains_a_disk_of_type_0", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_comments_0 {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_comments_0", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Sxd_all_track_CRCs_match_0 {
|
||||
get {
|
||||
return ResourceManager.GetString("Sxd_all_track_CRCs_match_0", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Calculated_header_checksum_equals_0_X2_1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Calculated_header_checksum_equals_0_X2_1", resourceCulture);
|
||||
|
||||
@@ -487,6 +487,39 @@
|
||||
</data>
|
||||
<data name="Dxp_comments_0" xml:space="preserve">
|
||||
<value>Comentarios DXP: {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_Name" xml:space="preserve">
|
||||
<value>Disco Auto-extraíble Sydex CopyQM+</value>
|
||||
</data>
|
||||
<data name="Sxd_header_CRC_mismatch" xml:space="preserve">
|
||||
<value>El CRC de la cabecera SXD no coincide; la imagen está dañada o no es SXD.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_is_password_protected_and_unsupported" xml:space="preserve">
|
||||
<value>La imagen SXD está protegida por contraseña; el descifrado no está soportado.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_has_invalid_geometry" xml:space="preserve">
|
||||
<value>La imagen SXD indica una geometría de disco inválida.</value>
|
||||
</data>
|
||||
<data name="Sxd_inconsistent_track_length" xml:space="preserve">
|
||||
<value>La longitud de pista de la cabecera SXD es inconsistente con los sectores por pista.</value>
|
||||
</data>
|
||||
<data name="Sxd_comment_CRC_mismatch_discarded" xml:space="preserve">
|
||||
<value>El CRC del comentario SXD no coincide; comentario descartado.</value>
|
||||
</data>
|
||||
<data name="Sxd_track_decompression_failed_0" xml:space="preserve">
|
||||
<value>Falló la descompresión de la pista SXD {0}.</value>
|
||||
</data>
|
||||
<data name="Sxd_track_compressed_length_out_of_range_0" xml:space="preserve">
|
||||
<value>La pista SXD tiene una longitud comprimida fuera de rango {0}.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_contains_a_disk_of_type_0" xml:space="preserve">
|
||||
<value>La imagen SXD contiene un disco de tipo {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_comments_0" xml:space="preserve">
|
||||
<value>Comentarios SXD: {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_all_track_CRCs_match_0" xml:space="preserve">
|
||||
<value>CRCs de todas las pistas SXD validados = {0}</value>
|
||||
</data>
|
||||
<data name="Copyright_string_0" xml:space="preserve">
|
||||
<value>Texto de copyright: {0}</value>
|
||||
|
||||
@@ -2003,6 +2003,39 @@
|
||||
</data>
|
||||
<data name="Dxp_comments_0" xml:space="preserve">
|
||||
<value>DXP comments: {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_Name" xml:space="preserve">
|
||||
<value>Sydex CopyQM+ Self-eXtracting Disk</value>
|
||||
</data>
|
||||
<data name="Sxd_header_CRC_mismatch" xml:space="preserve">
|
||||
<value>SXD header CRC does not match; image is corrupt or not an SXD image.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_is_password_protected_and_unsupported" xml:space="preserve">
|
||||
<value>SXD image is password protected; decryption is not supported.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_has_invalid_geometry" xml:space="preserve">
|
||||
<value>SXD image reports invalid disk geometry.</value>
|
||||
</data>
|
||||
<data name="Sxd_inconsistent_track_length" xml:space="preserve">
|
||||
<value>SXD header track length is inconsistent with sectors per track.</value>
|
||||
</data>
|
||||
<data name="Sxd_comment_CRC_mismatch_discarded" xml:space="preserve">
|
||||
<value>SXD comment CRC mismatch; comment discarded.</value>
|
||||
</data>
|
||||
<data name="Sxd_track_decompression_failed_0" xml:space="preserve">
|
||||
<value>Decompression failed for SXD track {0}.</value>
|
||||
</data>
|
||||
<data name="Sxd_track_compressed_length_out_of_range_0" xml:space="preserve">
|
||||
<value>SXD track has out-of-range compressed length {0}.</value>
|
||||
</data>
|
||||
<data name="Sxd_image_contains_a_disk_of_type_0" xml:space="preserve">
|
||||
<value>SXD image contains a disk of type {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_comments_0" xml:space="preserve">
|
||||
<value>SXD comments: {0}</value>
|
||||
</data>
|
||||
<data name="Sxd_all_track_CRCs_match_0" xml:space="preserve">
|
||||
<value>All SXD track CRCs validated = {0}</value>
|
||||
</data>
|
||||
<data name="Calculated_header_checksum_equals_0_X2_1" xml:space="preserve">
|
||||
<value>Calculated header checksum = 0x{0:X2}, {1}</value>
|
||||
|
||||
77
Aaru.Images/SXD/Constants.cs
Normal file
77
Aaru.Images/SXD/Constants.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Constants.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains constants for Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
public sealed partial class SXD
|
||||
{
|
||||
/// <summary>SXD header signature bytes: 'S','X','D'.</summary>
|
||||
static readonly byte[] _sxdSignature = [(byte)'S', (byte)'X', (byte)'D'];
|
||||
|
||||
/// <summary>Optional "WB" block signature bytes preceding the SXD payload.</summary>
|
||||
static readonly byte[] _wbSignature = [(byte)'W', (byte)'B'];
|
||||
|
||||
/// <summary>Size of the SXD header, in bytes.</summary>
|
||||
const int SXD_HEADER_SIZE = 33;
|
||||
|
||||
/// <summary>Number of header bytes protected by <c>crc_hdr</c>.</summary>
|
||||
const int SXD_CRC_HDR_COVER = 31;
|
||||
|
||||
/// <summary>Reversed ANSI CRC-16 polynomial used by the SXD format.</summary>
|
||||
const ushort SXD_CRC_POLY = 0xA001;
|
||||
|
||||
/// <summary>Maximum number of cylinders we will accept in a well-formed SXD image.</summary>
|
||||
const int NTRK_MAX = 84;
|
||||
|
||||
/// <summary>All SXD tracks use 512-byte sectors.</summary>
|
||||
const int SECTOR_SIZE = 512;
|
||||
|
||||
/// <summary>Fill byte used for tracks not present in the image (freshly formatted).</summary>
|
||||
const byte FMT_BYTE = 0xF6;
|
||||
|
||||
/// <summary>SXD drive type codes as reported by the <c>drv_typ</c> header field.</summary>
|
||||
enum SxdDriveType : byte
|
||||
{
|
||||
Unused = 0,
|
||||
Floppy360K = 1,
|
||||
Floppy12M = 2,
|
||||
Floppy720K = 3,
|
||||
Floppy144M = 4,
|
||||
EightInch = 5,
|
||||
Floppy288M = 6
|
||||
}
|
||||
}
|
||||
172
Aaru.Images/SXD/Identify.cs
Normal file
172
Aaru.Images/SXD/Identify.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Identify.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Identifies Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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.Interfaces;
|
||||
using Aaru.Helpers;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
public sealed partial class SXD
|
||||
{
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Identify(IFilter imageFilter)
|
||||
{
|
||||
Stream stream = imageFilter.GetDataForkStream();
|
||||
|
||||
return FindHeaderOffset(stream) >= 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Locate the SXD header within <paramref name="stream" />. SXD images are either "bare" (the 'SXD' signature at
|
||||
/// offset 0) or wrapped in a DOS or DOS/OS-2 self-extracting executable stub. An optional "WB" block may sit
|
||||
/// between the stub and the SXD payload.
|
||||
/// </summary>
|
||||
/// <returns>Offset of the SXD header in <paramref name="stream" />, or -1 if not found.</returns>
|
||||
static long FindHeaderOffset(Stream stream)
|
||||
{
|
||||
if(stream.Length < SXD_HEADER_SIZE) return -1;
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var first3 = new byte[3];
|
||||
|
||||
if(stream.EnsureRead(first3, 0, 3) != 3) return -1;
|
||||
|
||||
// Bare SXD image.
|
||||
if(SignatureMatches(first3, _sxdSignature)) return 0;
|
||||
|
||||
// Otherwise must start with the 'MZ' DOS executable signature.
|
||||
if(first3[0] != 'M' || first3[1] != 'Z') return -1;
|
||||
|
||||
long fileSize = stream.Length;
|
||||
|
||||
long hdrOfs = CalcSkip(stream, fileSize, 0);
|
||||
|
||||
if(hdrOfs < 0) return -1;
|
||||
|
||||
if(hdrOfs + SXD_HEADER_SIZE > fileSize) return -1;
|
||||
|
||||
stream.Seek(hdrOfs, SeekOrigin.Begin);
|
||||
|
||||
if(stream.EnsureRead(first3, 0, 3) != 3) return -1;
|
||||
|
||||
if(SignatureMatches(first3, _sxdSignature)) return hdrOfs;
|
||||
|
||||
// A 'WB' block may precede the actual SXD payload.
|
||||
if(first3[0] != 'W' || first3[1] != 'B') return -1;
|
||||
|
||||
stream.Seek(hdrOfs, SeekOrigin.Begin);
|
||||
|
||||
hdrOfs = CalcSkip(stream, fileSize, hdrOfs);
|
||||
|
||||
if(hdrOfs < 0) return -1;
|
||||
|
||||
if(hdrOfs + SXD_HEADER_SIZE > fileSize) return -1;
|
||||
|
||||
stream.Seek(hdrOfs, SeekOrigin.Begin);
|
||||
|
||||
if(stream.EnsureRead(first3, 0, 3) != 3) return -1;
|
||||
|
||||
return SignatureMatches(first3, _sxdSignature) ? hdrOfs : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip a DOS-style stub/block by reading its MZ-header length (bytes 2..3 = last-block bytes, bytes 4..5 =
|
||||
/// number of 512-byte blocks) from the current position, and computing the absolute offset of what follows.
|
||||
/// </summary>
|
||||
static long CalcSkip(Stream stream, long fileSize, long startOfs)
|
||||
{
|
||||
// The caller already consumed the first two bytes of the stub/block signature; skip to offset 2 of it.
|
||||
stream.Seek(startOfs + 2, SeekOrigin.Begin);
|
||||
|
||||
var word = new byte[2];
|
||||
|
||||
if(stream.EnsureRead(word, 0, 2) != 2) return -1;
|
||||
|
||||
var lastBlkSz = (ushort)(word[0] | word[1] << 8);
|
||||
|
||||
if(lastBlkSz > 512) return -1;
|
||||
|
||||
if(stream.EnsureRead(word, 0, 2) != 2) return -1;
|
||||
|
||||
var nBlks = (ushort)(word[0] | word[1] << 8);
|
||||
|
||||
if(nBlks > 256) return -1;
|
||||
|
||||
long skipSz = nBlks * 512L - (512 - lastBlkSz);
|
||||
|
||||
if(skipSz <= 0 || skipSz > fileSize) return -1;
|
||||
|
||||
return startOfs + skipSz;
|
||||
}
|
||||
|
||||
static bool SignatureMatches(ReadOnlySpan<byte> data, byte[] expected)
|
||||
{
|
||||
if(data.Length < expected.Length) return false;
|
||||
|
||||
for(var i = 0; i < expected.Length; i++)
|
||||
{
|
||||
if(data[i] != expected[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Compute the SXD CRC-16 (reversed poly 0xA001, initial value 0, non-inverted) over <paramref name="data" />.</summary>
|
||||
static ushort SxdCrc16(ReadOnlySpan<byte> data)
|
||||
{
|
||||
ushort crc = 0;
|
||||
|
||||
for(var i = 0; i < data.Length; i++)
|
||||
{
|
||||
crc ^= data[i];
|
||||
|
||||
for(var b = 0; b < 8; b++)
|
||||
{
|
||||
if((crc & 1) != 0)
|
||||
crc = (ushort)(crc >> 1 ^ SXD_CRC_POLY);
|
||||
else
|
||||
crc = (ushort)(crc >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
356
Aaru.Images/SXD/Lzh.cs
Normal file
356
Aaru.Images/SXD/Lzh.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Lzh.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// LZHUF / LH1-variant decompressor used by Sydex CopyQM+ Self-eXtracting
|
||||
// Disk (SXD) images. Plugin-local, buffer based, single pass.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg), which is itself adapted
|
||||
// from LZHUF by Haruyasu Yoshizaki (1988), as published in the English
|
||||
// port edited by Kenji Rikitake. The only difference from the standard
|
||||
// LH1 algorithm is that the sliding text buffer is pre-filled with zero
|
||||
// bytes instead of spaces.
|
||||
//
|
||||
// --[ 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 SXD
|
||||
{
|
||||
/// <summary>SXD LH1/LZHUF decompressor. Buffer based, single-shot; reused per image.</summary>
|
||||
sealed class Lzh
|
||||
{
|
||||
// LZSS parameters.
|
||||
const int N = 4096;
|
||||
const int F = 60;
|
||||
const int THRESHOLD = 2;
|
||||
|
||||
// Huffman coding parameters.
|
||||
const int N_CHAR = 256 - THRESHOLD + F;
|
||||
const int T = N_CHAR * 2 - 1;
|
||||
const int R = T - 1;
|
||||
const int MAX_FREQ = 0x8000;
|
||||
|
||||
// Tables for decoding upper 6 bits of sliding dictionary pointer.
|
||||
static readonly byte[] _dCode =
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
|
||||
0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E,
|
||||
0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12,
|
||||
0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16,
|
||||
0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D,
|
||||
0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26,
|
||||
0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E,
|
||||
0x2F, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E,
|
||||
0x3F
|
||||
];
|
||||
|
||||
static readonly byte[] _dLen =
|
||||
[
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
|
||||
0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08
|
||||
];
|
||||
|
||||
readonly ushort[] _freq = new ushort[T + 1];
|
||||
readonly int[] _prnt = new int[T + N_CHAR];
|
||||
readonly short[] _son = new short[T];
|
||||
readonly byte[] _textBuf = new byte[N + F - 1];
|
||||
|
||||
ushort _getbuf;
|
||||
byte _getlen;
|
||||
|
||||
byte[] _in;
|
||||
int _inEnd;
|
||||
int _inPos;
|
||||
|
||||
byte[] _out;
|
||||
int _outEnd;
|
||||
int _outPos;
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a single SXD LH1 block from <paramref name="src" /> into <paramref name="dst" /> at
|
||||
/// <paramref name="dstOfs" />.
|
||||
/// </summary>
|
||||
/// <param name="src">Compressed track data.</param>
|
||||
/// <param name="srcLength">Number of valid bytes in <paramref name="src" />.</param>
|
||||
/// <param name="dst">Destination buffer.</param>
|
||||
/// <param name="dstOfs">Offset in <paramref name="dst" /> at which to write the uncompressed data.</param>
|
||||
/// <param name="dstLength">Expected uncompressed size.</param>
|
||||
/// <returns>Number of bytes written. Should equal <paramref name="dstLength" /> on success.</returns>
|
||||
public int Decode(byte[] src, int srcLength, byte[] dst, int dstOfs, int dstLength)
|
||||
{
|
||||
_in = src;
|
||||
_inPos = 0;
|
||||
_inEnd = srcLength;
|
||||
_out = dst;
|
||||
_outPos = dstOfs;
|
||||
_outEnd = dstOfs + dstLength;
|
||||
_getbuf = 0;
|
||||
_getlen = 0;
|
||||
|
||||
StartHuff();
|
||||
|
||||
// Pre-initialize text buffer with zero bytes (the key difference from LH1 used in LHarc).
|
||||
for(var i = 0; i < N - F; i++) _textBuf[i] = 0;
|
||||
|
||||
int r = N - F;
|
||||
var count = 0;
|
||||
|
||||
while(count < dstLength)
|
||||
{
|
||||
int c = DecodeChar();
|
||||
|
||||
if(c < 256)
|
||||
{
|
||||
WriteByte((byte)c);
|
||||
_textBuf[r++] = (byte)c;
|
||||
r &= N - 1;
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = r - DecodePosition() - 1 & N - 1;
|
||||
int j = c - 255 + THRESHOLD;
|
||||
|
||||
for(var k = 0; k < j; k++)
|
||||
{
|
||||
byte b = _textBuf[i + k & N - 1];
|
||||
WriteByte(b);
|
||||
_textBuf[r++] = b;
|
||||
r &= N - 1;
|
||||
count++;
|
||||
|
||||
if(count >= dstLength) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int ReadByte()
|
||||
{
|
||||
if(_inPos < _inEnd) return _in[_inPos++];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void WriteByte(byte c)
|
||||
{
|
||||
if(_outPos < _outEnd) _out[_outPos++] = c;
|
||||
}
|
||||
|
||||
int GetBit()
|
||||
{
|
||||
while(_getlen <= 8)
|
||||
{
|
||||
int b = ReadByte();
|
||||
if(b < 0) b = 0;
|
||||
_getbuf |= (ushort)(b << 8 - _getlen);
|
||||
_getlen += 8;
|
||||
}
|
||||
|
||||
bool msb = (_getbuf & 0x8000) != 0;
|
||||
_getbuf <<= 1;
|
||||
_getlen--;
|
||||
|
||||
return msb ? 1 : 0;
|
||||
}
|
||||
|
||||
int GetByte()
|
||||
{
|
||||
while(_getlen <= 8)
|
||||
{
|
||||
int b = ReadByte();
|
||||
if(b < 0) b = 0;
|
||||
_getbuf |= (ushort)(b << 8 - _getlen);
|
||||
_getlen += 8;
|
||||
}
|
||||
|
||||
int i = _getbuf;
|
||||
_getbuf <<= 8;
|
||||
_getlen -= 8;
|
||||
|
||||
return (ushort)i >> 8;
|
||||
}
|
||||
|
||||
void StartHuff()
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < N_CHAR; i++)
|
||||
{
|
||||
_freq[i] = 1;
|
||||
_son[i] = (short)(i + T);
|
||||
_prnt[i + T] = i;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
int j = N_CHAR;
|
||||
|
||||
while(j <= R)
|
||||
{
|
||||
_freq[j] = (ushort)(_freq[i] + _freq[i + 1]);
|
||||
_son[j] = (short)i;
|
||||
_prnt[i] = _prnt[i + 1] = j;
|
||||
i += 2;
|
||||
j++;
|
||||
}
|
||||
|
||||
_freq[T] = 0xFFFF;
|
||||
_prnt[R] = 0;
|
||||
}
|
||||
|
||||
void Reconst()
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
j = 0;
|
||||
|
||||
for(i = 0; i < T; i++)
|
||||
{
|
||||
if(_son[i] >= T)
|
||||
{
|
||||
_freq[j] = (ushort)((_freq[i] + 1) / 2);
|
||||
_son[j] = _son[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0, j = N_CHAR; j < T; i += 2, j++)
|
||||
{
|
||||
k = i + 1;
|
||||
ushort f = _freq[j] = (ushort)(_freq[i] + _freq[k]);
|
||||
|
||||
for(k = j - 1; f < _freq[k]; k--) {}
|
||||
|
||||
k++;
|
||||
|
||||
// Shift freq[k..j-1] right by one.
|
||||
for(int m = j; m > k; m--) _freq[m] = _freq[m - 1];
|
||||
_freq[k] = f;
|
||||
for(int m = j; m > k; m--) _son[m] = _son[m - 1];
|
||||
_son[k] = (short)i;
|
||||
}
|
||||
|
||||
for(i = 0; i < T; i++)
|
||||
{
|
||||
k = _son[i];
|
||||
|
||||
if(k >= T)
|
||||
_prnt[k] = i;
|
||||
else
|
||||
_prnt[k] = _prnt[k + 1] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void Update(int c)
|
||||
{
|
||||
if(_freq[R] == MAX_FREQ) Reconst();
|
||||
|
||||
c = _prnt[c + T];
|
||||
|
||||
do
|
||||
{
|
||||
int k = ++_freq[c];
|
||||
int l = c + 1;
|
||||
|
||||
if(k > _freq[l])
|
||||
{
|
||||
while(k > _freq[++l]) {}
|
||||
|
||||
l--;
|
||||
_freq[c] = _freq[l];
|
||||
_freq[l] = (ushort)k;
|
||||
|
||||
int i = _son[c];
|
||||
_prnt[i] = l;
|
||||
if(i < T) _prnt[i + 1] = l;
|
||||
|
||||
int jj = _son[l];
|
||||
_son[l] = (short)i;
|
||||
_prnt[jj] = c;
|
||||
if(jj < T) _prnt[jj + 1] = c;
|
||||
_son[c] = (short)jj;
|
||||
|
||||
c = l;
|
||||
}
|
||||
|
||||
c = _prnt[c];
|
||||
} while(c != 0);
|
||||
}
|
||||
|
||||
int DecodeChar()
|
||||
{
|
||||
int c = _son[R];
|
||||
|
||||
while(c < T)
|
||||
{
|
||||
c += GetBit();
|
||||
c = _son[c];
|
||||
}
|
||||
|
||||
c -= T;
|
||||
Update(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int DecodePosition()
|
||||
{
|
||||
int i = GetByte();
|
||||
int c = _dCode[i] << 6;
|
||||
int j = _dLen[i];
|
||||
|
||||
j -= 2;
|
||||
while(j-- != 0) i = (i << 1) + GetBit();
|
||||
|
||||
return c | i & 0x3F;
|
||||
}
|
||||
}
|
||||
}
|
||||
339
Aaru.Images/SXD/Open.cs
Normal file
339
Aaru.Images/SXD/Open.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Open.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Opens Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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 System.Text;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Helpers;
|
||||
using Aaru.Logging;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
public sealed partial class SXD
|
||||
{
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber Open(IFilter imageFilter)
|
||||
{
|
||||
Stream stream = imageFilter.GetDataForkStream();
|
||||
|
||||
long hdrOfs = FindHeaderOffset(stream);
|
||||
|
||||
if(hdrOfs < 0) return ErrorNumber.InvalidArgument;
|
||||
|
||||
_headerOffset = hdrOfs;
|
||||
|
||||
var rawHeader = new byte[SXD_HEADER_SIZE];
|
||||
stream.Seek(hdrOfs, SeekOrigin.Begin);
|
||||
|
||||
if(stream.EnsureRead(rawHeader, 0, SXD_HEADER_SIZE) != SXD_HEADER_SIZE) return ErrorNumber.InvalidArgument;
|
||||
|
||||
_header = Marshal.ByteArrayToStructureLittleEndian<Header>(rawHeader);
|
||||
|
||||
ushort computedHdrCrc = SxdCrc16(rawHeader.AsSpan(0, SXD_CRC_HDR_COVER));
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME, "header.trk_len = {0}", _header.trk_len);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.n_trk_sec = {0}", _header.n_trk_sec);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.n_heads = {0}", _header.n_heads);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.n_cyl_dsk = {0}", _header.n_cyl_dsk);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.n_cyl_img = {0}", _header.n_cyl_img);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.first_sid = {0}", _header.first_sid);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.interleave = {0}", _header.interleave);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.skew = {0}", _header.skew);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.sec_sz_code = {0}", _header.sec_sz_code);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.drv_typ = {0} ({1})", _header.drv_typ, (SxdDriveType)_header.drv_typ);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.density = {0} ({1})", _header.density, DensityName(_header.density));
|
||||
AaruLogging.Debug(MODULE_NAME, "header.pwd_crc = 0x{0:X4}", _header.pwd_crc);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.pwd_hash = 0x{0:X4}", _header.pwd_hash);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.comment_len = {0}", _header.comment_len);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.crc_comment = 0x{0:X4}", _header.crc_comment);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"header.unk0 = {0:X2} {1:X2} {2:X2} {3:X2}",
|
||||
_header.unk0[0],
|
||||
_header.unk0[1],
|
||||
_header.unk0[2],
|
||||
_header.unk0[3]);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME, "header.dos_time = 0x{0:X4}", _header.dos_time);
|
||||
AaruLogging.Debug(MODULE_NAME, "header.dos_date = 0x{0:X4}", _header.dos_date);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME,
|
||||
"header.crc_hdr = 0x{0:X4} (computed 0x{1:X4})",
|
||||
_header.crc_hdr,
|
||||
computedHdrCrc);
|
||||
|
||||
if(computedHdrCrc != _header.crc_hdr)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_header_CRC_mismatch);
|
||||
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
// Password-protected images cannot be decoded; the scheme is not documented.
|
||||
if(_header.pwd_crc != 0 || _header.pwd_hash != 0)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_image_is_password_protected_and_unsupported);
|
||||
|
||||
return ErrorNumber.NotSupported;
|
||||
}
|
||||
|
||||
byte spt = _header.n_trk_sec;
|
||||
byte heads = _header.n_heads;
|
||||
byte cylsDsk = _header.n_cyl_dsk;
|
||||
byte cylsImg = _header.n_cyl_img;
|
||||
|
||||
// Basic sanity checks matching fdimg's sxd_open().
|
||||
if(spt == 0 || heads is 0 or > 2 || cylsDsk == 0 || cylsImg == 0 || cylsDsk > NTRK_MAX || cylsImg > cylsDsk)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_image_has_invalid_geometry);
|
||||
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
if(_header.trk_len != spt * SECTOR_SIZE)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_inconsistent_track_length);
|
||||
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
int trackLen = spt * SECTOR_SIZE;
|
||||
|
||||
// Read optional comment following the header.
|
||||
long dataOffset = _headerOffset + SXD_HEADER_SIZE + _header.comment_len;
|
||||
|
||||
if(_header.comment_len > 0)
|
||||
{
|
||||
var commentBytes = new byte[_header.comment_len];
|
||||
stream.Seek(_headerOffset + SXD_HEADER_SIZE, SeekOrigin.Begin);
|
||||
|
||||
if(stream.EnsureRead(commentBytes, 0, _header.comment_len) != _header.comment_len)
|
||||
return ErrorNumber.InvalidArgument;
|
||||
|
||||
ushort commentCrc = SxdCrc16(commentBytes);
|
||||
|
||||
if(commentCrc == _header.crc_comment)
|
||||
{
|
||||
// Replace embedded nulls with newlines, mirroring fdimg.
|
||||
for(var i = 0; i < commentBytes.Length; i++)
|
||||
{
|
||||
if(commentBytes[i] == 0) commentBytes[i] = (byte)'\n';
|
||||
}
|
||||
|
||||
_imageInfo.Comments = Encoding.GetEncoding("ibm437").GetString(commentBytes);
|
||||
}
|
||||
else
|
||||
AaruLogging.Debug(MODULE_NAME, Localization.Sxd_comment_CRC_mismatch_discarded);
|
||||
}
|
||||
|
||||
// Allocate flat disk buffer sized to the full physical disk and pre-fill with the freshly-formatted byte.
|
||||
long totalSectors = (long)cylsDsk * heads * spt;
|
||||
_decodedDisk = new byte[totalSectors * SECTOR_SIZE];
|
||||
Array.Fill(_decodedDisk, FMT_BYTE);
|
||||
|
||||
// Decompress every track stored in the image into its slot in the flat buffer.
|
||||
int tracksInImage = cylsImg * heads;
|
||||
Lzh lzh = null;
|
||||
var cmprBuf = new byte[trackLen + 128];
|
||||
var allCrcsOk = true;
|
||||
|
||||
stream.Seek(dataOffset, SeekOrigin.Begin);
|
||||
|
||||
for(var trkIdx = 0; trkIdx < tracksInImage; trkIdx++)
|
||||
{
|
||||
long dstOfs = (long)trkIdx * trackLen;
|
||||
|
||||
ErrorNumber err = DecompressTrack(stream,
|
||||
_decodedDisk,
|
||||
(int)dstOfs,
|
||||
trackLen,
|
||||
cmprBuf,
|
||||
ref lzh,
|
||||
out bool trackCrcOk);
|
||||
|
||||
if(err != ErrorNumber.NoError)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_track_decompression_failed_0, trkIdx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
if(!trackCrcOk) allCrcsOk = false;
|
||||
}
|
||||
|
||||
_dataCrcOk = allCrcsOk;
|
||||
|
||||
// Populate ImageInfo.
|
||||
_imageInfo.Application = "CopyQM+ / MAKESXD";
|
||||
_imageInfo.CreationTime = DosDateTimeToUtc(_header.dos_date, _header.dos_time, imageFilter.CreationTime);
|
||||
_imageInfo.LastModificationTime = _imageInfo.CreationTime;
|
||||
_imageInfo.MediaTitle = imageFilter.Filename;
|
||||
_imageInfo.ImageSize = (ulong)(stream.Length - dataOffset);
|
||||
_imageInfo.Sectors = (ulong)totalSectors;
|
||||
_imageInfo.SectorSize = SECTOR_SIZE;
|
||||
_imageInfo.Cylinders = cylsDsk;
|
||||
_imageInfo.Heads = heads;
|
||||
_imageInfo.SectorsPerTrack = spt;
|
||||
_imageInfo.MetadataMediaType = MetadataMediaType.BlockMedia;
|
||||
_imageInfo.MediaType = Geometry.GetMediaType((cylsDsk, heads, spt, SECTOR_SIZE, MediaEncoding.MFM, false));
|
||||
|
||||
AaruLogging.Verbose(Localization.Sxd_image_contains_a_disk_of_type_0, _imageInfo.MediaType);
|
||||
|
||||
if(!string.IsNullOrEmpty(_imageInfo.Comments))
|
||||
AaruLogging.Verbose(Localization.Sxd_comments_0, _imageInfo.Comments);
|
||||
|
||||
AaruLogging.Debug(MODULE_NAME, Localization.Sxd_all_track_CRCs_match_0, _dataCrcOk);
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>Read, decompress, and CRC-validate one track from <paramref name="stream" /> into <paramref name="outBuf" />.</summary>
|
||||
ErrorNumber DecompressTrack(Stream stream, byte[] outBuf, int outOfs, int trackLen, byte[] cmprBuf, ref Lzh lzh,
|
||||
out bool crcOk)
|
||||
{
|
||||
crcOk = false;
|
||||
|
||||
var header = new byte[4];
|
||||
|
||||
if(stream.EnsureRead(header, 0, 4) != 4) return ErrorNumber.InvalidArgument;
|
||||
|
||||
var crcStored = (ushort)(header[0] | header[1] << 8);
|
||||
var cmprLen = (short)(header[2] | header[3] << 8);
|
||||
|
||||
int cmprLenAbs = cmprLen < 0 ? -cmprLen : cmprLen;
|
||||
|
||||
if(cmprLenAbs > trackLen + 2)
|
||||
{
|
||||
AaruLogging.Error(Localization.Sxd_track_compressed_length_out_of_range_0, cmprLen);
|
||||
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
if(stream.EnsureRead(cmprBuf, 0, cmprLenAbs) != cmprLenAbs) return ErrorNumber.InvalidArgument;
|
||||
|
||||
if(cmprLen < 0)
|
||||
{
|
||||
// CopyQM-style signed-RLE (inline).
|
||||
var cmprPos = 0;
|
||||
var bytesWritten = 0;
|
||||
|
||||
while(bytesWritten < trackLen)
|
||||
{
|
||||
if(cmprPos + 2 > cmprLenAbs) return ErrorNumber.InvalidArgument;
|
||||
|
||||
var repCnt = (short)(cmprBuf[cmprPos] | cmprBuf[cmprPos + 1] << 8);
|
||||
cmprPos += 2;
|
||||
|
||||
if(repCnt > 0)
|
||||
{
|
||||
if(bytesWritten + repCnt > trackLen) return ErrorNumber.InvalidArgument;
|
||||
if(cmprPos + repCnt > cmprLenAbs) return ErrorNumber.InvalidArgument;
|
||||
|
||||
Buffer.BlockCopy(cmprBuf, cmprPos, outBuf, outOfs + bytesWritten, repCnt);
|
||||
cmprPos += repCnt;
|
||||
bytesWritten += repCnt;
|
||||
}
|
||||
else if(repCnt < 0)
|
||||
{
|
||||
int count = -repCnt;
|
||||
|
||||
if(bytesWritten + count > trackLen) return ErrorNumber.InvalidArgument;
|
||||
if(cmprPos + 1 > cmprLenAbs) return ErrorNumber.InvalidArgument;
|
||||
|
||||
byte b = cmprBuf[cmprPos++];
|
||||
|
||||
for(var i = 0; i < count; i++) outBuf[outOfs + bytesWritten + i] = b;
|
||||
|
||||
bytesWritten += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Zero run-count is invalid per fdimg.
|
||||
return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// LH1-variant LZHUF.
|
||||
lzh ??= new Lzh();
|
||||
|
||||
int produced = lzh.Decode(cmprBuf, cmprLenAbs, outBuf, outOfs, trackLen);
|
||||
|
||||
if(produced != trackLen) return ErrorNumber.InvalidArgument;
|
||||
}
|
||||
|
||||
ushort crcComputed = SxdCrc16(outBuf.AsSpan(outOfs, trackLen));
|
||||
|
||||
crcOk = crcComputed == crcStored;
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
|
||||
static string DensityName(byte density) => density switch
|
||||
{
|
||||
0 => "DD",
|
||||
1 => "HD",
|
||||
2 => "ED",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
static DateTime DosDateTimeToUtc(ushort dosDate, ushort dosTime, DateTime fallback)
|
||||
{
|
||||
if(dosDate == 0 && dosTime == 0) return fallback;
|
||||
|
||||
int year = (dosDate >> 9 & 0x7F) + 1980;
|
||||
int month = dosDate >> 5 & 0x0F;
|
||||
int day = dosDate & 0x1F;
|
||||
int hour = dosTime >> 11 & 0x1F;
|
||||
int minute = dosTime >> 5 & 0x3F;
|
||||
int second = (dosTime & 0x1F) * 2;
|
||||
|
||||
try
|
||||
{
|
||||
return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified);
|
||||
}
|
||||
catch(ArgumentOutOfRangeException)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Aaru.Images/SXD/Properties.cs
Normal file
70
Aaru.Images/SXD/Properties.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Properties.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains properties for Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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 SXD
|
||||
{
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
// ReSharper disable once ConvertToAutoProperty
|
||||
public ImageInfo Info => _imageInfo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Localization.Sxd_Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id => new("B3C6E8F2-4A2D-4F10-9E1C-8D3A7B5F2E94");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Format => "SXD";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<DumpHardware> DumpHardware => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Metadata AaruMetadata => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
79
Aaru.Images/SXD/Read.cs
Normal file
79
Aaru.Images/SXD/Read.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Read.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Reads Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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.Linq;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
public sealed partial class SXD
|
||||
{
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber ReadSector(ulong sectorAddress, bool negative, out byte[] buffer, out SectorStatus sectorStatus)
|
||||
{
|
||||
sectorStatus = SectorStatus.Dumped;
|
||||
|
||||
return ReadSectors(sectorAddress, negative, 1, out buffer, out _);
|
||||
}
|
||||
|
||||
/// <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 - 1) return ErrorNumber.OutOfRange;
|
||||
|
||||
if(sectorAddress + length > _imageInfo.Sectors) return ErrorNumber.OutOfRange;
|
||||
|
||||
buffer = new byte[length * _imageInfo.SectorSize];
|
||||
sectorStatus = Enumerable.Repeat(SectorStatus.Dumped, (int)length).ToArray();
|
||||
|
||||
Array.Copy(_decodedDisk,
|
||||
(long)sectorAddress * _imageInfo.SectorSize,
|
||||
buffer,
|
||||
0,
|
||||
length * _imageInfo.SectorSize);
|
||||
|
||||
return ErrorNumber.NoError;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
74
Aaru.Images/SXD/SXD.cs
Normal file
74
Aaru.Images/SXD/SXD.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : SXD.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Manages Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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 cref="Aaru.CommonTypes.Interfaces.IMediaImage" />
|
||||
/// <summary>Implements reading Sydex CopyQM+ Self-eXtracting Disk (SXD) images.</summary>
|
||||
public sealed partial class SXD : IMediaImage, IVerifiableImage
|
||||
{
|
||||
const string MODULE_NAME = "SXD plugin";
|
||||
bool _dataCrcOk;
|
||||
byte[] _decodedDisk;
|
||||
Header _header;
|
||||
long _headerOffset;
|
||||
ImageInfo _imageInfo;
|
||||
|
||||
public SXD() => _imageInfo = new ImageInfo
|
||||
{
|
||||
ReadableSectorTags = [],
|
||||
ReadableMediaTags = [],
|
||||
HasPartitions = false,
|
||||
HasSessions = false,
|
||||
Version = null,
|
||||
Application = null,
|
||||
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
|
||||
};
|
||||
}
|
||||
92
Aaru.Images/SXD/Structs.cs
Normal file
92
Aaru.Images/SXD/Structs.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Structs.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains structures for Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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;
|
||||
|
||||
namespace Aaru.Images;
|
||||
|
||||
public sealed partial class SXD
|
||||
{
|
||||
#region Nested type: Header
|
||||
|
||||
/// <summary>SXD image header, exactly 33 bytes long. The first 31 bytes are protected by <see cref="crc_hdr" />.</summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
readonly struct Header
|
||||
{
|
||||
/// <summary>0x00 'SXD' signature.</summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public readonly byte[] signature;
|
||||
/// <summary>0x03 Track length in bytes.</summary>
|
||||
public readonly ushort trk_len;
|
||||
/// <summary>0x05 Number of sectors per track.</summary>
|
||||
public readonly byte n_trk_sec;
|
||||
/// <summary>0x06 Number of heads (1 or 2 valid).</summary>
|
||||
public readonly byte n_heads;
|
||||
/// <summary>0x07 Number of cylinders on disk.</summary>
|
||||
public readonly byte n_cyl_dsk;
|
||||
/// <summary>0x08 Number of cylinders stored in the image.</summary>
|
||||
public readonly byte n_cyl_img;
|
||||
/// <summary>0x09 ID of first sector minus 1 (usually 0).</summary>
|
||||
public readonly byte first_sid;
|
||||
/// <summary>0x0A Interleave factor (usually 1).</summary>
|
||||
public readonly byte interleave;
|
||||
/// <summary>0x0B Skew factor, probably (usually 0).</summary>
|
||||
public readonly byte skew;
|
||||
/// <summary>0x0C Sector size code (2 = 512B).</summary>
|
||||
public readonly byte sec_sz_code;
|
||||
/// <summary>0x0D Drive type, 1-6 (see <see cref="SxdDriveType" />).</summary>
|
||||
public readonly byte drv_typ;
|
||||
/// <summary>0x0E Density: 0/1/2 for DD/HD/ED.</summary>
|
||||
public readonly byte density;
|
||||
/// <summary>0x0F Password CRC (nonzero if image is encrypted).</summary>
|
||||
public readonly ushort pwd_crc;
|
||||
/// <summary>0x11 Password hash.</summary>
|
||||
public readonly ushort pwd_hash;
|
||||
/// <summary>0x13 Length of optional comment following the header.</summary>
|
||||
public readonly ushort comment_len;
|
||||
/// <summary>0x15 CRC of the comment block.</summary>
|
||||
public readonly ushort crc_comment;
|
||||
/// <summary>0x17 Unknown 4 bytes.</summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public readonly byte[] unk0;
|
||||
/// <summary>0x1B Image timestamp in DOS time format.</summary>
|
||||
public readonly ushort dos_time;
|
||||
/// <summary>0x1D Image date in DOS date format.</summary>
|
||||
public readonly ushort dos_date;
|
||||
/// <summary>0x1F CRC of header bytes 0x00..0x1E.</summary>
|
||||
public readonly ushort crc_hdr;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
89
Aaru.Images/SXD/Unsupported.cs
Normal file
89
Aaru.Images/SXD/Unsupported.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Unsupported.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains features unsupported by Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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 SXD
|
||||
{
|
||||
#region IMediaImage Members
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
|
||||
{
|
||||
buffer = null;
|
||||
|
||||
return ErrorNumber.NotSupported;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
45
Aaru.Images/SXD/Verify.cs
Normal file
45
Aaru.Images/SXD/Verify.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Verify.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Verifies Sydex CopyQM+ Self-eXtracting Disk (SXD) images.
|
||||
//
|
||||
// Based on the work of Michal Necasek (fdimg).
|
||||
//
|
||||
// --[ 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 SXD
|
||||
{
|
||||
#region IVerifiableImage Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? VerifyMediaImage() => _dataCrcOk;
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user