diff --git a/DiscImageChef.DiscImages/AppleDOS.cs b/DiscImageChef.DiscImages/AppleDOS.cs index 91aff46d3..045527d62 100644 --- a/DiscImageChef.DiscImages/AppleDOS.cs +++ b/DiscImageChef.DiscImages/AppleDOS.cs @@ -38,35 +38,39 @@ using DiscImageChef.Filters; namespace DiscImageChef.DiscImages { - public class AppleDos : IMediaImage + public class AppleDos : IWritableImage { - readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15}; readonly int[] deinterleave = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - ImageInfo imageInfo; + readonly int[] interleave = {0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15}; + + byte[] deinterleaved; + string extension; + ImageInfo imageInfo; + FileStream writingStream; public AppleDos() { 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 }; } @@ -74,11 +78,12 @@ namespace DiscImageChef.DiscImages public ImageInfo Info => imageInfo; public string Name => "Apple ][ Interleaved Disk Image"; - public Guid Id => new Guid("A5828AC0-62C9-4304-81D4-EFD4AAE47360"); + public Guid Id => new Guid("A5828AC0-62C9-4304-81D4-EFD4AAE47360"); - public string Format => extension == ".po" - ? "Apple ][ Interleaved Disk Image (ProDOS order)" - : "Apple ][ Interleaved Disk Image (DOS order)"; + public string Format => + extension == ".po" + ? "Apple ][ Interleaved Disk Image (ProDOS order)" + : "Apple ][ Interleaved Disk Image (DOS order)"; public List Tracks => throw new FeatureUnsupportedImageException("Feature not supported by image format"); @@ -103,12 +108,14 @@ namespace DiscImageChef.DiscImages bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 && tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1; - + deinterleaved = new byte[tmp.Length]; extension = Path.GetExtension(imageFilter.GetFilename())?.ToLower(); - int[] offsets = extension == ".do" ? (isDos ? deinterleave : interleave) : (isDos ? interleave : deinterleave); + int[] offsets = extension == ".do" + ? (isDos ? deinterleave : interleave) + : (isDos ? interleave : deinterleave); for(int t = 0; t < 35; t++) { @@ -116,17 +123,17 @@ namespace DiscImageChef.DiscImages Array.Copy(tmp, t * 16 * 256 + s * 256, deinterleaved, t * 16 * 256 + offsets[s] * 256, 256); } - imageInfo.SectorSize = 256; - imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength(); - imageInfo.CreationTime = imageFilter.GetCreationTime(); + imageInfo.SectorSize = 256; + imageInfo.ImageSize = (ulong)imageFilter.GetDataForkLength(); + imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); - imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); - imageInfo.Sectors = 560; - imageInfo.MediaType = MediaType.Apple33SS; - imageInfo.XmlMediaType = XmlMediaType.BlockMedia; - imageInfo.Cylinders = 35; - imageInfo.Heads = 2; - imageInfo.SectorsPerTrack = 16; + imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); + imageInfo.Sectors = 560; + imageInfo.MediaType = MediaType.Apple33SS; + imageInfo.XmlMediaType = XmlMediaType.BlockMedia; + imageInfo.Cylinders = 35; + imageInfo.Heads = 2; + imageInfo.SectorsPerTrack = 16; return true; } @@ -162,7 +169,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(); @@ -173,7 +180,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(); @@ -218,9 +225,6 @@ namespace DiscImageChef.DiscImages throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - byte[] deinterleaved; - string extension; - public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); @@ -258,5 +262,168 @@ namespace DiscImageChef.DiscImages { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } + + public IEnumerable SupportedMediaTags => new MediaTagType[] { }; + public IEnumerable SupportedSectorTags => new SectorTagType[] { }; + public IEnumerable SupportedMediaTypes => + new[] {MediaType.Apple33SS}; + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] { }; + public IEnumerable KnownExtensions => new[] {".do", ".po"}; + 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 != 256) + { + ErrorMessage = "Unsupported sector size"; + return false; + } + + if(mediaType != MediaType.Apple33SS) + { + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + if(sectors > uint.MaxValue) + { + ErrorMessage = "Too many sectors"; + 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; + } + + deinterleaved = new byte[35 * 16 * 256]; + extension = Path.GetExtension(path); + + IsWriting = true; + ErrorMessage = null; + return true; + } + + public bool WriteMediaTag(byte[] data, MediaTagType tag) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + public bool WriteSector(byte[] data, ulong sectorAddress) + { + return WriteSectors(data, sectorAddress, 1); + } + + 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 % imageInfo.SectorSize != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length > imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + Array.Copy(data, 0, deinterleaved, (int)(sectorAddress * imageInfo.SectorSize), 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; + } + + bool isDos = deinterleaved[0x11001] == 17 && deinterleaved[0x11002] < 16 && + deinterleaved[0x11027] <= 122 && + deinterleaved[0x11034] == 35 && deinterleaved[0x11035] == 16 && + deinterleaved[0x11036] == 0 && + deinterleaved[0x11037] == 1; + + byte[] tmp = new byte[deinterleaved.Length]; + + int[] offsets = extension == ".do" + ? (isDos ? deinterleave : interleave) + : (isDos ? interleave : deinterleave); + + for(int t = 0; t < 35; t++) + { + for(int s = 0; s < 16; s++) + Array.Copy(deinterleaved, t * 16 * 256 + offsets[s] * 256, tmp, t * 16 * 256 + s * 256, 256); + } + + writingStream.Seek(0, SeekOrigin.Begin); + writingStream.Write(tmp, 0, tmp.Length); + writingStream.Flush(); + writingStream.Close(); + + ErrorMessage = ""; + IsWriting = false; + + return true; + } + + public bool SetMetadata(ImageInfo metadata) + { + return true; + } + + public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack) + { + // Geometry is not stored in image + return true; + } + + public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } + + public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not supported."; + return false; + } } } \ No newline at end of file