diff --git a/DiscImageChef.DiscImages/D88.cs b/DiscImageChef.DiscImages/D88.cs
index 3d4cd806..d92d9adf 100644
--- a/DiscImageChef.DiscImages/D88.cs
+++ b/DiscImageChef.DiscImages/D88.cs
@@ -30,12 +30,720 @@
// Copyright © 2011-2017 Natalia Portillo
// ****************************************************************************/
using System;
-namespace DiscImageChef.DiscImages
+using System.IO;
+using System.Collections.Generic;
+using DiscImageChef.Console;
+using DiscImageChef.CommonTypes;
+using System.Linq;
+using System.Text;
+using DiscImageChef.Filters;
+using System.Runtime.InteropServices;
+using DiscImageChef.Decoders.Floppy;
+
+namespace DiscImageChef.ImagePlugins
{
- public class D88
+ // Information from Quasi88's FORMAT.TXT file
+ // Japanese comments copied from there
+ public class D88 : ImagePlugin
{
+ #region Internal enumerations
+ enum DiskType : byte
+ {
+ D2 = 0x00,
+ DD2 = 0x10,
+ HD2 = 0x20,
+ }
+
+ enum DensityType : byte
+ {
+ MFM = 0x00,
+ FM = 0x40,
+ }
+
+ ///
+ /// Status as returned by PC-98 BIOS
+ /// ステータスは、PC-98x1 のBIOS が返してくるステータスで、
+ ///
+ enum StatusType : byte
+ {
+ ///
+ /// Normal
+ /// 正常
+ ///
+ Normal = 0x00,
+ ///
+ /// Deleted
+ /// 正常(DELETED DATA)
+ ///
+ Deleted = 0x10,
+ ///
+ /// CRC error in address fields
+ /// ID CRC エラー
+ ///
+ IDError = 0xA0,
+ ///
+ /// CRC error in data block
+ /// データ CRC エラー
+ ///
+ DataError = 0xB0,
+ ///
+ /// Address mark not found
+ /// アドレスマークなし
+ ///
+ AddressMarkNotFound = 0xE0,
+ ///
+ /// Data mark not found
+ /// データマークなし
+ ///
+ DataMarkNotFound = 0xF0,
+ }
+ #endregion
+
+ #region Internal constants
+ readonly byte[] ReservedEmpty = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ const byte ReadOnly = 0x10;
+ #endregion
+
+ #region Internal structures
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ struct D88Header
+ {
+ ///
+ /// Disk name, nul-terminated ASCII
+ /// ディスクの名前(ASCII + '\0')
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+ public byte[] name;
+ ///
+ /// Reserved
+ /// ディスクの名前(ASCII + '\0')
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
+ public byte[] reserved;
+ ///
+ /// Write protect status
+ /// ライトプロテクト: 0x00 なし、0x10 あり
+ ///
+ public byte write_protect;
+ ///
+ /// Disk type
+ /// ディスクの種類: 0x00 2D、 0x10 2DD、 0x20 2HD
+ ///
+ public DiskType disk_type;
+ ///
+ /// Disk image size
+ /// ディスクのサイズ
+ ///
+ public int disk_size;
+ ///
+ /// Track pointers
+ /// トラック部のオフセットテーブル 0 Track ~ 163 Track
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 164)]
+ public int[] track_table;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ struct SectorHeader
+ {
+ ///
+ /// Cylinder
+ /// ID の C
+ ///
+ public byte c;
+ ///
+ /// Head
+ /// ID の H
+ ///
+ public byte h;
+ ///
+ /// Sector number
+ /// ID の R
+ ///
+ public byte r;
+ ///
+ /// Sector size
+ /// ID の N
+ ///
+ public IBMSectorSizeCode n;
+ ///
+ /// Number of sectors in this track
+ /// このトラック内に存在するセクタの数
+ ///
+ public short spt;
+ ///
+ /// Density: 0x00 MFM, 0x40 FM
+ /// 記録密度: 0x00 倍密度、0x40 単密度
+ ///
+ public DensityType density;
+ ///
+ /// Deleted sector, 0x00 not deleted, 0x10 deleted
+ /// DELETED MARK: 0x00 ノーマル、 0x10 DELETED
+ ///
+ public byte deleted_mark;
+ ///
+ /// Sector status
+ /// ステータス
+ ///
+ public byte status;
+ ///
+ /// Reserved
+ /// リザーブ
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
+ public byte[] reserved;
+ ///
+ /// Size of data following this field
+ /// このセクタ部のデータサイズ
+ ///
+ public short size_of_data;
+ }
+ #endregion
+
+ List sectorsData;
+
public D88()
{
+ Name = "D88 Disk Image";
+ PluginUUID = new Guid("669EDC77-EC41-4720-A88C-49C38CFFBAA0");
+ ImageInfo = new ImageInfo()
+ {
+ readableSectorTags = new List(),
+ readableMediaTags = new List(),
+ imageHasPartitions = false,
+ imageHasSessions = false,
+ imageVersion = null,
+ imageApplication = null,
+ imageApplicationVersion = null,
+ imageCreator = null,
+ imageComments = null,
+ mediaManufacturer = null,
+ mediaModel = null,
+ mediaSerialNumber = null,
+ mediaBarcode = null,
+ mediaPartNumber = null,
+ mediaSequence = 0,
+ lastMediaSequence = 0,
+ driveManufacturer = null,
+ driveModel = null,
+ driveSerialNumber = null,
+ driveFirmwareRevision = null
+ };
}
+
+ public override bool IdentifyImage(Filter imageFilter)
+ {
+ Stream stream = imageFilter.GetDataForkStream();
+ stream.Seek(0, SeekOrigin.Begin);
+ // Even if disk name is supposedly ASCII, I'm pretty sure most emulators allow Shift-JIS to be used :p
+ Encoding shiftjis = Encoding.GetEncoding("shift_jis");
+
+ D88Header d88hdr = new D88Header();
+
+ if(stream.Length < Marshal.SizeOf(d88hdr))
+ return false;
+
+ byte[] hdr_b = new byte[Marshal.SizeOf(d88hdr)];
+ stream.Read(hdr_b, 0, hdr_b.Length);
+
+ GCHandle handle = GCHandle.Alloc(hdr_b, GCHandleType.Pinned);
+ d88hdr = (D88Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(D88Header));
+ handle.Free();
+
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.name = \"{0}\"", StringHandlers.CToString(d88hdr.name, shiftjis));
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.reserved is empty? = {0}", d88hdr.reserved.SequenceEqual(ReservedEmpty));
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.write_protect = 0x{0:X2}", d88hdr.write_protect);
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_type = {0} ({1})", d88hdr.disk_type, (byte)d88hdr.disk_type);
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_size = {0}", d88hdr.disk_size);
+
+ if(d88hdr.disk_size != stream.Length)
+ return false;
+
+ if(d88hdr.disk_type != DiskType.D2 && d88hdr.disk_type != DiskType.DD2 && d88hdr.disk_type != DiskType.HD2)
+ return false;
+
+ if(!d88hdr.reserved.SequenceEqual(ReservedEmpty))
+ return false;
+
+ int counter = 0;
+ for(int i = 0; i < d88hdr.track_table.Length; i++)
+ {
+ if(d88hdr.track_table[i] > 0)
+ counter++;
+
+ if(d88hdr.track_table[i] < 0 || d88hdr.track_table[i] > stream.Length)
+ return false;
+ }
+
+ DicConsole.DebugWriteLine("D88 plugin", "{0} tracks", counter);
+
+ return counter > 0;
+ }
+
+ public override bool OpenImage(Filter imageFilter)
+ {
+ Stream stream = imageFilter.GetDataForkStream();
+ stream.Seek(0, SeekOrigin.Begin);
+ // Even if disk name is supposedly ASCII, I'm pretty sure most emulators allow Shift-JIS to be used :p
+ Encoding shiftjis = Encoding.GetEncoding("shift_jis");
+
+ D88Header d88hdr = new D88Header();
+
+ if(stream.Length < Marshal.SizeOf(d88hdr))
+ return false;
+
+ byte[] hdr_b = new byte[Marshal.SizeOf(d88hdr)];
+ stream.Read(hdr_b, 0, hdr_b.Length);
+
+ GCHandle handle = GCHandle.Alloc(hdr_b, GCHandleType.Pinned);
+ d88hdr = (D88Header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(D88Header));
+ handle.Free();
+
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.name = \"{0}\"", StringHandlers.CToString(d88hdr.name, shiftjis));
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.reserved is empty? = {0}", d88hdr.reserved.SequenceEqual(ReservedEmpty));
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.write_protect = 0x{0:X2}", d88hdr.write_protect);
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_type = {0} ({1})", d88hdr.disk_type, (byte)d88hdr.disk_type);
+ DicConsole.DebugWriteLine("D88 plugin", "d88hdr.disk_size = {0}", d88hdr.disk_size);
+
+ if(d88hdr.disk_size != stream.Length)
+ return false;
+
+ if(d88hdr.disk_type != DiskType.D2 && d88hdr.disk_type != DiskType.DD2 && d88hdr.disk_type != DiskType.HD2)
+ return false;
+
+ if(!d88hdr.reserved.SequenceEqual(ReservedEmpty))
+ return false;
+
+ int trkCounter = 0;
+ for(int i = 0; i < d88hdr.track_table.Length; i++)
+ {
+ if(d88hdr.track_table[i] > 0)
+ trkCounter++;
+
+ if(d88hdr.track_table[i] < 0 || d88hdr.track_table[i] > stream.Length)
+ return false;
+ }
+
+ DicConsole.DebugWriteLine("D88 plugin", "{0} tracks", trkCounter);
+
+ if(trkCounter == 0)
+ return false;
+
+ SectorHeader sechdr = new SectorHeader();
+ hdr_b = new byte[Marshal.SizeOf(sechdr)];
+ stream.Seek(d88hdr.track_table[0], SeekOrigin.Begin);
+ stream.Read(hdr_b, 0, hdr_b.Length);
+
+ handle = GCHandle.Alloc(hdr_b, GCHandleType.Pinned);
+ sechdr = (SectorHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SectorHeader));
+ handle.Free();
+
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.c = {0}", sechdr.c);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.h = {0}", sechdr.h);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.r = {0}", sechdr.r);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.n = {0}", sechdr.n);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.spt = {0}", sechdr.spt);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.density = {0}", sechdr.density);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.deleted_mark = {0}", sechdr.deleted_mark);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.status = {0}", sechdr.status);
+ DicConsole.DebugWriteLine("D88 plugin", "sechdr.size_of_data = {0}", sechdr.size_of_data);
+
+ short spt = sechdr.spt;
+ IBMSectorSizeCode bps = sechdr.n;
+ bool allEqual = true;
+ sectorsData = new List();
+
+ for(int i = 0; i < trkCounter; i++)
+ {
+ stream.Seek(d88hdr.track_table[i], SeekOrigin.Begin);
+ stream.Read(hdr_b, 0, hdr_b.Length);
+ SortedDictionary sectors = new SortedDictionary();
+
+ handle = GCHandle.Alloc(hdr_b, GCHandleType.Pinned);
+ sechdr = (SectorHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SectorHeader));
+ handle.Free();
+
+ if(sechdr.spt != spt || sechdr.n != bps)
+ {
+ DicConsole.DebugWriteLine("D88 plugin", "Disk tracks are not same size. spt = {0} (expected {1}), bps = {2} (expected {3}) at track {4} sector {5}", sechdr.spt, spt, sechdr.n, bps, i, 0);
+ allEqual = false;
+ }
+
+ short maxJ = sechdr.spt;
+ byte[] sec_b;
+ for(short j = 1; j < maxJ; j++)
+ {
+ sec_b = new byte[sechdr.size_of_data];
+ stream.Read(sec_b, 0, sec_b.Length);
+ sectors.Add(sechdr.r, sec_b);
+ stream.Read(hdr_b, 0, hdr_b.Length);
+
+ handle = GCHandle.Alloc(hdr_b, GCHandleType.Pinned);
+ sechdr = (SectorHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SectorHeader));
+ handle.Free();
+
+ if(sechdr.spt != spt || sechdr.n != bps)
+ {
+ DicConsole.DebugWriteLine("D88 plugin", "Disk tracks are not same size. spt = {0} (expected {1}), bps = {2} (expected {3}) at track {4} sector {5}", sechdr.spt, spt, sechdr.n, bps, i, j, sechdr.deleted_mark);
+ allEqual = false;
+ }
+ }
+
+ sec_b = new byte[sechdr.size_of_data];
+ stream.Read(sec_b, 0, sec_b.Length);
+ sectors.Add(sechdr.r, sec_b);
+
+ foreach(KeyValuePair kvp in sectors)
+ sectorsData.Add(kvp.Value);
+ }
+
+ DicConsole.DebugWriteLine("D88 plugin", "{0} sectors", sectorsData.Count());
+
+ /*
+ FileStream debugStream = new FileStream("debug.img", FileMode.CreateNew, FileAccess.ReadWrite);
+ for(int i = 0; i < sectorsData.Count; i++)
+ debugStream.Write(sectorsData[i], 0, sectorsData[i].Length);
+ debugStream.Close();
+ */
+
+ ImageInfo.mediaType = MediaType.Unknown;
+ if(allEqual)
+ {
+ if(trkCounter == 154 && spt == 26 && bps == IBMSectorSizeCode.EighthKilo)
+ ImageInfo.mediaType = MediaType.NEC_8_SD;
+ else if(bps == IBMSectorSizeCode.QuarterKilo)
+ {
+ if(trkCounter == 80 && spt == 16)
+ ImageInfo.mediaType = MediaType.NEC_525_SS;
+ else if(trkCounter == 154 && spt == 26)
+ ImageInfo.mediaType = MediaType.NEC_8_DD;
+ else if(trkCounter == 160 && spt == 16)
+ ImageInfo.mediaType = MediaType.NEC_525_DS;
+ }
+ else if(trkCounter == 154 && spt == 8 && bps == IBMSectorSizeCode.Kilo)
+ ImageInfo.mediaType = MediaType.NEC_525_HD;
+ else if(bps == IBMSectorSizeCode.HalfKilo)
+ {
+ switch(d88hdr.track_table.Length)
+ {
+ case 40:
+ {
+ switch(spt)
+ {
+ case 8:
+ ImageInfo.mediaType = MediaType.DOS_525_SS_DD_8;
+ break;
+ case 9:
+ ImageInfo.mediaType = MediaType.DOS_525_SS_DD_9;
+ break;
+ }
+ }
+ break;
+ case 80:
+ {
+ switch(spt)
+ {
+ case 8:
+ ImageInfo.mediaType = MediaType.DOS_525_DS_DD_8;
+ break;
+ case 9:
+ ImageInfo.mediaType = MediaType.DOS_525_DS_DD_9;
+ break;
+ }
+ }
+ break;
+ case 160:
+ {
+ switch(spt)
+ {
+ case 15:
+ ImageInfo.mediaType = MediaType.NEC_35_HD_15;
+ break;
+ case 9:
+ ImageInfo.mediaType = MediaType.DOS_35_DS_DD_9;
+ break;
+ case 18:
+ ImageInfo.mediaType = MediaType.DOS_35_HD;
+ break;
+ case 36:
+ ImageInfo.mediaType = MediaType.DOS_35_ED;
+ break;
+ }
+ }
+ break;
+ case 480:
+ if(spt == 38)
+ ImageInfo.mediaType = MediaType.NEC_35_TD;
+ break;
+ }
+ }
+ }
+
+ DicConsole.DebugWriteLine("D88 plugin", "MediaType: {0}", ImageInfo.mediaType);
+
+ ImageInfo.imageSize = (ulong)d88hdr.disk_size;
+ ImageInfo.imageCreationTime = imageFilter.GetCreationTime();
+ ImageInfo.imageLastModificationTime = imageFilter.GetLastWriteTime();
+ ImageInfo.imageName = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
+ ImageInfo.sectors = (ulong)sectorsData.Count;
+ ImageInfo.imageComments = StringHandlers.CToString(d88hdr.name, shiftjis);
+ ImageInfo.xmlMediaType = XmlMediaType.BlockMedia;
+ ImageInfo.sectorSize = (uint)(128 << (int)bps);
+
+ return true;
+ }
+
+ public override bool ImageHasPartitions()
+ {
+ return false;
+ }
+
+ public override ulong GetImageSize()
+ {
+ return ImageInfo.imageSize;
+ }
+
+ public override ulong GetSectors()
+ {
+ return ImageInfo.sectors;
+ }
+
+ public override uint GetSectorSize()
+ {
+ return ImageInfo.sectorSize;
+ }
+
+ public override string GetImageFormat()
+ {
+ return "D88 disk image";
+ }
+
+ public override string GetImageVersion()
+ {
+ return ImageInfo.imageVersion;
+ }
+
+ public override string GetImageApplication()
+ {
+ return ImageInfo.imageApplication;
+ }
+
+ public override string GetImageApplicationVersion()
+ {
+ return ImageInfo.imageApplicationVersion;
+ }
+
+ public override string GetImageCreator()
+ {
+ return ImageInfo.imageCreator;
+ }
+
+ public override DateTime GetImageCreationTime()
+ {
+ return ImageInfo.imageCreationTime;
+ }
+
+ public override DateTime GetImageLastModificationTime()
+ {
+ return ImageInfo.imageLastModificationTime;
+ }
+
+ public override string GetImageName()
+ {
+ return ImageInfo.imageName;
+ }
+
+ public override string GetImageComments()
+ {
+ return ImageInfo.imageComments;
+ }
+
+ public override MediaType GetMediaType()
+ {
+ return ImageInfo.mediaType;
+ }
+
+ public override byte[] ReadSector(ulong sectorAddress)
+ {
+ return ReadSectors(sectorAddress, 1);
+ }
+
+ public override byte[] ReadSectors(ulong sectorAddress, uint length)
+ {
+ if(sectorAddress > ImageInfo.sectors - 1)
+ throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found");
+
+ if(sectorAddress + length > ImageInfo.sectors)
+ throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available");
+
+ MemoryStream buffer = new MemoryStream();
+ for(int i = 0; i < length; i++)
+ buffer.Write(sectorsData[(int)sectorAddress + i], 0, sectorsData[(int)sectorAddress + i].Length);
+
+ return buffer.ToArray();
+ }
+
+ #region Unsupported features
+
+ public override byte[] ReadDiskTag(MediaTagType tag)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSector(ulong sectorAddress, uint track)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectors(ulong sectorAddress, uint length, uint track)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorLong(ulong sectorAddress)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorLong(ulong sectorAddress, uint track)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorsLong(ulong sectorAddress, uint length)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track)
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override string GetMediaManufacturer()
+ {
+ return null;
+ }
+
+ public override string GetMediaModel()
+ {
+ return null;
+ }
+
+ public override string GetMediaSerialNumber()
+ {
+ return null;
+ }
+
+ public override string GetMediaBarcode()
+ {
+ return null;
+ }
+
+ public override string GetMediaPartNumber()
+ {
+ return null;
+ }
+
+ public override int GetMediaSequence()
+ {
+ return 0;
+ }
+
+ public override int GetLastDiskSequence()
+ {
+ return 0;
+ }
+
+ public override string GetDriveManufacturer()
+ {
+ return null;
+ }
+
+ public override string GetDriveModel()
+ {
+ return null;
+ }
+
+ public override string GetDriveSerialNumber()
+ {
+ return null;
+ }
+
+ public override List GetPartitions()
+ {
+ throw new FeatureUnsupportedImageException("Feature not supported by image format");
+ }
+
+ public override List