public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); byte[] header = new byte[2 + (2 * 82)]; stream.Read(header, 0, 2 + (2 * 82)); FileHeader fheader = Marshal.ByteArrayToStructureLittleEndian <FileHeader>(header); AaruConsole.DebugWriteLine("HDCP plugin", "Detected HD-Copy image with {0} tracks and {1} sectors per track.", fheader.lastCylinder + 1, fheader.sectorsPerTrack); _imageInfo.Cylinders = (uint)fheader.lastCylinder + 1; _imageInfo.SectorsPerTrack = fheader.sectorsPerTrack; _imageInfo.SectorSize = 512; // only 512 bytes per sector supported _imageInfo.Heads = 2; // only 2-sided floppies are supported _imageInfo.Sectors = 2 * _imageInfo.Cylinders * _imageInfo.SectorsPerTrack; _imageInfo.ImageSize = _imageInfo.Sectors * _imageInfo.SectorSize; _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, 2, (ushort)_imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM, false)); // the start offset of the track data long currentOffset = 2 + (2 * 82); // build table of track offsets for (int i = 0; i < _imageInfo.Cylinders * 2; i++) { if (fheader.trackMap[i] == 0) { _trackOffset[i] = -1; } else { // track is present, read the block header if (currentOffset + 3 >= stream.Length) { return(false); } byte[] blkHeader = new byte[2]; stream.Read(blkHeader, 0, 2); short blkLength = BitConverter.ToInt16(blkHeader, 0); // assume block sizes are positive if (blkLength < 0) { return(false); } AaruConsole.DebugWriteLine("HDCP plugin", "Track {0} offset 0x{1:x8}, size={2:x4}", i, currentOffset, blkLength); _trackOffset[i] = currentOffset; currentOffset += 2 + blkLength; // skip the block data stream.Seek(blkLength, SeekOrigin.Current); } } // ensure that the last track is present completely if (currentOffset > stream.Length) { return(false); } // save some variables for later use _fileHeader = fheader; _hdcpImageFilter = imageFilter; return(true); }
public Errno Stat(string path, out FileEntryInfo stat) { stat = null; if (!_mounted) { return(Errno.AccessDenied); } Errno err = GetFileEntry(path, out DecodedDirectoryEntry entry); if (err != Errno.NoError) { return(err); } stat = new FileEntryInfo { Attributes = new FileAttributes(), Blocks = (long)(entry.Size / 2048), // TODO: XA BlockSize = 2048, Length = (long)entry.Size, Links = 1, LastWriteTimeUtc = entry.Timestamp }; if (entry.Extents?.Count > 0) { stat.Inode = entry.Extents[0].extent; } if (entry.Size % 2048 > 0) { stat.Blocks++; } if (entry.Flags.HasFlag(FileFlags.Directory)) { stat.Attributes |= FileAttributes.Directory; } if (entry.Flags.HasFlag(FileFlags.Hidden)) { stat.Attributes |= FileAttributes.Hidden; } //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias) == true) // stat.Attributes |= FileAttributes.Alias; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible) == true) // stat.Attributes |= FileAttributes.Hidden; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited) == true) // stat.Attributes |= FileAttributes.HasBeenInited; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon) == true) // stat.Attributes |= FileAttributes.HasCustomIcon; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs) == true) // stat.Attributes |= FileAttributes.HasNoINITs; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk) == true) // stat.Attributes |= FileAttributes.IsOnDesk; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared) == true) // stat.Attributes |= FileAttributes.Shared; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery) == true) // stat.Attributes |= FileAttributes.Stationery; //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle) == true) // stat.Attributes |= FileAttributes.Bundle; //if(entry.AppleIcon != null) // stat.Attributes |= FileAttributes.HasCustomIcon; if (entry.XA != null) { if (entry.XA.Value.attributes.HasFlag(XaAttributes.GroupExecute)) { stat.Mode |= 8; } if (entry.XA.Value.attributes.HasFlag(XaAttributes.GroupRead)) { stat.Mode |= 32; } if (entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerExecute)) { stat.Mode |= 64; } if (entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerRead)) { stat.Mode |= 256; } if (entry.XA.Value.attributes.HasFlag(XaAttributes.SystemExecute)) { stat.Mode |= 1; } if (entry.XA.Value.attributes.HasFlag(XaAttributes.SystemRead)) { stat.Mode |= 4; } stat.UID = entry.XA.Value.user; stat.GID = entry.XA.Value.group; stat.Inode = entry.XA.Value.filenumber; } if (entry.PosixAttributes != null) { stat.Mode = (uint?)entry.PosixAttributes.Value.st_mode & 0x0FFF; if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Block)) { stat.Attributes |= FileAttributes.BlockDevice; } if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Character)) { stat.Attributes |= FileAttributes.CharDevice; } if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Pipe)) { stat.Attributes |= FileAttributes.Pipe; } if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Socket)) { stat.Attributes |= FileAttributes.Socket; } if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Symlink)) { stat.Attributes |= FileAttributes.Symlink; } stat.Links = entry.PosixAttributes.Value.st_nlink; stat.UID = entry.PosixAttributes.Value.st_uid; stat.GID = entry.PosixAttributes.Value.st_gid; stat.Inode = entry.PosixAttributes.Value.st_ino; } else if (entry.PosixAttributesOld != null) { stat.Mode = (uint?)entry.PosixAttributesOld.Value.st_mode & 0x0FFF; if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Block)) { stat.Attributes |= FileAttributes.BlockDevice; } if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Character)) { stat.Attributes |= FileAttributes.CharDevice; } if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Pipe)) { stat.Attributes |= FileAttributes.Pipe; } if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Socket)) { stat.Attributes |= FileAttributes.Socket; } if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Symlink)) { stat.Attributes |= FileAttributes.Symlink; } stat.Links = entry.PosixAttributesOld.Value.st_nlink; stat.UID = entry.PosixAttributesOld.Value.st_uid; stat.GID = entry.PosixAttributesOld.Value.st_gid; } //if(entry.AmigaProtection != null) //{ // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupExec)) // stat.Mode |= 8; // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupRead)) // stat.Mode |= 32; // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupWrite)) // stat.Mode |= 16; // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherExec)) // stat.Mode |= 1; // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherRead)) // stat.Mode |= 4; // if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherWrite)) // stat.Mode |= 2; // if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerExec)) // stat.Mode |= 64; // if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerRead)) // stat.Mode |= 256; // if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerWrite)) // stat.Mode |= 128; // if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.Archive)) // stat.Attributes |= FileAttributes.Archive; //} if (entry.PosixDeviceNumber != null) { stat.DeviceNo = ((ulong)entry.PosixDeviceNumber.Value.dev_t_high << 32) + entry.PosixDeviceNumber.Value.dev_t_low; } if (entry.RripModify != null) { stat.LastWriteTimeUtc = DecodeIsoDateTime(entry.RripModify); } if (entry.RripAccess != null) { stat.AccessTimeUtc = DecodeIsoDateTime(entry.RripAccess); } if (entry.RripAttributeChange != null) { stat.StatusChangeTimeUtc = DecodeIsoDateTime(entry.RripAttributeChange); } if (entry.RripBackup != null) { stat.BackupTimeUtc = DecodeIsoDateTime(entry.RripBackup); } if (entry.SymbolicLink != null) { stat.Attributes |= FileAttributes.Symlink; } if (entry.XattrLength == 0) { return(Errno.NoError); } //if(entry.CdiSystemArea != null) //{ // stat.UID = entry.CdiSystemArea.Value.owner; // stat.GID = entry.CdiSystemArea.Value.group; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupExecute)) // stat.Mode |= 8; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupRead)) // stat.Mode |= 32; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherExecute)) // stat.Mode |= 1; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherRead)) // stat.Mode |= 4; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerExecute)) // stat.Mode |= 64; // if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerRead)) // stat.Mode |= 256; //} byte[] ea = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent); ExtendedAttributeRecord ear = Marshal.ByteArrayToStructureLittleEndian <ExtendedAttributeRecord>(ea); stat.UID = ear.owner; stat.GID = ear.group; stat.Mode = 0; if (ear.permissions.HasFlag(Permissions.GroupExecute)) { stat.Mode |= 8; } if (ear.permissions.HasFlag(Permissions.GroupRead)) { stat.Mode |= 32; } if (ear.permissions.HasFlag(Permissions.OwnerExecute)) { stat.Mode |= 64; } if (ear.permissions.HasFlag(Permissions.OwnerRead)) { stat.Mode |= 256; } if (ear.permissions.HasFlag(Permissions.OtherExecute)) { stat.Mode |= 1; } if (ear.permissions.HasFlag(Permissions.OtherRead)) { stat.Mode |= 4; } stat.CreationTimeUtc = DateHandlers.Iso9660ToDateTime(ear.creation_date); stat.LastWriteTimeUtc = DateHandlers.Iso9660ToDateTime(ear.modification_date); return(Errno.NoError); }
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; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; 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 bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); byte[] hdr = new byte[40]; stream.Read(hdr, 0, 40); header = Marshal.ByteArrayToStructureLittleEndian <SaveDskFHeader>(hdr); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.magic = 0x{0:X4}", header.magic); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.mediaType = 0x{0:X2}", header.mediaType); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorSize = {0}", header.sectorSize); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clusterMask = {0}", header.clusterMask); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clusterShift = {0}", header.clusterShift); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.reservedSectors = {0}", header.reservedSectors); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.fatCopies = {0}", header.fatCopies); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.rootEntries = {0}", header.rootEntries); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.firstCluster = {0}", header.firstCluster); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clustersCopied = {0}", header.clustersCopied); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsPerFat = {0}", header.sectorsPerFat); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.checksum = 0x{0:X8}", header.checksum); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.cylinders = {0}", header.cylinders); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.heads = {0}", header.heads); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsPerTrack = {0}", header.sectorsPerTrack); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.padding = {0}", header.padding); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsCopied = {0}", header.sectorsCopied); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.commentOffset = {0}", header.commentOffset); AaruConsole.DebugWriteLine("SaveDskF plugin", "header.dataOffset = {0}", header.dataOffset); if (header.dataOffset == 0 && header.magic == SDF_MAGIC_OLD) { header.dataOffset = 512; } byte[] cmt = new byte[header.dataOffset - header.commentOffset]; stream.Seek(header.commentOffset, SeekOrigin.Begin); stream.Read(cmt, 0, cmt.Length); if (cmt.Length > 1) { imageInfo.Comments = StringHandlers.CToString(cmt, Encoding.GetEncoding("ibm437")); } calculatedChk = 0; stream.Seek(0, SeekOrigin.Begin); int b; do { b = stream.ReadByte(); if (b >= 0) { calculatedChk += (uint)b; } } while(b >= 0); AaruConsole.DebugWriteLine("SaveDskF plugin", "Calculated checksum = 0x{0:X8}, {1}", calculatedChk, calculatedChk == header.checksum); imageInfo.Application = "SaveDskF"; imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = imageFilter.GetFilename(); imageInfo.ImageSize = (ulong)(stream.Length - header.dataOffset); imageInfo.Sectors = (ulong)(header.sectorsPerTrack * header.heads * header.cylinders); imageInfo.SectorSize = header.sectorSize; imageInfo.MediaType = Geometry.GetMediaType((header.cylinders, (byte)header.heads, header.sectorsPerTrack, header.sectorSize, MediaEncoding.MFM, false)); imageInfo.XmlMediaType = XmlMediaType.BlockMedia; AaruConsole.VerboseWriteLine("SaveDskF image contains a disk of type {0}", imageInfo.MediaType); if (!string.IsNullOrEmpty(imageInfo.Comments)) { AaruConsole.VerboseWriteLine("SaveDskF comments: {0}", imageInfo.Comments); } // TODO: Support compressed images if (header.magic == SDF_MAGIC_COMPRESSED) { throw new FeatureSupportedButNotImplementedImageException("Compressed SaveDskF images are not supported."); } // SaveDskF only ommits ending clusters, leaving no gaps behind, so reading all data we have... stream.Seek(header.dataOffset, SeekOrigin.Begin); decodedDisk = new byte[imageInfo.Sectors * imageInfo.SectorSize]; stream.Read(decodedDisk, 0, (int)(stream.Length - header.dataOffset)); imageInfo.Cylinders = header.cylinders; imageInfo.Heads = header.heads; imageInfo.SectorsPerTrack = header.sectorsPerTrack; return(true); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); if (stream.Length < Marshal.SizeOf <Header>()) { return(false); } byte[] hdrB = new byte[Marshal.SizeOf <Header>()]; stream.Read(hdrB, 0, hdrB.Length); Header hdr = Marshal.ByteArrayToStructureLittleEndian <Header>(hdrB); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.addInfoLen = {0}", hdr.addInfoLen); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.cylinders = {0}", hdr.cylinders); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.dataOff = {0}", hdr.dataOff); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.descOff = {0}", hdr.descOff); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.flags = {0}", hdr.flags); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.heads = {0}", hdr.heads); stream.Seek(hdr.descOff, SeekOrigin.Begin); byte[] description = new byte[hdr.dataOff - hdr.descOff]; stream.Read(description, 0, description.Length); _imageInfo.Comments = StringHandlers.CToString(description); AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.description = \"{0}\"", _imageInfo.Comments); stream.Seek(0xE + hdr.addInfoLen, SeekOrigin.Begin); long spt = long.MaxValue; uint[][][] sectorsOff = new uint[hdr.cylinders][][]; _sectorsData = new byte[hdr.cylinders][][][]; _imageInfo.Cylinders = hdr.cylinders; _imageInfo.Heads = hdr.heads; // Read track descriptors for (ushort cyl = 0; cyl < hdr.cylinders; cyl++) { sectorsOff[cyl] = new uint[hdr.heads][]; _sectorsData[cyl] = new byte[hdr.heads][][]; for (ushort head = 0; head < hdr.heads; head++) { byte[] sctB = new byte[4]; stream.Read(sctB, 0, 4); stream.Seek(2, SeekOrigin.Current); byte sectors = (byte)stream.ReadByte(); uint trkOff = BitConverter.ToUInt32(sctB, 0); AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.c = {0}", cyl); AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.h = {0}", head); AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.sectors = {0}", sectors); AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.off = {0}", trkOff); sectorsOff[cyl][head] = new uint[sectors]; _sectorsData[cyl][head] = new byte[sectors][]; if (sectors < spt && sectors > 0) { spt = sectors; } for (ushort sec = 0; sec < sectors; sec++) { byte c = (byte)stream.ReadByte(); byte h = (byte)stream.ReadByte(); byte r = (byte)stream.ReadByte(); byte n = (byte)stream.ReadByte(); var f = (SectorFlags)stream.ReadByte(); byte[] offB = new byte[2]; stream.Read(offB, 0, 2); ushort secOff = BitConverter.ToUInt16(offB, 0); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.c = {0}", c); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.h = {0}", h); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.r = {0}", r); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.n = {0} ({1})", n, 128 << n); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.f = {0}", f); AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.off = {0} ({1})", secOff, secOff + trkOff + hdr.dataOff); // TODO: This assumes sequential sectors. sectorsOff[cyl][head][sec] = secOff + trkOff + hdr.dataOff; _sectorsData[cyl][head][sec] = new byte[128 << n]; if (128 << n > _imageInfo.SectorSize) { _imageInfo.SectorSize = (uint)(128 << n); } } } } // Read sectors for (ushort cyl = 0; cyl < hdr.cylinders; cyl++) { bool emptyCyl = false; for (ushort head = 0; head < hdr.heads; head++) { for (ushort sec = 0; sec < sectorsOff[cyl][head].Length; sec++) { stream.Seek(sectorsOff[cyl][head][sec], SeekOrigin.Begin); stream.Read(_sectorsData[cyl][head][sec], 0, _sectorsData[cyl][head][sec].Length); } // For empty cylinders if (sectorsOff[cyl][head].Length != 0) { continue; } if (cyl + 1 == hdr.cylinders || // Next cylinder is also empty sectorsOff[cyl + 1][head].Length == 0) { emptyCyl = true; } // Create empty sectors else { _sectorsData[cyl][head] = new byte[spt][]; for (int i = 0; i < spt; i++) { _sectorsData[cyl][head][i] = new byte[_imageInfo.SectorSize]; } } } if (emptyCyl) { _imageInfo.Cylinders--; } } // TODO: What about double sided, half track pitch compact floppies? _imageInfo.MediaType = MediaType.CompactFloppy; _imageInfo.ImageSize = (ulong)stream.Length - hdr.dataOff; _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.SectorsPerTrack = (uint)spt; _imageInfo.Sectors = _imageInfo.Cylinders * _imageInfo.Heads * _imageInfo.SectorsPerTrack; _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; return(true); }
/// <inheritdoc /> /// <summary>Lists contents from a directory.</summary> /// <param name="path">Directory path.</param> /// <param name="contents">Directory contents.</param> public Errno ReadDir(string path, out List <string> contents) { contents = null; if (!_mounted) { return(Errno.AccessDenied); } if (string.IsNullOrWhiteSpace(path) || path == "/") { contents = _rootDirectoryCache.Keys.ToList(); return(Errno.NoError); } string cutPath = path.StartsWith("/", StringComparison.Ordinal) ? path.Substring(1).ToLower(_cultureInfo) : path.ToLower(_cultureInfo); if (_directoryCache.TryGetValue(cutPath, out Dictionary <string, CompleteDirectoryEntry> currentDirectory)) { contents = currentDirectory.Keys.ToList(); return(Errno.NoError); } string[] pieces = cutPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); KeyValuePair <string, CompleteDirectoryEntry> entry = _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); if (string.IsNullOrEmpty(entry.Key)) { return(Errno.NoSuchFile); } if (!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory)) { return(Errno.NotDirectory); } string currentPath = pieces[0]; currentDirectory = _rootDirectoryCache; for (int p = 0; p < pieces.Length; p++) { entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); if (string.IsNullOrEmpty(entry.Key)) { return(Errno.NoSuchFile); } if (!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory)) { return(Errno.NotDirectory); } currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; uint currentCluster = entry.Value.Dirent.start_cluster; if (_fat32) { currentCluster += (uint)(entry.Value.Dirent.ea_handle << 16); } if (_directoryCache.TryGetValue(currentPath, out currentDirectory)) { continue; } // Reserved unallocated directory, seen in Atari ST if (currentCluster == 0) { _directoryCache[currentPath] = new Dictionary <string, CompleteDirectoryEntry>(); contents = new List <string>(); return(Errno.NoError); } uint[] clusters = GetClusters(currentCluster); if (clusters is null) { return(Errno.InvalidArgument); } byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; for (int i = 0; i < clusters.Length; i++) { byte[] buffer = _image.ReadSectors(_firstClusterSector + (clusters[i] * _sectorsPerCluster), _sectorsPerCluster); Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); } currentDirectory = new Dictionary <string, CompleteDirectoryEntry>(); byte[] lastLfnName = null; byte lastLfnChecksum = 0; for (int pos = 0; pos < directoryBuffer.Length; pos += Marshal.SizeOf <DirectoryEntry>()) { DirectoryEntry dirent = Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(directoryBuffer, pos, Marshal.SizeOf <DirectoryEntry>()); if (dirent.filename[0] == DIRENT_FINISHED) { break; } if (dirent.attributes.HasFlag(FatAttributes.LFN)) { if (_namespace != Namespace.Lfn && _namespace != Namespace.Ecs) { continue; } LfnEntry lfnEntry = Marshal.ByteArrayToStructureLittleEndian <LfnEntry>(directoryBuffer, pos, Marshal.SizeOf <LfnEntry>()); int lfnSequence = lfnEntry.sequence & LFN_MASK; if ((lfnEntry.sequence & LFN_ERASED) > 0) { continue; } if ((lfnEntry.sequence & LFN_LAST) > 0) { lastLfnName = new byte[lfnSequence * 26]; lastLfnChecksum = lfnEntry.checksum; } if (lastLfnName is null) { continue; } if (lfnEntry.checksum != lastLfnChecksum) { continue; } lfnSequence--; Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); continue; } // Not a correct entry if (dirent.filename[0] < DIRENT_MIN && dirent.filename[0] != DIRENT_E5) { continue; } // Self if (Encoding.GetString(dirent.filename).TrimEnd() == ".") { continue; } // Parent if (Encoding.GetString(dirent.filename).TrimEnd() == "..") { continue; } // Deleted if (dirent.filename[0] == DIRENT_DELETED) { continue; } string filename; if (dirent.attributes.HasFlag(FatAttributes.VolumeLabel)) { continue; } var completeEntry = new CompleteDirectoryEntry { Dirent = dirent }; if ((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && lastLfnName != null) { byte calculatedLfnChecksum = LfnChecksum(dirent.filename, dirent.extension); if (calculatedLfnChecksum == lastLfnChecksum) { filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); completeEntry.Lfn = filename; lastLfnName = null; lastLfnChecksum = 0; } } if (dirent.filename[0] == DIRENT_E5) { dirent.filename[0] = DIRENT_DELETED; } string name = Encoding.GetString(dirent.filename).TrimEnd(); string extension = Encoding.GetString(dirent.extension).TrimEnd(); if (name == "" && extension == "") { AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in {0}", path); if (!_debug || (dirent.size > 0 && dirent.start_cluster == 0)) { continue; // Skip invalid name } // If debug, add it name = ":{EMPTYNAME}:"; // Try to create a unique filename with an extension from 000 to 999 for (int uniq = 0; uniq < 1000; uniq++) { extension = $"{uniq:D03}"; if (!currentDirectory.ContainsKey($"{name}.{extension}")) { break; } } // If we couldn't find it, just skip over if (currentDirectory.ContainsKey($"{name}.{extension}")) { continue; } } if (_namespace == Namespace.Nt) { if (dirent.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) { extension = extension.ToLower(CultureInfo.CurrentCulture); } if (dirent.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) { name = name.ToLower(CultureInfo.CurrentCulture); } } if (extension != "") { filename = name + "." + extension; } else { filename = name; } if (_namespace == Namespace.Human) { HumanDirectoryEntry humanEntry = Marshal.ByteArrayToStructureLittleEndian <HumanDirectoryEntry>(directoryBuffer, pos, Marshal.SizeOf <HumanDirectoryEntry>()); completeEntry.HumanDirent = humanEntry; name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); if (extension != "") { filename = name + name2 + "." + extension; } else { filename = name + name2; } completeEntry.HumanName = filename; } // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems filename = filename.Replace('/', '\u2215'); // Using array accessor ensures that repeated entries just get substituted. // Repeated entries are not allowed but some bad implementations (e.g. FAT32.IFS)allow to create them // when using spaces completeEntry.Shortname = filename; currentDirectory[completeEntry.ToString()] = completeEntry; } // Check OS/2 .LONGNAME if (_eaCache != null && (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && !_fat32) { List <KeyValuePair <string, CompleteDirectoryEntry> > filesWithEas = currentDirectory.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); foreach (KeyValuePair <string, CompleteDirectoryEntry> fileWithEa in filesWithEas) { Dictionary <string, byte[]> eas = GetEas(fileWithEa.Value.Dirent.ea_handle); if (eas is null) { continue; } if (!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa))
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.ASCII; information = ""; StringBuilder isoMetadata = new StringBuilder(); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" string bootSpec = ""; PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; ElToritoBootRecord?torito = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return; } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return; } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (highSierraInfo) { hsOff = 8; } bool cdiInfo = false; bool evd = false; bool vpd = false; while (true) { DicConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor DicConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. DicConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return; } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return; } break; } cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { bvd = Marshal.ByteArrayToStructureLittleEndian <BootRecord>(vdSector, hsOff, 2048 - hsOff); bootSpec = "Unknown"; if (Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION") { bootSpec = "El Torito"; torito = Marshal.ByteArrayToStructureLittleEndian <ElToritoBootRecord>(vdSector, hsOff, 2048 - hsOff); } break; } case 1: { if (highSierraInfo) { hsvd = Marshal .ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (cdiInfo) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { DicConsole.WriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } } else { evd = true; } break; } case 3: { vpd = true; break; } } counter++; } DecodedVolumeDescriptor decodedVd; DecodedVolumeDescriptor decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { information = "ERROR: Could not find primary volume descriptor"; return; } if (highSierraInfo) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (cdiInfo) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } uint rootLocation = 0; uint rootSize = 0; // No need to read root on CD-i, as extensions are not supported... if (!cdiInfo) { rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; if (highSierraInfo) { rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) { rootSize++; } } else { rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) { rootSize++; } } } byte[] rootDir = new byte[0]; int rootOff = 0; bool xaExtensions = false; bool apple = false; bool susp = false; bool rrip = false; bool ziso = false; bool amiga = false; bool aaip = false; List <ContinuationArea> contareas = new List <ContinuationArea>(); List <byte[]> refareas = new List <byte[]>(); StringBuilder suspInformation = new StringBuilder(); if (rootLocation + rootSize < imagePlugin.Info.Sectors) { rootDir = imagePlugin.ReadSectors(rootLocation, rootSize); } // Walk thru root directory to see system area extensions in use while (rootOff + Marshal.SizeOf <DirectoryRecord>() < rootDir.Length && !cdiInfo) { DirectoryRecord record = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(rootDir, rootOff, Marshal.SizeOf <DirectoryRecord>()); int saOff = Marshal.SizeOf <DirectoryRecord>() + record.name_len; saOff += saOff % 2; int saLen = record.length - saOff; if (saLen > 0 && rootOff + saOff + saLen <= rootDir.Length) { byte[] sa = new byte[saLen]; Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); saOff = 0; while (saOff < saLen) { bool noneFound = true; if (Marshal.SizeOf <CdromXa>() + saOff <= saLen) { CdromXa xa = Marshal.ByteArrayToStructureBigEndian <CdromXa>(sa); if (xa.signature == XA_MAGIC) { xaExtensions = true; saOff += Marshal.SizeOf <CdromXa>(); noneFound = false; } } if (saOff + 2 >= saLen) { break; } ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { // Easy, contains size field case APPLE_MAGIC: apple = true; saOff += sa[saOff + 2]; noneFound = false; break; // Not easy, contains size field case APPLE_MAGIC_OLD: apple = true; AppleOldId appleId = (AppleOldId)sa[saOff + 2]; noneFound = false; switch (appleId) { case AppleOldId.ProDOS: saOff += Marshal.SizeOf <AppleProDOSOldSystemUse>(); break; case AppleOldId.TypeCreator: case AppleOldId.TypeCreatorBundle: saOff += Marshal.SizeOf <AppleHFSTypeCreatorSystemUse>(); break; case AppleOldId.TypeCreatorIcon: case AppleOldId.TypeCreatorIconBundle: saOff += Marshal.SizeOf <AppleHFSIconSystemUse>(); break; case AppleOldId.HFS: saOff += Marshal.SizeOf <AppleHFSOldSystemUse>(); break; } break; // IEEE-P1281 aka SUSP 1.12 case SUSP_INDICATOR: susp = true; saOff += sa[saOff + 2]; noneFound = false; while (saOff + 2 < saLen) { nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { case APPLE_MAGIC: if (sa[saOff + 3] == 1 && sa[saOff + 2] == 7) { apple = true; } else { apple |= sa[saOff + 3] != 1; } break; case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen: byte[] ce = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, ce, 0, ce.Length); ContinuationArea ca = Marshal.ByteArrayToStructureBigEndian <ContinuationArea>(ce); contareas.Add(ca); break; case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: byte[] er = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || nextSignature == AAIP_MAGIC_OLD && sa[saOff + 3] == 1 && sa[saOff + 2] >= 9; saOff += sa[saOff + 2]; if (nextSignature == SUSP_TERMINATOR) { break; } } break; } if (noneFound) { break; } } } rootOff += record.length; if (record.length == 0) { break; } } foreach (ContinuationArea ca in contareas) { uint caLen = (ca.ca_length_be + ca.offset_be) / (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size); if ((ca.ca_length_be + ca.offset_be) % (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) { caLen++; } byte[] caSectors = imagePlugin.ReadSectors(ca.block_be, caLen); byte[] caData = new byte[ca.ca_length_be]; Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); int caOff = 0; while (caOff < ca.ca_length_be) { ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff); switch (nextSignature) { // Apple never said to include its extensions inside a continuation area, but just in case case APPLE_MAGIC: if (caData[caOff + 3] == 1 && caData[caOff + 2] == 7) { apple = true; } else { apple |= caData[caOff + 3] != 1; } break; case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be: byte[] er = new byte[caData[caOff + 2]]; Array.Copy(caData, caOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 && caData[caOff + 2] >= 9; caOff += caData[caOff + 2]; } } if (refareas.Count > 0) { suspInformation.AppendLine("----------------------------------------"); suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:"); suspInformation.AppendLine("----------------------------------------"); counter = 1; foreach (byte[] erb in refareas) { ReferenceArea er = Marshal.ByteArrayToStructureBigEndian <ReferenceArea>(erb); string extId = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>(), er.id_len); string extDes = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len, er.des_len); string extSrc = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len + er.des_len, er.src_len); suspInformation.AppendFormat("Extension: {0}", counter).AppendLine(); suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine(); suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine(); suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine(); counter++; } } byte[] ipbinSector = imagePlugin.ReadSector(0 + partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); string fsFormat; if (highSierraInfo) { fsFormat = "High Sierra Format"; } else if (cdiInfo) { fsFormat = "CD-i"; } else { fsFormat = "ISO9660"; } isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine(); if (xaExtensions) { isoMetadata.AppendLine("CD-ROM XA extensions present."); } if (amiga) { isoMetadata.AppendLine("Amiga extensions present."); } if (apple) { isoMetadata.AppendLine("Apple extensions present."); } if (jolietvd != null) { isoMetadata.AppendLine("Joliet extensions present."); } if (susp) { isoMetadata.AppendLine("System Use Sharing Protocol present."); } if (rrip) { isoMetadata.AppendLine("Rock Ridge Interchange Protocol present."); } if (aaip) { isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present."); } if (ziso) { isoMetadata.AppendLine("zisofs compression present."); } if (evd) { isoMetadata.AppendLine("Contains Enhanved Volume Descriptor."); } if (vpd) { isoMetadata.AppendLine("Contains Volume Partition Descriptor."); } if (bvd != null) { isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine(); } if (segaCd != null) { isoMetadata.AppendLine("This is a SegaCD / MegaCD disc."); isoMetadata.AppendLine(CD.Prettify(segaCd)); } if (saturn != null) { isoMetadata.AppendLine("This is a Sega Saturn disc."); isoMetadata.AppendLine(Saturn.Prettify(saturn)); } if (dreamcast != null) { isoMetadata.AppendLine("This is a Sega Dreamcast disc."); isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); } isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "") .AppendLine(); isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : "") .AppendLine(); isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "") .AppendLine(); isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine(); if (decodedVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize) .AppendLine(); if (jolietvd != null) { isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier) .AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier) .AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier) .AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine(); if (decodedJolietVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime) .AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedJolietVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime) .AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedJolietVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } } if (torito != null) { vdSector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start); int toritoOff = 0; if (vdSector[toritoOff] != 1) { goto exit_torito; } ElToritoValidationEntry valentry = Marshal.ByteArrayToStructureLittleEndian <ElToritoValidationEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); if (valentry.signature != EL_TORITO_MAGIC) { goto exit_torito; } toritoOff += EL_TORITO_ENTRY_SIZE; ElToritoInitialEntry initialEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoInitialEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); DicConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}", initialEntry.load_rba); DicConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}", initialEntry.sector_count); byte[] bootImage = initialEntry.load_rba + partition.Start + initialEntry.sector_count - 1 <= partition.End ? imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count) : null; isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("EL TORITO INFORMATION:"); isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("Initial entry:"); isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine(); if (initialEntry.bootable == ElToritoIndicator.Bootable) { isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine(); isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", initialEntry.load_rba, initialEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg) .AppendLine(); } else { isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", (uint)initialEntry.load_seg * 10).AppendLine(); } switch (initialEntry.boot_type) { case ElToritoEmulation.None: isoMetadata.AppendLine("\tImage uses no emulation"); break; case ElToritoEmulation.Md2hd: isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2hd: isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2ed: isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)) .AppendLine(); } } else { isoMetadata.AppendLine("\tNot bootable"); } toritoOff += EL_TORITO_ENTRY_SIZE; const int SECTION_COUNTER = 2; while (toritoOff < vdSector.Length && (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) { ElToritoSectionHeaderEntry sectionHeader = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionHeaderEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("Boot section {0}:", SECTION_COUNTER); isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)) .AppendLine(); for (int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; entryCounter++) { ElToritoSectionEntry sectionEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("\tEntry {0}:", entryCounter); if (sectionEntry.bootable == ElToritoIndicator.Bootable) { bootImage = sectionEntry.load_rba + partition.Start + sectionEntry.sector_count - 1 <= partition.End ? imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, sectionEntry.sector_count) : null; isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine(); isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", sectionEntry.load_rba, sectionEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg) .AppendLine(); } else { isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", (uint)sectionEntry.load_seg * 10).AppendLine(); } switch ((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) { case ElToritoEmulation.None: isoMetadata.AppendLine("\t\tImage uses no emulation"); break; case ElToritoEmulation.Md2hd: isoMetadata .AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2hd: isoMetadata .AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2ed: isoMetadata .AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\t\tSelection criteria type: {0}", sectionEntry.selection_criteria_type).AppendLine(); isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type) .AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)).AppendLine(); } } else { isoMetadata.AppendLine("\t\tNot bootable"); } ElToritoFlags flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); if (flags.HasFlag(ElToritoFlags.ATAPI)) { isoMetadata.AppendLine("\t\tImage contains ATAPI drivers"); } if (flags.HasFlag(ElToritoFlags.SCSI)) { isoMetadata.AppendLine("\t\tImage contains SCSI drivers"); } if (!flags.HasFlag(ElToritoFlags.Continued)) { continue; } while (toritoOff < vdSector.Length) { ElToritoSectionEntryExtension sectionExtension = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntryExtension>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; if (!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued)) { break; } } } if (sectionHeader.header_id == ElToritoIndicator.LastHeader) { break; } } } exit_torito: if (refareas.Count > 0) { isoMetadata.Append(suspInformation); } XmlFsType.Type = fsFormat; if (jolietvd != null) { XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; XmlFsType.Clusters = decodedVd.Blocks; XmlFsType.ClusterSize = decodedVd.BlockSize; information = isoMetadata.ToString(); }
public bool Open(IFilter imageFilter) { string comments = string.Empty; Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); byte[] header = new byte[32]; stream.Read(header, 0, 32); WCDiskImageFileHeader fheader = Marshal.ByteArrayToStructureLittleEndian <WCDiskImageFileHeader>(header); DicConsole.DebugWriteLine("d2f plugin", "Detected WC DISK IMAGE with {0} heads, {1} tracks and {2} sectors per track.", fheader.heads, fheader.cylinders, fheader.sectorsPerTrack); imageInfo.Cylinders = fheader.cylinders; imageInfo.SectorsPerTrack = fheader.sectorsPerTrack; imageInfo.SectorSize = 512; // only 512 bytes per sector supported imageInfo.Heads = fheader.heads; imageInfo.Sectors = imageInfo.Heads * imageInfo.Cylinders * imageInfo.SectorsPerTrack; imageInfo.ImageSize = imageInfo.Sectors * imageInfo.SectorSize; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); imageInfo.MediaType = Geometry.GetMediaType(((ushort)imageInfo.Cylinders, (byte)imageInfo.Heads, (ushort)imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM, false)); /* buffer the entire disk in memory */ for (int cyl = 0; cyl < imageInfo.Cylinders; cyl++) { for (int head = 0; head < imageInfo.Heads; head++) { ReadTrack(stream, cyl, head); } } /* if there are extra tracks, read them as well */ if (fheader.extraTracks[0] == 1) { DicConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 0) present, reading"); ReadTrack(stream, (int)imageInfo.Cylinders, 0); } if (fheader.extraTracks[1] == 1) { DicConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 1) present, reading"); ReadTrack(stream, (int)imageInfo.Cylinders, 1); } if (fheader.extraTracks[2] == 1) { DicConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 0) present, reading"); ReadTrack(stream, (int)imageInfo.Cylinders + 1, 0); } if (fheader.extraTracks[3] == 1) { DicConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 1) present, reading"); ReadTrack(stream, (int)imageInfo.Cylinders + 1, 1); } /* adjust number of cylinders */ if (fheader.extraTracks[0] == 1 || fheader.extraTracks[1] == 1) { imageInfo.Cylinders++; } if (fheader.extraTracks[2] == 1 || fheader.extraTracks[3] == 1) { imageInfo.Cylinders++; } /* read the comment and directory data if present */ if (fheader.extraFlags.HasFlag(ExtraFlag.Comment)) { DicConsole.DebugWriteLine("d2f plugin", "Comment present, reading"); byte[] sheaderBuffer = new byte[6]; stream.Read(sheaderBuffer, 0, 6); WCDiskImageSectorHeader sheader = Marshal.ByteArrayToStructureLittleEndian <WCDiskImageSectorHeader>(sheaderBuffer); if (sheader.flag != SectorFlag.Comment) { throw new InvalidDataException(string.Format("Invalid sector type '{0}' encountered", sheader.flag.ToString())); } byte[] comm = new byte[sheader.crc]; stream.Read(comm, 0, sheader.crc); comments += Encoding.ASCII.GetString(comm) + Environment.NewLine; } if (fheader.extraFlags.HasFlag(ExtraFlag.Directory)) { DicConsole.DebugWriteLine("d2f plugin", "Directory listing present, reading"); byte[] sheaderBuffer = new byte[6]; stream.Read(sheaderBuffer, 0, 6); WCDiskImageSectorHeader sheader = Marshal.ByteArrayToStructureLittleEndian <WCDiskImageSectorHeader>(sheaderBuffer); if (sheader.flag != SectorFlag.Directory) { throw new InvalidDataException(string.Format("Invalid sector type '{0}' encountered", sheader.flag.ToString())); } byte[] dir = new byte[sheader.crc]; stream.Read(dir, 0, sheader.crc); comments += Encoding.ASCII.GetString(dir); } if (comments.Length > 0) { imageInfo.Comments = comments; } // save some variables for later use fileHeader = fheader; wcImageFilter = imageFilter; return(true); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); if (stream.Length < 512) { return(false); } byte[] pHdrB = new byte[Marshal.SizeOf <Header>()]; stream.Read(pHdrB, 0, Marshal.SizeOf <Header>()); _pHdr = Marshal.ByteArrayToStructureLittleEndian <Header>(pHdrB); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.magic = {0}", StringHandlers.CToString(_pHdr.magic)); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.filesystem = {0}", StringHandlers.CToString(_pHdr.filesystem)); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.version = {0}", StringHandlers.CToString(_pHdr.version)); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.blockSize = {0}", _pHdr.blockSize); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.deviceSize = {0}", _pHdr.deviceSize); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.totalBlocks = {0}", _pHdr.totalBlocks); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.usedBlocks = {0}", _pHdr.usedBlocks); _byteMap = new byte[_pHdr.totalBlocks]; AaruConsole.DebugWriteLine("PartClone plugin", "Reading bytemap {0} bytes", _byteMap.Length); stream.Read(_byteMap, 0, _byteMap.Length); byte[] bitmagic = new byte[8]; stream.Read(bitmagic, 0, 8); AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.bitmagic = {0}", StringHandlers.CToString(bitmagic)); if (!_biTmAgIc.SequenceEqual(bitmagic)) { throw new ImageNotSupportedException("Could not find partclone BiTmAgIc, not continuing..."); } _dataOff = stream.Position; AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.dataOff = {0}", _dataOff); AaruConsole.DebugWriteLine("PartClone plugin", "Filling extents"); DateTime start = DateTime.Now; _extents = new ExtentsULong(); _extentsOff = new Dictionary <ulong, ulong>(); bool current = _byteMap[0] > 0; ulong blockOff = 0; ulong extentStart = 0; for (ulong i = 1; i < _pHdr.totalBlocks; i++) { bool next = _byteMap[i] > 0; // Flux if (next != current) { if (next) { extentStart = i; _extentsOff.Add(i, ++blockOff); } else { _extents.Add(extentStart, i); _extentsOff.TryGetValue(extentStart, out _); } } if (next && current) { blockOff++; } current = next; } DateTime end = DateTime.Now; AaruConsole.DebugWriteLine("PartClone plugin", "Took {0} seconds to fill extents", (end - start).TotalSeconds); _sectorCache = new Dictionary <ulong, byte[]>(); _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.Sectors = _pHdr.totalBlocks; _imageInfo.SectorSize = _pHdr.blockSize; _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; _imageInfo.MediaType = MediaType.GENERIC_HDD; _imageInfo.ImageSize = (ulong)(stream.Length - (4096 + 0x40 + (long)_pHdr.totalBlocks)); _imageStream = stream; return(true); }
Errno CacheFile(string path) { string[] pathElements = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (pathElements.Length != 1) { return(Errno.NotSupported); } string filename = pathElements[0].ToUpperInvariant(); if (filename.Length > 30) { return(Errno.NameTooLong); } if (!catalogCache.TryGetValue(filename, out ushort ts)) { return(Errno.NoSuchFile); } ulong lba = (ulong)(((ts & 0xFF00) >> 8) * sectorsPerTrack + (ts & 0xFF)); MemoryStream fileMs = new MemoryStream(); MemoryStream tsListMs = new MemoryStream(); ushort expectedBlock = 0; while (lba != 0) { usedSectors++; byte[] tsSectorB = device.ReadSector(lba); if (debug) { tsListMs.Write(tsSectorB, 0, tsSectorB.Length); } // Read the track/sector list sector TrackSectorList tsSector = Marshal.ByteArrayToStructureLittleEndian <TrackSectorList>(tsSectorB); if (tsSector.sectorOffset > expectedBlock) { byte[] hole = new byte[(tsSector.sectorOffset - expectedBlock) * vtoc.bytesPerSector]; fileMs.Write(hole, 0, hole.Length); expectedBlock = tsSector.sectorOffset; } foreach (TrackSectorListEntry entry in tsSector.entries) { track1UsedByFiles |= entry.track == 1; track2UsedByFiles |= entry.track == 2; usedSectors++; ulong blockLba = (ulong)(entry.track * sectorsPerTrack + entry.sector); if (blockLba == 0) { break; } byte[] fileBlock = device.ReadSector(blockLba); fileMs.Write(fileBlock, 0, fileBlock.Length); expectedBlock++; } lba = (ulong)(tsSector.nextListTrack * sectorsPerTrack + tsSector.nextListSector); } if (fileCache.ContainsKey(filename)) { fileCache.Remove(filename); } if (extentCache.ContainsKey(filename)) { extentCache.Remove(filename); } fileCache.Add(filename, fileMs.ToArray()); extentCache.Add(filename, tsListMs.ToArray()); return(Errno.NoError); }
Errno ReadCatalog() { var catalogMs = new MemoryStream(); ulong lba = (ulong)((_vtoc.catalogTrack * _sectorsPerTrack) + _vtoc.catalogSector); _totalFileEntries = 0; _catalogCache = new Dictionary <string, ushort>(); _fileTypeCache = new Dictionary <string, byte>(); _fileSizeCache = new Dictionary <string, int>(); _lockedFiles = new List <string>(); if (lba == 0 || lba > _device.Info.Sectors) { return(Errno.InvalidArgument); } while (lba != 0) { _usedSectors++; byte[] catSectorB = _device.ReadSector(lba); _totalFileEntries += 7; if (_debug) { catalogMs.Write(catSectorB, 0, catSectorB.Length); } // Read the catalog sector CatalogSector catSector = Marshal.ByteArrayToStructureLittleEndian <CatalogSector>(catSectorB); foreach (FileEntry entry in catSector.entries.Where(entry => entry.extentTrack > 0)) { _track1UsedByFiles |= entry.extentTrack == 1; _track2UsedByFiles |= entry.extentTrack == 2; byte[] filenameB = new byte[30]; ushort ts = (ushort)((entry.extentTrack << 8) | entry.extentSector); // Apple DOS has high byte set over ASCII. for (int i = 0; i < 30; i++) { filenameB[i] = (byte)(entry.filename[i] & 0x7F); } string filename = StringHandlers.SpacePaddedToString(filenameB, Encoding); if (!_catalogCache.ContainsKey(filename)) { _catalogCache.Add(filename, ts); } if (!_fileTypeCache.ContainsKey(filename)) { _fileTypeCache.Add(filename, (byte)(entry.typeAndFlags & 0x7F)); } if (!_fileSizeCache.ContainsKey(filename)) { _fileSizeCache.Add(filename, entry.length * _vtoc.bytesPerSector); } if ((entry.typeAndFlags & 0x80) == 0x80 && !_lockedFiles.Contains(filename)) { _lockedFiles.Add(filename); } } lba = (ulong)((catSector.trackOfNext * _sectorsPerTrack) + catSector.sectorOfNext); if (lba > _device.Info.Sectors) { break; } } if (_debug) { _catalogBlocks = catalogMs.ToArray(); } return(Errno.NoError); }
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); if (stream.Length < Marshal.SizeOf <OobBlock>()) { return(false); } byte[] hdr = new byte[Marshal.SizeOf <OobBlock>()]; stream.Read(hdr, 0, Marshal.SizeOf <OobBlock>()); OobBlock header = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(hdr); stream.Seek(-Marshal.SizeOf <OobBlock>(), SeekOrigin.End); hdr = new byte[Marshal.SizeOf <OobBlock>()]; stream.Read(hdr, 0, Marshal.SizeOf <OobBlock>()); OobBlock footer = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(hdr); if (header.blockId != BlockIds.Oob || header.blockType != OobTypes.KFInfo || footer.blockId != BlockIds.Oob || footer.blockType != OobTypes.EOF || footer.length != 0x0D0D) { return(false); } // TODO: This is supposing NoFilter, shouldn't tracks = new SortedDictionary <byte, IFilter>(); byte step = 1; byte heads = 2; bool topHead = false; string basename = Path.Combine(imageFilter.GetParentFolder(), imageFilter. GetFilename().Substring(0, imageFilter.GetFilename().Length - 8)); for (byte t = 0; t < 166; t += step) { int cylinder = t / heads; int head = topHead ? 1 : t % heads; string trackfile = Directory.Exists(basename) ? Path.Combine(basename, $"{cylinder:D2}.{head:D1}.raw") : $"{basename}{cylinder:D2}.{head:D1}.raw"; if (!File.Exists(trackfile)) { if (cylinder == 0) { if (head == 0) { AaruConsole.DebugWriteLine("KryoFlux plugin", "Cannot find cyl 0 hd 0, supposing only top head was dumped"); topHead = true; heads = 1; continue; } AaruConsole.DebugWriteLine("KryoFlux plugin", "Cannot find cyl 0 hd 1, supposing only bottom head was dumped"); heads = 1; continue; } else if (cylinder == 1) { AaruConsole.DebugWriteLine("KryoFlux plugin", "Cannot find cyl 1, supposing double stepping"); step = 2; continue; } else { AaruConsole.DebugWriteLine("KryoFlux plugin", "Arrived end of disk at cylinder {0}", cylinder); break; } } var trackFilter = new ZZZNoFilter(); trackFilter.Open(trackfile); if (!trackFilter.IsOpened()) { throw new IOException("Could not open KryoFlux track file."); } imageInfo.CreationTime = DateTime.MaxValue; imageInfo.LastModificationTime = DateTime.MinValue; Stream trackStream = trackFilter.GetDataForkStream(); while (trackStream.Position < trackStream.Length) { byte blockId = (byte)trackStream.ReadByte(); switch (blockId) { case (byte)BlockIds.Oob: { trackStream.Position--; byte[] oob = new byte[Marshal.SizeOf <OobBlock>()]; trackStream.Read(oob, 0, Marshal.SizeOf <OobBlock>()); OobBlock oobBlk = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(oob); if (oobBlk.blockType == OobTypes.EOF) { trackStream.Position = trackStream.Length; break; } if (oobBlk.blockType != OobTypes.KFInfo) { trackStream.Position += oobBlk.length; break; } byte[] kfinfo = new byte[oobBlk.length]; trackStream.Read(kfinfo, 0, oobBlk.length); string kfinfoStr = StringHandlers.CToString(kfinfo); string[] lines = kfinfoStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); DateTime blockDate = DateTime.Now; DateTime blockTime = DateTime.Now; bool foundDate = false; foreach (string[] kvp in lines.Select(line => line.Split('=')).Where(kvp => kvp.Length == 2)) { kvp[0] = kvp[0].Trim(); kvp[1] = kvp[1].Trim(); AaruConsole.DebugWriteLine("KryoFlux plugin", "\"{0}\" = \"{1}\"", kvp[0], kvp[1]); switch (kvp[0]) { case hostDate: if (DateTime.TryParseExact(kvp[1], "yyyy.MM.dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out blockDate)) { foundDate = true; } break; case hostTime: DateTime.TryParseExact(kvp[1], "HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out blockTime); break; case kfName: imageInfo.Application = kvp[1]; break; case kfVersion: imageInfo.ApplicationVersion = kvp[1]; break; } } if (foundDate) { var blockTimestamp = new DateTime(blockDate.Year, blockDate.Month, blockDate.Day, blockTime.Hour, blockTime.Minute, blockTime.Second); AaruConsole.DebugWriteLine("KryoFlux plugin", "Found timestamp: {0}", blockTimestamp); if (blockTimestamp < Info.CreationTime) { imageInfo.CreationTime = blockTimestamp; } if (blockTimestamp > Info.LastModificationTime) { imageInfo.LastModificationTime = blockTimestamp; } } break; } case (byte)BlockIds.Flux2: case (byte)BlockIds.Flux2_1: case (byte)BlockIds.Flux2_2: case (byte)BlockIds.Flux2_3: case (byte)BlockIds.Flux2_4: case (byte)BlockIds.Flux2_5: case (byte)BlockIds.Flux2_6: case (byte)BlockIds.Flux2_7: case (byte)BlockIds.Nop2: trackStream.Position++; continue; case (byte)BlockIds.Nop3: case (byte)BlockIds.Flux3: trackStream.Position += 2; continue; default: continue; } } tracks.Add(t, trackFilter); } imageInfo.Heads = heads; imageInfo.Cylinders = (uint)(tracks.Count / heads); throw new NotImplementedException("Flux decoding is not yet implemented."); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { Encoding = encoding ?? Encoding.GetEncoding(1252); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } if (options.TryGetValue("use_path_table", out string usePathTableString)) { bool.TryParse(usePathTableString, out _usePathTable); } if (options.TryGetValue("use_trans_tbl", out string useTransTblString)) { bool.TryParse(useTransTblString, out _useTransTbl); } if (options.TryGetValue("use_evd", out string useEvdString)) { bool.TryParse(useEvdString, out _useEvd); } // Default namespace @namespace ??= "joliet"; switch (@namespace.ToLowerInvariant()) { case "normal": _namespace = Namespace.Normal; break; case "vms": _namespace = Namespace.Vms; break; case "joliet": _namespace = Namespace.Joliet; break; case "rrip": _namespace = Namespace.Rrip; break; case "romeo": _namespace = Namespace.Romeo; break; default: return(Errno.InvalidArgument); } PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return(Errno.InvalidArgument); } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return(Errno.InvalidArgument); } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (_highSierra) { hsOff = 8; } _cdi = false; List <ulong> bvdSectors = new List <ulong>(); List <ulong> pvdSectors = new List <ulong>(); List <ulong> svdSectors = new List <ulong>(); List <ulong> evdSectors = new List <ulong>(); List <ulong> vpdSectors = new List <ulong>(); while (true) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return(Errno.InvalidArgument); } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return(Errno.InvalidArgument); } break; } _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { if (_debug) { bvdSectors.Add(16 + counter + partition.Start); } break; } case 1: { if (_highSierra) { hsvd = Marshal. ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (_cdi) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } if (_debug) { pvdSectors.Add(16 + counter + partition.Start); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // TODO: Other escape sequences // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { AaruConsole.DebugWriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } if (_debug) { svdSectors.Add(16 + counter + partition.Start); } } else { if (_debug) { evdSectors.Add(16 + counter + partition.Start); } if (_useEvd) { // Basically until escape sequences are implemented, let the user chose the encoding. // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD _namespace = Namespace.Romeo; pvd = svd; } } break; } case 3: { if (_debug) { vpdSectors.Add(16 + counter + partition.Start); } break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); return(Errno.InvalidArgument); } if (_highSierra) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (_cdi) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } if (_namespace != Namespace.Romeo) { Encoding = Encoding.ASCII; } string fsFormat; byte[] pathTableData; uint pathTableMsbLocation; uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i _image = imagePlugin; if (_highSierra) { _blockSize = hsvd.Value.logical_block_size; pathTableData = ReadSingleExtent(hsvd.Value.path_table_size, Swapping.Swap(hsvd.Value.mandatory_path_table_msb)); fsFormat = "High Sierra Format"; pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; } else if (_cdi) { _blockSize = fsvd.Value.logical_block_size; pathTableData = ReadSingleExtent(fsvd.Value.path_table_size, fsvd.Value.path_table_addr); fsFormat = "CD-i"; pathTableMsbLocation = fsvd.Value.path_table_addr; // TODO: Until escape sequences are implemented this is the default CD-i encoding. Encoding = Encoding.GetEncoding("iso8859-1"); } else { _blockSize = pvd.Value.logical_block_size; pathTableData = ReadSingleExtent(pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table)); fsFormat = "ISO9660"; pathTableMsbLocation = pvd.Value.type_m_path_table; pathTableLsbLocation = pvd.Value.type_l_path_table; } _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); // High Sierra and CD-i do not support Joliet or RRIP if ((_highSierra || _cdi) && _namespace != Namespace.Normal && _namespace != Namespace.Vms) { _namespace = Namespace.Normal; } if (jolietvd is null && _namespace == Namespace.Joliet) { _namespace = Namespace.Normal; } uint rootLocation; uint rootSize; byte rootXattrLength = 0; if (!_cdi) { rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len : pvd.Value.root_directory_record.xattr_len; rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size; if (pathTableData.Length > 1 && rootLocation != _pathTable[0].Extent) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Path table and PVD do not point to the same location for the root directory!"); byte[] firstRootSector = ReadSector(rootLocation); bool pvdWrongRoot = false; if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } if (pvdWrongRoot) { AaruConsole.DebugWriteLine("ISO9660 plugin", "PVD does not point to correct root directory, checking path table..."); bool pathTableWrongRoot = false; rootLocation = _pathTable[0].Extent; firstRootSector = ReadSector(_pathTable[0].Extent); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } if (pathTableWrongRoot) { AaruConsole.ErrorWriteLine("Cannot find root directory..."); return(Errno.InvalidArgument); } _usePathTable = true; } } } else { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); CdiDirectoryRecord rootEntry = Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; _usePathTable = _usePathTable || _pathTable.Length == 1; _useTransTbl = false; } // In case the path table is incomplete if (_usePathTable && pathTableData.Length == 1) { _usePathTable = false; } if (_usePathTable && !_cdi) { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } rootXattrLength = _pathTable[0].XattrLength; } try { _ = ReadSingleExtent(rootSize, rootLocation); } catch { return(Errno.InvalidArgument); } byte[] ipbinSector = ReadSector(partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); if (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip) { _usePathTable = false; _useTransTbl = false; } // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL if (_useTransTbl) { _usePathTable = false; } if (_namespace != Namespace.Joliet) { _rootDirectoryCache = _cdi ? DecodeCdiDirectory(rootLocation + rootXattrLength, rootSize) : _highSierra ? DecodeHighSierraDirectory(rootLocation + rootXattrLength, rootSize) : DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); } XmlFsType.Type = fsFormat; if (jolietvd != null && (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip)) { rootLocation = jolietvd.Value.root_directory_record.extent; rootXattrLength = jolietvd.Value.root_directory_record.xattr_len; rootSize = jolietvd.Value.root_directory_record.size; _joliet = true; _rootDirectoryCache = DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } decodedVd = decodedJolietVd; } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } if (_debug) { _rootDirectoryCache.Add("$", new DecodedDirectoryEntry { Extents = new List <(uint extent, uint size)> { (rootLocation, rootSize) }, Filename = "$", Size = rootSize, Timestamp = decodedVd.CreationTime });
public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); if (stream.Length < Marshal.SizeOf <RayHdr>()) { return(false); } byte[] buffer = new byte[Marshal.SizeOf <RayHdr>()]; stream.Seek(0, SeekOrigin.Begin); stream.Read(buffer, 0, buffer.Length); RayHdr header = Marshal.ByteArrayToStructureLittleEndian <RayHdr>(buffer); string signature = StringHandlers.CToString(header.signature); var sx = new Regex(REGEX_SIGNATURE); Match sm = sx.Match(signature); if (!sm.Success) { return(false); } _imageInfo.ApplicationVersion = $"{sm.Groups["major"].Value}.{sm.Groups["minor"].Value}"; _imageInfo.Cylinders = (uint)(header.cylinders + 1); _imageInfo.Heads = (uint)(header.heads + 1); _imageInfo.SectorsPerTrack = header.sectorsPerTrack; _imageInfo.Sectors = _imageInfo.Cylinders * _imageInfo.Heads * _imageInfo.SectorsPerTrack; _imageInfo.SectorSize = 512; byte[] sectors = new byte[_imageInfo.SectorsPerTrack * _imageInfo.SectorSize]; _disk = new MemoryStream(); for (int i = 0; i < _imageInfo.SectorsPerTrack * _imageInfo.SectorSize; i++) { stream.Read(sectors, 0, sectors.Length); stream.Seek(_imageInfo.SectorsPerTrack, SeekOrigin.Current); _disk.Write(sectors, 0, sectors.Length); } _imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, (byte)_imageInfo.Heads, (ushort)_imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM, false)); switch (_imageInfo.MediaType) { case MediaType.NEC_525_HD when header.diskType == RayDiskTypes.Mf2hd || header.diskType == RayDiskTypes.Mf2ed: _imageInfo.MediaType = MediaType.NEC_35_HD_8; break; case MediaType.DOS_525_HD when header.diskType == RayDiskTypes.Mf2hd || header.diskType == RayDiskTypes.Mf2ed: _imageInfo.MediaType = MediaType.NEC_35_HD_15; break; case MediaType.RX50 when header.diskType == RayDiskTypes.Md2dd || header.diskType == RayDiskTypes.Md2hd: _imageInfo.MediaType = MediaType.ATARI_35_SS_DD; break; } _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; return(true); }