public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; var sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool apmFromHddOnCd = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != AppleCommon.HFS_MAGIC) { continue; } bbSector = new byte[1024]; mdbSector = new byte[512]; if (offset >= 0x400) { Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); } Array.Copy(tmpSector, offset, mdbSector, 0, 512); apmFromHddOnCd = true; break; } if (!apmFromHddOnCd) { return; } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord == AppleCommon.HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <MasterDirectoryBlock>(mdbSector); sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if (apmFromHddOnCd) { sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); } sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); if (mdb.drVolBkUp > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." : "Volume is mounted."); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) { sb.AppendLine("Volume has spared bad blocks."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) { sb.AppendLine("Volume does not need cache."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) { sb.AppendLine("Boot volume is inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) { sb.AppendLine("There are reused CNIDs."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled)) { sb.AppendLine("Volume is journaled."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) { sb.AppendLine("Volume is seriously inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); } if (mdb.drEmbedSigWord == AppleCommon.HFSP_MAGIC) { sb.AppendLine("Volume wraps a HFS+ volume."); sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); } else { sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); } string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); if (bootBlockInfo != null) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine(bootBlockInfo); } else if (mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0) { sb.AppendLine("Volume is bootable."); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drVolBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted); XmlFsType.Files = mdb.drFilCnt; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; if (mdb.drLsMod > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); XmlFsType.ModificationDateSpecified = true; } XmlFsType.Type = "HFS"; XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; } }
public static void UInt32ToBytesTest(uint n, byte[] expected) { Assert.Equal(expected, BigEndianBitConverter.GetBytes(n)); }
public bool WriteSector(byte[] data, ulong sectorAddress) { if (!IsWriting) { ErrorMessage = "Tried to write on a non-writable image"; return(false); } if (data.Length != imageInfo.SectorSize) { ErrorMessage = "Incorrect data size"; return(false); } if (sectorAddress >= imageInfo.Sectors) { ErrorMessage = "Tried to write past image size"; return(false); } // Ignore empty sectors if (ArrayHelpers.ArrayIsNullOrEmpty(data)) { return(true); } ulong byteAddress = sectorAddress * 512; ulong l1Off = (byteAddress & l1Mask) >> l1Shift; if ((long)l1Off >= l1Table.LongLength) { throw new ArgumentOutOfRangeException(nameof(l1Off), $"Trying to write past L1 table, position {l1Off} of a max {l1Table.LongLength}"); } if (l1Table[l1Off] == 0) { writingStream.Seek(0, SeekOrigin.End); l1Table[l1Off] = (ulong)writingStream.Position; byte[] l2TableB = new byte[l2Size * 8]; writingStream.Seek(0, SeekOrigin.End); writingStream.Write(l2TableB, 0, l2TableB.Length); } writingStream.Position = (long)l1Table[l1Off]; ulong l2Off = (byteAddress & l2Mask) >> qHdr.cluster_bits; writingStream.Seek((long)(l1Table[l1Off] + (l2Off * 8)), SeekOrigin.Begin); byte[] entry = new byte[8]; writingStream.Read(entry, 0, 8); ulong offset = BigEndianBitConverter.ToUInt64(entry, 0); if (offset == 0) { offset = (ulong)writingStream.Length; byte[] cluster = new byte[clusterSize]; entry = BigEndianBitConverter.GetBytes(offset); writingStream.Seek((long)(l1Table[l1Off] + (l2Off * 8)), SeekOrigin.Begin); writingStream.Write(entry, 0, 8); writingStream.Seek(0, SeekOrigin.End); writingStream.Write(cluster, 0, cluster.Length); } writingStream.Seek((long)(offset + (byteAddress & sectorMask)), SeekOrigin.Begin); writingStream.Write(data, 0, data.Length); ErrorMessage = ""; return(true); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { bool magicFound = false; byte[] labelSector; uint sectorSize; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) { sectorSize = 2048; } else { sectorSize = imagePlugin.Info.SectorSize; } partitions = new List <Partition>(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; ulong labelPosition = 0; foreach (ulong i in new ulong[] { 0, 4, 15, 16 }.TakeWhile(i => i + sectorOffset < imagePlugin.Info.Sectors)) { labelSector = imagePlugin.ReadSector(i + sectorOffset); uint magic = BigEndianBitConverter.ToUInt32(labelSector, 0x00); if (magic != NEXT_MAGIC1 && magic != NEXT_MAGIC2 && magic != NEXT_MAGIC3) { continue; } magicFound = true; labelPosition = i + sectorOffset; break; } if (!magicFound) { return(false); } uint sectorsToRead = 7680 / imagePlugin.Info.SectorSize; if (7680 % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } labelSector = imagePlugin.ReadSectors(labelPosition, sectorsToRead); NeXTLabel label = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTLabel>(labelSector); byte[] disktabB = new byte[498]; Array.Copy(labelSector, 44, disktabB, 0, 498); label.dl_dt = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTDiskTab>(disktabB); label.dl_dt.d_partitions = new NeXTEntry[8]; DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_version = 0x{0:X8}", label.dl_version); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_label_blkno = {0}", label.dl_label_blkno); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_size = {0}", label.dl_size); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_label = \"{0}\"", StringHandlers.CToString(label.dl_label)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_flags = {0}", label.dl_flags); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_tag = 0x{0:X8}", label.dl_tag); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_name = \"{0}\"", StringHandlers.CToString(label.dl_dt.d_name)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_type = \"{0}\"", StringHandlers.CToString(label.dl_dt.d_type)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_secsize = {0}", label.dl_dt.d_secsize); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ntracks = {0}", label.dl_dt.d_ntracks); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_nsectors = {0}", label.dl_dt.d_nsectors); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ncylinders = {0}", label.dl_dt.d_ncylinders); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rpm = {0}", label.dl_dt.d_rpm); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_front = {0}", label.dl_dt.d_front); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_back = {0}", label.dl_dt.d_back); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ngroups = {0}", label.dl_dt.d_ngroups); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_size = {0}", label.dl_dt.d_ag_size); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_alts = {0}", label.dl_dt.d_ag_alts); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_off = {0}", label.dl_dt.d_ag_off); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_boot0_blkno[0] = {0}", label.dl_dt.d_boot0_blkno[0]); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_boot0_blkno[1] = {0}", label.dl_dt.d_boot0_blkno[1]); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_bootfile = \"{0}\"", StringHandlers.CToString(label.dl_dt.d_bootfile)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_hostname = \"{0}\"", StringHandlers.CToString(label.dl_dt.d_hostname)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rootpartition = {0}", label.dl_dt.d_rootpartition); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rwpartition = {0}", label.dl_dt.d_rwpartition); for (int i = 0; i < 8; i++) { byte[] partB = new byte[44]; Array.Copy(labelSector, 44 + 146 + 44 * i, partB, 0, 44); label.dl_dt.d_partitions[i] = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTEntry>(partB); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_base = {1}", i, label.dl_dt.d_partitions[i].p_base); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_size = {1}", i, label.dl_dt.d_partitions[i].p_size); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_bsize = {1}", i, label.dl_dt.d_partitions[i].p_bsize); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_fsize = {1}", i, label.dl_dt.d_partitions[i].p_fsize); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_opt = {1}", i, label.dl_dt.d_partitions[i].p_opt); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_cpg = {1}", i, label.dl_dt.d_partitions[i].p_cpg); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_density = {1}", i, label.dl_dt.d_partitions[i].p_density); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_minfree = {1}", i, label.dl_dt.d_partitions[i].p_minfree); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_newfs = {1}", i, label.dl_dt.d_partitions[i].p_newfs); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_mountpt = \"{1}\"", i, StringHandlers.CToString(label.dl_dt.d_partitions[i].p_mountpt)); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_automnt = {1}", i, label.dl_dt.d_partitions[i].p_automnt); DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_type = \"{1}\"", i, StringHandlers.CToString(label.dl_dt.d_partitions[i].p_type)); if (label.dl_dt.d_partitions[i].p_size <= 0 || label.dl_dt.d_partitions[i].p_base < 0 || label.dl_dt.d_partitions[i].p_bsize < 0) { continue; } StringBuilder sb = new StringBuilder(); Partition part = new Partition { Size = (ulong)(label.dl_dt.d_partitions[i].p_size * label.dl_dt.d_secsize), Offset = (ulong)((label.dl_dt.d_partitions[i].p_base + label.dl_dt.d_front) * label.dl_dt.d_secsize), Type = StringHandlers.CToString(label.dl_dt.d_partitions[i].p_type), Sequence = (ulong)i, Name = StringHandlers.CToString(label.dl_dt.d_partitions[i].p_mountpt), Length = (ulong)(label.dl_dt.d_partitions[i].p_size * label.dl_dt.d_secsize / sectorSize), Start = (ulong)((label.dl_dt.d_partitions[i].p_base + label.dl_dt.d_front) * label.dl_dt.d_secsize / sectorSize), Scheme = Name }; if (part.Start + part.Length > imagePlugin.Info.Sectors) { DicConsole.DebugWriteLine("NeXT Plugin", "Partition bigger than device, reducing..."); part.Length = imagePlugin.Info.Sectors - part.Start; part.Size = part.Length * sectorSize; DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_size = {1}", i, part.Length); } sb.AppendFormat("{0} bytes per block", label.dl_dt.d_partitions[i].p_bsize).AppendLine(); sb.AppendFormat("{0} bytes per fragment", label.dl_dt.d_partitions[i].p_fsize).AppendLine(); if (label.dl_dt.d_partitions[i].p_opt == 's') { sb.AppendLine("Space optimized"); } else if (label.dl_dt.d_partitions[i].p_opt == 't') { sb.AppendLine("Time optimized"); } else { sb.AppendFormat("Unknown optimization {0:X2}", label.dl_dt.d_partitions[i].p_opt).AppendLine(); } sb.AppendFormat("{0} cylinders per group", label.dl_dt.d_partitions[i].p_cpg).AppendLine(); sb.AppendFormat("{0} bytes per inode", label.dl_dt.d_partitions[i].p_density).AppendLine(); sb.AppendFormat("{0}% of space must be free at minimum", label.dl_dt.d_partitions[i].p_minfree) .AppendLine(); if (label.dl_dt.d_partitions[i].p_newfs != 1) { sb.AppendLine("Filesystem should be formatted at start"); } if (label.dl_dt.d_partitions[i].p_automnt == 1) { sb.AppendLine("Filesystem should be automatically mounted"); } part.Description = sb.ToString(); partitions.Add(part); } return(true); }
/// <summary> /// Reads, interprets and caches the Catalog File /// </summary> Errno ReadCatalog() { if (!mounted) { return(Errno.AccessDenied); } catalogCache = new List <CatalogEntry>(); // Do differently for V1 and V2 if (mddf.fsversion == LISA_V2 || mddf.fsversion == LISA_V1) { Errno error = ReadFile((short)FILEID_CATALOG, out byte[] buf); if (error != Errno.NoError) { return(error); } int offset = 0; List <CatalogEntryV2> catalogV2 = new List <CatalogEntryV2>(); // For each entry on the catalog while (offset + 54 < buf.Length) { CatalogEntryV2 entV2 = new CatalogEntryV2 { filenameLen = buf[offset], filename = new byte[E_NAME], unknown1 = buf[offset + 0x21], fileType = buf[offset + 0x22], unknown2 = buf[offset + 0x23], fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x24), unknown3 = new byte[16] }; Array.Copy(buf, offset + 0x01, entV2.filename, 0, E_NAME); Array.Copy(buf, offset + 0x26, entV2.unknown3, 0, 16); offset += 54; // Check that the entry is correct, not empty or garbage if (entV2.filenameLen != 0 && entV2.filenameLen <= E_NAME && entV2.fileType != 0 && entV2.fileID > 0) { catalogV2.Add(entV2); } } // Convert entries to V3 format foreach (CatalogEntryV2 entV2 in catalogV2) { error = ReadExtentsFile(entV2.fileID, out ExtentFile ext); if (error != Errno.NoError) { continue; } CatalogEntry entV3 = new CatalogEntry { fileID = entV2.fileID, filename = new byte[32], fileType = entV2.fileType, length = (int)srecords[entV2.fileID].filesize, dtc = ext.dtc, dtm = ext.dtm }; Array.Copy(entV2.filename, 0, entV3.filename, 0, entV2.filenameLen); catalogCache.Add(entV3); } return(Errno.NoError); } byte[] firstCatalogBlock = null; // Search for the first sector describing the catalog // While root catalog is not stored in S-Records, probably rest are? (unchecked) // If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File? for (ulong i = 0; i < device.Info.Sectors; i++) { DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag catTag); if (catTag.FileId != FILEID_CATALOG || catTag.RelPage != 0) { continue; } firstCatalogBlock = device.ReadSectors(i, 4); break; } // Catalog not found if (firstCatalogBlock == null) { return(Errno.NoSuchFile); } ulong prevCatalogPointer; prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); // Traverse double-linked list until first catalog block while (prevCatalogPointer != 0xFFFFFFFF) { DecodeTag(device.ReadSectorTag(prevCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag), out LisaTag.PriamTag prevTag); if (prevTag.FileId != FILEID_CATALOG) { return(Errno.InvalidArgument); } firstCatalogBlock = device.ReadSectors(prevCatalogPointer + mddf.mddf_block + volumePrefix, 4); prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); } ulong nextCatalogPointer; nextCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7FA); List <byte[]> catalogBlocks = new List <byte[]> { firstCatalogBlock }; // Traverse double-linked list to read full catalog while (nextCatalogPointer != 0xFFFFFFFF) { DecodeTag(device.ReadSectorTag(nextCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag), out LisaTag.PriamTag nextTag); if (nextTag.FileId != FILEID_CATALOG) { return(Errno.InvalidArgument); } byte[] nextCatalogBlock = device.ReadSectors(nextCatalogPointer + mddf.mddf_block + volumePrefix, 4); nextCatalogPointer = BigEndianBitConverter.ToUInt32(nextCatalogBlock, 0x7FA); catalogBlocks.Add(nextCatalogBlock); } // Foreach catalog block foreach (byte[] buf in catalogBlocks) { int offset = 0; // Traverse all entries while (offset + 64 <= buf.Length) { // Catalog block header if (buf[offset + 0x24] == 0x08) { offset += 78; } // Maybe just garbage? Found in more than 1 disk else if (buf[offset + 0x24] == 0x7C) { offset += 50; } // Apparently reserved to indicate end of catalog? else if (buf[offset + 0x24] == 0xFF) { break; } // Normal entry else if (buf[offset + 0x24] == 0x03 && buf[offset] == 0x24) { CatalogEntry entry = new CatalogEntry { marker = buf[offset], parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), filename = new byte[E_NAME], terminator = buf[offset + 0x23], fileType = buf[offset + 0x24], unknown = buf[offset + 0x25], fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), length = BigEndianBitConverter.ToInt32(buf, offset + 0x30), wasted = BigEndianBitConverter.ToInt32(buf, offset + 0x34), tail = new byte[8] }; Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); Array.Copy(buf, offset + 0x38, entry.tail, 0, 8); if (ReadExtentsFile(entry.fileID, out _) == Errno.NoError) { if (!fileSizeCache.ContainsKey(entry.fileID)) { catalogCache.Add(entry); fileSizeCache.Add(entry.fileID, entry.length); } } offset += 64; } // Subdirectory entry else if (buf[offset + 0x24] == 0x01 && buf[offset] == 0x24) { CatalogEntry entry = new CatalogEntry { marker = buf[offset], parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), filename = new byte[E_NAME], terminator = buf[offset + 0x23], fileType = buf[offset + 0x24], unknown = buf[offset + 0x25], fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), length = 0, wasted = 0, tail = null }; Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); if (!directoryDtcCache.ContainsKey(entry.fileID)) { directoryDtcCache.Add(entry.fileID, DateHandlers.LisaToDateTime(entry.dtc)); } catalogCache.Add(entry); offset += 48; } else { break; } } } return(Errno.NoError); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new Apple2(); StringBuilder sbInformation = new StringBuilder(); information = ""; multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); if (imagePlugin.Info.Sectors < 3) { return; } // Blocks 0 and 1 are boot code byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier); // On Apple //, it's little endian // TODO: Fix //BigEndianBitConverter.IsLittleEndian = // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; PascalVolumeEntry volEntry = new PascalVolumeEntry { FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), VolumeName = new byte[8], Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) }; Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); // First block is always 0 (even is it's sector 2) if (volEntry.FirstBlock != 0) { return; } // Last volume record block must be after first block, and before end of device if (volEntry.LastBlock <= volEntry.FirstBlock || (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2) { return; } // Volume record entry type must be volume or secure if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure) { return; } // Volume name is max 7 characters if (volEntry.VolumeName[0] > 7) { return; } // Volume blocks is equal to volume sectors if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier) { return; } // There can be not less than zero files if (volEntry.Files < 0) { return; } sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock, volEntry.LastBlock).AppendLine(); sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding)) .AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine(); sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine(); sbInformation .AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot)) .AppendLine(); information = sbInformation.ToString(); XmlFsType = new FileSystemType { Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(imagePlugin.ReadSectors(partition.Start, multiplier * 2)), Clusters = (ulong)volEntry.Blocks, ClusterSize = imagePlugin.Info.SectorSize, Files = (ulong)volEntry.Files, FilesSpecified = true, Type = "UCSD Pascal", VolumeName = StringHandlers.PascalToString(volEntry.VolumeName, Encoding) }; }
/// <inheritdoc /> /// <summary>Returns a byte array of the hash value.</summary> public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed);
// TODO: Decode native nvlist static bool DecodeNvList(byte[] nvlist, out Dictionary <string, NVS_Item> decodedNvList, bool xdr, bool littleEndian) { decodedNvList = new Dictionary <string, NVS_Item>(); if (nvlist == null || nvlist.Length < 16) { return(false); } if (!xdr) { return(false); } int offset = 8; while (offset < nvlist.Length) { var item = new NVS_Item(); int currOff = offset; item.encodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); // Finished if (item.encodedSize == 0) { break; } offset += 4; item.decodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; uint nameLength = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; if (nameLength % 4 > 0) { nameLength += 4 - (nameLength % 4); } byte[] nameBytes = new byte[nameLength]; Array.Copy(nvlist, offset, nameBytes, 0, nameLength); item.name = StringHandlers.CToString(nameBytes); offset += (int)nameLength; item.dataType = (NVS_DataTypes)BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; item.elements = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; if (item.elements == 0) { decodedNvList.Add(item.name, item); continue; } switch (item.dataType) { case NVS_DataTypes.DATA_TYPE_BOOLEAN: case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY: case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE: if (item.elements > 1) { bool[] boolArray = new bool[item.elements]; for (int i = 0; i < item.elements; i++) { uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); boolArray[i] = temp > 0; offset += 4; } item.value = boolArray; } else { uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); item.value = temp > 0; offset += 4; } break; case NVS_DataTypes.DATA_TYPE_BYTE: case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY: case NVS_DataTypes.DATA_TYPE_UINT8: case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY: if (item.elements > 1) { byte[] byteArray = new byte[item.elements]; Array.Copy(nvlist, offset, byteArray, 0, item.elements); offset += (int)item.elements; if (item.elements % 4 > 0) { offset += 4 - (int)(item.elements % 4); } item.value = byteArray; } else { item.value = nvlist[offset]; offset += 4; } break; case NVS_DataTypes.DATA_TYPE_DOUBLE: if (item.elements > 1) { double[] doubleArray = new double[item.elements]; for (int i = 0; i < item.elements; i++) { double temp = BigEndianBitConverter.ToDouble(nvlist, offset); doubleArray[i] = temp; offset += 8; } item.value = doubleArray; } else { item.value = BigEndianBitConverter.ToDouble(nvlist, offset); offset += 8; } break; case NVS_DataTypes.DATA_TYPE_HRTIME: if (item.elements > 1) { DateTime[] hrtimeArray = new DateTime[item.elements]; for (int i = 0; i < item.elements; i++) { DateTime temp = DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset)); hrtimeArray[i] = temp; offset += 8; } item.value = hrtimeArray; } else { item.value = DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset)); offset += 8; } break; case NVS_DataTypes.DATA_TYPE_INT16: case NVS_DataTypes.DATA_TYPE_INT16_ARRAY: if (item.elements > 1) { short[] shortArray = new short[item.elements]; for (int i = 0; i < item.elements; i++) { short temp = BigEndianBitConverter.ToInt16(nvlist, offset); shortArray[i] = temp; offset += 4; } item.value = shortArray; } else { item.value = BigEndianBitConverter.ToInt16(nvlist, offset); offset += 4; } break; case NVS_DataTypes.DATA_TYPE_INT32: case NVS_DataTypes.DATA_TYPE_INT32_ARRAY: if (item.elements > 1) { int[] intArray = new int[item.elements]; for (int i = 0; i < item.elements; i++) { int temp = BigEndianBitConverter.ToInt32(nvlist, offset); intArray[i] = temp; offset += 4; } item.value = intArray; } else { item.value = BigEndianBitConverter.ToInt32(nvlist, offset); offset += 4; } break; case NVS_DataTypes.DATA_TYPE_INT64: case NVS_DataTypes.DATA_TYPE_INT64_ARRAY: if (item.elements > 1) { long[] longArray = new long[item.elements]; for (int i = 0; i < item.elements; i++) { long temp = BigEndianBitConverter.ToInt64(nvlist, offset); longArray[i] = temp; offset += 8; } item.value = longArray; } else { item.value = BigEndianBitConverter.ToInt64(nvlist, offset); offset += 8; } break; case NVS_DataTypes.DATA_TYPE_INT8: case NVS_DataTypes.DATA_TYPE_INT8_ARRAY: if (item.elements > 1) { sbyte[] sbyteArray = new sbyte[item.elements]; for (int i = 0; i < item.elements; i++) { sbyte temp = (sbyte)nvlist[offset]; sbyteArray[i] = temp; offset++; } item.value = sbyteArray; if (sbyteArray.Length % 4 > 0) { offset += 4 - (sbyteArray.Length % 4); } } else { item.value = BigEndianBitConverter.ToInt64(nvlist, offset); offset += 4; } break; case NVS_DataTypes.DATA_TYPE_STRING: case NVS_DataTypes.DATA_TYPE_STRING_ARRAY: if (item.elements > 1) { string[] stringArray = new string[item.elements]; for (int i = 0; i < item.elements; i++) { uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; byte[] strBytes = new byte[strLength]; Array.Copy(nvlist, offset, strBytes, 0, strLength); stringArray[i] = StringHandlers.CToString(strBytes); offset += (int)strLength; if (strLength % 4 > 0) { offset += 4 - (int)(strLength % 4); } } item.value = stringArray; } else { uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; byte[] strBytes = new byte[strLength]; Array.Copy(nvlist, offset, strBytes, 0, strLength); item.value = StringHandlers.CToString(strBytes); offset += (int)strLength; if (strLength % 4 > 0) { offset += 4 - (int)(strLength % 4); } } break; case NVS_DataTypes.DATA_TYPE_UINT16: case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY: if (item.elements > 1) { ushort[] ushortArray = new ushort[item.elements]; for (int i = 0; i < item.elements; i++) { ushort temp = BigEndianBitConverter.ToUInt16(nvlist, offset); ushortArray[i] = temp; offset += 4; } item.value = ushortArray; } else { item.value = BigEndianBitConverter.ToUInt16(nvlist, offset); offset += 4; } break; case NVS_DataTypes.DATA_TYPE_UINT32: case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY: if (item.elements > 1) { uint[] uintArray = new uint[item.elements]; for (int i = 0; i < item.elements; i++) { uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); uintArray[i] = temp; offset += 4; } item.value = uintArray; } else { item.value = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; } break; case NVS_DataTypes.DATA_TYPE_UINT64: case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY: if (item.elements > 1) { ulong[] ulongArray = new ulong[item.elements]; for (int i = 0; i < item.elements; i++) { ulong temp = BigEndianBitConverter.ToUInt64(nvlist, offset); ulongArray[i] = temp; offset += 8; } item.value = ulongArray; } else { item.value = BigEndianBitConverter.ToUInt64(nvlist, offset); offset += 8; } break; case NVS_DataTypes.DATA_TYPE_NVLIST: if (item.elements > 1) { goto default; } byte[] subListBytes = new byte[item.encodedSize - (offset - currOff)]; Array.Copy(nvlist, offset, subListBytes, 0, subListBytes.Length); if (DecodeNvList(subListBytes, out Dictionary <string, NVS_Item> subList, true, littleEndian)) { item.value = subList; } else { goto default; } offset = (int)(currOff + item.encodedSize); break; default: byte[] unknown = new byte[item.encodedSize - (offset - currOff)]; Array.Copy(nvlist, offset, unknown, 0, unknown.Length); item.value = unknown; offset = (int)(currOff + item.encodedSize); break; } decodedNvList.Add(item.name, item); } return(decodedNvList.Count > 0); }
private void ParseMp3Frames(byte[] buffer) { var mpeg1BitRate = new[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }; var mpeg2XBitRate = new[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }; var mpeg1SampleRate = new[] { 44100, 48000, 32000, 0 }; var mpeg20SampleRate = new[] { 22050, 24000, 16000, 0 }; var mpeg25SampleRate = new[] { 11025, 12000, 8000, 0 }; int offset = 0; int length = buffer.Length; while (length >= 4) { int mpegVersion, sampleRate, channelMode; ulong header = (ulong)BigEndianBitConverter.ToUInt32(buffer, offset) << 32; if (BitHelper.Read(ref header, 11) != 0x7FF) { break; } mpegVersion = BitHelper.Read(ref header, 2); int layer = BitHelper.Read(ref header, 2); BitHelper.Read(ref header, 1); int bitRate = BitHelper.Read(ref header, 4); sampleRate = BitHelper.Read(ref header, 2); int padding = BitHelper.Read(ref header, 1); BitHelper.Read(ref header, 1); channelMode = BitHelper.Read(ref header, 2); if (mpegVersion == 1 || layer != 1 || bitRate == 0 || bitRate == 15 || sampleRate == 3) { break; } bitRate = (mpegVersion == 3 ? mpeg1BitRate[bitRate] : mpeg2XBitRate[bitRate]) * 1000; switch (mpegVersion) { case 2: sampleRate = mpeg20SampleRate[sampleRate]; break; case 3: sampleRate = mpeg1SampleRate[sampleRate]; break; default: sampleRate = mpeg25SampleRate[sampleRate]; break; } int frameLenght = GetFrameLength(mpegVersion, bitRate, sampleRate, padding); if (frameLenght > length) { break; } bool isVbrHeaderFrame = false; if (_frameOffsets.Count == 0) { // Check for an existing VBR header just to be safe (I haven't seen any in FLVs) int o = offset + GetFrameDataOffset(mpegVersion, channelMode); if (BigEndianBitConverter.ToUInt32(buffer, o) == 0x58696E67) { // "Xing" isVbrHeaderFrame = true; _delayWrite = false; _hasVbrHeader = true; } } if (!isVbrHeaderFrame) { if (_firstBitRate == 0) { _firstBitRate = bitRate; _mpegVersion = mpegVersion; _sampleRate = sampleRate; _channelMode = channelMode; _firstFrameHeader = BigEndianBitConverter.ToUInt32(buffer, offset); } else if (!_isVbr && bitRate != _firstBitRate) { _isVbr = true; if (!_hasVbrHeader) { if (_delayWrite) { WriteVbrHeader(true); _writeVbrHeader = true; _delayWrite = false; } else { _warnings.Add("Detected VBR too late, cannot add VBR header."); } } } } _frameOffsets.Add(_totalFrameLength + (uint)offset); offset += frameLenght; length -= frameLenght; } _totalFrameLength += (uint)buffer.Length; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("IBM437"); information = ""; StringBuilder sb = new StringBuilder(); XmlFsType = new FileSystemType(); uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb); BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); bool isFat12 = false; bool isFat16 = false; bool isFat32 = false; ulong rootDirectorySector = 0; string extraInfo = null; string bootChk = null; XmlFsType.Bootable = bootable; // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. uint sectorsPerRealSector; // This is needed because some OSes don't put volume label as first entry in the root directory uint sectorsForRootDirectory = 0; switch (bpbKind) { case BpbKind.DecRainbow: case BpbKind.Hardcoded: case BpbKind.Msx: case BpbKind.Apricot: isFat12 = true; break; case BpbKind.ShortFat32: case BpbKind.LongFat32: { isFat32 = true; Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector); Fat32ParameterBlockShort shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector); // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fat32Bpb.bps *= 4; fat32Bpb.spc /= 4; fat32Bpb.big_spfat /= 4; fat32Bpb.hsectors /= 4; fat32Bpb.sptrk /= 4; } if (fat32Bpb.version != 0) { sb.AppendLine("FAT+"); XmlFsType.Type = "FAT+"; } else { sb.AppendLine("Microsoft FAT32"); XmlFsType.Type = "FAT32"; } if (fat32Bpb.oem_name != null) { if (fat32Bpb.oem_name[5] == 0x49 && fat32Bpb.oem_name[6] == 0x48 && fat32Bpb.oem_name[7] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); } } if (!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) { sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); } sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); if (fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28) { sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; } else { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; } sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; if ((fat32Bpb.flags & 0xF8) == 0x00) { if ((fat32Bpb.flags & 0x01) == 0x01) { sb.AppendLine("Volume should be checked on next mount."); XmlFsType.Dirty = true; } if ((fat32Bpb.flags & 0x02) == 0x02) { sb.AppendLine("Disk surface should be on next mount."); } } if ((fat32Bpb.mirror_flags & 0x80) == 0x80) { sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF) .AppendLine(); } else { sb.AppendLine("All copies of FAT are the same."); } if ((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) { sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); } else if ((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) { sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); } if (fat32Bpb.signature == 0x29) { XmlFsType.VolumeName = Encoding.ASCII.GetString(fat32Bpb.volume_label); sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)) .AppendLine(); bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); } else { bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... XmlFsType.Bootable = fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80 || fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC; sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; // First root directory sector rootDirectorySector = (ulong)((fat32Bpb.root_cluster - 2) * fat32Bpb.spc + fat32Bpb.big_spfat * fat32Bpb.fats_no + fat32Bpb.rsectors) * sectorsPerRealSector; sectorsForRootDirectory = 1; if (fat32Bpb.fsinfo_sector + partition.Start <= partition.End) { byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start); FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian <FsInfoSector>(fsinfoSector); if (fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 && fsInfo.signature3 == FSINFO_SIGNATURE3) { if (fsInfo.free_clusters < 0xFFFFFFFF) { sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); XmlFsType.FreeClusters = fsInfo.free_clusters; XmlFsType.FreeClustersSpecified = true; } if (fsInfo.last_cluster > 2 && fsInfo.last_cluster < 0xFFFFFFFF) { sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); } } } break; } // Some fields could overflow fake BPB, those will be handled below case BpbKind.Atari: { ushort sum = 0; for (int i = 0; i < bpbSector.Length; i += 2) { sum += BigEndianBitConverter.ToUInt16(bpbSector, i); } // TODO: Check this if (sum == 0x1234) { XmlFsType.Bootable = true; StringBuilder atariSb = new StringBuilder(); atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr) .AppendLine(); atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf) .AppendLine(); if (atariBpb.ldmode == 0) { byte[] tmp = new byte[8]; Array.Copy(atariBpb.fname, 0, tmp, 0, 8); string fname = Encoding.ASCII.GetString(tmp).Trim(); tmp = new byte[3]; Array.Copy(atariBpb.fname, 8, tmp, 0, 3); string extension = Encoding.ASCII.GetString(tmp).Trim(); string filename; if (string.IsNullOrEmpty(extension)) { filename = fname; } else { filename = fname + "." + extension; } atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); } else { atariSb .AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps) .AppendLine(); } extraInfo = atariSb.ToString(); } break; } case BpbKind.Human: XmlFsType.Bootable = true; break; } if (!isFat32) { // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fakeBpb.bps *= 4; fakeBpb.spc /= 4; fakeBpb.spfat /= 4; fakeBpb.hsectors /= 4; fakeBpb.sptrk /= 4; fakeBpb.rsectors /= 4; if (fakeBpb.spc == 0) { fakeBpb.spc = 1; } } // This assumes no sane implementation will violate cluster size rules // However nothing prevents this to happen // If first file on disk uses only one cluster there is absolutely no way to differentiate between FAT12 and FAT16, // so let's hope implementations use common sense? if (!isFat12 && !isFat16) { ulong clusters; if (fakeBpb.sectors == 0) { clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { clusters = fakeBpb.spc == 0 ? fakeBpb.sectors : (ulong)fakeBpb.sectors / fakeBpb.spc; } if (clusters < 4089) { isFat12 = true; } else { isFat16 = true; } } if (isFat12) { switch (bpbKind) { case BpbKind.Atari: sb.AppendLine("Atari FAT12"); break; case BpbKind.Apricot: sb.AppendLine("Apricot FAT12"); break; case BpbKind.Human: sb.AppendLine("Human68k FAT12"); break; default: sb.AppendLine("Microsoft FAT12"); break; } XmlFsType.Type = "FAT12"; } else if (isFat16) { sb.AppendLine(bpbKind == BpbKind.Atari ? "Atari FAT16" : bpbKind == BpbKind.Human ? "Human68k FAT16" : "Microsoft FAT16"); XmlFsType.Type = "FAT16"; } if (bpbKind == BpbKind.Atari) { if (atariBpb.serial_no[0] == 0x49 && atariBpb.serial_no[1] == 0x48 && atariBpb.serial_no[2] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { XmlFsType.VolumeSerial = $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; } XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); if (string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) { XmlFsType.SystemIdentifier = null; } } else if (fakeBpb.oem_name != null) { if (fakeBpb.oem_name[5] == 0x49 && fakeBpb.oem_name[6] == 0x48 && fakeBpb.oem_name[7] == 0x43) { sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); } else { // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies // OEM ID should be ASCII, otherwise ignore it if (fakeBpb.oem_name[0] >= 0x20 && fakeBpb.oem_name[0] <= 0x7F && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); } else if (fakeBpb.oem_name[0] < 0x20 && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); } } if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29) { XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; } } if (XmlFsType.SystemIdentifier != null) { sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); } sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine(); if (bpbKind != BpbKind.Human) { if (fakeBpb.sectors == 0) { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors, fakeBpb.big_sectors * fakeBpb.bps).AppendLine(); XmlFsType.Clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.sectors, fakeBpb.sectors * fakeBpb.bps).AppendLine(); XmlFsType.Clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors : fakeBpb.sectors / fakeBpb.spc); } } else { XmlFsType.Clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; sb.AppendFormat("{0} sectors on volume ({1} bytes).", XmlFsType.Clusters * humanBpb.bpc / imagePlugin.Info.SectorSize, XmlFsType.Clusters * humanBpb.bpc).AppendLine(); } sb.AppendFormat("{0} sectors per cluster.", fakeBpb.spc).AppendLine(); sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fakeBpb.rsectors).AppendLine(); sb.AppendFormat("{0} FATs.", fakeBpb.fats_no).AppendLine(); sb.AppendFormat("{0} entries on root directory.", fakeBpb.root_ent).AppendLine(); if (fakeBpb.media > 0) { sb.AppendFormat("Media descriptor: 0x{0:X2}", fakeBpb.media).AppendLine(); } sb.AppendFormat("{0} sectors per FAT.", fakeBpb.spfat).AppendLine(); if (fakeBpb.sptrk > 0 && fakeBpb.sptrk < 64 && fakeBpb.heads > 0 && fakeBpb.heads < 256) { sb.AppendFormat("{0} sectors per track.", fakeBpb.sptrk).AppendLine(); sb.AppendFormat("{0} heads.", fakeBpb.heads).AppendLine(); } if (fakeBpb.hsectors <= partition.Start) { sb.AppendFormat("{0} hidden sectors before BPB.", fakeBpb.hsectors).AppendLine(); } if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29 || andosOemCorrect) { sb.AppendFormat("Drive number: 0x{0:X2}", fakeBpb.drive_no).AppendLine(); if (XmlFsType.VolumeSerial != null) { sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); } if ((fakeBpb.flags & 0xF8) == 0x00) { if ((fakeBpb.flags & 0x01) == 0x01) { sb.AppendLine("Volume should be checked on next mount."); XmlFsType.Dirty = true; } if ((fakeBpb.flags & 0x02) == 0x02) { sb.AppendLine("Disk surface should be on next mount."); } } if (fakeBpb.signature == 0x29 || andosOemCorrect) { XmlFsType.VolumeName = Encoding.ASCII.GetString(fakeBpb.volume_label); sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine(); } } else if (bpbKind == BpbKind.Atari && XmlFsType.VolumeSerial != null) { sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); } bootChk = Sha1Context.Data(fakeBpb.boot_code, out _); // Workaround that PCExchange jumps into "FAT16 "... if (XmlFsType.SystemIdentifier == "PCX 2.0 ") { fakeBpb.jump[1] += 8; } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... if (XmlFsType.Bootable == false && fakeBpb.jump != null) { XmlFsType.Bootable |= fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80 || fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC; } sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; // First root directory sector rootDirectorySector = (ulong)(fakeBpb.spfat * fakeBpb.fats_no + fakeBpb.rsectors) * sectorsPerRealSector; sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize); } if (extraInfo != null) { sb.Append(extraInfo); } if (rootDirectorySector + partition.Start < partition.End && imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) { byte[] rootDirectory = imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory); if (bpbKind == BpbKind.DecRainbow) { MemoryStream rootMs = new MemoryStream(); foreach (byte[] tmp in from ulong rootSector in new[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 } select imagePlugin.ReadSector(rootSector)) { rootMs.Write(tmp, 0, tmp.Length); } rootDirectory = rootMs.ToArray(); } for (int i = 0; i < rootDirectory.Length; i += 32) { // Not a correct entry if (rootDirectory[i] < DIRENT_MIN && rootDirectory[i] != DIRENT_E5) { continue; } // Deleted or subdirectory entry if (rootDirectory[i] == DIRENT_SUBDIR || rootDirectory[i] == DIRENT_DELETED) { continue; } // Not a volume label if (rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28) { continue; } DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(rootDirectory, i, 32); byte[] fullname = new byte[11]; Array.Copy(entry.filename, 0, fullname, 0, 8); Array.Copy(entry.extension, 0, fullname, 8, 3); string volname = Encoding.GetString(fullname).Trim(); if (!string.IsNullOrEmpty(volname)) { XmlFsType.VolumeName = entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) ? volname.ToLower() : volname; } if (entry.ctime > 0 && entry.cdate > 0) { XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); if (entry.ctime_ms > 0) { XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); } XmlFsType.CreationDateSpecified = true; sb.AppendFormat("Volume created on {0}", XmlFsType.CreationDate).AppendLine(); } if (entry.mtime > 0 && entry.mdate > 0) { XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); XmlFsType.ModificationDateSpecified = true; sb.AppendFormat("Volume last modified on {0}", XmlFsType.ModificationDate).AppendLine(); } if (entry.adate > 0) { sb.AppendFormat("Volume last accessed on {0:d}", DateHandlers.DosToDateTime(entry.adate, 0)) .AppendLine(); } break; } } if (!string.IsNullOrEmpty(XmlFsType.VolumeName)) { sb.AppendFormat("Volume label: {0}", XmlFsType.VolumeName).AppendLine(); } if (XmlFsType.Bootable) { // Intel short jump if (bpbSector[0] == 0xEB && bpbSector[1] < 0x80) { int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; byte[] bootCode = new byte[512 - sigSize - bpbSector[1] - 2]; Array.Copy(bpbSector, bpbSector[1] + 2, bootCode, 0, bootCode.Length); Sha1Context.Data(bootCode, out _); } // Intel big jump else if (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) < 0x1FC) { int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; byte[] bootCode = new byte[512 - sigSize - BitConverter.ToUInt16(bpbSector, 1) - 3]; Array.Copy(bpbSector, BitConverter.ToUInt16(bpbSector, 1) + 3, bootCode, 0, bootCode.Length); Sha1Context.Data(bootCode, out _); } sb.AppendLine("Volume is bootable"); sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); string bootName = knownBootHashes.FirstOrDefault(t => t.hash == bootChk).name; if (string.IsNullOrWhiteSpace(bootName)) { sb.AppendLine("Unknown boot code."); } else { sb.AppendFormat("Boot code corresponds to {0}", bootName).AppendLine(); } } information = sb.ToString(); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { _device = imagePlugin; _partitionStart = partition.Start; Encoding = encoding ?? Encoding.GetEncoding("macintosh"); options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } _volMdb = new MasterDirectoryBlock(); _mdbBlocks = _device.ReadSector(2 + _partitionStart); _bootBlocks = _device.ReadSector(0 + _partitionStart); _volMdb.drSigWord = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x000); if (_volMdb.drSigWord != MFS_MAGIC) { return(Errno.InvalidArgument); } _volMdb.drCrDate = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x002); _volMdb.drLsBkUp = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x006); _volMdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00A); _volMdb.drNmFls = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00C); _volMdb.drDirSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00E); _volMdb.drBlLen = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x010); _volMdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x012); _volMdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x014); _volMdb.drClpSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x018); _volMdb.drAlBlSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x01C); _volMdb.drNxtFNum = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x01E); _volMdb.drFreeBks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x022); _volMdb.drVNSiz = _mdbBlocks[0x024]; byte[] variableSize = new byte[_volMdb.drVNSiz + 1]; Array.Copy(_mdbBlocks, 0x024, variableSize, 0, _volMdb.drVNSiz + 1); _volMdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); _directoryBlocks = _device.ReadSectors(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen); int bytesInBlockMap = ((_volMdb.drNmAlBlks * 12) / 8) + ((_volMdb.drNmAlBlks * 12) % 8); int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; int sectorsInWholeMdb = (bytesInWholeMdb / (int)_device.Info.SectorSize) + (bytesInWholeMdb % (int)_device.Info.SectorSize); byte[] wholeMdb = _device.ReadSectors(_partitionStart + 2, (uint)sectorsInWholeMdb); _blockMapBytes = new byte[bytesInBlockMap]; Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, _blockMapBytes, 0, _blockMapBytes.Length); int offset = 0; _blockMap = new uint[_volMdb.drNmAlBlks + 2 + 1]; for (int i = 2; i < _volMdb.drNmAlBlks + 2; i += 8) { uint tmp1 = 0; uint tmp2 = 0; uint tmp3 = 0; if (offset + 4 <= _blockMapBytes.Length) { tmp1 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset); } if (offset + 4 + 4 <= _blockMapBytes.Length) { tmp2 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 4); } if (offset + 8 + 4 <= _blockMapBytes.Length) { tmp3 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 8); } if (i < _blockMap.Length) { _blockMap[i] = (tmp1 & 0xFFF00000) >> 20; } if (i + 2 < _blockMap.Length) { _blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; } if (i + 3 < _blockMap.Length) { _blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); } if (i + 4 < _blockMap.Length) { _blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; } if (i + 5 < _blockMap.Length) { _blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; } if (i + 6 < _blockMap.Length) { _blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); } if (i + 7 < _blockMap.Length) { _blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; } if (i + 8 < _blockMap.Length) { _blockMap[i + 7] = tmp3 & 0xFFF; } offset += 12; } if (_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { _mdbTags = _device.ReadSectorTag(2 + _partitionStart, SectorTagType.AppleSectorTag); _bootTags = _device.ReadSectorTag(0 + _partitionStart, SectorTagType.AppleSectorTag); _directoryTags = _device.ReadSectorsTag(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen, SectorTagType.AppleSectorTag); _bitmapTags = _device.ReadSectorsTag(_partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag); } _sectorsPerBlock = (int)(_volMdb.drAlBlkSiz / _device.Info.SectorSize); if (!FillDirectory()) { return(Errno.InvalidArgument); } _mounted = true; ushort bbSig = BigEndianBitConverter.ToUInt16(_bootBlocks, 0x000); if (bbSig != AppleCommon.BB_MAGIC) { _bootBlocks = null; } XmlFsType = new FileSystemType(); if (_volMdb.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(_volMdb.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bbSig == AppleCommon.BB_MAGIC; XmlFsType.Clusters = _volMdb.drNmAlBlks; XmlFsType.ClusterSize = _volMdb.drAlBlkSiz; if (_volMdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(_volMdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = _volMdb.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = _volMdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = _volMdb.drVN; return(Errno.NoError); }
public override byte[] encode(object data, BigEndianBitConverter converter) { //TODO: World encoding return(new byte[] {}); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); byte[] buffer = new byte[0x58]; byte[] pString = new byte[64]; stream.Read(buffer, 0, 0x58); IsWriting = false; // Incorrect pascal string length, not DC42 if (buffer[0] > 63) { return(false); } header = new Dc42Header(); Array.Copy(buffer, 0, pString, 0, 64); header.DiskName = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh")); header.DataSize = BigEndianBitConverter.ToUInt32(buffer, 0x40); header.TagSize = BigEndianBitConverter.ToUInt32(buffer, 0x44); header.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48); header.TagChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x4C); header.Format = buffer[0x50]; header.FmtByte = buffer[0x51]; header.Valid = buffer[0x52]; header.Reserved = buffer[0x53]; DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName); DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize); DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize); DicConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum); DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum); DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format); DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte); DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid); DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved); if (header.Valid != 1 || header.Reserved != 0) { return(false); } // Some versions seem to incorrectly create little endian fields if (header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && header.Format != kSigmaFormatTwiggy) { header.DataSize = BitConverter.ToUInt32(buffer, 0x40); header.TagSize = BitConverter.ToUInt32(buffer, 0x44); header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48); header.TagChecksum = BitConverter.ToUInt32(buffer, 0x4C); if (header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() && header.Format != kSigmaFormatTwiggy) { return(false); } } if (header.Format != kSonyFormat400K && header.Format != kSonyFormat800K && header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K && header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy && header.Format != kNotStandardFormat) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown header.format = 0x{0:X2} value", header.Format); return(false); } if (header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K && header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos && header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy && header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte); return(false); } if (header.FmtByte == kInvalidFmtByte) { DicConsole.DebugWriteLine("DC42 plugin", "Image says it's unformatted"); return(false); } dataOffset = 0x54; tagOffset = header.TagSize != 0 ? 0x54 + header.DataSize : 0; imageInfo.SectorSize = 512; bptag = (uint)(header.TagSize != 0 ? 12 : 0); dc42ImageFilter = imageFilter; imageInfo.Sectors = header.DataSize / 512; if (header.TagSize != 0) { bptag = (uint)(header.TagSize / imageInfo.Sectors); DicConsole.DebugWriteLine("DC42 plugin", "bptag = {0} bytes", bptag); if (bptag != 12 && bptag != 20 && bptag != 24) { DicConsole.DebugWriteLine("DC42 plugin", "Unknown tag size"); return(false); } imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); } imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag; imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = header.DiskName; switch (header.Format) { case kSonyFormat400K: imageInfo.MediaType = imageInfo.Sectors == 1600 ? MediaType.AppleSonyDS : MediaType.AppleSonySS; break; case kSonyFormat800K: imageInfo.MediaType = MediaType.AppleSonyDS; break; case kSonyFormat720K: imageInfo.MediaType = MediaType.DOS_35_DS_DD_9; break; case kSonyFormat1440K: imageInfo.MediaType = MediaType.DOS_35_HD; break; case kSonyFormat1680K: imageInfo.MediaType = MediaType.DMF; break; case kSigmaFormatTwiggy: imageInfo.MediaType = MediaType.AppleFileWare; break; case kNotStandardFormat: switch (imageInfo.Sectors) { case 9728: imageInfo.MediaType = MediaType.AppleProfile; break; case 19456: imageInfo.MediaType = MediaType.AppleProfile; break; case 38912: imageInfo.MediaType = MediaType.AppleWidget; break; case 39040: imageInfo.MediaType = MediaType.AppleHD20; break; default: imageInfo.MediaType = MediaType.Unknown; break; } break; default: imageInfo.MediaType = MediaType.Unknown; break; } if (imageInfo.MediaType == MediaType.AppleFileWare) { byte[] data = new byte[header.DataSize]; byte[] tags = new byte[header.TagSize]; twiggyCache = new byte[header.DataSize]; twiggyCacheTags = new byte[header.TagSize]; twiggy = true; Stream datastream = imageFilter.GetDataForkStream(); datastream.Seek(dataOffset, SeekOrigin.Begin); datastream.Read(data, 0, (int)header.DataSize); Stream tagstream = imageFilter.GetDataForkStream(); tagstream.Seek(tagOffset, SeekOrigin.Begin); tagstream.Read(tags, 0, (int)header.TagSize); ushort mfsMagic = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400); ushort mfsAllBlocks = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x412); // Detect a Macintosh Twiggy if (mfsMagic == 0xD2D7 && mfsAllBlocks == 422) { DicConsole.DebugWriteLine("DC42 plugin", "Macintosh Twiggy detected, reversing disk sides"); Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2); Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2); Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2); Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2); } else { DicConsole.DebugWriteLine("DC42 plugin", "Lisa Twiggy detected, reversing second half of disk"); Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2); Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2); int copiedSectors = 0; int sectorsToCopy = 0; for (int i = 0; i < 46; i++) { if (i >= 0 && i <= 3) { sectorsToCopy = 22; } if (i >= 4 && i <= 10) { sectorsToCopy = 21; } if (i >= 11 && i <= 16) { sectorsToCopy = 20; } if (i >= 17 && i <= 22) { sectorsToCopy = 19; } if (i >= 23 && i <= 28) { sectorsToCopy = 18; } if (i >= 29 && i <= 34) { sectorsToCopy = 17; } if (i >= 35 && i <= 41) { sectorsToCopy = 16; } if (i >= 42 && i <= 45) { sectorsToCopy = 15; } Array.Copy(data, header.DataSize / 2 + copiedSectors * 512, twiggyCache, twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512, sectorsToCopy * 512); Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag, twiggyCacheTags, twiggyCacheTags.Length - copiedSectors * bptag - sectorsToCopy * bptag, sectorsToCopy * bptag); copiedSectors += sectorsToCopy; } } } try { if (imageFilter.HasResourceFork()) { ResourceFork rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream()); if (rsrcFork.ContainsKey(0x76657273)) { Resource versRsrc = rsrcFork.GetResource(0x76657273); byte[] vers = versRsrc?.GetResource(versRsrc.GetIds()[0]); if (vers != null) { Version version = new Version(vers); string release = null; string dev = null; string pre = null; string major = $"{version.MajorVersion}"; string minor = $".{version.MinorVersion / 10}"; if (version.MinorVersion % 10 > 0) { release = $".{version.MinorVersion % 10}"; } switch (version.DevStage) { case Version.DevelopmentStage.Alpha: dev = "a"; break; case Version.DevelopmentStage.Beta: dev = "b"; break; case Version.DevelopmentStage.PreAlpha: dev = "d"; break; } if (dev == null && version.PreReleaseVersion > 0) { dev = "f"; } if (dev != null) { pre = $"{version.PreReleaseVersion}"; } imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}"; imageInfo.Application = version.VersionString; imageInfo.Comments = version.VersionMessage; } } if (rsrcFork.ContainsKey(0x64437079)) { Resource dCpyRsrc = rsrcFork.GetResource(0x64437079); if (dCpyRsrc != null) { string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]), Encoding.GetEncoding("macintosh")); Regex dCpyEx = new Regex(REGEX_DCPY); Match dCpyMatch = dCpyEx.Match(dCpy); if (dCpyMatch.Success) { imageInfo.Application = dCpyMatch.Groups["application"].Value; imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value; } } } } } catch (InvalidCastException) { } DicConsole.DebugWriteLine("DC42 plugin", "Image application = {0} version {1}", imageInfo.Application, imageInfo.ApplicationVersion); imageInfo.XmlMediaType = XmlMediaType.BlockMedia; DicConsole.VerboseWriteLine("DiskCopy 4.2 image contains a disk of type {0}", imageInfo.MediaType); switch (imageInfo.MediaType) { case MediaType.AppleSonySS: imageInfo.Cylinders = 80; imageInfo.Heads = 1; imageInfo.SectorsPerTrack = 10; break; case MediaType.AppleSonyDS: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 10; break; case MediaType.DOS_35_DS_DD_9: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 9; break; case MediaType.DOS_35_HD: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 18; break; case MediaType.DMF: imageInfo.Cylinders = 80; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 21; break; case MediaType.AppleProfile: switch (imageInfo.Sectors) { case 9728: imageInfo.Cylinders = 152; break; case 19456: imageInfo.Cylinders = 304; break; } imageInfo.Heads = 4; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleWidget: imageInfo.Cylinders = 608; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; case MediaType.AppleHD20: imageInfo.Cylinders = 610; imageInfo.Heads = 2; imageInfo.SectorsPerTrack = 16; break; default: imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; break; } return(true); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (2 + partition.Start >= partition.End) { return(false); } byte[] mdbSector; ushort drSigWord; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { mdbSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { if (mdbSector.Length < offset + 0x7C + 2) { continue; } drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset); if (drSigWord != AppleCommon.HFS_MAGIC) { continue; } drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature return(drSigWord != AppleCommon.HFSP_MAGIC); } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); if (mdbSector.Length < 0x7C + 2) { return(false); } drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord != AppleCommon.HFS_MAGIC) { return(false); } drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature return(drSigWord != AppleCommon.HFSP_MAGIC); } return(false); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Start >= partition.End) { return(false); } BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; // Boot block is unless defined otherwise, 2 blocks // Funny, you may need boot block to find root block if it's not in standard place just to know size of // block size and then read the whole boot block. // However while you can set a block size different from the sector size on formatting, the bootblock block // size for floppies is the sector size, and for RDB is usually is the hard disk sector size, // so this is not entirely wrong... byte[] sector = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bblk = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(sector); // AROS boot floppies... if (sector.Length >= 512 && sector[510] == 0x55 && sector[511] == 0xAA && (bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { sector = imagePlugin.ReadSectors(1 + partition.Start, 2); bblk = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(sector); } // Not FFS or MuFS? if ((bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { return(false); } // Clear checksum on sector sector[4] = sector[5] = sector[6] = sector[7] = 0; uint bsum = AmigaBootChecksum(sector); DicConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bblk.checksum) { bRootPtr = bblk.root_ptr + partition.Start; DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start - 2, (partition.End - partition.Start + 1) / 2 + partition.Start - 1, (partition.End - partition.Start + 1) / 2 + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start + 4 }; RootBlock rblk = new RootBlock(); // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); sector = imagePlugin.ReadSector(rootPtr); rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type); if (rblk.type != TYPE_HEADER) { continue; } rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize); uint blockSize = (rblk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / sector.Length); DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % sector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } sector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); // Clear checksum on sector rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20); sector[20] = sector[21] = sector[22] = sector[23] = 0; uint rsum = AmigaChecksum(sector); DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4); DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type); if (rblk.sec_type == SUBTYPE_ROOT && rblk.checksum == rsum) { return(true); } } return(false); }
/// <summary>Mounts an Apple Lisa filesystem</summary> public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { try { device = imagePlugin; Encoding = new LisaRoman(); // Lisa OS is unable to work on disks without tags. // This code is designed like that. // However with some effort the code may be modified to ignore them. if (device.Info.ReadableSectorTags == null || !device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { AaruConsole.DebugWriteLine("LisaFS plugin", "Underlying device does not support Lisa tags"); return(Errno.InOutError); } // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors if (device.Info.Sectors < 800) { AaruConsole.DebugWriteLine("LisaFS plugin", "Device is too small"); return(Errno.InOutError); } // MDDF cannot be at end of device, of course volumePrefix = device.Info.Sectors; // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for (ulong i = 0; i < 100; i++) { DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag searchTag); AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); if (volumePrefix == device.Info.Sectors && searchTag.FileId == FILEID_LOADER_SIGNED) { volumePrefix = i - 1; } if (searchTag.FileId != FILEID_MDDF) { continue; } devTagSize = device.ReadSectorTag(i, SectorTagType.AppleSectorTag).Length; byte[] sector = device.ReadSector(i); mddf = new MDDF(); byte[] pString = new byte[33]; mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); Array.Copy(sector, 0x0C, pString, 0, 33); mddf.volname = StringHandlers.PascalToString(pString, Encoding); mddf.unknown1 = sector[0x2D]; Array.Copy(sector, 0x2E, pString, 0, 33); // Prevent garbage mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; mddf.unknown2 = sector[0x4F]; mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); mddf.vol_left_mounted = sector[0x138]; // Check that the MDDF is correct if (mddf.mddf_block != i - volumePrefix || mddf.vol_size > device.Info.Sectors || mddf.vol_size - 1 != mddf.volsize_minus_one || mddf.vol_size - i - 1 != mddf.volsize_minus_mddf_minus_one - volumePrefix || mddf.datasize > mddf.blocksize || mddf.blocksize < device.Info.SectorSize || mddf.datasize != device.Info.SectorSize) { AaruConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found"); return(Errno.InvalidArgument); } // Check MDDF version switch (mddf.fsversion) { case LISA_V1: AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v1"); break; case LISA_V2: AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v2"); break; case LISA_V3: AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v3"); break; default: AaruConsole.ErrorWriteLine("Cannot mount LisaFS version {0}", mddf.fsversion.ToString()); return(Errno.NotSupported); } // Initialize caches extentCache = new Dictionary <short, ExtentFile>(); systemFileCache = new Dictionary <short, byte[]>(); fileCache = new Dictionary <short, byte[]>(); //catalogCache = new Dictionary<short, List<CatalogEntry>>(); fileSizeCache = new Dictionary <short, int>(); mounted = true; if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } if (debug) { printedExtents = new List <short>(); } // Read the S-Records file Errno error = ReadSRecords(); if (error != Errno.NoError) { AaruConsole.ErrorWriteLine("Error {0} reading S-Records file.", error); return(error); } directoryDtcCache = new Dictionary <short, DateTime> { { DIRID_ROOT, mddf.dtcc } }; // Read the Catalog File error = ReadCatalog(); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Cannot read Catalog File, error {0}", error.ToString()); mounted = false; return(error); } // If debug, cache system files if (debug) { error = ReadSystemFile(FILEID_BOOT_SIGNED, out _); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot blocks"); mounted = false; return(error); } error = ReadSystemFile(FILEID_LOADER_SIGNED, out _); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot loader"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_MDDF, out _); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read MDDF"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_BITMAP, out _); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read volume bitmap"); mounted = false; return(error); } error = ReadSystemFile((short)FILEID_SRECORD, out _); if (error != Errno.NoError) { AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read S-Records file"); mounted = false; return(error); } } // Create XML metadata for mounted filesystem XmlFsType = new FileSystemType(); if (DateTime.Compare(mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.BackupDate = mddf.dtvb; XmlFsType.BackupDateSpecified = true; } XmlFsType.Clusters = mddf.vol_size; XmlFsType.ClusterSize = (uint)(mddf.clustersize * mddf.datasize); if (DateTime.Compare(mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) { XmlFsType.CreationDate = mddf.dtvc; XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = mddf.vol_left_mounted != 0; XmlFsType.Files = mddf.filecount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mddf.freecount; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "LisaFS"; XmlFsType.VolumeName = mddf.volname; XmlFsType.VolumeSerial = $"{mddf.volid:X16}"; return(Errno.NoError); } AaruConsole.DebugWriteLine("LisaFS plugin", "Not a Lisa filesystem"); return(Errno.NotSupported); } catch (Exception ex) { AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); return(Errno.InOutError); } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; StringBuilder sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool APMFromHDDOnCD = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != HFS_MAGIC) { continue; } bbSector = new byte[1024]; mdbSector = new byte[512]; if (offset >= 0x400) { Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); } Array.Copy(tmpSector, offset, mdbSector, 0, 512); APMFromHDDOnCD = true; break; } if (!APMFromHDDOnCD) { return; } } else { mdbSector = imagePlugin.ReadSector(2 + partition.Start); drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); if (drSigWord == HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } HFS_MasterDirectoryBlock MDB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_MasterDirectoryBlock>(mdbSector); HFS_BootBlock BB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_BootBlock>(bbSector); sb.AppendLine("Apple Hierarchical File System"); sb.AppendLine(); if (APMFromHDDOnCD) { sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); } sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(MDB.drCrDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(MDB.drLsMod)).AppendLine(); if (MDB.drVolBkUp > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(MDB.drVolBkUp)).AppendLine(); sb.AppendFormat("Backup sequence number: {0}", MDB.drVSeqNum).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if ((MDB.drAtrb & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine((MDB.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted."); if ((MDB.drAtrb & 0x200) == 0x200) { sb.AppendLine("Volume has spared bad blocks."); } if ((MDB.drAtrb & 0x400) == 0x400) { sb.AppendLine("Volume does not need cache."); } if ((MDB.drAtrb & 0x800) == 0x800) { sb.AppendLine("Boot volume is inconsistent."); } if ((MDB.drAtrb & 0x1000) == 0x1000) { sb.AppendLine("There are reused CNIDs."); } if ((MDB.drAtrb & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((MDB.drAtrb & 0x4000) == 0x4000) { sb.AppendLine("Volume is seriously inconsistent."); } if ((MDB.drAtrb & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on root directory", MDB.drNmFls).AppendLine(); sb.AppendFormat("{0} directories on root directory", MDB.drNmRtDirs).AppendLine(); sb.AppendFormat("{0} files on volume", MDB.drFilCnt).AppendLine(); sb.AppendFormat("{0} directories on volume", MDB.drDirCnt).AppendLine(); sb.AppendFormat("Volume write count: {0}", MDB.drWrCnt).AppendLine(); sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", MDB.drVBMSt).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", MDB.drAllocPtr).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", MDB.drNmAlBlks).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", MDB.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a file.", MDB.drClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", MDB.drXTClpSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", MDB.drCTClpSiz).AppendLine(); sb.AppendFormat("Sector of first allocation block: {0}", MDB.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused CNID: {0}", MDB.drNxtCNID).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", MDB.drFreeBks).AppendLine(); sb.AppendFormat("{0} bytes in the Extents B-Tree", MDB.drXTFlSize).AppendLine(); sb.AppendFormat("{0} bytes in the Catalog B-Tree", MDB.drCTFlSize).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(MDB.drVN, Encoding)).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", MDB.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", MDB.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", MDB.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", MDB.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", MDB.drFndrInfo5).AppendLine(); if (MDB.drFndrInfo6 != 0 && MDB.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", MDB.drFndrInfo6, MDB.drFndrInfo7).AppendLine(); } if (MDB.drEmbedSigWord == HFSP_MAGIC) { sb.AppendLine("Volume wraps a HFS+ volume."); sb.AppendFormat("Starting block of the HFS+ volume: {0}", MDB.xdrStABNt).AppendLine(); sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", MDB.xdrNumABlks).AppendLine(); } else { sb.AppendFormat("{0} blocks in volume cache", MDB.drVCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume bitmap cache", MDB.drVBMCSize).AppendLine(); sb.AppendFormat("{0} blocks in volume common cache", MDB.drCtlCSize).AppendLine(); } if (BB.signature == HFSBB_MAGIC) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine("Boot Block:"); if ((BB.boot_flags & 0x40) == 0x40) { sb.AppendLine("Boot block should be executed."); } if ((BB.boot_flags & 0x80) == 0x80) { sb.AppendLine("Boot block is in new unknown format."); } else { if (BB.boot_flags > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (BB.boot_flags < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(BB.system_name, Encoding)) .AppendLine(); sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(BB.finder_name, Encoding)) .AppendLine(); sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(BB.debug_name, Encoding)) .AppendLine(); sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(BB.disasm_name, Encoding)).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(BB.stupscr_name, Encoding)).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", StringHandlers.PascalToString(BB.bootup_name, Encoding)).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(BB.clipbrd_name, Encoding)) .AppendLine(); sb.AppendFormat("Maximum opened files: {0}", BB.max_files * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", BB.queue_size).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", BB.heap_128k).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", BB.heap_256k).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", BB.heap_512k).AppendLine(); } } else if (MDB.drFndrInfo0 != 0 || MDB.drFndrInfo3 != 0 || MDB.drFndrInfo5 != 0) { sb.AppendLine("Volume is bootable."); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (MDB.drVolBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(MDB.drVolBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = BB.signature == HFSBB_MAGIC || MDB.drFndrInfo0 != 0 || MDB.drFndrInfo3 != 0 || MDB.drFndrInfo5 != 0; XmlFsType.Clusters = MDB.drNmAlBlks; XmlFsType.ClusterSize = (int)MDB.drAlBlkSiz; if (MDB.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(MDB.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (MDB.drAtrb & 0x100) != 0x100; XmlFsType.Files = MDB.drFilCnt; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = MDB.drFreeBks; XmlFsType.FreeClustersSpecified = true; if (MDB.drLsMod > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(MDB.drLsMod); XmlFsType.ModificationDateSpecified = true; } XmlFsType.Type = "HFS"; XmlFsType.VolumeName = StringHandlers.PascalToString(MDB.drVN, Encoding); if (MDB.drFndrInfo6 != 0 && MDB.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{MDB.drFndrInfo6:X8}{MDB.drFndrInfo7:X8}"; } }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; StringBuilder sb = new StringBuilder(); BeSuperBlock besb = new BeSuperBlock(); byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start); bool littleEndian; besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // Magic is at offset { littleEndian = besb.magic1 == BEFS_CIGAM1; } else { sbSector = imagePlugin.ReadSector(1 + partition.Start); besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // There is a boot sector { littleEndian = besb.magic1 == BEFS_CIGAM1; } else if (sbSector.Length >= 0x400) { byte[] temp = imagePlugin.ReadSector(0 + partition.Start); besb.magic1 = BigEndianBitConverter.ToUInt32(temp, 0x220); if (besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // There is a boot sector { littleEndian = besb.magic1 == BEFS_CIGAM1; sbSector = new byte[0x200]; Array.Copy(temp, 0x200, sbSector, 0, 0x200); } else { return; } } else { return; } } if (littleEndian) { besb = Marshal.ByteArrayToStructureLittleEndian <BeSuperBlock>(sbSector); } else { besb = Marshal.ByteArrayToStructureBigEndian <BeSuperBlock>(sbSector); } sb.AppendLine(littleEndian ? "Little-endian BeFS" : "Big-endian BeFS"); if (besb.magic1 != BEFS_MAGIC1 || besb.fs_byte_order != BEFS_ENDIAN || besb.magic2 != BEFS_MAGIC2 || besb.magic3 != BEFS_MAGIC3 || besb.root_dir_len != 1 || besb.indices_len != 1 || 1 << (int)besb.block_shift != besb.block_size) { sb.AppendLine("Superblock seems corrupt, following information may be incorrect"); sb.AppendFormat("Magic 1: 0x{0:X8} (Should be 0x42465331)", besb.magic1).AppendLine(); sb.AppendFormat("Magic 2: 0x{0:X8} (Should be 0xDD121031)", besb.magic2).AppendLine(); sb.AppendFormat("Magic 3: 0x{0:X8} (Should be 0x15B6830E)", besb.magic3).AppendLine(); sb.AppendFormat("Filesystem endianness: 0x{0:X8} (Should be 0x42494745)", besb.fs_byte_order) .AppendLine(); sb.AppendFormat("Root folder's i-node size: {0} blocks (Should be 1)", besb.root_dir_len).AppendLine(); sb.AppendFormat("Indices' i-node size: {0} blocks (Should be 1)", besb.indices_len).AppendLine(); sb.AppendFormat("1 << block_shift == block_size => 1 << {0} == {1} (Should be {2})", besb.block_shift, 1 << (int)besb.block_shift, besb.block_size).AppendLine(); } switch (besb.flags) { case BEFS_CLEAN: sb.AppendLine(besb.log_start == besb.log_end ? "Filesystem is clean" : "Filesystem is dirty"); break; case BEFS_DIRTY: sb.AppendLine("Filesystem is dirty"); break; default: sb.AppendFormat("Unknown flags: {0:X8}", besb.flags).AppendLine(); break; } sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(besb.name, Encoding)).AppendLine(); sb.AppendFormat("{0} bytes per block", besb.block_size).AppendLine(); sb.AppendFormat("{0} blocks in volume ({1} bytes)", besb.num_blocks, besb.num_blocks * besb.block_size) .AppendLine(); sb.AppendFormat("{0} used blocks ({1} bytes)", besb.used_blocks, besb.used_blocks * besb.block_size) .AppendLine(); sb.AppendFormat("{0} bytes per i-node", besb.inode_size).AppendLine(); sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", besb.blocks_per_ag, besb.blocks_per_ag * besb.block_size).AppendLine(); sb.AppendFormat("{0} allocation groups in volume", besb.num_ags).AppendLine(); sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.log_blocks_start, besb.log_blocks_ag, besb.log_blocks_len, besb.log_blocks_len * besb.block_size).AppendLine(); sb.AppendFormat("Journal starts in byte {0} and ends in byte {1}", besb.log_start, besb.log_end) .AppendLine(); sb .AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.root_dir_start, besb.root_dir_ag, besb.root_dir_len, besb.root_dir_len * besb.block_size).AppendLine(); sb .AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", besb.indices_start, besb.indices_ag, besb.indices_len, besb.indices_len * besb.block_size) .AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Clusters = (ulong)besb.num_blocks, ClusterSize = besb.block_size, Dirty = besb.flags == BEFS_DIRTY, FreeClusters = (ulong)(besb.num_blocks - besb.used_blocks), FreeClustersSpecified = true, Type = "BeFS", VolumeName = StringHandlers.CToString(besb.name, Encoding) }; }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Length < 3) { return(false); } multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); // Blocks 0 and 1 are boot code byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier); // On Apple II, it's little endian // TODO: Fix /*BigEndianBitConverter.IsLittleEndian = * multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;*/ PascalVolumeEntry volEntry = new PascalVolumeEntry { FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), VolumeName = new byte[8], Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) }; Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.firstBlock = {0}", volEntry.FirstBlock); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBlock = {0}", volEntry.LastBlock); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.entryType = {0}", volEntry.EntryType); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.volumeName = {0}", volEntry.VolumeName); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.blocks = {0}", volEntry.Blocks); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.files = {0}", volEntry.Files); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.dummy = {0}", volEntry.Dummy); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBoot = {0}", volEntry.LastBoot); DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.tail = {0}", volEntry.Tail); // First block is always 0 (even is it's sector 2) if (volEntry.FirstBlock != 0) { return(false); } // Last volume record block must be after first block, and before end of device if (volEntry.LastBlock <= volEntry.FirstBlock || (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2) { return(false); } // Volume record entry type must be volume or secure if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure) { return(false); } // Volume name is max 7 characters if (volEntry.VolumeName[0] > 7) { return(false); } // Volume blocks is equal to volume sectors if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier) { return(false); } // There can be not less than zero files return(volEntry.Files >= 0); }
public bool Close() { if (!IsWriting) { ErrorMessage = "Image is not opened for writing"; return(false); } if (currentChunk.type != CHUNK_TYPE_NOCOPY) { currentChunk.length = currentChunk.sectors * 512; } chunks.Add(currentChunk.sector, currentChunk); chunks.Add(imageInfo.Sectors, new BlockChunk { type = CHUNK_TYPE_END, sector = imageInfo.Sectors }); var bHdr = new BlockHeader { signature = CHUNK_SIGNATURE, version = 1, sectorCount = imageInfo.Sectors, checksumType = UDIF_CHECKSUM_TYPE_CRC32, checksumLen = 32, checksum = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0), chunks = (uint)chunks.Count }; var chunkMs = new MemoryStream(); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.signature), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.version), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorStart), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorCount), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.dataOffset), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.buffers), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.descriptor), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved1), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved2), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved3), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved4), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved5), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved6), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumType), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumLen), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksum), 0, 4); chunkMs.Write(new byte[124], 0, 124); chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.chunks), 0, 4); foreach (BlockChunk chunk in chunks.Values) { chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.type), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.comment), 0, 4); chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sector), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sectors), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.offset), 0, 8); chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.length), 0, 8); } byte[] plist = Encoding.UTF8.GetBytes(new NSDictionary { { "resource-fork", new NSDictionary { { "blkx", new NSArray { new NSDictionary { { "Attributes", "0x0050" }, { "CFName", "whole disk (Aaru : 0)" }, { "Data", chunkMs.ToArray() }, { "ID", "0" }, { "Name", "whole disk (Aaru : 0)" } } } } } } }.ToXmlPropertyList()); footer = new UdifFooter { signature = UDIF_SIGNATURE, version = 4, headerSize = 512, flags = 1, dataForkLen = (ulong)writingStream.Length, segmentNumber = 1, segmentCount = 1, segmentId = Guid.NewGuid(), dataForkChkType = UDIF_CHECKSUM_TYPE_CRC32, dataForkChkLen = 32, dataForkChk = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0), plistOff = (ulong)writingStream.Length, plistLen = (ulong)plist.Length, // TODO: Find how is this calculated /*masterChkType = 2, * masterChkLen = 32, * masterChk = BitConverter.ToUInt32(masterChecksum.Final().Reverse().ToArray(), 0),*/ imageVariant = 2, sectorCount = imageInfo.Sectors }; writingStream.Seek(0, SeekOrigin.End); writingStream.Write(plist, 0, plist.Length); writingStream.Write(BigEndianBitConverter.GetBytes(footer.signature), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.version), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.headerSize), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.flags), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.runningDataForkOff), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkOff), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkLen), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkOff), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkLen), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentNumber), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentCount), 0, 4); writingStream.Write(footer.segmentId.ToByteArray(), 0, 16); writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkType), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkLen), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChk), 0, 4); writingStream.Write(new byte[124], 0, 124); writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistOff), 0, 8); writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistLen), 0, 8); writingStream.Write(new byte[120], 0, 120); writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkType), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkLen), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChk), 0, 4); writingStream.Write(new byte[124], 0, 124); writingStream.Write(BigEndianBitConverter.GetBytes(footer.imageVariant), 0, 4); writingStream.Write(BigEndianBitConverter.GetBytes(footer.sectorCount), 0, 8); writingStream.Write(new byte[12], 0, 12); writingStream.Flush(); writingStream.Close(); IsWriting = false; ErrorMessage = ""; return(true); }
/// <summary> /// Returns a byte array of the hash value. /// </summary> public byte[] Final() { uint finalSum = (uint)((sum2 << 16) | sum1); return(BigEndianBitConverter.GetBytes(finalSum)); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.BigEndianUnicode; information = ""; var vh = new VolumeHeader(); ulong hfspOffset; bool wrapped; uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; if (0x800 % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } byte[] vhSector = imagePlugin.ReadSectors(partition.Start, sectorsToRead); ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (drSigWord == AppleCommon.HFS_MAGIC) // "BD" { drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature if (drSigWord == AppleCommon.HFSP_MAGIC) // "H+" { ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); wrapped = true; } else { hfspOffset = 0; wrapped = false; } } else { hfspOffset = 0; wrapped = false; } vhSector = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead); // Read volume header vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (vh.signature != AppleCommon.HFSP_MAGIC && vh.signature != AppleCommon.HFSX_MAGIC) { return; } var sb = new StringBuilder(); if (vh.signature == 0x482B) { sb.AppendLine("HFS+ filesystem."); } if (vh.signature == 0x4858) { sb.AppendLine("HFSX filesystem."); } if (wrapped) { sb.AppendLine("Volume is wrapped inside an HFS volume."); } byte[] tmp = new byte[0x400]; Array.Copy(vhSector, 0x400, tmp, 0, 0x400); vhSector = tmp; vh = Marshal.ByteArrayToStructureBigEndian <VolumeHeader>(vhSector); if (vh.version == 4 || vh.version == 5) { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); if ((vh.attributes & 0x80) == 0x80) { sb.AppendLine("Volume is locked on hardware."); } if ((vh.attributes & 0x100) == 0x100) { sb.AppendLine("Volume is unmounted."); } if ((vh.attributes & 0x200) == 0x200) { sb.AppendLine("There are bad blocks in the extents file."); } if ((vh.attributes & 0x400) == 0x400) { sb.AppendLine("Volume does not require cache."); } if ((vh.attributes & 0x800) == 0x800) { sb.AppendLine("Volume state is inconsistent."); } if ((vh.attributes & 0x1000) == 0x1000) { sb.AppendLine("CNIDs are reused."); } if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((vh.attributes & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked on software."); } sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".", Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine(); if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine(); } sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine(); if (vh.backupDate > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if (vh.backupDate > 0) { sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine(); } else { sb.AppendLine("Volume has never been checked up"); } sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine(); sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine(); sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine(); sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine(); sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine(); sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine(); sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine(); sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine(); sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine(); sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine(); sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine(); sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine(); sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine(); if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine(); } XmlFsType = new FileSystemType(); if (vh.backupDate > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(vh.backupDate); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0; XmlFsType.Clusters = vh.totalBlocks; XmlFsType.ClusterSize = vh.blockSize; if (vh.createDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(vh.createDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (vh.attributes & 0x100) != 0x100; XmlFsType.Files = vh.fileCount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = vh.freeBlocks; XmlFsType.FreeClustersSpecified = true; if (vh.modifyDate > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(vh.modifyDate); XmlFsType.ModificationDateSpecified = true; } if (vh.signature == 0x482B) { XmlFsType.Type = "HFS+"; } if (vh.signature == 0x4858) { XmlFsType.Type = "HFSX"; } if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}"; } XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion); } else { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); sb.AppendLine("This version is not supported yet."); } information = sb.ToString(); }
public bool Close() { if (!IsWriting) { ErrorMessage = "Image is not opened for writing"; return(false); } Version thisVersion = GetType().Assembly.GetName().Version; if (imageInfo.Cylinders == 0) { imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; while (imageInfo.Cylinders == 0) { imageInfo.Heads--; if (imageInfo.Heads == 0) { imageInfo.SectorsPerTrack--; imageInfo.Heads = 16; } imageInfo.Cylinders = (uint)(imageInfo.Sectors / imageInfo.Heads / imageInfo.SectorsPerTrack); if (imageInfo.Cylinders == 0 && imageInfo.Heads == 0 && imageInfo.SectorsPerTrack == 0) { break; } } } HardDiskFooter footer = new HardDiskFooter { Cookie = IMAGE_COOKIE, Features = FEATURES_RESERVED, Version = VERSION1, Timestamp = (uint)(DateTime.Now - new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds, CreatorApplication = CREATOR_DISCIMAGECHEF, CreatorVersion = (uint)(((thisVersion.Major & 0xFF) << 24) + ((thisVersion.Minor & 0xFF) << 16) + ((thisVersion.Build & 0xFF) << 8) + (thisVersion.Revision & 0xFF)), CreatorHostOs = DetectOS.GetRealPlatformID() == PlatformID.MacOSX ? CREATOR_MACINTOSH : CREATOR_WINDOWS, DiskType = TYPE_FIXED, UniqueId = Guid.NewGuid(), DiskGeometry = ((imageInfo.Cylinders & 0xFFFF) << 16) + ((imageInfo.Heads & 0xFF) << 8) + (imageInfo.SectorsPerTrack & 0xFF), OriginalSize = imageInfo.Sectors * 512, CurrentSize = imageInfo.Sectors * 512 }; footer.Offset = footer.DiskType == TYPE_FIXED ? ulong.MaxValue : 512; byte[] footerBytes = new byte[512]; Array.Copy(BigEndianBitConverter.GetBytes(footer.Cookie), 0, footerBytes, 0x00, 8); Array.Copy(BigEndianBitConverter.GetBytes(footer.Features), 0, footerBytes, 0x08, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.Version), 0, footerBytes, 0x0C, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.Offset), 0, footerBytes, 0x10, 8); Array.Copy(BigEndianBitConverter.GetBytes(footer.Timestamp), 0, footerBytes, 0x18, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorApplication), 0, footerBytes, 0x1C, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorVersion), 0, footerBytes, 0x20, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorHostOs), 0, footerBytes, 0x24, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.OriginalSize), 0, footerBytes, 0x28, 8); Array.Copy(BigEndianBitConverter.GetBytes(footer.CurrentSize), 0, footerBytes, 0x30, 8); Array.Copy(BigEndianBitConverter.GetBytes(footer.DiskGeometry), 0, footerBytes, 0x38, 4); Array.Copy(BigEndianBitConverter.GetBytes(footer.DiskType), 0, footerBytes, 0x3C, 4); Array.Copy(footer.UniqueId.ToByteArray(), 0, footerBytes, 0x44, 4); footer.Checksum = VhdChecksum(footerBytes); Array.Copy(BigEndianBitConverter.GetBytes(footer.Checksum), 0, footerBytes, 0x40, 4); writingStream.Seek((long)(footer.DiskType == TYPE_FIXED ? footer.OriginalSize : 0), SeekOrigin.Begin); writingStream.Write(footerBytes, 0, 512); writingStream.Flush(); writingStream.Close(); IsWriting = false; ErrorMessage = ""; return(true); }
public DeviceInfo(Device dev) { Type = dev.Type; Manufacturer = dev.Manufacturer; Model = dev.Model; FirmwareRevision = dev.FirmwareRevision; Serial = dev.Serial; ScsiType = dev.ScsiType; IsRemovable = dev.IsRemovable; IsUsb = dev.IsUsb; UsbVendorId = dev.UsbVendorId; UsbProductId = dev.UsbProductId; UsbDescriptors = dev.UsbDescriptors; UsbManufacturerString = dev.UsbManufacturerString; UsbProductString = dev.UsbProductString; UsbSerialString = dev.UsbSerialString; IsFireWire = dev.IsFireWire; FireWireGuid = dev.FireWireGuid; FireWireModel = dev.FireWireModel; FireWireModelName = dev.FireWireModelName; FireWireVendor = dev.FireWireVendor; FireWireVendorName = dev.FireWireVendorName; IsCompactFlash = dev.IsCompactFlash; IsPcmcia = dev.IsPcmcia; Cis = dev.Cis; switch (dev.Type) { case DeviceType.ATA: { bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); if (sense) { AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", errorRegisters.SectorCount); AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", errorRegisters.CylinderHigh); AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", errorRegisters.CylinderLow); AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", errorRegisters.DeviceHead); AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); break; } if (dev.Error) { AaruConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError); break; } AtaIdentify = ataBuf; dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _); if (errorRegisters.Sector == 0xAA && errorRegisters.SectorCount == 0x55) { AtaMcptError = errorRegisters; } break; } case DeviceType.ATAPI: { bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); if (sense) { AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", errorRegisters.SectorCount); AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", errorRegisters.CylinderHigh); AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", errorRegisters.CylinderLow); AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", errorRegisters.DeviceHead); AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); break; } if (!dev.Error) { AtapiIdentify = ataBuf; } else { AaruConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError); } // ATAPI devices are also SCSI devices goto case DeviceType.SCSI; } case DeviceType.SCSI: { bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf); if (sense) { AaruConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf)); break; } ScsiInquiryData = inqBuf; ScsiInquiry = Inquiry.Decode(inqBuf); sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00); if (!sense) { ScsiEvpdPages = new Dictionary <byte, byte[]>(); byte[] pages = EVPD.DecodePage00(inqBuf); if (pages != null) { foreach (byte page in pages) { sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); if (sense) { continue; } ScsiEvpdPages.Add(page, inqBuf); } } } var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType; sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); if (!sense && !dev.Error) { ScsiModeSense10 = modeBuf; } if (sense || dev.Error) { sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); if (!sense && !dev.Error) { ScsiModeSense10 = modeBuf; } } if (!sense && !dev.Error) { ScsiMode = Modes.DecodeMode10(modeBuf, devType); } bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue); sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } if (sense || dev.Error) { sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } } if (sense || dev.Error) { sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _); if (!sense && !dev.Error) { ScsiModeSense6 = modeBuf; } } if (!sense && !dev.Error && !useMode10) { ScsiMode = Modes.DecodeMode6(modeBuf, devType); } switch (devType) { case PeripheralDeviceTypes.MultiMediaDevice: { sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _); if (!sense) { MmcConfiguration = confBuf; } var dvdDecrypt = new DVDDecryption(dev); sense = dvdDecrypt.ReadRpc(out byte[] cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, dev.Timeout, out _); if (!sense) { CSS_CPRM.RegionalPlaybackControlState?rpc = CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); if (rpc.HasValue) { RPC = rpc; } } // TODO: DVD drives respond correctly to BD status. // While specification says if no medium is present // it should inform all possible capabilities, // testing drives show only supported media capabilities. /* * byte[] strBuf; * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); * * if (!sense) * { * Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); * if (caps != null) * { * foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) * { * if (cap.SDS && cap.RDS) * AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.SDS) * AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.RDS) * AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); * } * } * } * * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); * * if (!sense) * { * Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); * if (caps != null) * { * foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) * { * if (cap.SDS && cap.RDS) * AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.SDS) * AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); * else if (cap.RDS) * AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); * } * } * } */ #region Plextor if (dev.Manufacturer == "PLEXTOR") { bool plxtSense = true; bool plxtDvd = false; byte[] plxtBuf = null; switch (dev.Model) { case "DVDR PX-708A": case "DVDR PX-708A2": case "DVDR PX-712A": plxtDvd = true; plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout, out _); break; case "DVDR PX-714A": case "DVDR PX-716A": case "DVDR PX-716AL": case "DVDR PX-755A": case "DVDR PX-760A": { plxtBuf = new byte[256 * 4]; for (byte i = 0; i < 4; i++) { plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall, out senseBuf, i, 256, dev.Timeout, out _); if (plxtSense) { break; } Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256); } plxtDvd = true; break; } default: { if (dev.Model.StartsWith("CD-R ", StringComparison.Ordinal)) { plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout, out _); } break; } } PlextorFeatures = new Plextor { IsDvd = plxtDvd }; if (!plxtSense) { PlextorFeatures.Eeprom = plxtBuf; if (plxtDvd) { PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120); PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122); PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126); PlextorFeatures.DvdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A); PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E); } else { PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078); PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C); PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A); } } plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled, out ushort plxtPwrRecSpeed, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.PoweRec = true; if (plxtPwrRecEnabled) { PlextorFeatures.PoweRecEnabled = true; PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed; plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected, out ushort plxtPwrRecMax, out ushort plxtPwrRecLast, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.PoweRecSelected = plxtPwrRecSelected; PlextorFeatures.PoweRecMax = plxtPwrRecMax; PlextorFeatures.PoweRecLast = plxtPwrRecLast; } } } // TODO: Check it with a drive plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { if (plxtBuf[0] == 1) { PlextorFeatures.SilentModeEnabled = true; PlextorFeatures.AccessTimeLimit = plxtBuf[1]; PlextorFeatures.CdReadSpeedLimit = plxtBuf[2]; PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3]; PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4]; // TODO: Check which one is each one /* * if(plxtBuf[6] > 0) * AaruConsole.WriteLine("\tTray eject speed limited to {0}", * -(plxtBuf[6] + 48)); * if(plxtBuf[7] > 0) * AaruConsole.WriteLine("\tTray eject speed limited to {0}", * plxtBuf[7] - 47); */ } } plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.GigaRec = true; } plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.SecuRec = true; } plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.SpeedRead = true; if ((plxtBuf[2] & 0x01) == 0x01) { PlextorFeatures.SpeedReadEnabled = true; } } plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.Hiding = true; if ((plxtBuf[2] & 0x02) == 0x02) { PlextorFeatures.HidesRecordables = true; } if ((plxtBuf[2] & 0x01) == 0x01) { PlextorFeatures.HidesSessions = true; } } plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.VariRec = true; } if (plxtDvd) { plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.VariRecDvd = true; } plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.BitSetting = true; } plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.BitSettingDl = true; } plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout, out _); if (!plxtSense) { PlextorFeatures.DvdPlusWriteTest = true; } } } #endregion Plextor if (ScsiInquiry.Value.KreonPresent) { if (!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout, out _)) { KreonFeatures = krFeatures; } } break; } case PeripheralDeviceTypes.SequentialAccess: { sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _); if (sense) { AaruConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf)); } else { BlockLimits = seqBuf; } sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _); if (sense) { AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}", Sense.PrettifySense(senseBuf)); } else { DensitySupport = seqBuf; DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); } sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); if (sense) { AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}", Sense.PrettifySense(senseBuf)); } else { MediumDensitySupport = seqBuf; MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); } break; } } break; } case DeviceType.MMC: { bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _); if (!sense) { CID = mmcBuf; } sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _); if (!sense) { CSD = mmcBuf; } sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _); if (!sense) { OCR = mmcBuf; } sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _); if (!sense && !ArrayHelpers.ArrayIsNullOrEmpty(mmcBuf)) { ExtendedCSD = mmcBuf; } } break; case DeviceType.SecureDigital: { bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _); if (!sense) { CID = sdBuf; } sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _); if (!sense) { CSD = sdBuf; } sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _); if (!sense) { OCR = sdBuf; } sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _); if (!sense) { SCR = sdBuf; } } break; default: AaruConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type); break; } }
/// <summary> /// Returns a byte array of the hash value. /// </summary> public byte[] Final() { hashInt ^= crc32Seed; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; return(BigEndianBitConverter.GetBytes(hashInt)); }
public bool Close() { if (!IsWriting) { ErrorMessage = "Image is not opened for writing"; return(false); } byte sessions = byte.MinValue; foreach (Track t in writingTracks) { if (t.TrackSession > byte.MinValue) { sessions = (byte)t.TrackSession; } } var header = new AlcoholHeader { signature = alcoholSignature, version = new byte[] { 1, 5 }, type = MediaTypeToAlcohol(imageInfo.MediaType), sessions = sessions, structuresOffset = (uint)(pfi == null ? 0 : 96), sessionOffset = (uint)(pfi == null ? 96 : 4196), unknown1 = new ushort[2], unknown2 = new uint[2], unknown3 = new uint[6], unknown4 = new uint[3] }; // Alcohol sets this always, Daemon Tool expects this header.unknown1[0] = 2; alcSessions = new Dictionary <int, AlcoholSession>(); alcTracks = new Dictionary <int, AlcoholTrack>(); alcToc = new Dictionary <int, Dictionary <int, AlcoholTrack> >(); writingTracks = writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).ToList(); alcTrackExtras = new Dictionary <int, AlcoholTrackExtra>(); long currentTrackOffset = header.sessionOffset + (Marshal.SizeOf <AlcoholSession>() * sessions); byte[] tmpToc = null; if (fullToc != null) { byte[] fullTocSize = BigEndianBitConverter.GetBytes((short)fullToc.Length); tmpToc = new byte[fullToc.Length + 2]; Array.Copy(fullToc, 0, tmpToc, 2, fullToc.Length); tmpToc[0] = fullTocSize[0]; tmpToc[1] = fullTocSize[1]; } FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(tmpToc); long currentExtraOffset = currentTrackOffset; int extraCount = 0; for (int i = 1; i <= sessions; i++) { if (decodedToc.HasValue) { extraCount += decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i); currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i); } else { currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 3; extraCount += 3; currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * writingTracks.Count(t => t.TrackSession == i); extraCount += writingTracks.Count(t => t.TrackSession == i); if (i < sessions) { currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 2; extraCount += 2; } } } long footerOffset = currentExtraOffset + (Marshal.SizeOf <AlcoholTrackExtra>() * extraCount); if (bca != null) { header.bcaOffset = (uint)footerOffset; footerOffset += bca.Length; } if (isDvd) { alcSessions.Add(1, new AlcoholSession { sessionEnd = (int)((writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector) + 1), sessionSequence = 1, allBlocks = 1, nonTrackBlocks = 3, firstTrack = 1, lastTrack = 1, trackOffset = 4220 }); footerOffset = 4300; if (bca != null) { footerOffset += bca.Length; } alcTracks.Add(1, new AlcoholTrack { mode = AlcoholTrackMode.DVD, adrCtl = 20, point = 1, extraOffset = (uint)((writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector) + 1), sectorSize = 2048, files = 1, footerOffset = (uint)footerOffset, unknown = new byte[18], unknown2 = new byte[24] }); alcToc.Add(1, alcTracks); } else { for (int i = 1; i <= sessions; i++) { Track firstTrack = writingTracks.First(t => t.TrackSession == i); Track lastTrack = writingTracks.Last(t => t.TrackSession == i); alcSessions.Add(i, new AlcoholSession { sessionStart = (int)firstTrack.TrackStartSector - 150, sessionEnd = (int)lastTrack.TrackEndSector + 1, sessionSequence = (ushort)i, allBlocks = (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i) ?? writingTracks.Count(t => t.TrackSession == i) + 3), nonTrackBlocks = (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i && t.POINT >= 0xA0 && t.POINT <= 0xAF) ?? 3), firstTrack = (ushort)firstTrack.TrackSequence, lastTrack = (ushort)lastTrack.TrackSequence, trackOffset = (uint)currentTrackOffset }); Dictionary <int, AlcoholTrack> thisSessionTracks = new Dictionary <int, AlcoholTrack>(); trackFlags.TryGetValue((byte)firstTrack.TrackSequence, out byte firstTrackControl); trackFlags.TryGetValue((byte)lastTrack.TrackSequence, out byte lastTrackControl); if (firstTrackControl == 0 && firstTrack.TrackType != TrackType.Audio) { firstTrackControl = (byte)CdFlags.DataTrack; } if (lastTrackControl == 0 && lastTrack.TrackType != TrackType.Audio) { lastTrackControl = (byte)CdFlags.DataTrack; } (byte minute, byte second, byte frame)leadinPmsf = LbaToMsf(lastTrack.TrackEndSector + 1); if (decodedToc.HasValue && decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xA0 && t.POINT <= 0xAF)) { foreach (FullTOC.TrackDataDescriptor tocTrk in decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xA0 && t.POINT <= 0xAF)) { thisSessionTracks.Add(tocTrk.POINT, new AlcoholTrack { adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL), tno = tocTrk.TNO, point = tocTrk.POINT, min = tocTrk.Min, sec = tocTrk.Sec, frame = tocTrk.Frame, zero = tocTrk.Zero, pmin = tocTrk.PMIN, psec = tocTrk.PSEC, pframe = tocTrk.PFRAME, mode = AlcoholTrackMode.NoData, unknown = new byte[18], unknown2 = new byte[24], extraOffset = (uint)currentExtraOffset }); currentTrackOffset += Marshal.SizeOf <AlcoholTrack>(); currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>(); } } else { thisSessionTracks.Add(0xA0, new AlcoholTrack { adrCtl = (byte)((1 << 4) + firstTrackControl), pmin = (byte)firstTrack.TrackSequence, mode = AlcoholTrackMode.NoData, point = 0xA0, unknown = new byte[18], unknown2 = new byte[24], psec = (byte)(imageInfo.MediaType == MediaType.CDI ? 0x10 : writingTracks.Any(t => t.TrackType == TrackType. CdMode2Form1 || t.TrackType == TrackType. CdMode2Form2 || t.TrackType == TrackType. CdMode2Formless) ? 0x20 : 0), extraOffset = (uint)currentExtraOffset }); thisSessionTracks.Add(0xA1, new AlcoholTrack { adrCtl = (byte)((1 << 4) + lastTrackControl), pmin = (byte)lastTrack.TrackSequence, mode = AlcoholTrackMode.NoData, point = 0xA1, unknown = new byte[18], unknown2 = new byte[24], extraOffset = (uint)currentExtraOffset }); thisSessionTracks.Add(0xA2, new AlcoholTrack { adrCtl = (byte)((1 << 4) + firstTrackControl), zero = 0, pmin = leadinPmsf.minute, psec = leadinPmsf.second, pframe = leadinPmsf.frame, mode = AlcoholTrackMode.NoData, point = 0xA2, unknown = new byte[18], unknown2 = new byte[24], extraOffset = (uint)currentExtraOffset }); currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>() * 3; currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 3; } foreach (Track track in writingTracks.Where(t => t.TrackSession == i).OrderBy(t => t.TrackSequence)) { var alcTrk = new AlcoholTrack(); if (decodedToc.HasValue && decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT == track.TrackSequence)) { FullTOC.TrackDataDescriptor tocTrk = decodedToc.Value.TrackDescriptors.First(t => t.SessionNumber == i && t.POINT == track.TrackSequence); alcTrk.adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL); alcTrk.tno = tocTrk.TNO; alcTrk.point = tocTrk.POINT; alcTrk.min = tocTrk.Min; alcTrk.sec = tocTrk.Sec; alcTrk.frame = tocTrk.Frame; alcTrk.zero = tocTrk.Zero; alcTrk.pmin = tocTrk.PMIN; alcTrk.psec = tocTrk.PSEC; alcTrk.pframe = tocTrk.PFRAME; } else { (byte minute, byte second, byte frame)msf = LbaToMsf(track.TrackStartSector); trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl); if (trackControl == 0 && track.TrackType != TrackType.Audio) { trackControl = (byte)CdFlags.DataTrack; } alcTrk.adrCtl = (byte)((1 << 4) + trackControl); alcTrk.point = (byte)track.TrackSequence; alcTrk.zero = 0; alcTrk.pmin = msf.minute; alcTrk.psec = msf.second; alcTrk.pframe = msf.frame; } alcTrk.mode = TrackTypeToAlcohol(track.TrackType); alcTrk.subMode = track.TrackSubchannelType != TrackSubchannelType.None ? AlcoholSubchannelMode.Interleaved : AlcoholSubchannelMode.None; alcTrk.sectorSize = (ushort)(track.TrackRawBytesPerSector + (track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0)); alcTrk.startLba = (uint)(track.TrackStartSector + track.TrackPregap); alcTrk.startOffset = track.TrackFileOffset + (alcTrk.sectorSize * track.TrackPregap); alcTrk.files = 1; alcTrk.extraOffset = (uint)currentExtraOffset; alcTrk.footerOffset = (uint)footerOffset; // Alcohol seems to set that for all CD tracks // Daemon Tools expect it to be like this alcTrk.unknown = new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; alcTrk.unknown2 = new byte[24]; thisSessionTracks.Add((int)track.TrackSequence, alcTrk); currentTrackOffset += Marshal.SizeOf <AlcoholTrack>(); currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>(); var trkExtra = new AlcoholTrackExtra { sectors = (uint)((track.TrackEndSector - track.TrackStartSector) + 1) }; if (track.TrackSequence == firstTrack.TrackSequence) { trkExtra.pregap = 150; trkExtra.sectors += 150; } // When track mode changes there's a mandatory gap, Alcohol needs it else if (thisSessionTracks.TryGetValue((int)(track.TrackSequence - 1), out AlcoholTrack previousTrack) && alcTrackExtras.TryGetValue((int)(track.TrackSequence - 1), out AlcoholTrackExtra previousExtra) && previousTrack.mode != alcTrk.mode) { previousExtra.sectors -= 150; trkExtra.pregap = 150; alcTrackExtras.Remove((int)(track.TrackSequence - 1)); alcTrackExtras.Add((int)(track.TrackSequence - 1), previousExtra); } else { trkExtra.pregap = 0; } if (track.TrackSequence == lastTrack.TrackSequence) { trkExtra.sectors -= 150; } alcTrackExtras.Add((int)track.TrackSequence, trkExtra); } if (decodedToc.HasValue && decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xB0)) { foreach (FullTOC.TrackDataDescriptor tocTrk in decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xB0)) { thisSessionTracks.Add(tocTrk.POINT, new AlcoholTrack { adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL), tno = tocTrk.TNO, point = tocTrk.POINT, min = tocTrk.Min, sec = tocTrk.Sec, frame = tocTrk.Frame, zero = tocTrk.Zero, pmin = tocTrk.PMIN, psec = tocTrk.PSEC, pframe = tocTrk.PFRAME, mode = AlcoholTrackMode.NoData, unknown = new byte[18], unknown2 = new byte[24], extraOffset = (uint)currentExtraOffset }); currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>(); currentTrackOffset += Marshal.SizeOf <AlcoholTrack>(); } } else if (i < sessions) { (byte minute, byte second, byte frame)leadoutAmsf = LbaToMsf(writingTracks.First(t => t.TrackSession == i + 1).TrackStartSector - 150); (byte minute, byte second, byte frame)leadoutPmsf = LbaToMsf(writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last(). TrackStartSector); thisSessionTracks.Add(0xB0, new AlcoholTrack { point = 0xB0, adrCtl = 0x50, zero = 0, min = leadoutAmsf.minute, sec = leadoutAmsf.second, frame = leadoutAmsf.frame, pmin = leadoutPmsf.minute, psec = leadoutPmsf.second, pframe = leadoutPmsf.frame, unknown = new byte[18], unknown2 = new byte[24] }); thisSessionTracks.Add(0xC0, new AlcoholTrack { point = 0xC0, adrCtl = 0x50, min = 128, pmin = 97, psec = 25, unknown = new byte[18], unknown2 = new byte[24] }); currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 2; } alcToc.Add(i, thisSessionTracks); } } alcFooter = new AlcoholFooter { filenameOffset = (uint)(footerOffset + Marshal.SizeOf <AlcoholFooter>()), widechar = 1 }; byte[] filename = Encoding.Unicode.GetBytes("*.mdf"); // Yup, Alcohol stores no filename but a wildcard. IntPtr blockPtr; // Write header descriptorStream.Seek(0, SeekOrigin.Begin); byte[] block = new byte[Marshal.SizeOf <AlcoholHeader>()]; blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholHeader>()); System.Runtime.InteropServices.Marshal.StructureToPtr(header, blockPtr, true); System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr); descriptorStream.Write(block, 0, block.Length); // Write DVD structures if pressent if (header.structuresOffset != 0) { if (dmi != null) { descriptorStream.Seek(header.structuresOffset, SeekOrigin.Begin); if (dmi.Length == 2052) { descriptorStream.Write(dmi, 0, 2052); } else if (dmi.Length == 2048) { descriptorStream.Write(new byte[] { 0x08, 0x02, 0x00, 0x00 }, 0, 4); descriptorStream.Write(dmi, 0, 2048); } } // TODO: Create fake PFI if none present if (pfi != null) { descriptorStream.Seek(header.structuresOffset + 2052, SeekOrigin.Begin); descriptorStream.Write(pfi, pfi.Length - 2048, 2048); } } // Write sessions descriptorStream.Seek(header.sessionOffset, SeekOrigin.Begin); foreach (AlcoholSession session in alcSessions.Values) { block = new byte[Marshal.SizeOf <AlcoholSession>()]; blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholSession>()); System.Runtime.InteropServices.Marshal.StructureToPtr(session, blockPtr, true); System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr); descriptorStream.Write(block, 0, block.Length); } // Write tracks foreach (KeyValuePair <int, Dictionary <int, AlcoholTrack> > kvp in alcToc) { descriptorStream.Seek(alcSessions.First(t => t.Key == kvp.Key).Value.trackOffset, SeekOrigin.Begin); foreach (AlcoholTrack track in kvp.Value.Values) { AlcoholTrack alcoholTrack = track; // Write extra if (!isDvd) { long position = descriptorStream.Position; descriptorStream.Seek(alcoholTrack.extraOffset, SeekOrigin.Begin); block = new byte[Marshal.SizeOf <AlcoholTrackExtra>()]; if (alcTrackExtras.TryGetValue(alcoholTrack.point, out AlcoholTrackExtra extra)) { blockPtr = System.Runtime.InteropServices.Marshal. AllocHGlobal(Marshal.SizeOf <AlcoholTrackExtra>()); System.Runtime.InteropServices.Marshal.StructureToPtr(extra, blockPtr, true); System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr); } else { alcoholTrack.extraOffset = 0; } descriptorStream.Write(block, 0, block.Length); descriptorStream.Seek(position, SeekOrigin.Begin); } block = new byte[Marshal.SizeOf <AlcoholTrack>()]; blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholTrack>()); System.Runtime.InteropServices.Marshal.StructureToPtr(alcoholTrack, blockPtr, true); System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr); descriptorStream.Write(block, 0, block.Length); } } // Write BCA if (bca != null) { descriptorStream.Seek(header.bcaOffset, SeekOrigin.Begin); descriptorStream.Write(bca, 0, bca.Length); } // Write footer descriptorStream.Seek(footerOffset, SeekOrigin.Begin); block = new byte[Marshal.SizeOf <AlcoholFooter>()]; blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholFooter>()); System.Runtime.InteropServices.Marshal.StructureToPtr(alcFooter, blockPtr, true); System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length); System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr); descriptorStream.Write(block, 0, block.Length); // Write filename descriptorStream.Write(filename, 0, filename.Length); // Write filename null termination descriptorStream.Write(new byte[] { 0, 0 }, 0, 2); descriptorStream.Flush(); descriptorStream.Close(); imageStream.Flush(); imageStream.Close(); IsWriting = false; ErrorMessage = ""; return(true); }
public static void BytesToUInt32Test(uint expected, byte[] n) { Assert.Equal(expected, BigEndianBitConverter.ToUInt32(n)); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); StringBuilder sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = null; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bootBlk = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(bootBlockSectors); bootBlk.bootCode = new byte[bootBlockSectors.Length - 12]; Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length); bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0; uint bsum = AmigaBootChecksum(bootBlockSectors); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bootBlk.checksum) { bRootPtr = bootBlk.root_ptr + partition.Start; DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start - 2, (partition.End - partition.Start + 1) / 2 + partition.Start - 1, (partition.End - partition.Start + 1) / 2 + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start + 4 }; RootBlock rootBlk = new RootBlock(); byte[] rootBlockSector = null; bool rootFound = false; uint blockSize = 0; // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); rootBlockSector = imagePlugin.ReadSector(rootPtr); rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); if (rootBlk.type != TYPE_HEADER) { continue; } rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); blockSize = (rootBlk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % rootBlockSector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); // Clear checksum on sector rootBlk.checksum = BigEndianBitConverter.ToUInt32(rootBlockSector, 20); rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0; uint rsum = AmigaChecksum(rootBlockSector); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type); if (rootBlk.sec_type != SUBTYPE_ROOT || rootBlk.checksum != rsum) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); rootFound = true; break; } if (!rootFound) { return; } rootBlk = MarshalRootBlock(rootBlockSector); string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); switch (bootBlk.diskType & 0xFF) { case 0: sbInformation.Append("Amiga Original File System"); XmlFsType.Type = "Amiga OFS"; break; case 1: sbInformation.Append("Amiga Fast File System"); XmlFsType.Type = "Amiga FFS"; break; case 2: sbInformation.Append("Amiga Original File System with international characters"); XmlFsType.Type = "Amiga OFS"; break; case 3: sbInformation.Append("Amiga Fast File System with international characters"); XmlFsType.Type = "Amiga FFS"; break; case 4: sbInformation.Append("Amiga Original File System with directory cache"); XmlFsType.Type = "Amiga OFS"; break; case 5: sbInformation.Append("Amiga Fast File System with directory cache"); XmlFsType.Type = "Amiga FFS"; break; case 6: sbInformation.Append("Amiga Original File System with long filenames"); XmlFsType.Type = "Amiga OFS2"; break; case 7: sbInformation.Append("Amiga Fast File System with long filenames"); XmlFsType.Type = "Amiga FFS2"; break; } if ((bootBlk.diskType & 0x6D754600) == 0x6D754600) { sbInformation.Append(", with multi-user patches"); } sbInformation.AppendLine(); sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); if (bootBlk.checksum == bsum) { Sha1Context sha1Ctx = new Sha1Context(); sha1Ctx.Update(bootBlk.bootCode); sbInformation.AppendLine("Volume is bootable"); sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine(); } if (rootBlk.bitmapFlag == 0xFFFFFFFF) { sbInformation.AppendLine("Volume bitmap is valid"); } if (rootBlk.bitmapExtensionBlock != 0x00000000 && rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) { sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); } if ((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) { sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); } long blocks = (long)((partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / blockSize); sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine(); sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)) .AppendLine(); sbInformation.AppendFormat("Volume last modified on {0}", DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)) .AppendLine(); sbInformation.AppendFormat("Volume root directory last modified on on {0}", DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)) .AppendLine(); sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); information = sbInformation.ToString(); XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); XmlFsType.CreationDateSpecified = true; XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); XmlFsType.ModificationDateSpecified = true; XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; XmlFsType.Clusters = blocks; XmlFsType.ClusterSize = (int)blockSize; XmlFsType.VolumeName = diskName; XmlFsType.Bootable = bsum == bootBlk.checksum; // Useful as a serial XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; }
public void ToXXX_ThrowsOnNull() { var sut = new BigEndianBitConverter(); Check.ThatCode(() => sut.ToInt32(null, 0)).Throws <ArgumentNullException>(); }
/// <summary>Gets the hash of the specified data buffer.</summary> /// <param name="data">Data buffer.</param> /// <param name="len">Length of the data buffer to hash.</param> /// <param name="hash">Byte array of the hash value.</param> /// <param name="polynomial">CRC polynomial</param> /// <param name="seed">CRC seed</param> /// <param name="table">CRC lookup table</param> /// <param name="inverse">Is CRC inverted?</param> public static string Data(byte[] data, uint len, out byte[] hash, ushort polynomial, ushort seed, ushort[][] table, bool inverse) { bool useNative = Native.IsSupported; bool useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY && seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse; bool useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED && !inverse; IntPtr nativeContext = IntPtr.Zero; ushort localHashInt = seed; switch (useNative) { case true when useCcitt: nativeContext = crc16_ccitt_init(); useNative = nativeContext != IntPtr.Zero; break; case true when useIbm: nativeContext = crc16_init(); useNative = nativeContext != IntPtr.Zero; break; } ushort[][] localTable = table ?? GenerateTable(polynomial, inverse); switch (useNative) { case true when useCcitt: crc16_ccitt_update(nativeContext, data, len); break; case true when useIbm: crc16_update(nativeContext, data, len); break; default: { if (inverse) { StepInverse(ref localHashInt, localTable, data, len); } else { Step(ref localHashInt, localTable, data, len); } break; } } localHashInt ^= seed; switch (useNative) { case true when useCcitt: crc16_ccitt_final(nativeContext, ref localHashInt); crc16_ccitt_free(nativeContext); break; case true when useIbm: crc16_final(nativeContext, ref localHashInt); crc16_free(nativeContext); break; default: { if (inverse) { localHashInt = (ushort)~localHashInt; } break; } } hash = BigEndianBitConverter.GetBytes(localHashInt); var crc16Output = new StringBuilder(); foreach (byte h in hash) { crc16Output.Append(h.ToString("x2")); } return(crc16Output.ToString()); }