2017-05-19 20:28:49 +01:00
|
|
|
// /***************************************************************************
|
2016-09-21 01:54:09 +01:00
|
|
|
// The Disc Image Chef
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : CHD.cs
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
2016-09-21 01:55:41 +01:00
|
|
|
// Component : Disc image plugins.
|
2016-09-21 01:54:09 +01:00
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
2016-09-21 01:55:41 +01:00
|
|
|
// Manages MAME Compressed Hunks of Data disk images.
|
2016-09-21 01:54:09 +01:00
|
|
|
//
|
|
|
|
|
// --[ 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/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2017-12-19 03:50:57 +00:00
|
|
|
// Copyright © 2011-2018 Natalia Portillo
|
2016-09-21 01:54:09 +01:00
|
|
|
// ****************************************************************************/
|
2016-09-21 01:55:41 +01:00
|
|
|
|
2016-09-21 01:54:09 +01:00
|
|
|
using System;
|
2016-09-21 01:55:41 +01:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2016-10-17 04:41:27 +01:00
|
|
|
using System.Linq;
|
2016-09-21 01:55:41 +01:00
|
|
|
using System.Runtime.InteropServices;
|
2016-10-17 04:41:27 +01:00
|
|
|
using System.Text;
|
2016-09-21 01:55:41 +01:00
|
|
|
using System.Text.RegularExpressions;
|
2017-12-21 14:30:38 +00:00
|
|
|
using DiscImageChef.Checksums;
|
2016-09-21 01:55:41 +01:00
|
|
|
using DiscImageChef.CommonTypes;
|
|
|
|
|
using DiscImageChef.Console;
|
2017-12-21 14:30:38 +00:00
|
|
|
using DiscImageChef.Decoders.ATA;
|
2016-09-21 01:55:41 +01:00
|
|
|
using DiscImageChef.Filters;
|
2016-09-30 04:32:18 +01:00
|
|
|
using SharpCompress.Compressors;
|
2016-10-17 04:41:27 +01:00
|
|
|
using SharpCompress.Compressors.Deflate;
|
2016-09-21 01:55:41 +01:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
namespace DiscImageChef.DiscImages
|
2016-09-21 01:54:09 +01:00
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
// TODO: Implement PCMCIA support
|
2017-12-26 06:05:12 +00:00
|
|
|
public class Chd : IMediaImage
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
/// <summary>"GDDD"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint HARD_DISK_METADATA = 0x47444444;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"IDNT"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint HARD_DISK_IDENT_METADATA = 0x49444E54;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"KEY "</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint HARD_DISK_KEY_METADATA = 0x4B455920;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CIS "</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint PCMCIA_CIS_METADATA = 0x43495320;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CHCD"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint CDROM_OLD_METADATA = 0x43484344;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CHTR"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint CDROM_TRACK_METADATA = 0x43485452;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CHT2"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint CDROM_TRACK_METADATA2 = 0x43485432;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CHGT"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint GDROM_OLD_METADATA = 0x43484754;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"CHGD"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint GDROM_METADATA = 0x43484744;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"AVAV"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint AV_METADATA = 0x41564156;
|
2017-12-19 20:33:03 +00:00
|
|
|
/// <summary>"AVLD"</summary>
|
2017-12-20 17:15:26 +00:00
|
|
|
const uint AV_LASER_DISC_METADATA = 0x41564C44;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
const string REGEX_METADATA_HDD =
|
2017-12-24 20:26:23 +00:00
|
|
|
@"CYLS:(?<cylinders>\d+),HEADS:(?<heads>\d+),SECS:(?<sectors>\d+),BPS:(?<bps>\d+)";
|
2017-12-24 00:12:31 +00:00
|
|
|
const string REGEX_METADATA_CDROM =
|
2017-12-24 20:26:23 +00:00
|
|
|
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+)";
|
2017-12-24 00:12:31 +00:00
|
|
|
const string REGEX_METADATA_CDROM2 =
|
2017-12-24 20:26:23 +00:00
|
|
|
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+) PREGAP:(?<pregap>\d+) PGTYPE:(?<pgtype>\S+) PGSUB:(?<pgsub>\S+) POSTGAP:(?<postgap>\d+)"
|
2017-12-19 20:33:03 +00:00
|
|
|
;
|
2017-12-24 00:12:31 +00:00
|
|
|
const string REGEX_METADATA_GDROM =
|
2017-12-24 20:26:23 +00:00
|
|
|
@"TRACK:(?<track>\d+) TYPE:(?<track_type>\S+) SUBTYPE:(?<sub_type>\S+) FRAMES:(?<frames>\d+) PAD:(?<pad>\d+) PREGAP:(?<pregap>\d+) PGTYPE:(?<pgtype>\S+) PGSUB:(?<pgsub>\S+) POSTGAP:(?<postgap>\d+)"
|
2017-12-19 20:33:03 +00:00
|
|
|
;
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
const string TRACK_TYPE_MODE1 = "MODE1";
|
|
|
|
|
const string TRACK_TYPE_MODE1_2K = "MODE1/2048";
|
|
|
|
|
const string TRACK_TYPE_MODE1_RAW = "MODE1_RAW";
|
|
|
|
|
const string TRACK_TYPE_MODE1_RAW_2K = "MODE1/2352";
|
|
|
|
|
const string TRACK_TYPE_MODE2 = "MODE2";
|
|
|
|
|
const string TRACK_TYPE_MODE2_2K = "MODE2/2336";
|
|
|
|
|
const string TRACK_TYPE_MODE2_F1 = "MODE2_FORM1";
|
|
|
|
|
const string TRACK_TYPE_MODE2_F1_2K = "MODE2/2048";
|
|
|
|
|
const string TRACK_TYPE_MODE2_F2 = "MODE2_FORM2";
|
|
|
|
|
const string TRACK_TYPE_MODE2_F2_2K = "MODE2/2324";
|
|
|
|
|
const string TRACK_TYPE_MODE2_FM = "MODE2_FORM_MIX";
|
|
|
|
|
const string TRACK_TYPE_MODE2_RAW = "MODE2_RAW";
|
|
|
|
|
const string TRACK_TYPE_MODE2_RAW_2K = "MODE2/2352";
|
|
|
|
|
const string TRACK_TYPE_AUDIO = "AUDIO";
|
|
|
|
|
|
|
|
|
|
const string SUB_TYPE_COOKED = "RW";
|
|
|
|
|
const string SUB_TYPE_RAW = "RW_RAW";
|
|
|
|
|
const string SUB_TYPE_NONE = "NONE";
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
const int MAX_CACHE_SIZE = 16777216;
|
|
|
|
|
|
|
|
|
|
/// <summary>"MComprHD"</summary>
|
|
|
|
|
readonly byte[] chdTag = {0x4D, 0x43, 0x6F, 0x6D, 0x70, 0x72, 0x48, 0x44};
|
|
|
|
|
uint bytesPerHunk;
|
|
|
|
|
byte[] cis;
|
|
|
|
|
byte[] expectedChecksum;
|
2017-12-19 20:33:03 +00:00
|
|
|
uint hdrCompression;
|
|
|
|
|
uint hdrCompression1;
|
|
|
|
|
uint hdrCompression2;
|
|
|
|
|
uint hdrCompression3;
|
2017-12-24 00:12:31 +00:00
|
|
|
Dictionary<ulong, byte[]> hunkCache;
|
2017-12-19 20:33:03 +00:00
|
|
|
byte[] hunkMap;
|
2017-12-24 00:12:31 +00:00
|
|
|
ulong[] hunkTable;
|
|
|
|
|
uint[] hunkTableSmall;
|
|
|
|
|
byte[] identify;
|
2017-12-26 06:05:12 +00:00
|
|
|
ImageInfo imageInfo;
|
2017-12-24 00:12:31 +00:00
|
|
|
Stream imageStream;
|
2017-12-19 20:33:03 +00:00
|
|
|
bool isCdrom;
|
|
|
|
|
bool isGdrom;
|
2017-12-24 00:12:31 +00:00
|
|
|
bool isHdd;
|
|
|
|
|
uint mapVersion;
|
2017-12-19 20:33:03 +00:00
|
|
|
int maxBlockCache;
|
|
|
|
|
int maxSectorCache;
|
2017-12-24 00:12:31 +00:00
|
|
|
Dictionary<ulong, uint> offsetmap;
|
|
|
|
|
List<Partition> partitions;
|
2017-12-19 20:33:03 +00:00
|
|
|
Dictionary<ulong, byte[]> sectorCache;
|
2017-12-24 00:12:31 +00:00
|
|
|
uint sectorsPerHunk;
|
|
|
|
|
bool swapAudio;
|
|
|
|
|
uint totalHunks;
|
2017-12-19 20:33:03 +00:00
|
|
|
Dictionary<uint, Track> tracks;
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
public Chd()
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo = new ImageInfo
|
2017-12-22 06:55:04 +00:00
|
|
|
{
|
|
|
|
|
ReadableSectorTags = new List<SectorTagType>(),
|
|
|
|
|
ReadableMediaTags = new List<MediaTagType>(),
|
2017-12-26 02:51:10 +00:00
|
|
|
HasPartitions = false,
|
|
|
|
|
HasSessions = false,
|
|
|
|
|
Application = "MAME",
|
|
|
|
|
Creator = null,
|
|
|
|
|
Comments = null,
|
2017-12-22 06:55:04 +00:00
|
|
|
MediaManufacturer = null,
|
|
|
|
|
MediaModel = null,
|
|
|
|
|
MediaSerialNumber = null,
|
|
|
|
|
MediaBarcode = null,
|
|
|
|
|
MediaPartNumber = null,
|
|
|
|
|
MediaSequence = 0,
|
|
|
|
|
LastMediaSequence = 0,
|
|
|
|
|
DriveManufacturer = null,
|
|
|
|
|
DriveModel = null,
|
|
|
|
|
DriveSerialNumber = null,
|
|
|
|
|
DriveFirmwareRevision = null
|
|
|
|
|
};
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public ImageInfo Info => imageInfo;
|
2017-12-26 02:51:10 +00:00
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public string Name => "MAME Compressed Hunks of Data";
|
|
|
|
|
public Guid Id => new Guid("0D50233A-08BD-47D4-988B-27EAA0358597");
|
2017-12-26 06:05:12 +00:00
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public string ImageFormat => "Compressed Hunks of Data";
|
2017-12-26 06:05:12 +00:00
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Partition> Partitions
|
2017-12-26 02:51:10 +00:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return partitions;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Track> Tracks
|
2017-12-26 02:51:10 +00:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return tracks.Values.ToList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Session> Sessions
|
2017-12-26 02:51:10 +00:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
FeaturedNotSupportedByDiscImageException("Cannot access optical sessions on a hard disk image");
|
|
|
|
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool IdentifyImage(IFilter imageFilter)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
Stream stream = imageFilter.GetDataForkStream();
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
byte[] magic = new byte[8];
|
|
|
|
|
stream.Read(magic, 0, 8);
|
|
|
|
|
|
|
|
|
|
return chdTag.SequenceEqual(magic);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool OpenImage(IFilter imageFilter)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
Stream stream = imageFilter.GetDataForkStream();
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
byte[] magic = new byte[8];
|
|
|
|
|
stream.Read(magic, 0, 8);
|
|
|
|
|
if(!chdTag.SequenceEqual(magic)) return false;
|
|
|
|
|
// Read length
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] buffer = new byte[4];
|
2017-12-19 20:33:03 +00:00
|
|
|
stream.Read(buffer, 0, 4);
|
|
|
|
|
uint length = BitConverter.ToUInt32(buffer.Reverse().ToArray(), 0);
|
|
|
|
|
buffer = new byte[4];
|
|
|
|
|
stream.Read(buffer, 0, 4);
|
|
|
|
|
uint version = BitConverter.ToUInt32(buffer.Reverse().ToArray(), 0);
|
|
|
|
|
|
|
|
|
|
buffer = new byte[length];
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
stream.Read(buffer, 0, (int)length);
|
|
|
|
|
|
|
|
|
|
ulong nextMetaOff = 0;
|
|
|
|
|
|
|
|
|
|
switch(version)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdHeaderV1 hdrV1 = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdHeaderV1>(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.tag = \"{0}\"", Encoding.ASCII.GetString(hdrV1.tag));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.length = {0} bytes", hdrV1.length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.version = {0}", hdrV1.version);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.flags = {0}", (ChdFlags)hdrV1.flags);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.compression = {0}",
|
2017-12-20 17:15:26 +00:00
|
|
|
(ChdCompression)hdrV1.compression);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.hunksize = {0}", hdrV1.hunksize);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.totalhunks = {0}", hdrV1.totalhunks);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.cylinders = {0}", hdrV1.cylinders);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.heads = {0}", hdrV1.heads);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.sectors = {0}", hdrV1.sectors);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.md5 = {0}", ArrayHelpers.ByteArrayToHex(hdrV1.md5));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV1.parentmd5 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV1.parentmd5)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV1.parentmd5));
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading Hunk map.");
|
|
|
|
|
DateTime start = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
hunkTable = new ulong[hdrV1.totalhunks];
|
|
|
|
|
|
2017-12-20 17:26:28 +00:00
|
|
|
uint hunkSectorCount = (uint)Math.Ceiling((double)hdrV1.totalhunks * 8 / 512);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunkSectorBytes = new byte[512];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < hunkSectorCount; i++)
|
|
|
|
|
{
|
|
|
|
|
stream.Read(hunkSectorBytes, 0, 512);
|
|
|
|
|
// This does the big-endian trick but reverses the order of elements also
|
|
|
|
|
Array.Reverse(hunkSectorBytes);
|
|
|
|
|
GCHandle handle = GCHandle.Alloc(hunkSectorBytes, GCHandleType.Pinned);
|
2017-12-24 00:12:31 +00:00
|
|
|
HunkSector hunkSector =
|
|
|
|
|
(HunkSector)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(HunkSector));
|
2017-12-19 20:33:03 +00:00
|
|
|
handle.Free();
|
|
|
|
|
// This restores the order of elements
|
|
|
|
|
Array.Reverse(hunkSector.hunkEntry);
|
2017-12-20 17:26:28 +00:00
|
|
|
if(hunkTable.Length >= i * 512 / 8 + 512 / 8)
|
|
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTable, i * 512 / 8, 512 / 8);
|
2017-12-19 20:33:03 +00:00
|
|
|
else
|
2017-12-24 00:12:31 +00:00
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTable, i * 512 / 8, hunkTable.Length - i * 512 / 8);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DateTime end = DateTime.UtcNow;
|
|
|
|
|
System.Console.WriteLine("Took {0} seconds", (end - start).TotalSeconds);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaType = MediaType.GENERIC_HDD;
|
|
|
|
|
imageInfo.Sectors = hdrV1.hunksize * hdrV1.totalhunks;
|
|
|
|
|
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
|
|
|
|
|
imageInfo.SectorSize = 512;
|
|
|
|
|
imageInfo.Version = "1";
|
|
|
|
|
imageInfo.ImageSize = imageInfo.SectorSize * hdrV1.hunksize * hdrV1.totalhunks;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalHunks = hdrV1.totalhunks;
|
|
|
|
|
sectorsPerHunk = hdrV1.hunksize;
|
|
|
|
|
hdrCompression = hdrV1.compression;
|
|
|
|
|
mapVersion = 1;
|
|
|
|
|
isHdd = true;
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Cylinders = hdrV1.cylinders;
|
|
|
|
|
imageInfo.Heads = hdrV1.heads;
|
|
|
|
|
imageInfo.SectorsPerTrack = hdrV1.sectors;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdHeaderV2 hdrV2 = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdHeaderV2>(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.tag = \"{0}\"", Encoding.ASCII.GetString(hdrV2.tag));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.length = {0} bytes", hdrV2.length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.version = {0}", hdrV2.version);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.flags = {0}", (ChdFlags)hdrV2.flags);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.compression = {0}",
|
2017-12-20 17:15:26 +00:00
|
|
|
(ChdCompression)hdrV2.compression);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.hunksize = {0}", hdrV2.hunksize);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.totalhunks = {0}", hdrV2.totalhunks);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.cylinders = {0}", hdrV2.cylinders);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.heads = {0}", hdrV2.heads);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.sectors = {0}", hdrV2.sectors);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.md5 = {0}", ArrayHelpers.ByteArrayToHex(hdrV2.md5));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.parentmd5 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV2.parentmd5)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV2.parentmd5));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV2.seclen = {0}", hdrV2.seclen);
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading Hunk map.");
|
|
|
|
|
DateTime start = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
hunkTable = new ulong[hdrV2.totalhunks];
|
|
|
|
|
|
|
|
|
|
// How many sectors uses the BAT
|
2017-12-20 17:26:28 +00:00
|
|
|
uint hunkSectorCount = (uint)Math.Ceiling((double)hdrV2.totalhunks * 8 / 512);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunkSectorBytes = new byte[512];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < hunkSectorCount; i++)
|
|
|
|
|
{
|
|
|
|
|
stream.Read(hunkSectorBytes, 0, 512);
|
|
|
|
|
// This does the big-endian trick but reverses the order of elements also
|
|
|
|
|
Array.Reverse(hunkSectorBytes);
|
|
|
|
|
GCHandle handle = GCHandle.Alloc(hunkSectorBytes, GCHandleType.Pinned);
|
2017-12-24 00:12:31 +00:00
|
|
|
HunkSector hunkSector =
|
|
|
|
|
(HunkSector)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(HunkSector));
|
2017-12-19 20:33:03 +00:00
|
|
|
handle.Free();
|
|
|
|
|
// This restores the order of elements
|
|
|
|
|
Array.Reverse(hunkSector.hunkEntry);
|
2017-12-20 17:26:28 +00:00
|
|
|
if(hunkTable.Length >= i * 512 / 8 + 512 / 8)
|
|
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTable, i * 512 / 8, 512 / 8);
|
2017-12-19 20:33:03 +00:00
|
|
|
else
|
2017-12-24 00:12:31 +00:00
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTable, i * 512 / 8, hunkTable.Length - i * 512 / 8);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DateTime end = DateTime.UtcNow;
|
|
|
|
|
System.Console.WriteLine("Took {0} seconds", (end - start).TotalSeconds);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaType = MediaType.GENERIC_HDD;
|
|
|
|
|
imageInfo.Sectors = hdrV2.hunksize * hdrV2.totalhunks;
|
|
|
|
|
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
|
|
|
|
|
imageInfo.SectorSize = hdrV2.seclen;
|
|
|
|
|
imageInfo.Version = "2";
|
|
|
|
|
imageInfo.ImageSize = imageInfo.SectorSize * hdrV2.hunksize * hdrV2.totalhunks;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalHunks = hdrV2.totalhunks;
|
|
|
|
|
sectorsPerHunk = hdrV2.hunksize;
|
|
|
|
|
hdrCompression = hdrV2.compression;
|
|
|
|
|
mapVersion = 1;
|
|
|
|
|
isHdd = true;
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Cylinders = hdrV2.cylinders;
|
|
|
|
|
imageInfo.Heads = hdrV2.heads;
|
|
|
|
|
imageInfo.SectorsPerTrack = hdrV2.sectors;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdHeaderV3 hdrV3 = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdHeaderV3>(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.tag = \"{0}\"", Encoding.ASCII.GetString(hdrV3.tag));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.length = {0} bytes", hdrV3.length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.version = {0}", hdrV3.version);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.flags = {0}", (ChdFlags)hdrV3.flags);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.compression = {0}",
|
2017-12-20 17:15:26 +00:00
|
|
|
(ChdCompression)hdrV3.compression);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.totalhunks = {0}", hdrV3.totalhunks);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.logicalbytes = {0}", hdrV3.logicalbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.metaoffset = {0}", hdrV3.metaoffset);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.md5 = {0}", ArrayHelpers.ByteArrayToHex(hdrV3.md5));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.parentmd5 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV3.parentmd5)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV3.parentmd5));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.hunkbytes = {0}", hdrV3.hunkbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.sha1 = {0}",
|
|
|
|
|
ArrayHelpers.ByteArrayToHex(hdrV3.sha1));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV3.parentsha1 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV3.parentsha1)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV3.parentsha1));
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading Hunk map.");
|
|
|
|
|
DateTime start = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
hunkMap = new byte[hdrV3.totalhunks * 16];
|
|
|
|
|
stream.Read(hunkMap, 0, hunkMap.Length);
|
|
|
|
|
|
|
|
|
|
DateTime end = DateTime.UtcNow;
|
|
|
|
|
System.Console.WriteLine("Took {0} seconds", (end - start).TotalSeconds);
|
|
|
|
|
|
|
|
|
|
nextMetaOff = hdrV3.metaoffset;
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.ImageSize = hdrV3.logicalbytes;
|
|
|
|
|
imageInfo.Version = "3";
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalHunks = hdrV3.totalhunks;
|
|
|
|
|
bytesPerHunk = hdrV3.hunkbytes;
|
|
|
|
|
hdrCompression = hdrV3.compression;
|
|
|
|
|
mapVersion = 3;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 4:
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdHeaderV4 hdrV4 = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdHeaderV4>(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.tag = \"{0}\"", Encoding.ASCII.GetString(hdrV4.tag));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.length = {0} bytes", hdrV4.length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.version = {0}", hdrV4.version);
|
2017-12-20 17:15:26 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.flags = {0}", (ChdFlags)hdrV4.flags);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.compression = {0}",
|
2017-12-20 17:15:26 +00:00
|
|
|
(ChdCompression)hdrV4.compression);
|
2017-12-19 20:33:03 +00:00
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.totalhunks = {0}", hdrV4.totalhunks);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.logicalbytes = {0}", hdrV4.logicalbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.metaoffset = {0}", hdrV4.metaoffset);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.hunkbytes = {0}", hdrV4.hunkbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.sha1 = {0}",
|
|
|
|
|
ArrayHelpers.ByteArrayToHex(hdrV4.sha1));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.parentsha1 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV4.parentsha1)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV4.parentsha1));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV4.rawsha1 = {0}",
|
|
|
|
|
ArrayHelpers.ByteArrayToHex(hdrV4.rawsha1));
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading Hunk map.");
|
|
|
|
|
DateTime start = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
hunkMap = new byte[hdrV4.totalhunks * 16];
|
|
|
|
|
stream.Read(hunkMap, 0, hunkMap.Length);
|
|
|
|
|
|
|
|
|
|
DateTime end = DateTime.UtcNow;
|
|
|
|
|
System.Console.WriteLine("Took {0} seconds", (end - start).TotalSeconds);
|
|
|
|
|
|
|
|
|
|
nextMetaOff = hdrV4.metaoffset;
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.ImageSize = hdrV4.logicalbytes;
|
|
|
|
|
imageInfo.Version = "4";
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalHunks = hdrV4.totalhunks;
|
|
|
|
|
bytesPerHunk = hdrV4.hunkbytes;
|
|
|
|
|
hdrCompression = hdrV4.compression;
|
|
|
|
|
mapVersion = 3;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 5:
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdHeaderV5 hdrV5 = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdHeaderV5>(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.tag = \"{0}\"", Encoding.ASCII.GetString(hdrV5.tag));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.length = {0} bytes", hdrV5.length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.version = {0}", hdrV5.version);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.compressor0 = \"{0}\"",
|
|
|
|
|
Encoding.ASCII.GetString(BigEndianBitConverter
|
|
|
|
|
.GetBytes(hdrV5.compressor0)));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.compressor1 = \"{0}\"",
|
|
|
|
|
Encoding.ASCII.GetString(BigEndianBitConverter
|
|
|
|
|
.GetBytes(hdrV5.compressor1)));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.compressor2 = \"{0}\"",
|
|
|
|
|
Encoding.ASCII.GetString(BigEndianBitConverter
|
|
|
|
|
.GetBytes(hdrV5.compressor2)));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.compressor3 = \"{0}\"",
|
|
|
|
|
Encoding.ASCII.GetString(BigEndianBitConverter
|
|
|
|
|
.GetBytes(hdrV5.compressor3)));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.logicalbytes = {0}", hdrV5.logicalbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.mapoffset = {0}", hdrV5.mapoffset);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.metaoffset = {0}", hdrV5.metaoffset);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.hunkbytes = {0}", hdrV5.hunkbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.unitbytes = {0}", hdrV5.unitbytes);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.sha1 = {0}",
|
|
|
|
|
ArrayHelpers.ByteArrayToHex(hdrV5.sha1));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.parentsha1 = {0}",
|
|
|
|
|
ArrayHelpers.ArrayIsNullOrEmpty(hdrV5.parentsha1)
|
|
|
|
|
? "null"
|
|
|
|
|
: ArrayHelpers.ByteArrayToHex(hdrV5.parentsha1));
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "hdrV5.rawsha1 = {0}",
|
|
|
|
|
ArrayHelpers.ByteArrayToHex(hdrV5.rawsha1));
|
|
|
|
|
|
|
|
|
|
// TODO: Implement compressed CHD v5
|
|
|
|
|
if(hdrV5.compressor0 == 0)
|
|
|
|
|
{
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading Hunk map.");
|
|
|
|
|
DateTime start = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
hunkTableSmall = new uint[hdrV5.logicalbytes / hdrV5.hunkbytes];
|
|
|
|
|
|
2017-12-20 17:26:28 +00:00
|
|
|
uint hunkSectorCount = (uint)Math.Ceiling((double)hunkTableSmall.Length * 4 / 512);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunkSectorBytes = new byte[512];
|
|
|
|
|
|
|
|
|
|
stream.Seek((long)hdrV5.mapoffset, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < hunkSectorCount; i++)
|
|
|
|
|
{
|
|
|
|
|
stream.Read(hunkSectorBytes, 0, 512);
|
|
|
|
|
// This does the big-endian trick but reverses the order of elements also
|
|
|
|
|
Array.Reverse(hunkSectorBytes);
|
|
|
|
|
GCHandle handle = GCHandle.Alloc(hunkSectorBytes, GCHandleType.Pinned);
|
2017-12-24 00:12:31 +00:00
|
|
|
HunkSectorSmall hunkSector =
|
|
|
|
|
(HunkSectorSmall)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),
|
|
|
|
|
typeof(HunkSectorSmall));
|
2017-12-19 20:33:03 +00:00
|
|
|
handle.Free();
|
|
|
|
|
// This restores the order of elements
|
|
|
|
|
Array.Reverse(hunkSector.hunkEntry);
|
2017-12-20 17:26:28 +00:00
|
|
|
if(hunkTableSmall.Length >= i * 512 / 4 + 512 / 4)
|
|
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTableSmall, i * 512 / 4, 512 / 4);
|
2017-12-19 20:33:03 +00:00
|
|
|
else
|
2017-12-20 17:26:28 +00:00
|
|
|
Array.Copy(hunkSector.hunkEntry, 0, hunkTableSmall, i * 512 / 4,
|
|
|
|
|
hunkTableSmall.Length - i * 512 / 4);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DateTime end = DateTime.UtcNow;
|
|
|
|
|
System.Console.WriteLine("Took {0} seconds", (end - start).TotalSeconds);
|
|
|
|
|
}
|
|
|
|
|
else throw new ImageNotSupportedException("Cannot read compressed CHD version 5");
|
|
|
|
|
|
|
|
|
|
nextMetaOff = hdrV5.metaoffset;
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.ImageSize = hdrV5.logicalbytes;
|
|
|
|
|
imageInfo.Version = "5";
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
totalHunks = (uint)(hdrV5.logicalbytes / hdrV5.hunkbytes);
|
|
|
|
|
bytesPerHunk = hdrV5.hunkbytes;
|
|
|
|
|
hdrCompression = hdrV5.compressor0;
|
|
|
|
|
hdrCompression1 = hdrV5.compressor1;
|
|
|
|
|
hdrCompression2 = hdrV5.compressor2;
|
|
|
|
|
hdrCompression3 = hdrV5.compressor3;
|
|
|
|
|
mapVersion = 5;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-21 17:58:51 +00:00
|
|
|
default: throw new ImageNotSupportedException($"Unsupported CHD version {version}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(mapVersion >= 3)
|
|
|
|
|
{
|
|
|
|
|
isCdrom = false;
|
|
|
|
|
isHdd = false;
|
|
|
|
|
isGdrom = false;
|
|
|
|
|
swapAudio = false;
|
|
|
|
|
tracks = new Dictionary<uint, Track>();
|
|
|
|
|
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Reading metadata.");
|
|
|
|
|
|
|
|
|
|
ulong currentSector = 0;
|
|
|
|
|
uint currentTrack = 1;
|
|
|
|
|
|
|
|
|
|
while(nextMetaOff > 0)
|
|
|
|
|
{
|
|
|
|
|
byte[] hdrBytes = new byte[16];
|
|
|
|
|
stream.Seek((long)nextMetaOff, SeekOrigin.Begin);
|
|
|
|
|
stream.Read(hdrBytes, 0, hdrBytes.Length);
|
2017-12-20 17:15:26 +00:00
|
|
|
ChdMetadataHeader header =
|
|
|
|
|
BigEndianMarshal.ByteArrayToStructureBigEndian<ChdMetadataHeader>(hdrBytes);
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] meta = new byte[header.flagsAndLength & 0xFFFFFF];
|
2017-12-19 20:33:03 +00:00
|
|
|
stream.Read(meta, 0, meta.Length);
|
|
|
|
|
DicConsole.DebugWriteLine("CHD plugin", "Found metadata \"{0}\"",
|
|
|
|
|
Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(header.tag)));
|
|
|
|
|
|
|
|
|
|
switch(header.tag)
|
|
|
|
|
{
|
|
|
|
|
// "GDDD"
|
2017-12-20 17:15:26 +00:00
|
|
|
case HARD_DISK_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isCdrom || isGdrom)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a hard disk and a C/GD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
string gddd = StringHandlers.CToString(meta);
|
2017-12-24 00:12:31 +00:00
|
|
|
Regex gdddRegEx = new Regex(REGEX_METADATA_HDD);
|
2017-12-19 20:33:03 +00:00
|
|
|
Match gdddMatch = gdddRegEx.Match(gddd);
|
|
|
|
|
if(gdddMatch.Success)
|
|
|
|
|
{
|
|
|
|
|
isHdd = true;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.SectorSize = uint.Parse(gdddMatch.Groups["bps"].Value);
|
|
|
|
|
imageInfo.Cylinders = uint.Parse(gdddMatch.Groups["cylinders"].Value);
|
|
|
|
|
imageInfo.Heads = uint.Parse(gdddMatch.Groups["heads"].Value);
|
|
|
|
|
imageInfo.SectorsPerTrack = uint.Parse(gdddMatch.Groups["sectors"].Value);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
// "CHCD"
|
2017-12-20 17:15:26 +00:00
|
|
|
case CDROM_OLD_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a hard disk and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
if(isGdrom)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a GD-ROM and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
uint chdTracksNumber = BigEndianBitConverter.ToUInt32(meta, 0);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
// Byteswapped
|
2017-12-22 06:55:04 +00:00
|
|
|
if(chdTracksNumber > 99)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
BigEndianBitConverter.IsLittleEndian = !BitConverter.IsLittleEndian;
|
2017-12-22 06:55:04 +00:00
|
|
|
chdTracksNumber = BigEndianBitConverter.ToUInt32(meta, 0);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentSector = 0;
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
for(uint i = 0; i < chdTracksNumber; i++)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
ChdTrackOld chdTrack = new ChdTrackOld
|
|
|
|
|
{
|
|
|
|
|
type = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 0)),
|
|
|
|
|
subType = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 4)),
|
|
|
|
|
dataSize = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 8)),
|
|
|
|
|
subSize = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 12)),
|
|
|
|
|
frames = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 16)),
|
|
|
|
|
extraFrames = BigEndianBitConverter.ToUInt32(meta, (int)(4 + i * 24 + 20))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Track dicTrack = new Track();
|
|
|
|
|
switch((ChdOldTrackType)chdTrack.type)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Audio:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.Audio;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode1:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode1Raw:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode2:
|
|
|
|
|
case ChdOldTrackType.Mode2FormMix:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode2Form1:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode2Form2:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form2;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldTrackType.Mode2Raw:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-22 06:55:04 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported track type {chdTrack.type}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
switch((ChdOldSubType)chdTrack.subType)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldSubType.Cooked:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.PackedInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldSubType.None:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.None;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case ChdOldSubType.Raw:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new
|
2017-12-22 06:55:04 +00:00
|
|
|
ImageNotSupportedException($"Unsupported subchannel type {chdTrack.type}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.Indexes = new Dictionary<int, ulong>();
|
|
|
|
|
dicTrack.TrackDescription = $"Track {i + 1}";
|
|
|
|
|
dicTrack.TrackEndSector = currentSector + chdTrack.frames - 1;
|
|
|
|
|
dicTrack.TrackFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackFileType = "BINARY";
|
|
|
|
|
dicTrack.TrackFilter = imageFilter;
|
|
|
|
|
dicTrack.TrackStartSector = currentSector;
|
|
|
|
|
dicTrack.TrackSequence = i + 1;
|
|
|
|
|
dicTrack.TrackSession = 1;
|
|
|
|
|
currentSector += chdTrack.frames + chdTrack.extraFrames;
|
|
|
|
|
tracks.Add(dicTrack.TrackSequence, dicTrack);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
|
|
|
|
|
isCdrom = true;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
// "CHTR"
|
2017-12-20 17:15:26 +00:00
|
|
|
case CDROM_TRACK_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a hard disk and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
if(isGdrom)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a GD-ROM and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
string chtr = StringHandlers.CToString(meta);
|
2017-12-24 00:12:31 +00:00
|
|
|
Regex chtrRegEx = new Regex(REGEX_METADATA_CDROM);
|
2017-12-19 20:33:03 +00:00
|
|
|
Match chtrMatch = chtrRegEx.Match(chtr);
|
|
|
|
|
if(chtrMatch.Success)
|
|
|
|
|
{
|
|
|
|
|
isCdrom = true;
|
|
|
|
|
|
|
|
|
|
uint trackNo = uint.Parse(chtrMatch.Groups["track"].Value);
|
|
|
|
|
uint frames = uint.Parse(chtrMatch.Groups["frames"].Value);
|
|
|
|
|
string subtype = chtrMatch.Groups["sub_type"].Value;
|
|
|
|
|
string tracktype = chtrMatch.Groups["track_type"].Value;
|
|
|
|
|
|
|
|
|
|
if(trackNo != currentTrack)
|
|
|
|
|
throw new ImageNotSupportedException("Unsorted tracks, cannot proceed.");
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
Track dicTrack = new Track();
|
2017-12-19 20:33:03 +00:00
|
|
|
switch(tracktype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_AUDIO:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.Audio;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1:
|
|
|
|
|
case TRACK_TYPE_MODE1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE1_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2:
|
|
|
|
|
case TRACK_TYPE_MODE2_2K:
|
|
|
|
|
case TRACK_TYPE_MODE2_FM:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F1:
|
|
|
|
|
case TRACK_TYPE_MODE2_F1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F2:
|
|
|
|
|
case TRACK_TYPE_MODE2_F2_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form2;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE2_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 17:58:51 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported track type {tracktype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(subtype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_COOKED:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.PackedInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_NONE:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.None;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_RAW:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-24 00:12:31 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported subchannel type {subtype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.Indexes = new Dictionary<int, ulong>();
|
|
|
|
|
dicTrack.TrackDescription = $"Track {trackNo}";
|
|
|
|
|
dicTrack.TrackEndSector = currentSector + frames - 1;
|
|
|
|
|
dicTrack.TrackFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackFileType = "BINARY";
|
|
|
|
|
dicTrack.TrackFilter = imageFilter;
|
|
|
|
|
dicTrack.TrackStartSector = currentSector;
|
|
|
|
|
dicTrack.TrackSequence = trackNo;
|
|
|
|
|
dicTrack.TrackSession = 1;
|
2017-12-19 20:33:03 +00:00
|
|
|
currentSector += frames;
|
|
|
|
|
currentTrack++;
|
2017-12-22 06:55:04 +00:00
|
|
|
tracks.Add(dicTrack.TrackSequence, dicTrack);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
// "CHT2"
|
2017-12-20 17:15:26 +00:00
|
|
|
case CDROM_TRACK_METADATA2:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a hard disk and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
if(isGdrom)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a GD-ROM and a CD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
string cht2 = StringHandlers.CToString(meta);
|
2017-12-24 00:12:31 +00:00
|
|
|
Regex cht2RegEx = new Regex(REGEX_METADATA_CDROM2);
|
2017-12-19 20:33:03 +00:00
|
|
|
Match cht2Match = cht2RegEx.Match(cht2);
|
|
|
|
|
if(cht2Match.Success)
|
|
|
|
|
{
|
|
|
|
|
isCdrom = true;
|
|
|
|
|
|
|
|
|
|
uint trackNo = uint.Parse(cht2Match.Groups["track"].Value);
|
|
|
|
|
uint frames = uint.Parse(cht2Match.Groups["frames"].Value);
|
|
|
|
|
string subtype = cht2Match.Groups["sub_type"].Value;
|
|
|
|
|
string tracktype = cht2Match.Groups["track_type"].Value;
|
|
|
|
|
// TODO: Check pregap and postgap behaviour
|
|
|
|
|
uint pregap = uint.Parse(cht2Match.Groups["pregap"].Value);
|
|
|
|
|
string pregapType = cht2Match.Groups["pgtype"].Value;
|
|
|
|
|
string pregapSubType = cht2Match.Groups["pgsub"].Value;
|
|
|
|
|
uint postgap = uint.Parse(cht2Match.Groups["postgap"].Value);
|
|
|
|
|
|
|
|
|
|
if(trackNo != currentTrack)
|
|
|
|
|
throw new ImageNotSupportedException("Unsorted tracks, cannot proceed.");
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
Track dicTrack = new Track();
|
2017-12-19 20:33:03 +00:00
|
|
|
switch(tracktype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_AUDIO:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.Audio;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1:
|
|
|
|
|
case TRACK_TYPE_MODE1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE1_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2:
|
|
|
|
|
case TRACK_TYPE_MODE2_2K:
|
|
|
|
|
case TRACK_TYPE_MODE2_FM:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F1:
|
|
|
|
|
case TRACK_TYPE_MODE2_F1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F2:
|
|
|
|
|
case TRACK_TYPE_MODE2_F2_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form2;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE2_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 17:58:51 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported track type {tracktype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(subtype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_COOKED:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.PackedInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_NONE:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.None;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_RAW:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-24 00:12:31 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported subchannel type {subtype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.Indexes = new Dictionary<int, ulong>();
|
|
|
|
|
dicTrack.TrackDescription = $"Track {trackNo}";
|
|
|
|
|
dicTrack.TrackEndSector = currentSector + frames - 1;
|
|
|
|
|
dicTrack.TrackFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackFileType = "BINARY";
|
|
|
|
|
dicTrack.TrackFilter = imageFilter;
|
|
|
|
|
dicTrack.TrackStartSector = currentSector;
|
|
|
|
|
dicTrack.TrackSequence = trackNo;
|
|
|
|
|
dicTrack.TrackSession = 1;
|
2017-12-19 20:33:03 +00:00
|
|
|
currentSector += frames;
|
|
|
|
|
currentTrack++;
|
2017-12-22 06:55:04 +00:00
|
|
|
tracks.Add(dicTrack.TrackSequence, dicTrack);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
// "CHGT"
|
2017-12-20 17:15:26 +00:00
|
|
|
case GDROM_OLD_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
swapAudio = true;
|
2017-12-20 17:15:26 +00:00
|
|
|
goto case GDROM_METADATA;
|
2017-12-19 20:33:03 +00:00
|
|
|
// "CHGD"
|
2017-12-20 17:15:26 +00:00
|
|
|
case GDROM_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a hard disk and a GD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
if(isCdrom)
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException("Image cannot be a CD-ROM and a GD-ROM at the same time, aborting.");
|
|
|
|
|
|
|
|
|
|
string chgd = StringHandlers.CToString(meta);
|
2017-12-24 00:12:31 +00:00
|
|
|
Regex chgdRegEx = new Regex(REGEX_METADATA_GDROM);
|
2017-12-19 20:33:03 +00:00
|
|
|
Match chgdMatch = chgdRegEx.Match(chgd);
|
|
|
|
|
if(chgdMatch.Success)
|
|
|
|
|
{
|
|
|
|
|
isGdrom = true;
|
|
|
|
|
|
|
|
|
|
uint trackNo = uint.Parse(chgdMatch.Groups["track"].Value);
|
|
|
|
|
uint frames = uint.Parse(chgdMatch.Groups["frames"].Value);
|
|
|
|
|
string subtype = chgdMatch.Groups["sub_type"].Value;
|
|
|
|
|
string tracktype = chgdMatch.Groups["track_type"].Value;
|
|
|
|
|
// TODO: Check pregap, postgap and pad behaviour
|
|
|
|
|
uint pregap = uint.Parse(chgdMatch.Groups["pregap"].Value);
|
|
|
|
|
string pregapType = chgdMatch.Groups["pgtype"].Value;
|
|
|
|
|
string pregapSubType = chgdMatch.Groups["pgsub"].Value;
|
|
|
|
|
uint postgap = uint.Parse(chgdMatch.Groups["postgap"].Value);
|
|
|
|
|
uint pad = uint.Parse(chgdMatch.Groups["pad"].Value);
|
|
|
|
|
|
|
|
|
|
if(trackNo != currentTrack)
|
|
|
|
|
throw new ImageNotSupportedException("Unsorted tracks, cannot proceed.");
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
Track dicTrack = new Track();
|
2017-12-19 20:33:03 +00:00
|
|
|
switch(tracktype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_AUDIO:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.Audio;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1:
|
|
|
|
|
case TRACK_TYPE_MODE1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE1_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE1_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2:
|
|
|
|
|
case TRACK_TYPE_MODE2_2K:
|
|
|
|
|
case TRACK_TYPE_MODE2_FM:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F1:
|
|
|
|
|
case TRACK_TYPE_MODE2_F1_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2048;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form1;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_F2:
|
|
|
|
|
case TRACK_TYPE_MODE2_F2_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2324;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Form2;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TRACK_TYPE_MODE2_RAW:
|
|
|
|
|
case TRACK_TYPE_MODE2_RAW_2K:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackBytesPerSector = 2336;
|
|
|
|
|
dicTrack.TrackRawBytesPerSector = 2352;
|
|
|
|
|
dicTrack.TrackType = TrackType.CdMode2Formless;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-21 17:58:51 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported track type {tracktype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(subtype)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_COOKED:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.PackedInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_NONE:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.None;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case SUB_TYPE_RAW:
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.TrackSubchannelFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackSubchannelType = TrackSubchannelType.RawInterleaved;
|
|
|
|
|
dicTrack.TrackSubchannelFilter = imageFilter;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-24 00:12:31 +00:00
|
|
|
throw new ImageNotSupportedException($"Unsupported subchannel type {subtype}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
dicTrack.Indexes = new Dictionary<int, ulong>();
|
|
|
|
|
dicTrack.TrackDescription = $"Track {trackNo}";
|
|
|
|
|
dicTrack.TrackEndSector = currentSector + frames - 1;
|
|
|
|
|
dicTrack.TrackFile = imageFilter.GetFilename();
|
|
|
|
|
dicTrack.TrackFileType = "BINARY";
|
|
|
|
|
dicTrack.TrackFilter = imageFilter;
|
|
|
|
|
dicTrack.TrackStartSector = currentSector;
|
|
|
|
|
dicTrack.TrackSequence = trackNo;
|
|
|
|
|
dicTrack.TrackSession = (ushort)(trackNo > 2 ? 2 : 1);
|
2017-12-19 20:33:03 +00:00
|
|
|
currentSector += frames;
|
|
|
|
|
currentTrack++;
|
2017-12-22 06:55:04 +00:00
|
|
|
tracks.Add(dicTrack.TrackSequence, dicTrack);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
// "IDNT"
|
2017-12-20 17:15:26 +00:00
|
|
|
case HARD_DISK_IDENT_METADATA:
|
2017-12-21 14:30:38 +00:00
|
|
|
Identify.IdentifyDevice? idnt = Identify.Decode(meta);
|
2017-12-19 20:33:03 +00:00
|
|
|
if(idnt.HasValue)
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaManufacturer = idnt.Value.MediaManufacturer;
|
|
|
|
|
imageInfo.MediaSerialNumber = idnt.Value.MediaSerial;
|
|
|
|
|
imageInfo.DriveModel = idnt.Value.Model;
|
|
|
|
|
imageInfo.DriveSerialNumber = idnt.Value.SerialNumber;
|
|
|
|
|
imageInfo.DriveFirmwareRevision = idnt.Value.FirmwareRevision;
|
2017-12-19 20:33:03 +00:00
|
|
|
if(idnt.Value.CurrentCylinders > 0 && idnt.Value.CurrentHeads > 0 &&
|
|
|
|
|
idnt.Value.CurrentSectorsPerTrack > 0)
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Cylinders = idnt.Value.CurrentCylinders;
|
|
|
|
|
imageInfo.Heads = idnt.Value.CurrentHeads;
|
|
|
|
|
imageInfo.SectorsPerTrack = idnt.Value.CurrentSectorsPerTrack;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Cylinders = idnt.Value.Cylinders;
|
|
|
|
|
imageInfo.Heads = idnt.Value.Heads;
|
|
|
|
|
imageInfo.SectorsPerTrack = idnt.Value.SectorsPerTrack;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
identify = meta;
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
|
|
|
|
|
imageInfo.ReadableMediaTags.Add(MediaTagType.ATA_IDENTIFY);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case PCMCIA_CIS_METADATA:
|
2017-12-19 20:33:03 +00:00
|
|
|
cis = meta;
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableMediaTags.Contains(MediaTagType.PCMCIA_CIS))
|
|
|
|
|
imageInfo.ReadableMediaTags.Add(MediaTagType.PCMCIA_CIS);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextMetaOff = header.next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(isHdd)
|
|
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
sectorsPerHunk = bytesPerHunk / imageInfo.SectorSize;
|
|
|
|
|
imageInfo.Sectors = imageInfo.ImageSize / imageInfo.SectorSize;
|
|
|
|
|
imageInfo.MediaType = MediaType.GENERIC_HDD;
|
|
|
|
|
imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else if(isCdrom)
|
|
|
|
|
{
|
|
|
|
|
// Hardcoded on MAME for CD-ROM
|
|
|
|
|
sectorsPerHunk = 8;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaType = MediaType.CDROM;
|
|
|
|
|
imageInfo.XmlMediaType = XmlMediaType.OpticalDisc;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
foreach(Track dicTrack in tracks.Values)
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Sectors += dicTrack.TrackEndSector - dicTrack.TrackStartSector + 1;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else if(isGdrom)
|
|
|
|
|
{
|
|
|
|
|
// Hardcoded on MAME for GD-ROM
|
|
|
|
|
sectorsPerHunk = 8;
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.MediaType = MediaType.GDROM;
|
|
|
|
|
imageInfo.XmlMediaType = XmlMediaType.OpticalDisc;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
foreach(Track dicTrack in tracks.Values)
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.Sectors += dicTrack.TrackEndSector - dicTrack.TrackStartSector + 1;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else throw new ImageNotSupportedException("Image does not represent a known media, aborting");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(isCdrom || isGdrom)
|
|
|
|
|
{
|
|
|
|
|
offsetmap = new Dictionary<ulong, uint>();
|
|
|
|
|
partitions = new List<Partition>();
|
|
|
|
|
ulong partPos = 0;
|
2017-12-22 06:55:04 +00:00
|
|
|
foreach(Track dicTrack in tracks.Values)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
Partition partition = new Partition
|
|
|
|
|
{
|
|
|
|
|
Description = dicTrack.TrackDescription,
|
|
|
|
|
Size =
|
|
|
|
|
(dicTrack.TrackEndSector - dicTrack.TrackStartSector + 1) *
|
|
|
|
|
(ulong)dicTrack.TrackRawBytesPerSector,
|
|
|
|
|
Length = dicTrack.TrackEndSector - dicTrack.TrackStartSector + 1,
|
|
|
|
|
Sequence = dicTrack.TrackSequence,
|
|
|
|
|
Offset = partPos,
|
|
|
|
|
Start = dicTrack.TrackStartSector,
|
|
|
|
|
Type = dicTrack.TrackType.ToString()
|
|
|
|
|
};
|
2017-12-19 20:33:03 +00:00
|
|
|
partPos += partition.Length;
|
2017-12-22 06:55:04 +00:00
|
|
|
offsetmap.Add(dicTrack.TrackStartSector, dicTrack.TrackSequence);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
if(dicTrack.TrackSubchannelType != TrackSubchannelType.None)
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
switch(dicTrack.TrackType)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode1:
|
|
|
|
|
case TrackType.CdMode2Form1:
|
2017-12-22 06:55:04 +00:00
|
|
|
if(dicTrack.TrackRawBytesPerSector == 2352)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Form2:
|
2017-12-22 06:55:04 +00:00
|
|
|
if(dicTrack.TrackRawBytesPerSector == 2352)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Formless:
|
2017-12-22 06:55:04 +00:00
|
|
|
if(dicTrack.TrackRawBytesPerSector == 2352)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
|
|
|
|
if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
|
|
|
imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(dicTrack.TrackBytesPerSector > imageInfo.SectorSize)
|
|
|
|
|
imageInfo.SectorSize = (uint)dicTrack.TrackBytesPerSector;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
partitions.Add(partition);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
imageInfo.HasPartitions = true;
|
|
|
|
|
imageInfo.HasSessions = true;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
maxBlockCache = (int)(MAX_CACHE_SIZE / (imageInfo.SectorSize * sectorsPerHunk));
|
|
|
|
|
maxSectorCache = (int)(MAX_CACHE_SIZE / imageInfo.SectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
imageStream = stream;
|
|
|
|
|
|
|
|
|
|
sectorCache = new Dictionary<ulong, byte[]>();
|
|
|
|
|
hunkCache = new Dictionary<ulong, byte[]>();
|
|
|
|
|
|
|
|
|
|
// TODO: Detect CompactFlash
|
|
|
|
|
// TODO: Get manufacturer and drive name from CIS if applicable
|
2017-12-26 06:05:12 +00:00
|
|
|
if(cis != null) imageInfo.MediaType = MediaType.PCCardTypeI;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Track GetTrack(ulong sector)
|
|
|
|
|
{
|
|
|
|
|
Track track = new Track();
|
2017-12-24 00:12:31 +00:00
|
|
|
foreach(KeyValuePair<ulong, uint> kvp in offsetmap.Where(kvp => sector >= kvp.Key))
|
|
|
|
|
tracks.TryGetValue(kvp.Value, out track);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
return track;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ulong GetAbsoluteSector(ulong relativeSector, uint track)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
tracks.TryGetValue(track, out Track dicTrack);
|
|
|
|
|
return dicTrack.TrackStartSector + relativeSector;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] GetHunk(ulong hunkNo)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
if(hunkCache.TryGetValue(hunkNo, out byte[] hunk)) return hunk;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
switch(mapVersion)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
ulong offset = hunkTable[hunkNo] & 0x00000FFFFFFFFFFF;
|
|
|
|
|
ulong length = hunkTable[hunkNo] >> 44;
|
|
|
|
|
|
|
|
|
|
byte[] compHunk = new byte[length];
|
|
|
|
|
imageStream.Seek((long)offset, SeekOrigin.Begin);
|
|
|
|
|
imageStream.Read(compHunk, 0, compHunk.Length);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(length == sectorsPerHunk * imageInfo.SectorSize) hunk = compHunk;
|
2017-12-21 06:06:19 +00:00
|
|
|
else if((ChdCompression)hdrCompression > ChdCompression.Zlib)
|
2017-12-24 00:12:31 +00:00
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException($"Unsupported compression {(ChdCompression)hdrCompression}");
|
2017-12-21 06:06:19 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DeflateStream zStream =
|
|
|
|
|
new DeflateStream(new MemoryStream(compHunk), CompressionMode.Decompress);
|
2017-12-26 06:05:12 +00:00
|
|
|
hunk = new byte[sectorsPerHunk * imageInfo.SectorSize];
|
|
|
|
|
int read = zStream.Read(hunk, 0, (int)(sectorsPerHunk * imageInfo.SectorSize));
|
|
|
|
|
if(read != sectorsPerHunk * imageInfo.SectorSize)
|
2017-12-21 06:06:19 +00:00
|
|
|
throw new
|
2017-12-26 06:05:12 +00:00
|
|
|
IOException($"Unable to decompress hunk correctly, got {read} bytes, expected {sectorsPerHunk * imageInfo.SectorSize}");
|
2017-12-21 06:06:19 +00:00
|
|
|
|
|
|
|
|
zStream.Close();
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
byte[] entryBytes = new byte[16];
|
|
|
|
|
Array.Copy(hunkMap, (int)(hunkNo * 16), entryBytes, 0, 16);
|
|
|
|
|
ChdMapV3Entry entry = BigEndianMarshal.ByteArrayToStructureBigEndian<ChdMapV3Entry>(entryBytes);
|
|
|
|
|
switch((Chdv3EntryFlags)(entry.flags & 0x0F))
|
|
|
|
|
{
|
|
|
|
|
case Chdv3EntryFlags.Invalid: throw new ArgumentException("Invalid hunk found.");
|
|
|
|
|
case Chdv3EntryFlags.Compressed:
|
|
|
|
|
switch((ChdCompression)hdrCompression)
|
|
|
|
|
{
|
|
|
|
|
case ChdCompression.None: goto uncompressedV3;
|
|
|
|
|
case ChdCompression.Zlib:
|
|
|
|
|
case ChdCompression.ZlibPlus:
|
|
|
|
|
if(isHdd)
|
|
|
|
|
{
|
|
|
|
|
byte[] zHunk = new byte[(entry.lengthLsb << 16) + entry.lengthLsb];
|
|
|
|
|
imageStream.Seek((long)entry.offset, SeekOrigin.Begin);
|
|
|
|
|
imageStream.Read(zHunk, 0, zHunk.Length);
|
|
|
|
|
DeflateStream zStream =
|
|
|
|
|
new DeflateStream(new MemoryStream(zHunk), CompressionMode.Decompress);
|
|
|
|
|
hunk = new byte[bytesPerHunk];
|
|
|
|
|
int read = zStream.Read(hunk, 0, (int)bytesPerHunk);
|
|
|
|
|
if(read != bytesPerHunk)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new
|
2017-12-21 17:58:51 +00:00
|
|
|
IOException($"Unable to decompress hunk correctly, got {read} bytes, expected {bytesPerHunk}");
|
2017-12-21 06:06:19 +00:00
|
|
|
|
|
|
|
|
zStream.Close();
|
|
|
|
|
}
|
|
|
|
|
// TODO: Guess wth is MAME doing with these hunks
|
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new
|
2017-12-21 06:06:19 +00:00
|
|
|
ImageNotSupportedException("Compressed CD/GD-ROM hunks are not yet supported");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case ChdCompression.Av:
|
|
|
|
|
throw new
|
2017-12-21 17:58:51 +00:00
|
|
|
ImageNotSupportedException($"Unsupported compression {(ChdCompression)hdrCompression}");
|
2017-12-21 06:06:19 +00:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case Chdv3EntryFlags.Uncompressed:
|
|
|
|
|
uncompressedV3:
|
2017-12-19 20:33:03 +00:00
|
|
|
hunk = new byte[bytesPerHunk];
|
2017-12-21 06:06:19 +00:00
|
|
|
imageStream.Seek((long)entry.offset, SeekOrigin.Begin);
|
2017-12-19 20:33:03 +00:00
|
|
|
imageStream.Read(hunk, 0, hunk.Length);
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case Chdv3EntryFlags.Mini:
|
|
|
|
|
hunk = new byte[bytesPerHunk];
|
2017-12-21 16:07:20 +00:00
|
|
|
byte[] mini;
|
2017-12-21 06:06:19 +00:00
|
|
|
mini = BigEndianBitConverter.GetBytes(entry.offset);
|
|
|
|
|
for(int i = 0; i < bytesPerHunk; i++) hunk[i] = mini[i % 8];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case Chdv3EntryFlags.SelfHunk: return GetHunk(entry.offset);
|
|
|
|
|
case Chdv3EntryFlags.ParentHunk:
|
|
|
|
|
throw new ImageNotSupportedException("Parent images are not supported");
|
|
|
|
|
case Chdv3EntryFlags.SecondCompressed:
|
|
|
|
|
throw new ImageNotSupportedException("FLAC is not supported");
|
|
|
|
|
default:
|
2017-12-21 17:58:51 +00:00
|
|
|
throw new ImageNotSupportedException($"Hunk type {entry.flags & 0xF} is not supported");
|
2017-12-21 06:06:19 +00:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
if(hdrCompression == 0)
|
|
|
|
|
{
|
|
|
|
|
hunk = new byte[bytesPerHunk];
|
|
|
|
|
imageStream.Seek(hunkTableSmall[hunkNo] * bytesPerHunk, SeekOrigin.Begin);
|
|
|
|
|
imageStream.Read(hunk, 0, hunk.Length);
|
|
|
|
|
}
|
|
|
|
|
else throw new ImageNotSupportedException("Compressed v5 hunks not yet supported");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
break;
|
2017-12-24 00:12:31 +00:00
|
|
|
default: throw new ImageNotSupportedException($"Unsupported hunk map version {mapVersion}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
if(hunkCache.Count >= maxBlockCache) hunkCache.Clear();
|
|
|
|
|
|
|
|
|
|
hunkCache.Add(hunkNo, hunk);
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
return hunk;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySector(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd) return null;
|
|
|
|
|
|
|
|
|
|
byte[] buffer = ReadSectorLong(sectorAddress);
|
2017-12-21 14:30:38 +00:00
|
|
|
return CdChecksums.CheckCdSector(buffer);
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySector(ulong sectorAddress, uint track)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return VerifySector(GetAbsoluteSector(sectorAddress, track));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySectors(ulong sectorAddress, uint length, out List<ulong> failingLbas,
|
2017-12-20 17:15:26 +00:00
|
|
|
out List<ulong> unknownLbas)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
unknownLbas = new List<ulong>();
|
|
|
|
|
failingLbas = new List<ulong>();
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd) return null;
|
|
|
|
|
|
|
|
|
|
byte[] buffer = ReadSectorsLong(sectorAddress, length);
|
|
|
|
|
int bps = (int)(buffer.Length / length);
|
|
|
|
|
byte[] sector = new byte[bps];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(buffer, i * bps, sector, 0, bps);
|
2017-12-21 14:30:38 +00:00
|
|
|
bool? sectorStatus = CdChecksums.CheckCdSector(sector);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
switch(sectorStatus)
|
|
|
|
|
{
|
|
|
|
|
case null:
|
2017-12-20 17:15:26 +00:00
|
|
|
unknownLbas.Add((ulong)i + sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
case false:
|
2017-12-20 17:15:26 +00:00
|
|
|
failingLbas.Add((ulong)i + sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(unknownLbas.Count > 0) return null;
|
2017-12-26 02:51:10 +00:00
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
return failingLbas.Count <= 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List<ulong> failingLbas,
|
2017-12-20 17:15:26 +00:00
|
|
|
out List<ulong> unknownLbas)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
unknownLbas = new List<ulong>();
|
|
|
|
|
failingLbas = new List<ulong>();
|
2017-12-19 20:33:03 +00:00
|
|
|
if(isHdd) return null;
|
|
|
|
|
|
|
|
|
|
byte[] buffer = ReadSectorsLong(sectorAddress, length, track);
|
|
|
|
|
int bps = (int)(buffer.Length / length);
|
|
|
|
|
byte[] sector = new byte[bps];
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(buffer, i * bps, sector, 0, bps);
|
2017-12-21 14:30:38 +00:00
|
|
|
bool? sectorStatus = CdChecksums.CheckCdSector(sector);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
switch(sectorStatus)
|
|
|
|
|
{
|
|
|
|
|
case null:
|
2017-12-20 17:15:26 +00:00
|
|
|
unknownLbas.Add((ulong)i + sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
case false:
|
2017-12-20 17:15:26 +00:00
|
|
|
failingLbas.Add((ulong)i + sectorAddress);
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(unknownLbas.Count > 0) return null;
|
2017-12-26 02:51:10 +00:00
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
return failingLbas.Count <= 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public bool? VerifyMediaImage()
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
byte[] calculated;
|
|
|
|
|
if(mapVersion >= 3)
|
|
|
|
|
{
|
2017-12-21 14:30:38 +00:00
|
|
|
Sha1Context sha1Ctx = new Sha1Context();
|
2017-12-19 20:33:03 +00:00
|
|
|
sha1Ctx.Init();
|
|
|
|
|
for(uint i = 0; i < totalHunks; i++) sha1Ctx.Update(GetHunk(i));
|
|
|
|
|
|
|
|
|
|
calculated = sha1Ctx.Final();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-12-21 14:30:38 +00:00
|
|
|
Md5Context md5Ctx = new Md5Context();
|
2017-12-19 20:33:03 +00:00
|
|
|
md5Ctx.Init();
|
|
|
|
|
for(uint i = 0; i < totalHunks; i++) md5Ctx.Update(GetHunk(i));
|
|
|
|
|
|
|
|
|
|
calculated = md5Ctx.Final();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expectedChecksum.SequenceEqual(calculated);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSector(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
Track track = new Track();
|
2017-12-22 06:55:04 +00:00
|
|
|
uint sectorSize;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
if(!sectorCache.TryGetValue(sectorAddress, out byte[] sector))
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(isHdd) sectorSize = imageInfo.SectorSize;
|
2017-12-19 20:33:03 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
track = GetTrack(sectorAddress);
|
|
|
|
|
sectorSize = (uint)track.TrackRawBytesPerSector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ulong hunkNo = sectorAddress / sectorsPerHunk;
|
2017-12-20 17:26:28 +00:00
|
|
|
ulong secOff = sectorAddress * sectorSize % (sectorsPerHunk * sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunk = GetHunk(hunkNo);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
sector = new byte[imageInfo.SectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
Array.Copy(hunk, (int)secOff, sector, 0, sector.Length);
|
|
|
|
|
|
|
|
|
|
if(sectorCache.Count >= maxSectorCache) sectorCache.Clear();
|
|
|
|
|
|
|
|
|
|
sectorCache.Add(sectorAddress, sector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(isHdd) return sector;
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
uint sectorOffset;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
switch(track.TrackType)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode1:
|
|
|
|
|
case TrackType.CdMode2Form1:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 16;
|
|
|
|
|
sectorSize = 2048;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 2048;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Form2:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 16;
|
|
|
|
|
sectorSize = 2324;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 2324;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Formless:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 16;
|
|
|
|
|
sectorSize = 2336;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 2336;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackType.Audio:
|
|
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 2352;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] buffer = new byte[sectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
if(track.TrackType == TrackType.Audio && swapAudio)
|
|
|
|
|
for(int i = 0; i < 2352; i += 2)
|
|
|
|
|
{
|
|
|
|
|
buffer[i + 1] = sector[i];
|
|
|
|
|
buffer[i] = sector[i + 1];
|
|
|
|
|
}
|
2017-12-22 06:55:04 +00:00
|
|
|
else Array.Copy(sector, sectorOffset, buffer, 0, sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd) throw new FeatureNotPresentImageException("Hard disk images do not have sector tags");
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
Track track = new Track();
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
uint sectorSize;
|
|
|
|
|
|
|
|
|
|
if(!sectorCache.TryGetValue(sectorAddress, out byte[] sector))
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
track = GetTrack(sectorAddress);
|
|
|
|
|
sectorSize = (uint)track.TrackRawBytesPerSector;
|
|
|
|
|
|
|
|
|
|
ulong hunkNo = sectorAddress / sectorsPerHunk;
|
2017-12-20 17:26:28 +00:00
|
|
|
ulong secOff = sectorAddress * sectorSize % (sectorsPerHunk * sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunk = GetHunk(hunkNo);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
sector = new byte[imageInfo.SectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
Array.Copy(hunk, (int)secOff, sector, 0, sector.Length);
|
|
|
|
|
|
|
|
|
|
if(sectorCache.Count >= maxSectorCache) sectorCache.Clear();
|
|
|
|
|
|
|
|
|
|
sectorCache.Add(sectorAddress, sector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(isHdd) return sector;
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
uint sectorOffset;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-20 17:15:26 +00:00
|
|
|
if(tag == SectorTagType.CdSectorSubchannel)
|
2017-12-24 00:12:31 +00:00
|
|
|
switch(track.TrackSubchannelType)
|
|
|
|
|
{
|
|
|
|
|
case TrackSubchannelType.None:
|
|
|
|
|
throw new FeatureNotPresentImageException("Requested sector does not contain subchannel");
|
2017-12-21 04:43:29 +00:00
|
|
|
case TrackSubchannelType.RawInterleaved:
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = (uint)track.TrackRawBytesPerSector;
|
|
|
|
|
sectorSize = 96;
|
2017-12-21 04:43:29 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new
|
2017-12-21 17:58:51 +00:00
|
|
|
FeatureSupportedButNotImplementedImageException($"Unsupported subchannel type {track.TrackSubchannelType}");
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
switch(track.TrackType)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode1:
|
|
|
|
|
case TrackType.CdMode2Form1:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSync:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 12;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 12;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSubHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentException("Unsupported tag requested for this track",
|
|
|
|
|
nameof(tag));
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEcc:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2076;
|
|
|
|
|
sectorSize = 276;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEccP:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2076;
|
|
|
|
|
sectorSize = 172;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEccQ:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2248;
|
|
|
|
|
sectorSize = 104;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEdc:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2064;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: throw new ArgumentException("Unsupported tag requested", nameof(tag));
|
|
|
|
|
}
|
|
|
|
|
else throw new FeatureNotPresentImageException("Requested sector does not contain tags");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Form2:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSync:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 12;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 12;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSubHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 16;
|
|
|
|
|
sectorSize = 8;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEdc:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2348;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: throw new ArgumentException("Unsupported tag requested", nameof(tag));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSync:
|
|
|
|
|
case SectorTagType.CdSectorHeader:
|
|
|
|
|
case SectorTagType.CdSectorSubchannel:
|
|
|
|
|
case SectorTagType.CdSectorEcc:
|
|
|
|
|
case SectorTagType.CdSectorEccP:
|
|
|
|
|
case SectorTagType.CdSectorEccQ:
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentException("Unsupported tag requested for this track",
|
|
|
|
|
nameof(tag));
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSubHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 8;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEdc:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2332;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: throw new ArgumentException("Unsupported tag requested", nameof(tag));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case TrackType.CdMode2Formless:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(track.TrackRawBytesPerSector == 2352)
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSync:
|
|
|
|
|
case SectorTagType.CdSectorHeader:
|
|
|
|
|
case SectorTagType.CdSectorEcc:
|
|
|
|
|
case SectorTagType.CdSectorEccP:
|
|
|
|
|
case SectorTagType.CdSectorEccQ:
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentException("Unsupported tag requested for this track",
|
|
|
|
|
nameof(tag));
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorSubHeader:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 0;
|
|
|
|
|
sectorSize = 8;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-12-20 17:15:26 +00:00
|
|
|
case SectorTagType.CdSectorEdc:
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-22 06:55:04 +00:00
|
|
|
sectorOffset = 2332;
|
|
|
|
|
sectorSize = 4;
|
2017-12-19 20:33:03 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: throw new ArgumentException("Unsupported tag requested", nameof(tag));
|
|
|
|
|
}
|
|
|
|
|
else throw new FeatureNotPresentImageException("Requested sector does not contain tags");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackType.Audio:
|
|
|
|
|
throw new FeatureNotPresentImageException("Requested sector does not contain tags");
|
|
|
|
|
default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type");
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
byte[] buffer = new byte[sectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
if(track.TrackType == TrackType.Audio && swapAudio)
|
|
|
|
|
for(int i = 0; i < 2352; i += 2)
|
|
|
|
|
{
|
|
|
|
|
buffer[i + 1] = sector[i];
|
|
|
|
|
buffer[i] = sector[i + 1];
|
|
|
|
|
}
|
2017-12-22 06:55:04 +00:00
|
|
|
else Array.Copy(sector, sectorOffset, buffer, 0, sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
if(track.TrackType == TrackType.Audio && swapAudio)
|
|
|
|
|
for(int i = 0; i < 2352; i += 2)
|
|
|
|
|
{
|
|
|
|
|
buffer[i + 1] = sector[i];
|
|
|
|
|
buffer[i] = sector[i + 1];
|
|
|
|
|
}
|
2017-12-22 06:55:04 +00:00
|
|
|
else Array.Copy(sector, sectorOffset, buffer, 0, sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectors(ulong sectorAddress, uint length)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress + length > imageInfo.Sectors)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(length),
|
2017-12-26 06:05:12 +00:00
|
|
|
$"Requested more sectors ({sectorAddress + length}) than available ({imageInfo.Sectors})");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
|
|
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
byte[] sector = ReadSector(sectorAddress + i);
|
|
|
|
|
ms.Write(sector, 0, sector.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress + length > imageInfo.Sectors)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(length),
|
2017-12-26 06:05:12 +00:00
|
|
|
$"Requested more sectors ({sectorAddress + length}) than available ({imageInfo.Sectors})");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
|
|
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
byte[] sector = ReadSectorTag(sectorAddress + i, tag);
|
|
|
|
|
ms.Write(sector, 0, sector.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorLong(ulong sectorAddress)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd) return ReadSector(sectorAddress);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
Track track = new Track();
|
|
|
|
|
|
2017-12-22 06:55:04 +00:00
|
|
|
if(!sectorCache.TryGetValue(sectorAddress, out byte[] sector))
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
|
|
|
|
uint sectorSize;
|
|
|
|
|
|
|
|
|
|
track = GetTrack(sectorAddress);
|
|
|
|
|
sectorSize = (uint)track.TrackRawBytesPerSector;
|
|
|
|
|
|
|
|
|
|
ulong hunkNo = sectorAddress / sectorsPerHunk;
|
2017-12-20 17:26:28 +00:00
|
|
|
ulong secOff = sectorAddress * sectorSize % (sectorsPerHunk * sectorSize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
byte[] hunk = GetHunk(hunkNo);
|
|
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
sector = new byte[imageInfo.SectorSize];
|
2017-12-19 20:33:03 +00:00
|
|
|
Array.Copy(hunk, (int)secOff, sector, 0, sector.Length);
|
|
|
|
|
|
|
|
|
|
if(sectorCache.Count >= maxSectorCache) sectorCache.Clear();
|
|
|
|
|
|
|
|
|
|
sectorCache.Add(sectorAddress, sector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] buffer = new byte[track.TrackRawBytesPerSector];
|
|
|
|
|
|
|
|
|
|
if(track.TrackType == TrackType.Audio && swapAudio)
|
|
|
|
|
for(int i = 0; i < 2352; i += 2)
|
|
|
|
|
{
|
|
|
|
|
buffer[i + 1] = sector[i];
|
|
|
|
|
buffer[i] = sector[i + 1];
|
|
|
|
|
}
|
|
|
|
|
else Array.Copy(sector, 0, buffer, 0, track.TrackRawBytesPerSector);
|
|
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorsLong(ulong sectorAddress, uint length)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress > imageInfo.Sectors - 1)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(sectorAddress),
|
2017-12-21 17:58:51 +00:00
|
|
|
$"Sector address {sectorAddress} not found");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(sectorAddress + length > imageInfo.Sectors)
|
2017-12-19 20:33:03 +00:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(length),
|
2017-12-26 06:05:12 +00:00
|
|
|
$"Requested more sectors ({sectorAddress + length}) than available ({imageInfo.Sectors})");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
|
|
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
byte[] sector = ReadSectorLong(sectorAddress + i);
|
|
|
|
|
ms.Write(sector, 0, sector.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadDiskTag(MediaTagType tag)
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-26 06:05:12 +00:00
|
|
|
if(imageInfo.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) return identify;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2017-12-26 06:05:12 +00:00
|
|
|
if(imageInfo.ReadableMediaTags.Contains(MediaTagType.PCMCIA_CIS)) return cis;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
throw new FeatureUnsupportedImageException("Feature not supported by image format");
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Track> GetSessionTracks(Session session)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return GetSessionTracks(session.SessionSequence);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public List<Track> GetSessionTracks(ushort session)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return tracks.Values.Where(track => track.TrackSession == session).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSector(ulong sectorAddress, uint track)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSector(GetAbsoluteSector(sectorAddress, track));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSectorTag(GetAbsoluteSector(sectorAddress, track), tag);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSectors(GetAbsoluteSector(sectorAddress, track), length);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSectorsTag(GetAbsoluteSector(sectorAddress, track), length, tag);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorLong(ulong sectorAddress, uint track)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSectorLong(GetAbsoluteSector(sectorAddress, track));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-26 07:28:40 +00:00
|
|
|
public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
|
2017-12-24 00:12:31 +00:00
|
|
|
{
|
|
|
|
|
if(isHdd)
|
|
|
|
|
throw new FeaturedNotSupportedByDiscImageException("Cannot access optical tracks on a hard disk image");
|
|
|
|
|
|
|
|
|
|
return ReadSectorLong(GetAbsoluteSector(sectorAddress, track), length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChdCompression : uint
|
|
|
|
|
{
|
|
|
|
|
None = 0,
|
|
|
|
|
Zlib = 1,
|
|
|
|
|
ZlibPlus = 2,
|
|
|
|
|
Av = 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChdFlags : uint
|
|
|
|
|
{
|
|
|
|
|
HasParent = 1,
|
|
|
|
|
Writable = 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum Chdv3EntryFlags : byte
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Invalid</summary>
|
|
|
|
|
Invalid = 0,
|
|
|
|
|
/// <summary>Compressed with primary codec</summary>
|
|
|
|
|
Compressed = 1,
|
|
|
|
|
/// <summary>Uncompressed</summary>
|
|
|
|
|
Uncompressed = 2,
|
|
|
|
|
/// <summary>Use offset as data</summary>
|
|
|
|
|
Mini = 3,
|
|
|
|
|
/// <summary>Same as another hunk in file</summary>
|
|
|
|
|
SelfHunk = 4,
|
|
|
|
|
/// <summary>Same as another hunk in parent</summary>
|
|
|
|
|
ParentHunk = 5,
|
|
|
|
|
/// <summary>Compressed with secondary codec (FLAC)</summary>
|
|
|
|
|
SecondCompressed = 6
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChdOldTrackType : uint
|
|
|
|
|
{
|
|
|
|
|
Mode1 = 0,
|
|
|
|
|
Mode1Raw,
|
|
|
|
|
Mode2,
|
|
|
|
|
Mode2Form1,
|
|
|
|
|
Mode2Form2,
|
|
|
|
|
Mode2FormMix,
|
|
|
|
|
Mode2Raw,
|
|
|
|
|
Audio
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChdOldSubType : uint
|
|
|
|
|
{
|
|
|
|
|
Cooked = 0,
|
|
|
|
|
Raw,
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hunks are represented in a 64 bit integer with 44 bit as offset, 20 bits as length
|
|
|
|
|
// Sectors are fixed at 512 bytes/sector
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdHeaderV1
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Magic identifier, 'MComprHD'
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] tag;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of header
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image format version
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint version;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image flags, <see cref="ChdFlags" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint flags;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compression algorithm, <see cref="ChdCompression" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compression;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sectors per hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint hunksize;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total # of hunk in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint totalhunks;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cylinders on disk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint cylinders;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Heads per cylinder
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint heads;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sectors per track
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint sectors;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] md5;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] parentmd5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hunks are represented in a 64 bit integer with 44 bit as offset, 20 bits as length
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdHeaderV2
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Magic identifier, 'MComprHD'
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] tag;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of header
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image format version
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint version;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image flags, <see cref="ChdFlags" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint flags;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compression algorithm, <see cref="ChdCompression" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compression;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sectors per hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint hunksize;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total # of hunk in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint totalhunks;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Cylinders on disk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint cylinders;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Heads per cylinder
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint heads;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sectors per track
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint sectors;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] md5;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] parentmd5;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bytes per sector
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint seclen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdHeaderV3
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Magic identifier, 'MComprHD'
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] tag;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of header
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image format version
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint version;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image flags, <see cref="ChdFlags" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint flags;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compression algorithm, <see cref="ChdCompression" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compression;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total # of hunk in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint totalhunks;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total bytes in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong logicalbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset to first metadata blob
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong metaoffset;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] md5;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MD5 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] parentmd5;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bytes per hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint hunkbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] sha1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] parentsha1;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdMapV3Entry
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Offset to hunk from start of image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong offset;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// CRC32 of uncompressed hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint crc;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Lower 16 bits of length
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ushort lengthLsb;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Upper 8 bits of length
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Hunk flags
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte flags;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdTrackOld
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
public uint type;
|
|
|
|
|
public uint subType;
|
|
|
|
|
public uint dataSize;
|
|
|
|
|
public uint subSize;
|
|
|
|
|
public uint frames;
|
|
|
|
|
public uint extraFrames;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdHeaderV4
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Magic identifier, 'MComprHD'
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] tag;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of header
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image format version
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint version;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image flags, <see cref="ChdFlags" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint flags;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compression algorithm, <see cref="ChdCompression" />
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compression;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total # of hunk in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint totalhunks;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total bytes in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong logicalbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset to first metadata blob
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong metaoffset;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bytes per hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint hunkbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of raw+meta data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] sha1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] parentsha1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] rawsha1;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdHeaderV5
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Magic identifier, 'MComprHD'
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] tag;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Length of header
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Image format version
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint version;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compressor 0
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compressor0;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compressor 1
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compressor1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compressor 2
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compressor2;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compressor 3
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compressor3;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Total bytes in image
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong logicalbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset to hunk map
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong mapoffset;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset to first metadata blob
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong metaoffset;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bytes per hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint hunkbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bytes per unit within hunk
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint unitbytes;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of raw data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] rawsha1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of raw+meta data
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] sha1;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SHA1 of parent file
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public byte[] parentsha1;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdCompressedMapHeaderV5
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Length of compressed map
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint length;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset of first block (48 bits) and CRC16 of map (16 bits)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong startAndCrc;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bits used to encode compressed length on map entry
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte bitsUsedToEncodeCompLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bits used to encode self-refs
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte bitsUsedToEncodeSelfRefs;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bits used to encode parent unit refs
|
|
|
|
|
/// </summary>
|
|
|
|
|
public byte bitsUsedToEncodeParentUnits;
|
|
|
|
|
public byte reserved;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdMapV5Entry
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Compression (8 bits) and length (24 bits)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public uint compAndLength;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Offset (48 bits) and CRC (16 bits)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ulong offsetAndCrc;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct ChdMetadataHeader
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
public uint tag;
|
|
|
|
|
public uint flagsAndLength;
|
|
|
|
|
public ulong next;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct HunkSector
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public ulong[] hunkEntry;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-24 00:12:31 +00:00
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
|
|
|
struct HunkSectorSmall
|
2017-12-19 20:33:03 +00:00
|
|
|
{
|
2017-12-24 00:12:31 +00:00
|
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] public uint[] hunkEntry;
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|