Implement writing support for DiskCopy 4.2 format, fixes #129

This commit is contained in:
2017-12-28 21:44:25 +00:00
parent feecb2f70a
commit 6f21ffa70c
3 changed files with 502 additions and 159 deletions

View File

@@ -31,6 +31,9 @@
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Claunia.Encoding, Version=1.4.6570.38892, Culture=neutral, PublicKeyToken=null">
<HintPath>..\packages\Claunia.Encoding.1.4.0\lib\portable40-net40+sl5+win8+wp8\Claunia.Encoding.dll</HintPath>
</Reference>
<Reference Include="SharpCompress, Version=0.19.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96"> <Reference Include="SharpCompress, Version=0.19.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96">
<HintPath>..\packages\SharpCompress.0.19.2\lib\net35\SharpCompress.dll</HintPath> <HintPath>..\packages\SharpCompress.0.19.2\lib\net35\SharpCompress.dll</HintPath>
</Reference> </Reference>

View File

@@ -34,19 +34,20 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Claunia.Encoding;
using Claunia.RsrcFork; using Claunia.RsrcFork;
using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes;
using DiscImageChef.Console; using DiscImageChef.Console;
using DiscImageChef.Filters; using DiscImageChef.Filters;
using Encoding = System.Text.Encoding;
using Version = Resources.Version; using Version = Resources.Version;
namespace DiscImageChef.DiscImages namespace DiscImageChef.DiscImages
{ {
// Checked using several images and strings inside Apple's DiskImages.framework // Checked using several images and strings inside Apple's DiskImages.framework
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
public class DiskCopy42 : IMediaImage public class DiskCopy42 : IWritableImage
{ {
// format byte // format byte
/// <summary>3.5", single side, double density, GCR</summary> /// <summary>3.5", single side, double density, GCR</summary>
@@ -72,8 +73,8 @@ namespace DiscImageChef.DiscImages
/// <summary>Defined by Sigma Seven's BLU</summary> /// <summary>Defined by Sigma Seven's BLU</summary>
const byte kSigmaFmtByteTwiggy = 0x01; const byte kSigmaFmtByteTwiggy = 0x01;
/// <summary>3.5" single side double density GCR and MFM all use same code</summary> /// <summary>3.5" single side double density GCR and MFM all use same code</summary>
const byte kSonyFmtByte400K = 0x02; const byte kSonyFmtByte400K = 0x02;
const byte kSonyFmtByte720K = kSonyFmtByte400K; const byte kSonyFmtByte720K = kSonyFmtByte400K;
const byte kSonyFmtByte1440K = kSonyFmtByte400K; const byte kSonyFmtByte1440K = kSonyFmtByte400K;
const byte kSonyFmtByte1680K = kSonyFmtByte400K; const byte kSonyFmtByte1680K = kSonyFmtByte400K;
/// <summary>3.5" double side double density GCR, 512 bytes/sector, interleave 2:1</summary> /// <summary>3.5" double side double density GCR, 512 bytes/sector, interleave 2:1</summary>
@@ -90,7 +91,8 @@ namespace DiscImageChef.DiscImages
/// <summary>Defined by LisaEm</summary> /// <summary>Defined by LisaEm</summary>
const byte kFmtNotStandard = 0x93; const byte kFmtNotStandard = 0x93;
/// <summary>Used incorrectly by Mac OS X with certaing disk images</summary> /// <summary>Used incorrectly by Mac OS X with certaing disk images</summary>
const byte kMacOSXFmtByte = 0x00; const byte kMacOSXFmtByte = 0x00;
const string REGEX_DCPY = @"(?<application>\S+)\s(?<version>\S+)\rData checksum=\$(?<checksum>\S+)$";
/// <summary>Bytes per tag, should be 12</summary> /// <summary>Bytes per tag, should be 12</summary>
uint bptag; uint bptag;
@@ -100,49 +102,54 @@ namespace DiscImageChef.DiscImages
IFilter dc42ImageFilter; IFilter dc42ImageFilter;
/// <summary>Header of opened image</summary> /// <summary>Header of opened image</summary>
Dc42Header header; Dc42Header header;
ImageInfo imageInfo;
ulong sectorsToWrite;
/// <summary>Start of tags in disk image, after data sectors</summary> /// <summary>Start of tags in disk image, after data sectors</summary>
uint tagOffset; uint tagOffset;
bool twiggy; bool twiggy;
byte[] twiggyCache; byte[] twiggyCache;
byte[] twiggyCacheTags; byte[] twiggyCacheTags;
ImageInfo imageInfo; MemoryStream twiggyDataCache;
public ImageInfo Info => imageInfo; MemoryStream twiggyTagCache;
public string Name => "Apple DiskCopy 4.2"; FileStream writingStream;
public Guid Id => new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88");
public DiskCopy42() public DiskCopy42()
{ {
imageInfo = new ImageInfo imageInfo = new ImageInfo
{ {
ReadableSectorTags = new List<SectorTagType>(), ReadableSectorTags = new List<SectorTagType>(),
ReadableMediaTags = new List<MediaTagType>(), ReadableMediaTags = new List<MediaTagType>(),
HasPartitions = false, HasPartitions = false,
HasSessions = false, HasSessions = false,
Version = "4.2", Version = "4.2",
Application = "Apple DiskCopy", Application = "Apple DiskCopy",
ApplicationVersion = "4.2", ApplicationVersion = "4.2",
Creator = null, Creator = null,
Comments = null, Comments = null,
MediaManufacturer = null, MediaManufacturer = null,
MediaModel = null, MediaModel = null,
MediaSerialNumber = null, MediaSerialNumber = null,
MediaBarcode = null, MediaBarcode = null,
MediaPartNumber = null, MediaPartNumber = null,
MediaSequence = 0, MediaSequence = 0,
LastMediaSequence = 0, LastMediaSequence = 0,
DriveManufacturer = null, DriveManufacturer = null,
DriveModel = null, DriveModel = null,
DriveSerialNumber = null, DriveSerialNumber = null,
DriveFirmwareRevision = null DriveFirmwareRevision = null
}; };
} }
public ImageInfo Info => imageInfo;
public string Name => "Apple DiskCopy 4.2";
public Guid Id => new Guid("0240B7B1-E959-4CDC-B0BD-386D6E467B88");
public bool Identify(IFilter imageFilter) public bool Identify(IFilter imageFilter)
{ {
Stream stream = imageFilter.GetDataForkStream(); Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[0x58]; byte[] buffer = new byte[0x58];
byte[] pString = new byte[64]; byte[] pString = new byte[64];
stream.Read(buffer, 0, 0x58); stream.Read(buffer, 0, 0x58);
@@ -155,43 +162,43 @@ namespace DiscImageChef.DiscImages
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
tmpHeader.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); tmpHeader.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh"));
tmpHeader.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); tmpHeader.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40);
tmpHeader.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); tmpHeader.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44);
tmpHeader.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48); tmpHeader.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48);
tmpHeader.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); tmpHeader.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C);
tmpHeader.Format = buffer[0x50]; tmpHeader.Format = buffer[0x50];
tmpHeader.FmtByte = buffer[0x51]; tmpHeader.FmtByte = buffer[0x51];
tmpHeader.Valid = buffer[0x52]; tmpHeader.Valid = buffer[0x52];
tmpHeader.Reserved = buffer[0x53]; tmpHeader.Reserved = buffer[0x53];
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.diskName = \"{0}\"", tmpHeader.DiskName); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.diskName = \"{0}\"", tmpHeader.DiskName);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataSize = {0} bytes", tmpHeader.DataSize); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataSize = {0} bytes", tmpHeader.DataSize);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagSize = {0} bytes", tmpHeader.TagSize); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagSize = {0} bytes", tmpHeader.TagSize);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataChecksum = 0x{0:X8}", tmpHeader.DataChecksum); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.dataChecksum = 0x{0:X8}", tmpHeader.DataChecksum);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagChecksum = 0x{0:X8}", tmpHeader.TagChecksum); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.tagChecksum = 0x{0:X8}", tmpHeader.TagChecksum);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.format = 0x{0:X2}", tmpHeader.Format); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.format = 0x{0:X2}", tmpHeader.Format);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.fmtByte = 0x{0:X2}", tmpHeader.FmtByte); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.fmtByte = 0x{0:X2}", tmpHeader.FmtByte);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.valid = {0}", tmpHeader.Valid); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.valid = {0}", tmpHeader.Valid);
DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.reserved = {0}", tmpHeader.Reserved); DicConsole.DebugWriteLine("DC42 plugin", "tmp_header.reserved = {0}", tmpHeader.Reserved);
if(tmpHeader.Valid != 1 || tmpHeader.Reserved != 0) return false; if(tmpHeader.Valid != 1 || tmpHeader.Reserved != 0) return false;
// Some versions seem to incorrectly create little endian fields // Some versions seem to incorrectly create little endian fields
if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() && if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
tmpHeader.Format != kSigmaFormatTwiggy) tmpHeader.Format != kSigmaFormatTwiggy)
{ {
tmpHeader.DataSize = BitConverter.ToUInt32(buffer, 0x40); tmpHeader.DataSize = BitConverter.ToUInt32(buffer, 0x40);
tmpHeader.TagSize = BitConverter.ToUInt32(buffer, 0x44); tmpHeader.TagSize = BitConverter.ToUInt32(buffer, 0x44);
tmpHeader.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); tmpHeader.DataChecksum = BitConverter.ToUInt32(buffer, 0x48);
tmpHeader.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); tmpHeader.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C);
if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() && if(tmpHeader.DataSize + tmpHeader.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
tmpHeader.Format != kSigmaFormatTwiggy) return false; tmpHeader.Format != kSigmaFormatTwiggy) return false;
} }
if(tmpHeader.Format != kSonyFormat400K && tmpHeader.Format != kSonyFormat800K && if(tmpHeader.Format != kSonyFormat400K && tmpHeader.Format != kSonyFormat800K &&
tmpHeader.Format != kSonyFormat720K && tmpHeader.Format != kSonyFormat1440K && tmpHeader.Format != kSonyFormat720K && tmpHeader.Format != kSonyFormat1440K &&
tmpHeader.Format != kSonyFormat1680K && tmpHeader.Format != kSigmaFormatTwiggy && tmpHeader.Format != kSonyFormat1680K && tmpHeader.Format != kSigmaFormatTwiggy &&
tmpHeader.Format != kNotStandardFormat) tmpHeader.Format != kNotStandardFormat)
{ {
@@ -201,10 +208,10 @@ namespace DiscImageChef.DiscImages
return false; return false;
} }
if(tmpHeader.FmtByte != kSonyFmtByte400K && tmpHeader.FmtByte != kSonyFmtByte800K && if(tmpHeader.FmtByte != kSonyFmtByte400K && tmpHeader.FmtByte != kSonyFmtByte800K &&
tmpHeader.FmtByte != kSonyFmtByte800KIncorrect && tmpHeader.FmtByte != kSonyFmtByteProDos && tmpHeader.FmtByte != kSonyFmtByte800KIncorrect && tmpHeader.FmtByte != kSonyFmtByteProDos &&
tmpHeader.FmtByte != kInvalidFmtByte && tmpHeader.FmtByte != kSigmaFmtByteTwiggy && tmpHeader.FmtByte != kInvalidFmtByte && tmpHeader.FmtByte != kSigmaFmtByteTwiggy &&
tmpHeader.FmtByte != kFmtNotStandard && tmpHeader.FmtByte != kMacOSXFmtByte) tmpHeader.FmtByte != kFmtNotStandard && tmpHeader.FmtByte != kMacOSXFmtByte)
{ {
DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value",
tmpHeader.FmtByte); tmpHeader.FmtByte);
@@ -223,54 +230,55 @@ namespace DiscImageChef.DiscImages
{ {
Stream stream = imageFilter.GetDataForkStream(); Stream stream = imageFilter.GetDataForkStream();
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[0x58]; byte[] buffer = new byte[0x58];
byte[] pString = new byte[64]; byte[] pString = new byte[64];
stream.Read(buffer, 0, 0x58); stream.Read(buffer, 0, 0x58);
IsWriting = false;
// Incorrect pascal string length, not DC42 // Incorrect pascal string length, not DC42
if(buffer[0] > 63) return false; if(buffer[0] > 63) return false;
header = new Dc42Header(); header = new Dc42Header();
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
Array.Copy(buffer, 0, pString, 0, 64); Array.Copy(buffer, 0, pString, 0, 64);
header.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); header.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh"));
header.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); header.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40);
header.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); header.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44);
header.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48); header.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48);
header.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); header.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C);
header.Format = buffer[0x50]; header.Format = buffer[0x50];
header.FmtByte = buffer[0x51]; header.FmtByte = buffer[0x51];
header.Valid = buffer[0x52]; header.Valid = buffer[0x52];
header.Reserved = buffer[0x53]; header.Reserved = buffer[0x53];
DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName); DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName);
DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize); DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize);
DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize); DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize);
DicConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum); DicConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum);
DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum); DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum);
DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format); DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format);
DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte); DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte);
DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid); DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid);
DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved); DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved);
if(header.Valid != 1 || header.Reserved != 0) return false; if(header.Valid != 1 || header.Reserved != 0) return false;
// Some versions seem to incorrectly create little endian fields // Some versions seem to incorrectly create little endian fields
if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
header.Format != kSigmaFormatTwiggy) header.Format != kSigmaFormatTwiggy)
{ {
header.DataSize = BitConverter.ToUInt32(buffer, 0x40); header.DataSize = BitConverter.ToUInt32(buffer, 0x40);
header.TagSize = BitConverter.ToUInt32(buffer, 0x44); header.TagSize = BitConverter.ToUInt32(buffer, 0x44);
header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48);
header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C);
if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && if(header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
header.Format != kSigmaFormatTwiggy) return false; header.Format != kSigmaFormatTwiggy) return false;
} }
if(header.Format != kSonyFormat400K && header.Format != kSonyFormat800K && if(header.Format != kSonyFormat400K && header.Format != kSonyFormat800K &&
header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K && header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K &&
header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy && header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy &&
header.Format != kNotStandardFormat) header.Format != kNotStandardFormat)
{ {
@@ -279,10 +287,10 @@ namespace DiscImageChef.DiscImages
return false; return false;
} }
if(header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K && if(header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K &&
header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos && header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos &&
header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy && header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy &&
header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte) header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte)
{ {
DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte); DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte);
@@ -296,11 +304,11 @@ namespace DiscImageChef.DiscImages
return false; return false;
} }
dataOffset = 0x54; dataOffset = 0x54;
tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0;
imageInfo.SectorSize = 512; imageInfo.SectorSize = 512;
bptag = (uint)(header.TagSize != 0 ? 12 : 0); bptag = (uint)(header.TagSize != 0 ? 12 : 0);
dc42ImageFilter = imageFilter; dc42ImageFilter = imageFilter;
imageInfo.Sectors = header.DataSize / 512; imageInfo.Sectors = header.DataSize / 512;
@@ -318,10 +326,10 @@ namespace DiscImageChef.DiscImages
imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
} }
imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag; imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag;
imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.CreationTime = imageFilter.GetCreationTime();
imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
imageInfo.MediaTitle = header.DiskName; imageInfo.MediaTitle = header.DiskName;
switch(header.Format) switch(header.Format)
{ {
@@ -374,9 +382,9 @@ namespace DiscImageChef.DiscImages
byte[] data = new byte[header.DataSize]; byte[] data = new byte[header.DataSize];
byte[] tags = new byte[header.TagSize]; byte[] tags = new byte[header.TagSize];
twiggyCache = new byte[header.DataSize]; twiggyCache = new byte[header.DataSize];
twiggyCacheTags = new byte[header.TagSize]; twiggyCacheTags = new byte[header.TagSize];
twiggy = true; twiggy = true;
Stream datastream = imageFilter.GetDataForkStream(); Stream datastream = imageFilter.GetDataForkStream();
datastream.Seek(dataOffset, SeekOrigin.Begin); datastream.Seek(dataOffset, SeekOrigin.Begin);
@@ -386,31 +394,31 @@ namespace DiscImageChef.DiscImages
tagstream.Seek(tagOffset, SeekOrigin.Begin); tagstream.Seek(tagOffset, SeekOrigin.Begin);
tagstream.Read(tags, 0, (int)header.TagSize); tagstream.Read(tags, 0, (int)header.TagSize);
ushort mfsMagic = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400); ushort mfsMagic = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400);
ushort mfsAllBlocks = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x412); ushort mfsAllBlocks = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x412);
// Detect a Macintosh Twiggy // Detect a Macintosh Twiggy
if(mfsMagic == 0xD2D7 && mfsAllBlocks == 422) if(mfsMagic == 0xD2D7 && mfsAllBlocks == 422)
{ {
DicConsole.DebugWriteLine("DC42 plugin", "Macintosh Twiggy detected, reversing disk sides"); DicConsole.DebugWriteLine("DC42 plugin", "Macintosh Twiggy detected, reversing disk sides");
Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2); Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2);
Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2); Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2);
Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2); Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2);
Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2); Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2);
} }
else else
{ {
DicConsole.DebugWriteLine("DC42 plugin", "Lisa Twiggy detected, reversing second half of disk"); DicConsole.DebugWriteLine("DC42 plugin", "Lisa Twiggy detected, reversing second half of disk");
Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2); Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2);
Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2); Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2);
int copiedSectors = 0; int copiedSectors = 0;
int sectorsToCopy = 0; int sectorsToCopy = 0;
for(int i = 0; i < 46; i++) for(int i = 0; i < 46; i++)
{ {
if(i >= 0 && i <= 3) sectorsToCopy = 22; if(i >= 0 && i <= 3) sectorsToCopy = 22;
if(i >= 4 && i <= 10) sectorsToCopy = 21; if(i >= 4 && i <= 10) sectorsToCopy = 21;
if(i >= 11 && i <= 16) sectorsToCopy = 20; if(i >= 11 && i <= 16) sectorsToCopy = 20;
if(i >= 17 && i <= 22) sectorsToCopy = 19; if(i >= 17 && i <= 22) sectorsToCopy = 19;
if(i >= 23 && i <= 28) sectorsToCopy = 18; if(i >= 23 && i <= 28) sectorsToCopy = 18;
@@ -419,10 +427,13 @@ namespace DiscImageChef.DiscImages
if(i >= 42 && i <= 45) sectorsToCopy = 15; if(i >= 42 && i <= 45) sectorsToCopy = 15;
Array.Copy(data, header.DataSize / 2 + copiedSectors * 512, twiggyCache, Array.Copy(data, header.DataSize / 2 + copiedSectors * 512, twiggyCache,
twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512, sectorsToCopy * 512); twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512,
Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag, twiggyCacheTags, sectorsToCopy * 512);
twiggyCacheTags.Length - copiedSectors * bptag - sectorsToCopy * bptag, Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag,
sectorsToCopy * bptag); twiggyCacheTags,
twiggyCacheTags.Length - copiedSectors * bptag -
sectorsToCopy * bptag,
sectorsToCopy * bptag);
copiedSectors += sectorsToCopy; copiedSectors += sectorsToCopy;
} }
@@ -445,12 +456,12 @@ namespace DiscImageChef.DiscImages
Version version = new Version(vers); Version version = new Version(vers);
string release = null; string release = null;
string dev = null; string dev = null;
string pre = null; string pre = null;
string major = $"{version.MajorVersion}"; string major = $"{version.MajorVersion}";
string minor = $".{version.MinorVersion / 10}"; string minor = $".{version.MinorVersion / 10}";
if(version.MinorVersion % 10 > 0) release = $".{version.MinorVersion % 10}"; if(version.MinorVersion % 10 > 0) release = $".{version.MinorVersion % 10}";
switch(version.DevStage) switch(version.DevStage)
{ {
case Version.DevelopmentStage.Alpha: case Version.DevelopmentStage.Alpha:
@@ -469,8 +480,8 @@ namespace DiscImageChef.DiscImages
if(dev != null) pre = $"{version.PreReleaseVersion}"; if(dev != null) pre = $"{version.PreReleaseVersion}";
imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}";
imageInfo.Application = version.VersionString; imageInfo.Application = version.VersionString;
imageInfo.Comments = version.VersionMessage; imageInfo.Comments = version.VersionMessage;
} }
} }
@@ -481,18 +492,13 @@ namespace DiscImageChef.DiscImages
{ {
string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]), string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]),
Encoding.GetEncoding("macintosh")); Encoding.GetEncoding("macintosh"));
string dCpyRegEx = Regex dCpyEx = new Regex(REGEX_DCPY);
@"(?<application>\S+)\s(?<version>\S+)\rData checksum=\$(?<checksum>\S+)$";
Regex dCpyEx = new Regex(dCpyRegEx);
Match dCpyMatch = dCpyEx.Match(dCpy); Match dCpyMatch = dCpyEx.Match(dCpy);
if(dCpyMatch.Success) if(dCpyMatch.Success)
{ {
imageInfo.Application = dCpyMatch.Groups["application"].Value; imageInfo.Application = dCpyMatch.Groups["application"].Value;
imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value; imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value;
// Until MacRoman is implemented
if(imageInfo.Application == "ShrinkWrap?") imageInfo.Application = "ShrinkWrap™";
} }
} }
} }
@@ -509,28 +515,28 @@ namespace DiscImageChef.DiscImages
switch(imageInfo.MediaType) switch(imageInfo.MediaType)
{ {
case MediaType.AppleSonySS: case MediaType.AppleSonySS:
imageInfo.Cylinders = 80; imageInfo.Cylinders = 80;
imageInfo.Heads = 1; imageInfo.Heads = 1;
imageInfo.SectorsPerTrack = 10; imageInfo.SectorsPerTrack = 10;
break; break;
case MediaType.AppleSonyDS: case MediaType.AppleSonyDS:
imageInfo.Cylinders = 80; imageInfo.Cylinders = 80;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 10; imageInfo.SectorsPerTrack = 10;
break; break;
case MediaType.DOS_35_DS_DD_9: case MediaType.DOS_35_DS_DD_9:
imageInfo.Cylinders = 80; imageInfo.Cylinders = 80;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 9; imageInfo.SectorsPerTrack = 9;
break; break;
case MediaType.DOS_35_HD: case MediaType.DOS_35_HD:
imageInfo.Cylinders = 80; imageInfo.Cylinders = 80;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 18; imageInfo.SectorsPerTrack = 18;
break; break;
case MediaType.DMF: case MediaType.DMF:
imageInfo.Cylinders = 80; imageInfo.Cylinders = 80;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 21; imageInfo.SectorsPerTrack = 21;
break; break;
case MediaType.AppleProfile: case MediaType.AppleProfile:
@@ -544,22 +550,22 @@ namespace DiscImageChef.DiscImages
break; break;
} }
imageInfo.Heads = 4; imageInfo.Heads = 4;
imageInfo.SectorsPerTrack = 16; imageInfo.SectorsPerTrack = 16;
break; break;
case MediaType.AppleWidget: case MediaType.AppleWidget:
imageInfo.Cylinders = 608; imageInfo.Cylinders = 608;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16; imageInfo.SectorsPerTrack = 16;
break; break;
case MediaType.AppleHD20: case MediaType.AppleHD20:
imageInfo.Cylinders = 610; imageInfo.Cylinders = 610;
imageInfo.Heads = 2; imageInfo.Heads = 2;
imageInfo.SectorsPerTrack = 16; imageInfo.SectorsPerTrack = 16;
break; break;
default: default:
imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63);
imageInfo.Heads = 16; imageInfo.Heads = 16;
imageInfo.SectorsPerTrack = 63; imageInfo.SectorsPerTrack = 63;
break; break;
} }
@@ -578,7 +584,7 @@ namespace DiscImageChef.DiscImages
} }
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas, public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
out List<ulong> unknownLbas) out List<ulong> unknownLbas)
{ {
failingLbas = new List<ulong>(); failingLbas = new List<ulong>();
unknownLbas = new List<ulong>(); unknownLbas = new List<ulong>();
@@ -589,7 +595,7 @@ namespace DiscImageChef.DiscImages
} }
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas, public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
out List<ulong> unknownLbas) out List<ulong> unknownLbas)
{ {
failingLbas = new List<ulong>(); failingLbas = new List<ulong>();
unknownLbas = new List<ulong>(); unknownLbas = new List<ulong>();
@@ -601,10 +607,9 @@ namespace DiscImageChef.DiscImages
public bool? VerifyMediaImage() public bool? VerifyMediaImage()
{ {
byte[] data = new byte[header.DataSize]; byte[] data = new byte[header.DataSize];
byte[] tags = new byte[header.TagSize]; byte[] tags = new byte[header.TagSize];
uint dataChk; uint tagsChk = 0;
uint tagsChk = 0;
DicConsole.DebugWriteLine("DC42 plugin", "Reading data"); DicConsole.DebugWriteLine("DC42 plugin", "Reading data");
Stream datastream = dc42ImageFilter.GetDataForkStream(); Stream datastream = dc42ImageFilter.GetDataForkStream();
@@ -612,9 +617,9 @@ namespace DiscImageChef.DiscImages
datastream.Read(data, 0, (int)header.DataSize); datastream.Read(data, 0, (int)header.DataSize);
DicConsole.DebugWriteLine("DC42 plugin", "Calculating data checksum"); DicConsole.DebugWriteLine("DC42 plugin", "Calculating data checksum");
dataChk = DC42CheckSum(data); uint dataChk = DC42CheckSum(data);
DicConsole.DebugWriteLine("DC42 plugin", "Calculated data checksum = 0x{0:X8}", dataChk); DicConsole.DebugWriteLine("DC42 plugin", "Calculated data checksum = 0x{0:X8}", dataChk);
DicConsole.DebugWriteLine("DC42 plugin", "Stored data checksum = 0x{0:X8}", header.DataChecksum); DicConsole.DebugWriteLine("DC42 plugin", "Stored data checksum = 0x{0:X8}", header.DataChecksum);
if(header.TagSize <= 0) return dataChk == header.DataChecksum && tagsChk == header.TagChecksum; if(header.TagSize <= 0) return dataChk == header.DataChecksum && tagsChk == header.TagChecksum;
@@ -626,7 +631,7 @@ namespace DiscImageChef.DiscImages
DicConsole.DebugWriteLine("DC42 plugin", "Calculating tag checksum"); DicConsole.DebugWriteLine("DC42 plugin", "Calculating tag checksum");
tagsChk = DC42CheckSum(tags); tagsChk = DC42CheckSum(tags);
DicConsole.DebugWriteLine("DC42 plugin", "Calculated tag checksum = 0x{0:X8}", tagsChk); DicConsole.DebugWriteLine("DC42 plugin", "Calculated tag checksum = 0x{0:X8}", tagsChk);
DicConsole.DebugWriteLine("DC42 plugin", "Stored tag checksum = 0x{0:X8}", header.TagChecksum); DicConsole.DebugWriteLine("DC42 plugin", "Stored tag checksum = 0x{0:X8}", header.TagChecksum);
return dataChk == header.DataChecksum && tagsChk == header.TagChecksum; return dataChk == header.DataChecksum && tagsChk == header.TagChecksum;
} }
@@ -653,12 +658,12 @@ namespace DiscImageChef.DiscImages
if(twiggy) if(twiggy)
Array.Copy(twiggyCache, (int)sectorAddress * imageInfo.SectorSize, buffer, 0, Array.Copy(twiggyCache, (int)sectorAddress * imageInfo.SectorSize, buffer, 0,
length * imageInfo.SectorSize); length * imageInfo.SectorSize);
else else
{ {
Stream stream = dc42ImageFilter.GetDataForkStream(); Stream stream = dc42ImageFilter.GetDataForkStream();
stream.Seek((long)(dataOffset + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin); stream.Seek((long)(dataOffset + sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize)); stream.Read(buffer, 0, (int)(length * imageInfo.SectorSize));
} }
return buffer; return buffer;
@@ -684,7 +689,7 @@ namespace DiscImageChef.DiscImages
{ {
Stream stream = dc42ImageFilter.GetDataForkStream(); Stream stream = dc42ImageFilter.GetDataForkStream();
stream.Seek((long)(tagOffset + sectorAddress * bptag), SeekOrigin.Begin); stream.Seek((long)(tagOffset + sectorAddress * bptag), SeekOrigin.Begin);
stream.Read(buffer, 0, (int)(length * bptag)); stream.Read(buffer, 0, (int)(length * bptag));
} }
return buffer; return buffer;
@@ -703,8 +708,8 @@ namespace DiscImageChef.DiscImages
if(sectorAddress + length > imageInfo.Sectors) if(sectorAddress + length > imageInfo.Sectors)
throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available"); throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
byte[] data = ReadSectors(sectorAddress, length); byte[] data = ReadSectors(sectorAddress, length);
byte[] tags = ReadSectorsTag(sectorAddress, length, SectorTagType.AppleSectorTag); byte[] tags = ReadSectorsTag(sectorAddress, length, SectorTagType.AppleSectorTag);
byte[] buffer = new byte[data.Length + tags.Length]; byte[] buffer = new byte[data.Length + tags.Length];
for(uint i = 0; i < length; i++) for(uint i = 0; i < length; i++)
@@ -773,6 +778,333 @@ namespace DiscImageChef.DiscImages
throw new FeatureUnsupportedImageException("Feature not supported by image format"); throw new FeatureUnsupportedImageException("Feature not supported by image format");
} }
public IEnumerable<MediaTagType> SupportedMediaTags => new MediaTagType[] { };
public IEnumerable<SectorTagType> SupportedSectorTags => new[] {SectorTagType.AppleSectorTag};
public IEnumerable<MediaType> SupportedMediaTypes =>
new[]
{
MediaType.AppleFileWare, MediaType.AppleHD20, MediaType.AppleProfile, MediaType.AppleSonyDS,
MediaType.AppleSonySS, MediaType.AppleWidget, MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD,
MediaType.DMF
};
public IEnumerable<(string name, Type type, string description)> SupportedOptions =>
new[] {("macosx", typeof(bool), "Use Mac OS X format byte")};
public IEnumerable<string> KnownExtensions => new[] {".dc42", ".diskcopy42", ".image"};
public bool IsWriting { get; private set; }
public string ErrorMessage { get; private set; }
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors)
{
header = new Dc42Header();
bool tags = false;
bool macosx;
if(options != null && options.TryGetValue("macosx", out string tmpOption))
bool.TryParse(tmpOption, out macosx);
switch(mediaType)
{
case MediaType.AppleFileWare:
header.FmtByte = kSigmaFmtByteTwiggy;
header.Format = kSigmaFormatTwiggy;
twiggy = true;
tags = true;
twiggyDataCache = new MemoryStream();
twiggyTagCache = new MemoryStream();
// TODO
ErrorMessage = "Twiggy write support not yet implemented";
return false;
case MediaType.AppleHD20:
if(sectors != 39040)
{
ErrorMessage = "Incorrect number of sectors for Apple HD20 image";
return false;
}
header.FmtByte = kFmtNotStandard;
header.Format = kNotStandardFormat;
tags = true;
break;
case MediaType.AppleProfile:
if(sectors != 9728 && sectors != 19456)
{
ErrorMessage = "Incorrect number of sectors for Apple Profile image";
return false;
}
header.FmtByte = kFmtNotStandard;
header.Format = kNotStandardFormat;
tags = true;
break;
case MediaType.AppleSonyDS:
if(sectors != 1600)
{
ErrorMessage = "Incorrect number of sectors for Apple MF2DD image";
return false;
}
header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte800K;
header.Format = kSonyFormat800K;
tags = true;
break;
case MediaType.AppleSonySS:
if(sectors != 800)
{
ErrorMessage = "Incorrect number of sectors for Apple MF1DD image";
return false;
}
header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte400K;
header.Format = kSonyFormat400K;
tags = true;
break;
case MediaType.AppleWidget:
if(sectors != 39040)
{
ErrorMessage = "Incorrect number of sectors for Apple Widget image";
return false;
}
header.FmtByte = kFmtNotStandard;
header.Format = kNotStandardFormat;
tags = true;
break;
case MediaType.DOS_35_DS_DD_9:
if(sectors != 1440)
{
ErrorMessage = "Incorrect number of sectors for MF2DD image";
return false;
}
header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte720K;
header.Format = kSonyFormat720K;
break;
case MediaType.DOS_35_HD:
if(sectors != 2880)
{
ErrorMessage = "Incorrect number of sectors for MF2HD image";
return false;
}
header.Format = kSonyFmtByte1440K;
header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1440K;
break;
case MediaType.DMF:
if(sectors != 3360)
{
ErrorMessage = "Incorrect number of sectors for DMF image";
return false;
}
header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1680K;
header.Format = kSonyFormat1680K;
break;
default:
ErrorMessage = $"Unsupport media format {mediaType}";
return false;
}
dataOffset = 0x54;
tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0;
header.DiskName = "-DiscImageChef converted image-";
header.Valid = 1;
header.DataSize = (uint)(sectors * 512);
if(tags) header.TagSize = (uint)(sectors * 12);
sectorsToWrite = sectors;
try { writingStream = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); }
catch(IOException e)
{
ErrorMessage = $"Could not create new image file, exception {e.Message}";
return false;
}
ErrorMessage = null;
return true;
}
public bool WriteMediaTag(byte[] data, MediaTagType tag)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool WriteSector(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(sectorAddress >= sectorsToWrite)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(data.Length != 512)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(data.Length % 512 != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length >= sectorsToWrite)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin);
writingStream.Write(data, 0, data.Length);
ErrorMessage = "";
return true;
}
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(header.TagSize == 0)
{
ErrorMessage = "Image does not support tags";
return false;
}
if(data.Length != 524)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress >= sectorsToWrite)
{
ErrorMessage = "Tried to write past image size";
return false;
}
writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin);
writingStream.Write(data, 0, 512);
writingStream.Seek((long)(tagOffset + sectorAddress * 12), SeekOrigin.Begin);
writingStream.Write(data, 512, 12);
ErrorMessage = "";
return true;
}
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
{
if(!IsWriting)
{
ErrorMessage = "Tried to write on a non-writable image";
return false;
}
if(header.TagSize == 0)
{
ErrorMessage = "Image does not support tags";
return false;
}
if(data.Length % 524 != 0)
{
ErrorMessage = "Incorrect data size";
return false;
}
if(sectorAddress + length >= sectorsToWrite)
{
ErrorMessage = "Tried to write past image size";
return false;
}
for(uint i = 0; i < length; i++)
{
writingStream.Seek((long)(dataOffset + (sectorAddress + i) * 512), SeekOrigin.Begin);
writingStream.Write(data, (int)(i * 524 + 0), 512);
writingStream.Seek((long)(tagOffset + (sectorAddress + i) * 12),
SeekOrigin.Begin);
writingStream.Write(data, (int)(i * 524 + 512),
12);
}
ErrorMessage = "";
return true;
}
public bool SetTracks(List<Track> tracks)
{
ErrorMessage = "Unsupported feature";
return false;
}
public bool Close()
{
if(!IsWriting)
{
ErrorMessage = "Image is not opened for writing";
return false;
}
writingStream.Seek(0x54, SeekOrigin.Begin);
byte[] data = new byte[header.DataSize];
writingStream.Read(data, 0, (int)header.DataSize);
header.DataChecksum = DC42CheckSum(data);
writingStream.Seek(0x54 + header.DataSize, SeekOrigin.Begin);
data = new byte[header.TagSize];
writingStream.Read(data, 0, (int)header.TagSize);
header.TagChecksum = DC42CheckSum(data);
writingStream.Seek(0, SeekOrigin.Begin);
if(header.DiskName.Length > 63) header.DiskName = header.DiskName.Substring(0, 63);
writingStream.WriteByte((byte)header.DiskName.Length);
Encoding macRoman = new MacRoman();
writingStream.Write(macRoman.GetBytes(header.DiskName), 0, header.DiskName.Length);
writingStream.Seek(64, SeekOrigin.Begin);
writingStream.Write(BigEndianBitConverter.GetBytes(header.DataSize), 0, 4);
writingStream.Write(BigEndianBitConverter.GetBytes(header.TagSize), 0, 4);
writingStream.Write(BigEndianBitConverter.GetBytes(header.DataChecksum), 0, 4);
writingStream.Write(BigEndianBitConverter.GetBytes(header.TagChecksum), 0, 4);
writingStream.WriteByte(header.Format);
writingStream.WriteByte(header.FmtByte);
writingStream.WriteByte(1);
writingStream.WriteByte(0);
writingStream.Flush();
writingStream.Close();
IsWriting = false;
return true;
}
static uint DC42CheckSum(byte[] buffer) static uint DC42CheckSum(byte[] buffer)
{ {
uint dc42Chk = 0; uint dc42Chk = 0;
@@ -782,12 +1114,17 @@ namespace DiscImageChef.DiscImages
{ {
dc42Chk += (uint)(buffer[i] << 8); dc42Chk += (uint)(buffer[i] << 8);
dc42Chk += buffer[i + 1]; dc42Chk += buffer[i + 1];
dc42Chk = (dc42Chk >> 1) | (dc42Chk << 31); dc42Chk = (dc42Chk >> 1) | (dc42Chk << 31);
} }
return dc42Chk; return dc42Chk;
} }
~DiskCopy42()
{
Close();
}
// DiskCopy 4.2 header, big-endian, data-fork, start of file, 84 bytes // DiskCopy 4.2 header, big-endian, data-fork, start of file, 84 bytes
struct Dc42Header struct Dc42Header
{ {

View File

@@ -45,15 +45,15 @@ namespace DiscImageChef.DiscImages
/// <summary> /// <summary>
/// Gets a list of <see cref="MediaTagType" /> that are supported by the media image format /// Gets a list of <see cref="MediaTagType" /> that are supported by the media image format
/// </summary> /// </summary>
List<MediaTagType> SupportedMediaTags { get; } IEnumerable<MediaTagType> SupportedMediaTags { get; }
/// <summary> /// <summary>
/// Gets a list of <see cref="SectorTagType" /> that are supported by the media image format /// Gets a list of <see cref="SectorTagType" /> that are supported by the media image format
/// </summary> /// </summary>
List<SectorTagType> SupportedSectorTags { get; } IEnumerable<SectorTagType> SupportedSectorTags { get; }
/// <summary> /// <summary>
/// Gets a list of <see cref="MediaType" /> that are supported by the media image format /// Gets a list of <see cref="MediaType" /> that are supported by the media image format
/// </summary> /// </summary>
List<MediaType> SupportedMediaTypes { get; } IEnumerable<MediaType> SupportedMediaTypes { get; }
/// <summary> /// <summary>
/// Retrieves a list of options supported by the filesystem, with name, type and description /// Retrieves a list of options supported by the filesystem, with name, type and description
/// </summary> /// </summary>
@@ -61,8 +61,11 @@ namespace DiscImageChef.DiscImages
/// <summary> /// <summary>
/// Gets a list of known extensions for format auto-chosing /// Gets a list of known extensions for format auto-chosing
/// </summary> /// </summary>
List<string> KnownExtensions { get; } IEnumerable<string> KnownExtensions { get; }
bool IsWriting { get; }
string ErrorMessage { get; }
/// <summary> /// <summary>
/// Creates a new image in the specified path, for the specified <see cref="MediaType" />, with the /// Creates a new image in the specified path, for the specified <see cref="MediaType" />, with the
/// specified options to hold a media with the specified number of sectors /// specified options to hold a media with the specified number of sectors