// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Info.cs // Author(s) : Natalia Portillo // // Component : ISO9660 filesystem plugin. // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2025 Natalia Portillo // In the loving memory of Facunda "Tata" Suárez Domínguez, R.I.P. 2019/07/24 // ****************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Aaru.Checksums; using Aaru.CommonTypes.AaruMetadata; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Decoders.Sega; using Aaru.Helpers; using Aaru.Logging; using Partition = Aaru.CommonTypes.Partition; namespace Aaru.Filesystems; public sealed partial class ISO9660 { #region IReadOnlyFilesystem Members /// public bool Identify(IMediaImage imagePlugin, Partition partition) { // ISO9660 is designed for 2048 bytes/sector devices if(imagePlugin.Info.SectorSize < 2048) return false; // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if(partition.End <= 16 + partition.Start) return false; // Read to Volume Descriptor ErrorNumber errno = imagePlugin.ReadSector(16 + partition.Start, false, out byte[] vdSector, out _); if(errno != ErrorNumber.NoError) return false; var xaOff = 0; if(vdSector.Length == 2336) xaOff = 8; byte vdType = vdSector[0 + xaOff]; var vdMagic = new byte[5]; var hsMagic = new byte[5]; // This indicates the end of a volume descriptor. HighSierra here would have 16 so no problem if(vdType == 255) return false; Array.Copy(vdSector, 0x001 + xaOff, vdMagic, 0, 5); Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); AaruLogging.Debug(MODULE_NAME, "VDMagic = {0}", Encoding.ASCII.GetString(vdMagic)); AaruLogging.Debug(MODULE_NAME, "HSMagic = {0}", Encoding.ASCII.GetString(hsMagic)); return Encoding.ASCII.GetString(vdMagic) == ISO_MAGIC || Encoding.ASCII.GetString(hsMagic) == HIGH_SIERRA_MAGIC || Encoding.ASCII.GetString(vdMagic) == CDI_MAGIC; } /// public void GetInformation(IMediaImage imagePlugin, Partition partition, Encoding encoding, out string information, out FileSystem metadata) { encoding ??= Encoding.ASCII; information = ""; metadata = new FileSystem(); var isoMetadata = new StringBuilder(); var vdMagic = new byte[5]; // Volume Descriptor magic "CD001" var hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" var 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; ErrorNumber errno = imagePlugin.ReadSector(16 + partition.Start, false, out byte[] vdSector, out _); if(errno != ErrorNumber.NoError) return; int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); bool highSierraInfo = encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; var hsOff = 0; if(highSierraInfo) hsOff = 8; var cdiInfo = false; var evd = false; var vpd = false; while(true) { AaruLogging.Debug(MODULE_NAME, Localization.Processing_VD_loop_no_0, counter); // Seek to Volume Descriptor AaruLogging.Debug(MODULE_NAME, Localization.Reading_sector_0, 16 + counter + partition.Start); errno = imagePlugin.ReadSector(16 + counter + partition.Start, false, out byte[] vdSectorTmp, out _); if(errno != ErrorNumber.NoError) return; 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. AaruLogging.Debug(MODULE_NAME, "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(vdSector, hsOff, 2048 - hsOff); bootSpec = Localization.Unknown_specification; if(encoding.GetString(bvd.Value.system_id)[..23] == "EL TORITO SPECIFICATION") { bootSpec = "[green]El Torito[/]"; torito = Marshal.ByteArrayToStructureLittleEndian(vdSector, hsOff, 2048 - hsOff); } break; } case 1: { if(highSierraInfo) hsvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); else if(cdiInfo) fsvd = Marshal.ByteArrayToStructureBigEndian(vdSector); else pvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian(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 { AaruLogging.WriteLine(MODULE_NAME, Localization.Found_unknown_supplementary_volume_descriptor); } } } else evd = true; break; } case 3: { vpd = true; break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); metadata = new FileSystem(); if(pvd == null && hsvd == null && fsvd == null) { information = Localization.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 = []; var rootOff = 0; var xaExtensions = false; var apple = false; var susp = false; var rrip = false; var ziso = false; var amiga = false; var aaip = false; List contareas = []; List refareas = []; var suspInformation = new StringBuilder(); if(rootLocation + rootSize < imagePlugin.Info.Sectors) { errno = imagePlugin.ReadSectors(rootLocation, false, rootSize, out rootDir, out _); if(errno != ErrorNumber.NoError) return; } // Walk thru root directory to see system area extensions in use while(rootOff + Marshal.SizeOf() < rootDir.Length && !cdiInfo) { DirectoryRecord record = Marshal.ByteArrayToStructureLittleEndian(rootDir, rootOff, Marshal.SizeOf()); int saOff = Marshal.SizeOf() + record.name_len; saOff += saOff % 2; int saLen = record.length - saOff; if(saLen > 0 && rootOff + saOff + saLen <= rootDir.Length) { var sa = new byte[saLen]; Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); saOff = 0; while(saOff < saLen) { var noneFound = true; if(Marshal.SizeOf() + saOff <= saLen) { CdromXa xa = Marshal.ByteArrayToStructureBigEndian(sa); if(xa.signature == XA_MAGIC) { xaExtensions = true; saOff += Marshal.SizeOf(); noneFound = false; } } if(saOff + 2 >= saLen) break; var 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; var appleId = (AppleOldId)sa[saOff + 2]; noneFound = false; switch(appleId) { case AppleOldId.ProDOS: saOff += Marshal.SizeOf(); break; case AppleOldId.TypeCreator: case AppleOldId.TypeCreatorBundle: saOff += Marshal.SizeOf(); break; case AppleOldId.TypeCreatorIcon: case AppleOldId.TypeCreatorIconBundle: saOff += Marshal.SizeOf(); break; case AppleOldId.HFS: saOff += Marshal.SizeOf(); 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: var ce = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, ce, 0, ce.Length); ContinuationArea ca = Marshal.ByteArrayToStructureBigEndian(ce); contareas.Add(ca); break; case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: var er = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature is RRIP_MAGIC or RRIP_POSIX_ATTRIBUTES or RRIP_POSIX_DEV_NO or RRIP_SYMLINK or RRIP_NAME or RRIP_CHILDLINK or RRIP_PARENTLINK or RRIP_RELOCATED_DIR or RRIP_TIMESTAMPS or 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++; errno = imagePlugin.ReadSectors(ca.block_be, false, caLen, out byte[] caSectors, out _); if(errno != ErrorNumber.NoError) return; var caData = new byte[ca.ca_length_be]; Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); var caOff = 0; while(caOff < ca.ca_length_be) { var 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: var er = new byte[caData[caOff + 2]]; Array.Copy(caData, caOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature is RRIP_MAGIC or RRIP_POSIX_ATTRIBUTES or RRIP_POSIX_DEV_NO or RRIP_SYMLINK or RRIP_NAME or RRIP_CHILDLINK or RRIP_PARENTLINK or RRIP_RELOCATED_DIR or RRIP_TIMESTAMPS or 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(Localization.SYSTEM_USE_SHARING_PROTOCOL_INFORMATION_border); suspInformation.AppendLine(Localization.SYSTEM_USE_SHARING_PROTOCOL_INFORMATION); suspInformation.AppendLine(Localization.SYSTEM_USE_SHARING_PROTOCOL_INFORMATION_border); counter = 1; foreach(byte[] erb in refareas) { ReferenceArea er = Marshal.ByteArrayToStructureBigEndian(erb); string extId = encoding.GetString(erb, Marshal.SizeOf(), er.id_len); string extDes = encoding.GetString(erb, Marshal.SizeOf() + er.id_len, er.des_len); string extSrc = encoding.GetString(erb, Marshal.SizeOf() + er.id_len + er.des_len, er.src_len); suspInformation.AppendFormat(Localization.Extension_0, counter).AppendLine(); suspInformation.AppendFormat("\t" + Localization.ID_0_version_1, extId, er.ext_ver).AppendLine(); suspInformation.AppendFormat("\t" + Localization.Description_0, extDes).AppendLine(); suspInformation.AppendFormat("\t" + Localization.Source_0, extSrc).AppendLine(); counter++; } } errno = imagePlugin.ReadSector(0 + partition.Start, false, out byte[] ipbinSector, out _); if(errno != ErrorNumber.NoError) return; CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin? dreamcast = Dreamcast.DecodeIPBin(ipbinSector); if(highSierraInfo) isoMetadata.AppendLine(Localization.High_Sierra_Format_file_system); else if(cdiInfo) isoMetadata.AppendLine(Localization.CD_i_file_system); else isoMetadata.AppendLine(Localization.ISO9660_file_system); if(xaExtensions) isoMetadata.AppendLine(Localization.CD_ROM_XA_extensions_present); if(amiga) isoMetadata.AppendLine(Localization.Amiga_extensions_present); if(apple) isoMetadata.AppendLine(Localization.Apple_extensions_present); if(jolietvd != null) isoMetadata.AppendLine(Localization.Joliet_extensions_present); if(susp) isoMetadata.AppendLine(Localization.System_Use_Sharing_Protocol_present); if(rrip) isoMetadata.AppendLine(Localization.Rock_Ridge_Interchange_Protocol_present); if(aaip) isoMetadata.AppendLine(Localization.Arbitrary_Attribute_Interchange_Protocol_present); if(ziso) isoMetadata.AppendLine(Localization.zisofs_compression_present); if(evd) isoMetadata.AppendLine(Localization.Contains_Enhanced_Volume_Descriptor); if(vpd) isoMetadata.AppendLine(Localization.Contains_Volume_Partition_Descriptor); if(bvd != null) isoMetadata.AppendFormat(Localization.Disc_bootable_following_0_specifications, bootSpec).AppendLine(); if(segaCd != null) { isoMetadata.AppendLine(Localization.This_is_a_SegaCD_MegaCD_disc); isoMetadata.AppendLine(CD.Prettify(segaCd)); } if(saturn != null) { isoMetadata.AppendLine(Localization.This_is_a_Sega_Saturn_disc); isoMetadata.AppendLine(Saturn.Prettify(saturn)); } if(dreamcast != null) { isoMetadata.AppendLine(Localization.This_is_a_Sega_Dreamcast_disc); isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); } if(cdiInfo) { isoMetadata.AppendLine(Localization.FILE_STRUCTURE_VOLUME_DESCRIPTOR_INFORMATION_border); isoMetadata.AppendLine(Localization.FILE_STRUCTURE_VOLUME_DESCRIPTOR_INFORMATION); isoMetadata.AppendLine(Localization.FILE_STRUCTURE_VOLUME_DESCRIPTOR_INFORMATION_border); } else { isoMetadata.AppendLine(Localization.VOLUME_DESCRIPTOR_INFORMATION_border); isoMetadata.AppendLine(Localization.VOLUME_DESCRIPTOR_INFORMATION); isoMetadata.AppendLine(Localization.VOLUME_DESCRIPTOR_INFORMATION_border); } isoMetadata.AppendFormat(Localization.System_identifier_0, decodedVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_identifier_0, decodedVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_set_identifier_0, decodedVd.VolumeSetIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Publisher_identifier_0, decodedVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Data_preparer_identifier_0, decodedVd.DataPreparerIdentifier) .AppendLine(); isoMetadata.AppendFormat(Localization.Application_identifier_0, decodedVd.ApplicationIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_creation_date_0, decodedVd.CreationTime).AppendLine(); if(decodedVd.HasModificationTime) isoMetadata.AppendFormat(Localization.Volume_modification_date_0, decodedVd.ModificationTime).AppendLine(); else isoMetadata.AppendFormat(Localization.Volume_has_not_been_modified).AppendLine(); if(decodedVd.HasExpirationTime) isoMetadata.AppendFormat(Localization.Volume_expiration_date_0, decodedVd.ExpirationTime).AppendLine(); else isoMetadata.AppendFormat(Localization.Volume_does_not_expire).AppendLine(); if(decodedVd.HasEffectiveTime) isoMetadata.AppendFormat(Localization.Volume_effective_date_0, decodedVd.EffectiveTime).AppendLine(); else isoMetadata.AppendFormat(Localization.Volume_has_always_been_effective).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_has_0_blocks_of_1_bytes_each, decodedVd.Blocks, decodedVd.BlockSize) .AppendLine(); if(jolietvd != null) { isoMetadata.AppendLine(Localization.JOLIET_VOLUME_DESCRIPTOR_INFORMATION_border); isoMetadata.AppendLine(Localization.JOLIET_VOLUME_DESCRIPTOR_INFORMATION); isoMetadata.AppendLine(Localization.JOLIET_VOLUME_DESCRIPTOR_INFORMATION_border); isoMetadata.AppendFormat(Localization.System_identifier_0, decodedJolietVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_identifier_0, decodedJolietVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat(Localization.Volume_set_identifier_0, decodedJolietVd.VolumeSetIdentifier) .AppendLine(); isoMetadata.AppendFormat(Localization.Publisher_identifier_0, decodedJolietVd.PublisherIdentifier) .AppendLine(); isoMetadata.AppendFormat(Localization.Data_preparer_identifier_0, decodedJolietVd.DataPreparerIdentifier) .AppendLine(); isoMetadata.AppendFormat(Localization.Application_identifier_0, decodedJolietVd.ApplicationIdentifier) .AppendLine(); isoMetadata.AppendFormat(Localization.Volume_creation_date_0, decodedJolietVd.CreationTime).AppendLine(); if(decodedJolietVd.HasModificationTime) { isoMetadata.AppendFormat(Localization.Volume_modification_date_0, decodedJolietVd.ModificationTime) .AppendLine(); } else isoMetadata.AppendFormat(Localization.Volume_has_not_been_modified).AppendLine(); if(decodedJolietVd.HasExpirationTime) { isoMetadata.AppendFormat(Localization.Volume_expiration_date_0, decodedJolietVd.ExpirationTime) .AppendLine(); } else isoMetadata.AppendFormat(Localization.Volume_does_not_expire).AppendLine(); if(decodedJolietVd.HasEffectiveTime) { isoMetadata.AppendFormat(Localization.Volume_effective_date_0, decodedJolietVd.EffectiveTime) .AppendLine(); } else isoMetadata.AppendFormat(Localization.Volume_has_always_been_effective).AppendLine(); } if(torito != null) { errno = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start, false, out vdSector, out _); if(errno != ErrorNumber.NoError) return; var toritoOff = 0; if(vdSector[toritoOff] != 1) goto exit_torito; ElToritoValidationEntry valentry = Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); if(valentry.signature != EL_TORITO_MAGIC) goto exit_torito; toritoOff = EL_TORITO_ENTRY_SIZE; ElToritoInitialEntry initialEntry = Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); AaruLogging.Debug(MODULE_NAME, "initialEntry.load_rba = {0}", initialEntry.load_rba); AaruLogging.Debug(MODULE_NAME, "initialEntry.sector_count = {0}", initialEntry.sector_count); byte[] bootImage = null; if(initialEntry.load_rba + partition.Start + initialEntry.sector_count - 1 <= partition.End) { imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, false, initialEntry.sector_count, out bootImage, out _); } isoMetadata.AppendLine(Localization.EL_TORITO_INFORMATION_border); isoMetadata.AppendLine(Localization.EL_TORITO_INFORMATION); isoMetadata.AppendLine(Localization.EL_TORITO_INFORMATION_border); isoMetadata.AppendLine(Localization.Initial_entry); isoMetadata.AppendFormat("\t" + Localization.Developer_ID_0, encoding.GetString(valentry.developer_id)) .AppendLine(); if(initialEntry.bootable == ElToritoIndicator.Bootable) { isoMetadata.AppendFormat("\t" + Localization.Bootable_on_0, valentry.platform_id).AppendLine(); isoMetadata.AppendFormat("\t" + Localization.Bootable_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("\t" + Localization.Bootable_image_will_be_loaded_at_segment_0, initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg) .AppendLine(); } else { isoMetadata.AppendFormat("\t" + Localization.Bootable_image_will_be_loaded_at_0, (uint)initialEntry.load_seg * 10) .AppendLine(); } switch(initialEntry.boot_type) { case ElToritoEmulation.None: isoMetadata.AppendLine("\t" + Localization.Image_uses_no_emulation); break; case ElToritoEmulation.Md2Hd: isoMetadata.AppendLine("\t" + Localization.Image_emulates_a_high_density_MD2HD_floppy); break; case ElToritoEmulation.Mf2Hd: isoMetadata.AppendLine("\t" + Localization.Image_emulates_a_high_density_MF2HD_floppy); break; case ElToritoEmulation.Mf2Ed: isoMetadata.AppendLine("\t" + Localization.Image_emulates_a_extra_density_MF2ED_floppy); break; default: isoMetadata.AppendFormat("\t" + Localization.Image_uses_unknown_emulation_type_0, (byte)initialEntry.boot_type) .AppendLine(); break; } isoMetadata.AppendFormat("\t" + Localization.System_type_0, initialEntry.system_type).AppendLine(); if(bootImage != null) { isoMetadata.AppendFormat("\t" + Localization.Bootable_image_SHA1_0, Sha1Context.Data(bootImage, out _)) .AppendLine(); } } else isoMetadata.AppendLine("\t" + Localization.Not_bootable); toritoOff += EL_TORITO_ENTRY_SIZE; const int sectionCounter = 2; while(toritoOff < vdSector.Length && (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) { ElToritoSectionHeaderEntry sectionHeader = Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat(Localization.Boot_section_0, sectionCounter); isoMetadata.AppendFormat("\t" + Localization.Section_ID_0, encoding.GetString(sectionHeader.identifier)) .AppendLine(); for(var entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; entryCounter++) { ElToritoSectionEntry sectionEntry = Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("\t" + Localization.Entry_0, entryCounter); if(sectionEntry.bootable == ElToritoIndicator.Bootable) { bootImage = null; if(sectionEntry.load_rba + partition.Start + sectionEntry.sector_count - 1 <= partition.End) { imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, false, sectionEntry.sector_count, out bootImage, out _); } isoMetadata.AppendFormat("\t\t" + Localization.Bootable_on_0, sectionHeader.platform_id) .AppendLine(); isoMetadata .AppendFormat("\t\t" + Localization.Bootable_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\t" + Localization.Bootable_image_will_be_loaded_at_segment_0, sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg) .AppendLine(); } else { isoMetadata.AppendFormat("\t\t" + Localization.Bootable_image_will_be_loaded_at_0, (uint)sectionEntry.load_seg * 10) .AppendLine(); } switch((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) { case ElToritoEmulation.None: isoMetadata.AppendLine("\t\t" + Localization.Image_uses_no_emulation); break; case ElToritoEmulation.Md2Hd: isoMetadata.AppendLine("\t\t" + Localization.Image_emulates_a_high_density_MD2HD_floppy); break; case ElToritoEmulation.Mf2Hd: isoMetadata.AppendLine("\t\t" + Localization.Image_emulates_a_high_density_MF2HD_floppy); break; case ElToritoEmulation.Mf2Ed: isoMetadata.AppendLine("\t\t" + Localization.Image_emulates_a_extra_density_MF2ED_floppy); break; default: isoMetadata.AppendFormat("\t\t" + Localization.Image_uses_unknown_emulation_type_0, (byte)initialEntry.boot_type) .AppendLine(); break; } isoMetadata.AppendFormat("\t\t" + Localization.Selection_criteria_type_0, sectionEntry.selection_criteria_type) .AppendLine(); isoMetadata.AppendFormat("\t\t" + Localization.System_type_0, sectionEntry.system_type) .AppendLine(); if(bootImage != null) { isoMetadata.AppendFormat("\t\t" + Localization.Bootable_image_SHA1_0, Sha1Context.Data(bootImage, out _)) .AppendLine(); } } else isoMetadata.AppendLine("\t\t" + Localization.Not_bootable); var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); if(flags.HasFlag(ElToritoFlags.ATAPI)) isoMetadata.AppendLine("\t\t" + Localization.Image_contains_ATAPI_drivers); if(flags.HasFlag(ElToritoFlags.SCSI)) isoMetadata.AppendLine("\t\t" + Localization.Image_contains_SCSI_drivers); if(!flags.HasFlag(ElToritoFlags.Continued)) continue; while(toritoOff < vdSector.Length) { ElToritoSectionEntryExtension sectionExtension = Marshal.ByteArrayToStructureLittleEndian(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); if(highSierraInfo) metadata.Type = FS_TYPE_HSF; else if(cdiInfo) metadata.Type = FS_TYPE_CDI; else metadata.Type = FS_TYPE_ISO; if(jolietvd != null) { metadata.VolumeName = decodedJolietVd.VolumeIdentifier; if(string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) metadata.SystemIdentifier = decodedVd.SystemIdentifier; else { metadata.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if(string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) metadata.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; else { metadata.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if(string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) metadata.PublisherIdentifier = decodedVd.PublisherIdentifier; else { metadata.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if(string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) metadata.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; else { metadata.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if(string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) metadata.ApplicationIdentifier = decodedVd.ApplicationIdentifier; else { metadata.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } metadata.CreationDate = decodedJolietVd.CreationTime; if(decodedJolietVd.HasModificationTime) metadata.ModificationDate = decodedJolietVd.ModificationTime; if(decodedJolietVd.HasExpirationTime) metadata.ExpirationDate = decodedJolietVd.ExpirationTime; if(decodedJolietVd.HasEffectiveTime) metadata.EffectiveDate = decodedJolietVd.EffectiveTime; } else { metadata.SystemIdentifier = decodedVd.SystemIdentifier; metadata.VolumeName = decodedVd.VolumeIdentifier; metadata.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; metadata.PublisherIdentifier = decodedVd.PublisherIdentifier; metadata.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; metadata.ApplicationIdentifier = decodedVd.ApplicationIdentifier; metadata.CreationDate = decodedVd.CreationTime; if(decodedVd.HasModificationTime) metadata.ModificationDate = decodedVd.ModificationTime; if(decodedVd.HasExpirationTime) metadata.ExpirationDate = decodedVd.ExpirationTime; if(decodedVd.HasEffectiveTime) metadata.EffectiveDate = decodedVd.EffectiveTime; } metadata.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; metadata.Clusters = decodedVd.Blocks; metadata.ClusterSize = decodedVd.BlockSize; information = isoMetadata.ToString(); } #endregion }