public bool Close() { if (!IsWriting) { ErrorMessage = "Image is not opened for writing"; return(false); } _writingStream.Seek(0x40 + (17 * 16 * 256), SeekOrigin.Begin); byte[] tmp = new byte[256]; _writingStream.Read(tmp, 0, tmp.Length); bool isDos = tmp[0x01] == 17 && tmp[0x02] < 16 && tmp[0x27] <= 122 && tmp[0x34] == 35 && tmp[0x35] == 16 && tmp[0x36] == 0 && tmp[0x37] == 1; _imageHeader = new A2ImgHeader { Blocks = (uint)(_imageInfo.Sectors * _imageInfo.SectorSize) / 512, Creator = CREATOR_AARU, DataOffset = 0x40, DataSize = (uint)(_imageInfo.Sectors * _imageInfo.SectorSize), Flags = (uint)(_imageInfo.LastMediaSequence != 0 ? VALID_VOLUME_NUMBER + (_imageInfo.MediaSequence & 0xFF) : 0), HeaderSize = 0x40, ImageFormat = isDos ? SectorOrder.Dos : SectorOrder.ProDos, Magic = MAGIC, Version = 1 }; if (!string.IsNullOrEmpty(_imageInfo.Comments)) { _writingStream.Seek(0, SeekOrigin.End); tmp = Encoding.UTF8.GetBytes(_imageInfo.Comments); _imageHeader.CommentOffset = (uint)_writingStream.Position; _imageHeader.CommentSize = (uint)(tmp.Length + 1); _writingStream.Write(tmp, 0, tmp.Length); _writingStream.WriteByte(0); } byte[] hdr = new byte[Marshal.SizeOf <A2ImgHeader>()]; IntPtr hdrPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <A2ImgHeader>()); System.Runtime.InteropServices.Marshal.StructureToPtr(_imageHeader, hdrPtr, true); System.Runtime.InteropServices.Marshal.Copy(hdrPtr, hdr, 0, hdr.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(hdrPtr); _writingStream.Seek(0, SeekOrigin.Begin); _writingStream.Write(hdr, 0, hdr.Length); _writingStream.Flush(); _writingStream.Close(); IsWriting = false; ErrorMessage = ""; return(true); }
public bool Identify(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); if (stream.Length < 65) { return(false); } byte[] header = new byte[64]; stream.Read(header, 0, 64); A2ImgHeader hdr = Marshal.SpanToStructureLittleEndian <A2ImgHeader>(header); if (hdr.Magic != MAGIC) { return(false); } if (hdr.DataOffset > stream.Length) { return(false); } // There seems to be incorrect endian in some images on the wild if (hdr.DataSize == 0x00800C00) { hdr.DataSize = 0x000C8000; } if (hdr.DataOffset + hdr.DataSize > stream.Length) { return(false); } if (hdr.CommentOffset > stream.Length) { return(false); } if (hdr.CommentOffset + hdr.CommentSize > stream.Length) { return(false); } if (hdr.CreatorSpecificOffset > stream.Length) { return(false); } return(hdr.CreatorSpecificOffset + hdr.CreatorSpecificSize <= stream.Length); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); imageHeader = new A2ImgHeader(); byte[] header = new byte[64]; stream.Read(header, 0, 64); byte[] magic = new byte[4]; byte[] creator = new byte[4]; Array.Copy(header, 0, magic, 0, 4); Array.Copy(header, 4, creator, 0, 4); imageHeader = Marshal.SpanToStructureLittleEndian <A2ImgHeader>(header); if (imageHeader.DataSize == 0x00800C00) { imageHeader.DataSize = 0x000C8000; AaruConsole.DebugWriteLine("2MG plugin", "Detected incorrect endian on data size field, correcting."); } AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.magic = \"{0}\"", Encoding.ASCII.GetString(magic)); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.creator = \"{0}\"", Encoding.ASCII.GetString(creator)); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.headerSize = {0}", imageHeader.HeaderSize); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.version = {0}", imageHeader.Version); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.imageFormat = {0}", imageHeader.ImageFormat); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.flags = 0x{0:X8}", imageHeader.Flags); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.blocks = {0}", imageHeader.Blocks); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataOffset = 0x{0:X8}", imageHeader.DataOffset); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.dataSize = {0}", imageHeader.DataSize); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentOffset = 0x{0:X8}", imageHeader.CommentOffset); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.commentSize = {0}", imageHeader.CommentSize); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificOffset = 0x{0:X8}", imageHeader.CreatorSpecificOffset); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.creatorSpecificSize = {0}", imageHeader.CreatorSpecificSize); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved1 = 0x{0:X8}", imageHeader.Reserved1); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved2 = 0x{0:X8}", imageHeader.Reserved2); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved3 = 0x{0:X8}", imageHeader.Reserved3); AaruConsole.DebugWriteLine("2MG plugin", "ImageHeader.reserved4 = 0x{0:X8}", imageHeader.Reserved4); if (imageHeader.DataSize == 0 && imageHeader.Blocks == 0 && imageHeader.ImageFormat != SectorOrder.ProDos) { return(false); } byte[] tmp; int[] offsets; switch (imageHeader.ImageFormat) { case SectorOrder.Nibbles: tmp = new byte[imageHeader.DataSize]; stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin); stream.Read(tmp, 0, tmp.Length); var nibPlugin = new AppleNib(); var noFilter = new ZZZNoFilter(); noFilter.Open(tmp); nibPlugin.Open(noFilter); decodedImage = nibPlugin.ReadSectors(0, (uint)nibPlugin.Info.Sectors); imageInfo.Sectors = nibPlugin.Info.Sectors; imageInfo.SectorSize = nibPlugin.Info.SectorSize; break; case SectorOrder.Dos when imageHeader.DataSize == 143360: case SectorOrder.ProDos when imageHeader.DataSize == 143360: stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin); tmp = new byte[imageHeader.DataSize]; stream.Read(tmp, 0, tmp.Length); bool isDos = tmp[0x11001] == 17 && tmp[0x11002] < 16 && tmp[0x11027] <= 122 && tmp[0x11034] == 35 && tmp[0x11035] == 16 && tmp[0x11036] == 0 && tmp[0x11037] == 1; decodedImage = new byte[imageHeader.DataSize]; offsets = imageHeader.ImageFormat == SectorOrder.Dos ? isDos ? deinterleave : interleave : isDos ? interleave : deinterleave; for (int t = 0; t < 35; t++) { for (int s = 0; s < 16; s++) { Array.Copy(tmp, (t * 16 * 256) + (s * 256), decodedImage, (t * 16 * 256) + (offsets[s] * 256), 256); } } imageInfo.Sectors = 560; imageInfo.SectorSize = 256; break; case SectorOrder.Dos when imageHeader.DataSize == 819200: stream.Seek(imageHeader.DataOffset, SeekOrigin.Begin); tmp = new byte[imageHeader.DataSize]; stream.Read(tmp, 0, tmp.Length); decodedImage = new byte[imageHeader.DataSize]; offsets = interleave; for (int t = 0; t < 200; t++) { for (int s = 0; s < 16; s++) { Array.Copy(tmp, (t * 16 * 256) + (s * 256), decodedImage, (t * 16 * 256) + (offsets[s] * 256), 256); } } imageInfo.Sectors = 1600; imageInfo.SectorSize = 512; break; default: decodedImage = null; imageInfo.SectorSize = 512; imageInfo.Sectors = imageHeader.DataSize / 512; break; } imageInfo.ImageSize = imageHeader.DataSize; switch (imageHeader.Creator) { case CREATOR_ASIMOV: imageInfo.Application = "ASIMOV2"; break; case CREATOR_BERNIE: imageInfo.Application = "Bernie ][ the Rescue"; break; case CREATOR_CATAKIG: imageInfo.Application = "Catakig"; break; case CREATOR_SHEPPY: imageInfo.Application = "Sheppy's ImageMaker"; break; case CREATOR_SWEET: imageInfo.Application = "Sweet16"; break; case CREATOR_XGS: imageInfo.Application = "XGS"; break; case CREATOR_CIDER: imageInfo.Application = "CiderPress"; break; case CREATOR_DIC: imageInfo.Application = "DiscImageChef"; break; case CREATOR_AARU: imageInfo.Application = "Aaru"; break; default: imageInfo.Application = $"Unknown creator code \"{Encoding.ASCII.GetString(creator)}\""; break; } imageInfo.Version = imageHeader.Version.ToString(); if (imageHeader.CommentOffset != 0 && imageHeader.CommentSize != 0) { stream.Seek(imageHeader.CommentOffset, SeekOrigin.Begin); byte[] comments = new byte[imageHeader.CommentSize]; stream.Read(comments, 0, (int)imageHeader.CommentSize); imageInfo.Comments = Encoding.ASCII.GetString(comments); } imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); imageInfo.MediaType = GetMediaType(); a2MgImageFilter = imageFilter; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; AaruConsole.VerboseWriteLine("2MG image contains a disk of type {0}", imageInfo.MediaType); if (!string.IsNullOrEmpty(imageInfo.Comments)) { AaruConsole.VerboseWriteLine("2MG comments: {0}", imageInfo.Comments); } switch (imageInfo.MediaType) { case MediaType.Apple32SS: imageInfo.Cylinders = 35; imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 13; break; case MediaType.Apple32DS: imageInfo.Cylinders = 35; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 13; break; case MediaType.Apple33SS: imageInfo.Cylinders = 35; imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 16; break; case MediaType.Apple33DS: imageInfo.Cylinders = 35; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleSonySS: imageInfo.Cylinders = 80; imageInfo.Heads = 1; // Variable sectors per track, this suffices imageInfo.SectorsPerTrack = 10; break; case MediaType.AppleSonyDS: imageInfo.Cylinders = 80; imageInfo.Heads = 2; // Variable sectors per track, this suffices imageInfo.SectorsPerTrack = 10; break; case MediaType.DOS_35_HD: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; break; } return(true); }