diff --git a/DiscImageChef.DiscImages/CisCopy.cs b/DiscImageChef.DiscImages/CisCopy.cs index 063278252..76a433310 100644 --- a/DiscImageChef.DiscImages/CisCopy.cs +++ b/DiscImageChef.DiscImages/CisCopy.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.Filters; @@ -55,40 +56,42 @@ namespace DiscImageChef.DiscImages * 2) High compression, algorithm unknown * Then the data for whole tracks follow. */ - public class CisCopy : IMediaImage + public class CisCopy : IWritableImage { - byte[] decodedDisk; - ImageInfo imageInfo; + byte[] decodedDisk; + ImageInfo imageInfo; + long writingOffset; + FileStream writingStream; public CisCopy() { imageInfo = new ImageInfo { - ReadableSectorTags = new List(), - ReadableMediaTags = new List(), - HasPartitions = false, - HasSessions = false, - Version = null, - Application = null, - ApplicationVersion = null, - Creator = null, - Comments = null, - MediaManufacturer = null, - MediaModel = null, - MediaSerialNumber = null, - MediaBarcode = null, - MediaPartNumber = null, - MediaSequence = 0, - LastMediaSequence = 0, - DriveManufacturer = null, - DriveModel = null, - DriveSerialNumber = null, + ReadableSectorTags = new List(), + ReadableMediaTags = new List(), + HasPartitions = false, + HasSessions = false, + Version = null, + Application = null, + ApplicationVersion = null, + Creator = null, + Comments = null, + MediaManufacturer = null, + MediaModel = null, + MediaSerialNumber = null, + MediaBarcode = null, + MediaPartNumber = null, + MediaSequence = 0, + LastMediaSequence = 0, + DriveManufacturer = null, + DriveModel = null, + DriveSerialNumber = null, DriveFirmwareRevision = null }; } public string Name => "CisCopy Disk Image (DC-File)"; - public Guid Id => new Guid("EDF20CC7-6012-49E2-9E92-663A53E42130"); + public Guid Id => new Guid("EDF20CC7-6012-49E2-9E92-663A53E42130"); public string Format => "CisCopy"; @@ -108,7 +111,7 @@ namespace DiscImageChef.DiscImages stream.Seek(0, SeekOrigin.Begin); DiskType type = (DiskType)stream.ReadByte(); - byte tracks; + byte tracks; switch(type) { @@ -129,9 +132,10 @@ namespace DiscImageChef.DiscImages byte[] trackBytes = new byte[tracks]; stream.Read(trackBytes, 0, tracks); - for(int i = 0; i < tracks; i++) + for(int i = 0; i < tracks; i++) if(trackBytes[i] != (byte)TrackType.Copied && trackBytes[i] != (byte)TrackType.Omitted && - trackBytes[i] != (byte)TrackType.OmittedAlternate) return false; + trackBytes[i] != (byte)TrackType.OmittedAlternate) + return false; Compression cmpr = (Compression)stream.ReadByte(); @@ -178,7 +182,7 @@ namespace DiscImageChef.DiscImages stream.Seek(0, SeekOrigin.Begin); DiskType type = (DiskType)stream.ReadByte(); - byte tracks; + byte tracks; switch(type) { @@ -225,7 +229,7 @@ namespace DiscImageChef.DiscImages break; } - int headstep = 1; + int headstep = 1; if(type == DiskType.MD1DD || type == DiskType.MD1DD8) headstep = 2; MemoryStream decodedImage = new MemoryStream(); @@ -246,68 +250,68 @@ namespace DiscImageChef.DiscImages debugStream.Close(); */ - imageInfo.Application = "CisCopy"; - imageInfo.CreationTime = imageFilter.GetCreationTime(); + imageInfo.Application = "CisCopy"; + imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); - imageInfo.MediaTitle = imageFilter.GetFilename(); - imageInfo.ImageSize = (ulong)(stream.Length - 2 - trackBytes.Length); - imageInfo.SectorSize = 512; + imageInfo.MediaTitle = imageFilter.GetFilename(); + imageInfo.ImageSize = (ulong)(stream.Length - 2 - trackBytes.Length); + imageInfo.SectorSize = 512; switch(type) { case DiskType.MD1DD8: - imageInfo.MediaType = MediaType.DOS_525_SS_DD_8; - imageInfo.Sectors = 40 * 1 * 8; - imageInfo.Heads = 1; - imageInfo.Cylinders = 40; + imageInfo.MediaType = MediaType.DOS_525_SS_DD_8; + imageInfo.Sectors = 40 * 1 * 8; + imageInfo.Heads = 1; + imageInfo.Cylinders = 40; imageInfo.SectorsPerTrack = 8; break; case DiskType.MD2DD8: - imageInfo.MediaType = MediaType.DOS_525_DS_DD_8; - imageInfo.Sectors = 40 * 2 * 8; - imageInfo.Heads = 2; - imageInfo.Cylinders = 40; + imageInfo.MediaType = MediaType.DOS_525_DS_DD_8; + imageInfo.Sectors = 40 * 2 * 8; + imageInfo.Heads = 2; + imageInfo.Cylinders = 40; imageInfo.SectorsPerTrack = 8; break; case DiskType.MD1DD: - imageInfo.MediaType = MediaType.DOS_525_SS_DD_9; - imageInfo.Sectors = 40 * 1 * 9; - imageInfo.Heads = 1; - imageInfo.Cylinders = 40; + imageInfo.MediaType = MediaType.DOS_525_SS_DD_9; + imageInfo.Sectors = 40 * 1 * 9; + imageInfo.Heads = 1; + imageInfo.Cylinders = 40; imageInfo.SectorsPerTrack = 9; break; case DiskType.MD2DD: - imageInfo.MediaType = MediaType.DOS_525_DS_DD_9; - imageInfo.Sectors = 40 * 2 * 9; - imageInfo.Heads = 2; - imageInfo.Cylinders = 40; + imageInfo.MediaType = MediaType.DOS_525_DS_DD_9; + imageInfo.Sectors = 40 * 2 * 9; + imageInfo.Heads = 2; + imageInfo.Cylinders = 40; imageInfo.SectorsPerTrack = 9; break; case DiskType.MF2DD: - imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; - imageInfo.Sectors = 80 * 2 * 9; - imageInfo.Heads = 2; - imageInfo.Cylinders = 80; + imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; + imageInfo.Sectors = 80 * 2 * 9; + imageInfo.Heads = 2; + imageInfo.Cylinders = 80; imageInfo.SectorsPerTrack = 9; break; case DiskType.MD2HD: - imageInfo.MediaType = MediaType.DOS_525_HD; - imageInfo.Sectors = 80 * 2 * 15; - imageInfo.Heads = 2; - imageInfo.Cylinders = 80; + imageInfo.MediaType = MediaType.DOS_525_HD; + imageInfo.Sectors = 80 * 2 * 15; + imageInfo.Heads = 2; + imageInfo.Cylinders = 80; imageInfo.SectorsPerTrack = 15; break; case DiskType.MF2HD: - imageInfo.MediaType = MediaType.DOS_35_HD; - imageInfo.Sectors = 80 * 2 * 18; - imageInfo.Heads = 2; - imageInfo.Cylinders = 80; + imageInfo.MediaType = MediaType.DOS_35_HD; + imageInfo.Sectors = 80 * 2 * 18; + imageInfo.Heads = 2; + imageInfo.Cylinders = 80; imageInfo.SectorsPerTrack = 18; break; } imageInfo.XmlMediaType = XmlMediaType.BlockMedia; - decodedDisk = decodedImage.ToArray(); + decodedDisk = decodedImage.ToArray(); decodedImage.Close(); @@ -327,7 +331,7 @@ namespace DiscImageChef.DiscImages } public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, - out List unknownLbas) + out List unknownLbas) { failingLbas = new List(); unknownLbas = new List(); @@ -338,7 +342,7 @@ namespace DiscImageChef.DiscImages } public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, - out List unknownLbas) + out List unknownLbas) { failingLbas = new List(); unknownLbas = new List(); @@ -369,7 +373,7 @@ namespace DiscImageChef.DiscImages byte[] buffer = new byte[length * imageInfo.SectorSize]; Array.Copy(decodedDisk, (int)sectorAddress * imageInfo.SectorSize, buffer, 0, - length * imageInfo.SectorSize); + length * imageInfo.SectorSize); return buffer; } @@ -439,29 +443,232 @@ namespace DiscImageChef.DiscImages throw new FeatureUnsupportedImageException("Feature not supported by image format"); } + public IEnumerable SupportedMediaTags => new MediaTagType[] { }; + public IEnumerable SupportedSectorTags => new SectorTagType[] { }; + // TODO: Test with real hardware to see real supported media + public IEnumerable SupportedMediaTypes => + new[] + { + MediaType.DOS_35_DS_DD_9, MediaType.DOS_35_HD, MediaType.DOS_525_DS_DD_8, MediaType.DOS_525_DS_DD_9, + MediaType.DOS_525_HD, MediaType.DOS_525_SS_DD_8, MediaType.DOS_525_SS_DD_9 + }; + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new(string name, Type type, string description)[] { }; + public IEnumerable KnownExtensions => new[] {".dcf"}; + + public bool IsWriting { get; private set; } + public string ErrorMessage { get; private set; } + + public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, + uint sectorSize) + { + if(sectorSize == 512) + { + ErrorMessage = "Unsupported sector size"; + return false; + } + + if(!SupportedMediaTypes.Contains(mediaType)) + { + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; + + try { writingStream = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); } + catch(IOException e) + { + ErrorMessage = $"Could not create new image file, exception {e.Message}"; + return false; + } + + DiskType diskType; + switch(mediaType) + { + case MediaType.DOS_35_DS_DD_9: + diskType = DiskType.MF2DD; + break; + case MediaType.DOS_35_HD: + diskType = DiskType.MF2HD; + break; + case MediaType.DOS_525_DS_DD_8: + diskType = DiskType.MD2DD8; + break; + case MediaType.DOS_525_DS_DD_9: + diskType = DiskType.MD2DD; + break; + case MediaType.DOS_525_HD: + diskType = DiskType.MD2HD; + break; + case MediaType.DOS_525_SS_DD_8: + diskType = DiskType.MD1DD8; + break; + case MediaType.DOS_525_SS_DD_9: + diskType = DiskType.MD1DD; + break; + default: + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + writingStream.WriteByte((byte)diskType); + + byte tracks = 0; + + switch(diskType) + { + case DiskType.MD1DD8: + case DiskType.MD1DD: + case DiskType.MD2DD8: + case DiskType.MD2DD: + tracks = 80; + break; + case DiskType.MF2DD: + case DiskType.MD2HD: + case DiskType.MF2HD: + tracks = 160; + break; + } + + int headstep = 1; + if(diskType == DiskType.MD1DD || diskType == DiskType.MD1DD8) headstep = 2; + + for(int i = 0; i < tracks; i += headstep) + { + writingStream.WriteByte((byte)TrackType.Copied); + if(headstep == 2) writingStream.WriteByte(0); + } + + writingStream.WriteByte((byte)Compression.None); + writingOffset = writingStream.Position; + + IsWriting = true; + ErrorMessage = null; + return true; + } + + public bool WriteMediaTag(byte[] data, MediaTagType tag) + { + ErrorMessage = "Writing media tags is not supported."; + return false; + } + + public bool WriteSector(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(data.Length != 512) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress >= imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + writingStream.Seek(writingOffset + (long)(sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin); + writingStream.Write(data, 0, data.Length); + + ErrorMessage = ""; + return true; + } + + public bool WriteSectors(byte[] data, ulong sectorAddress, uint length) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(data.Length % 512 != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length > imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + writingStream.Seek(writingOffset + (long)(sectorAddress * imageInfo.SectorSize), SeekOrigin.Begin); + writingStream.Write(data, 0, data.Length); + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorLong(byte[] data, ulong sectorAddress) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool SetTracks(List tracks) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + public bool Close() + { + if(!IsWriting) + { + ErrorMessage = "Image is not opened for writing"; + return false; + } + + writingStream.Flush(); + writingStream.Close(); + IsWriting = false; + + return true; + } + + public bool SetMetadata(ImageInfo metadata) + { + return true; + } + [SuppressMessage("ReSharper", "InconsistentNaming")] enum DiskType : byte { MD1DD8 = 1, - MD1DD = 2, + MD1DD = 2, MD2DD8 = 3, - MD2DD = 4, - MF2DD = 5, - MD2HD = 6, - MF2HD = 7 + MD2DD = 4, + MF2DD = 5, + MD2HD = 6, + MF2HD = 7 } enum Compression : byte { - None = 0, + None = 0, Normal = 1, - High = 2 + High = 2 } enum TrackType : byte { - Copied = 0x4C, - Omitted = 0xFA, + Copied = 0x4C, + Omitted = 0xFA, OmittedAlternate = 0xFE } }