// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : Write.cs // Author(s) : Natalia Portillo // // Component : Disk image plugins. // // --[ Description ] ---------------------------------------------------------- // // Writes Apple DiskCopy 4.2 disk images. // // --[ 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 . // // ---------------------------------------------------------------------------- // Copyright © 2011-2018 Natalia Portillo // ****************************************************************************/ using System.Collections.Generic; using System.IO; using Claunia.Encoding; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Enums; using DiscImageChef.CommonTypes.Structs; using Schemas; namespace DiscImageChef.DiscImages { public partial class DiskCopy42 { public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, uint sectorSize) { header = new Dc42Header(); bool tags = false; bool macosx = false; if(options != null && options.TryGetValue("macosx", out string tmpOption)) bool.TryParse(tmpOption, out macosx); if(sectorSize != 512) { ErrorMessage = "Unsupported sector size"; return false; } switch(mediaType) { case MediaType.AppleFileWare: header.FmtByte = kSigmaFmtByteTwiggy; header.Format = kSigmaFormatTwiggy; twiggy = true; // TODO ErrorMessage = "Twiggy write support not yet implemented"; return false; case MediaType.AppleHD20: if(sectors != 39040) { ErrorMessage = "Incorrect number of sectors for Apple HD20 image"; return false; } header.FmtByte = kFmtNotStandard; header.Format = kNotStandardFormat; tags = true; break; case MediaType.AppleProfile: if(sectors != 9728 && sectors != 19456) { ErrorMessage = "Incorrect number of sectors for Apple Profile image"; return false; } header.FmtByte = kFmtNotStandard; header.Format = kNotStandardFormat; tags = true; break; case MediaType.AppleSonyDS: if(sectors != 1600) { ErrorMessage = "Incorrect number of sectors for Apple MF2DD image"; return false; } header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte800K; header.Format = kSonyFormat800K; tags = true; break; case MediaType.AppleSonySS: if(sectors != 800) { ErrorMessage = "Incorrect number of sectors for Apple MF1DD image"; return false; } header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte400K; header.Format = kSonyFormat400K; tags = true; break; case MediaType.AppleWidget: if(sectors != 39040) { ErrorMessage = "Incorrect number of sectors for Apple Widget image"; return false; } header.FmtByte = kFmtNotStandard; header.Format = kNotStandardFormat; tags = true; break; case MediaType.DOS_35_DS_DD_9: if(sectors != 1440) { ErrorMessage = "Incorrect number of sectors for MF2DD image"; return false; } header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte720K; header.Format = kSonyFormat720K; break; case MediaType.DOS_35_HD: if(sectors != 2880) { ErrorMessage = "Incorrect number of sectors for MF2HD image"; return false; } header.Format = kSonyFmtByte1440K; header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1440K; break; case MediaType.DMF: if(sectors != 3360) { ErrorMessage = "Incorrect number of sectors for DMF image"; return false; } header.FmtByte = macosx ? kMacOSXFmtByte : kSonyFmtByte1680K; header.Format = kSonyFormat1680K; break; default: ErrorMessage = $"Unsupport media format {mediaType}"; return false; } dataOffset = 0x54; tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; header.DiskName = "-DiscImageChef converted image-"; header.Valid = 1; header.DataSize = (uint)(sectors * 512); if(tags) header.TagSize = (uint)(sectors * 12); imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; try { writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } catch(IOException e) { ErrorMessage = $"Could not create new image file, exception {e.Message}"; return false; } 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) { 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((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); writingStream.Write(data, 0, data.Length); ErrorMessage = ""; return true; } public bool WriteSectors(byte[] data, ulong sectorAddress, uint length) { if(!IsWriting) { ErrorMessage = "Tried to write on a non-writable image"; return false; } if(data.Length % 512 != 0) { ErrorMessage = "Incorrect data size"; return false; } if(sectorAddress + length > imageInfo.Sectors) { ErrorMessage = "Tried to write past image size"; return false; } writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); writingStream.Write(data, 0, data.Length); ErrorMessage = ""; return true; } public bool WriteSectorLong(byte[] data, ulong sectorAddress) { if(!IsWriting) { ErrorMessage = "Tried to write on a non-writable image"; return false; } if(header.TagSize == 0) { ErrorMessage = "Image does not support tags"; return false; } if(data.Length != 524) { ErrorMessage = "Incorrect data size"; return false; } if(sectorAddress >= imageInfo.Sectors) { ErrorMessage = "Tried to write past image size"; return false; } writingStream.Seek((long)(dataOffset + sectorAddress * 512), SeekOrigin.Begin); writingStream.Write(data, 0, 512); writingStream.Seek((long)(tagOffset + sectorAddress * 12), SeekOrigin.Begin); writingStream.Write(data, 512, 12); ErrorMessage = ""; return true; } public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) { if(!IsWriting) { ErrorMessage = "Tried to write on a non-writable image"; return false; } if(header.TagSize == 0) { ErrorMessage = "Image does not support tags"; return false; } if(data.Length % 524 != 0) { ErrorMessage = "Incorrect data size"; return false; } if(sectorAddress + length > imageInfo.Sectors) { ErrorMessage = "Tried to write past image size"; return false; } for(uint i = 0; i < length; i++) { writingStream.Seek((long)(dataOffset + (sectorAddress + i) * 512), SeekOrigin.Begin); writingStream.Write(data, (int)(i * 524 + 0), 512); writingStream.Seek((long)(tagOffset + (sectorAddress + i) * 12), SeekOrigin.Begin); writingStream.Write(data, (int)(i * 524 + 512), 12); } ErrorMessage = ""; return true; } public bool SetTracks(List tracks) { ErrorMessage = "Unsupported feature"; return false; } public bool Close() { if(!IsWriting) { ErrorMessage = "Image is not opened for writing"; return false; } // No tags where written if(writingStream.Length == 0x54 + header.DataSize) header.TagSize = 0; writingStream.Seek(0x54, SeekOrigin.Begin); byte[] data = new byte[header.DataSize]; writingStream.Read(data, 0, (int)header.DataSize); header.DataChecksum = DC42CheckSum(data); writingStream.Seek(0x54 + header.DataSize, SeekOrigin.Begin); data = new byte[header.TagSize]; writingStream.Read(data, 0, (int)header.TagSize); header.TagChecksum = DC42CheckSum(data); writingStream.Seek(0, SeekOrigin.Begin); if(header.DiskName.Length > 63) header.DiskName = header.DiskName.Substring(0, 63); writingStream.WriteByte((byte)header.DiskName.Length); Encoding macRoman = new MacRoman(); writingStream.Write(macRoman.GetBytes(header.DiskName), 0, header.DiskName.Length); writingStream.Seek(64, SeekOrigin.Begin); writingStream.Write(BigEndianBitConverter.GetBytes(header.DataSize), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(header.TagSize), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(header.DataChecksum), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(header.TagChecksum), 0, 4); writingStream.WriteByte(header.Format); writingStream.WriteByte(header.FmtByte); writingStream.WriteByte(1); writingStream.WriteByte(0); writingStream.Flush(); writingStream.Close(); IsWriting = false; ErrorMessage = ""; return true; } public bool SetMetadata(ImageInfo metadata) { header.DiskName = metadata.MediaTitle ?? "-DiscImageChef converted image-"; 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 = "Unsupported feature"; return false; } public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) { ErrorMessage = "Unsupported feature"; return false; } public bool SetDumpHardware(List dumpHardware) { // Not supported return false; } public bool SetCicmMetadata(CICMMetadataType metadata) { // Not supported return false; } } }