diff --git a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user b/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user index 0d6a3e7..1fb038a 100644 --- a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - True|2023-04-16T21:56:35.5065135Z;True|2023-04-17T09:22:54.8607008+12:00;True|2023-04-17T08:27:16.5281469+12:00;True|2023-04-17T08:22:02.0531219+12:00; + True|2023-04-17T12:00:51.4131559Z;True|2023-04-17T09:56:35.5065135+12:00;True|2023-04-17T09:22:54.8607008+12:00;True|2023-04-17T08:27:16.5281469+12:00;True|2023-04-17T08:22:02.0531219+12:00; \ No newline at end of file diff --git a/DiscUtils/Core/ApplePartitionMap/BlockZero.cs b/DiscUtils/ApplePartitionMap/BlockZero.cs similarity index 100% rename from DiscUtils/Core/ApplePartitionMap/BlockZero.cs rename to DiscUtils/ApplePartitionMap/BlockZero.cs diff --git a/DiscUtils/Core/ApplePartitionMap/PartitionMap.cs b/DiscUtils/ApplePartitionMap/PartitionMap.cs similarity index 100% rename from DiscUtils/Core/ApplePartitionMap/PartitionMap.cs rename to DiscUtils/ApplePartitionMap/PartitionMap.cs diff --git a/DiscUtils/Core/ApplePartitionMap/PartitionMapEntry.cs b/DiscUtils/ApplePartitionMap/PartitionMapEntry.cs similarity index 100% rename from DiscUtils/Core/ApplePartitionMap/PartitionMapEntry.cs rename to DiscUtils/ApplePartitionMap/PartitionMapEntry.cs diff --git a/DiscUtils/Core/ApplePartitionMap/PartitionMapFactory.cs b/DiscUtils/ApplePartitionMap/PartitionMapFactory.cs similarity index 100% rename from DiscUtils/Core/ApplePartitionMap/PartitionMapFactory.cs rename to DiscUtils/ApplePartitionMap/PartitionMapFactory.cs diff --git a/DiscUtils/Core/Archives/FileRecord.cs b/DiscUtils/Archives/FileRecord.cs similarity index 100% rename from DiscUtils/Core/Archives/FileRecord.cs rename to DiscUtils/Archives/FileRecord.cs diff --git a/DiscUtils/Core/Archives/TarFile.cs b/DiscUtils/Archives/TarFile.cs similarity index 100% rename from DiscUtils/Core/Archives/TarFile.cs rename to DiscUtils/Archives/TarFile.cs diff --git a/DiscUtils/Core/Archives/TarFileBuilder.cs b/DiscUtils/Archives/TarFileBuilder.cs similarity index 100% rename from DiscUtils/Core/Archives/TarFileBuilder.cs rename to DiscUtils/Archives/TarFileBuilder.cs diff --git a/DiscUtils/Core/Archives/TarHeader.cs b/DiscUtils/Archives/TarHeader.cs similarity index 100% rename from DiscUtils/Core/Archives/TarHeader.cs rename to DiscUtils/Archives/TarHeader.cs diff --git a/DiscUtils/Core/Archives/TarHeaderExtent.cs b/DiscUtils/Archives/TarHeaderExtent.cs similarity index 100% rename from DiscUtils/Core/Archives/TarHeaderExtent.cs rename to DiscUtils/Archives/TarHeaderExtent.cs diff --git a/DiscUtils/Core/Archives/UnixBuildFileRecord.cs b/DiscUtils/Archives/UnixBuildFileRecord.cs similarity index 100% rename from DiscUtils/Core/Archives/UnixBuildFileRecord.cs rename to DiscUtils/Archives/UnixBuildFileRecord.cs diff --git a/DiscUtils/Core/ChsAddress.cs b/DiscUtils/ChsAddress.cs similarity index 100% rename from DiscUtils/Core/ChsAddress.cs rename to DiscUtils/ChsAddress.cs diff --git a/DiscUtils/Core/ClusterMap.cs b/DiscUtils/ClusterMap.cs similarity index 100% rename from DiscUtils/Core/ClusterMap.cs rename to DiscUtils/ClusterMap.cs diff --git a/DiscUtils/Core/ClusterRoles.cs b/DiscUtils/ClusterRoles.cs similarity index 100% rename from DiscUtils/Core/ClusterRoles.cs rename to DiscUtils/ClusterRoles.cs diff --git a/DiscUtils/Core/Compression/Adler32.cs b/DiscUtils/Compression/Adler32.cs similarity index 100% rename from DiscUtils/Core/Compression/Adler32.cs rename to DiscUtils/Compression/Adler32.cs diff --git a/DiscUtils/Core/Compression/BZip2BlockDecoder.cs b/DiscUtils/Compression/BZip2BlockDecoder.cs similarity index 100% rename from DiscUtils/Core/Compression/BZip2BlockDecoder.cs rename to DiscUtils/Compression/BZip2BlockDecoder.cs diff --git a/DiscUtils/Core/Compression/BZip2CombinedHuffmanTrees.cs b/DiscUtils/Compression/BZip2CombinedHuffmanTrees.cs similarity index 100% rename from DiscUtils/Core/Compression/BZip2CombinedHuffmanTrees.cs rename to DiscUtils/Compression/BZip2CombinedHuffmanTrees.cs diff --git a/DiscUtils/Core/Compression/BZip2DecoderStream.cs b/DiscUtils/Compression/BZip2DecoderStream.cs similarity index 100% rename from DiscUtils/Core/Compression/BZip2DecoderStream.cs rename to DiscUtils/Compression/BZip2DecoderStream.cs diff --git a/DiscUtils/Core/Compression/BZip2Randomizer.cs b/DiscUtils/Compression/BZip2Randomizer.cs similarity index 100% rename from DiscUtils/Core/Compression/BZip2Randomizer.cs rename to DiscUtils/Compression/BZip2Randomizer.cs diff --git a/DiscUtils/Core/Compression/BZip2RleStream.cs b/DiscUtils/Compression/BZip2RleStream.cs similarity index 100% rename from DiscUtils/Core/Compression/BZip2RleStream.cs rename to DiscUtils/Compression/BZip2RleStream.cs diff --git a/DiscUtils/Core/Compression/BigEndianBitStream.cs b/DiscUtils/Compression/BigEndianBitStream.cs similarity index 100% rename from DiscUtils/Core/Compression/BigEndianBitStream.cs rename to DiscUtils/Compression/BigEndianBitStream.cs diff --git a/DiscUtils/Core/Compression/BitStream.cs b/DiscUtils/Compression/BitStream.cs similarity index 100% rename from DiscUtils/Core/Compression/BitStream.cs rename to DiscUtils/Compression/BitStream.cs diff --git a/DiscUtils/Core/Compression/BlockCompressor.cs b/DiscUtils/Compression/BlockCompressor.cs similarity index 100% rename from DiscUtils/Core/Compression/BlockCompressor.cs rename to DiscUtils/Compression/BlockCompressor.cs diff --git a/DiscUtils/Core/Compression/CompressionResult.cs b/DiscUtils/Compression/CompressionResult.cs similarity index 100% rename from DiscUtils/Core/Compression/CompressionResult.cs rename to DiscUtils/Compression/CompressionResult.cs diff --git a/DiscUtils/Core/Compression/DataBlockTransform.cs b/DiscUtils/Compression/DataBlockTransform.cs similarity index 100% rename from DiscUtils/Core/Compression/DataBlockTransform.cs rename to DiscUtils/Compression/DataBlockTransform.cs diff --git a/DiscUtils/Core/Compression/HuffmanTree.cs b/DiscUtils/Compression/HuffmanTree.cs similarity index 100% rename from DiscUtils/Core/Compression/HuffmanTree.cs rename to DiscUtils/Compression/HuffmanTree.cs diff --git a/DiscUtils/Core/Compression/InverseBurrowsWheeler.cs b/DiscUtils/Compression/InverseBurrowsWheeler.cs similarity index 100% rename from DiscUtils/Core/Compression/InverseBurrowsWheeler.cs rename to DiscUtils/Compression/InverseBurrowsWheeler.cs diff --git a/DiscUtils/Core/Compression/MoveToFront.cs b/DiscUtils/Compression/MoveToFront.cs similarity index 100% rename from DiscUtils/Core/Compression/MoveToFront.cs rename to DiscUtils/Compression/MoveToFront.cs diff --git a/DiscUtils/Core/Compression/SizedDeflateStream.cs b/DiscUtils/Compression/SizedDeflateStream.cs similarity index 100% rename from DiscUtils/Core/Compression/SizedDeflateStream.cs rename to DiscUtils/Compression/SizedDeflateStream.cs diff --git a/DiscUtils/Core/Compression/ZlibBuffer.cs b/DiscUtils/Compression/ZlibBuffer.cs similarity index 100% rename from DiscUtils/Core/Compression/ZlibBuffer.cs rename to DiscUtils/Compression/ZlibBuffer.cs diff --git a/DiscUtils/Core/Compression/ZlibStream.cs b/DiscUtils/Compression/ZlibStream.cs similarity index 100% rename from DiscUtils/Core/Compression/ZlibStream.cs rename to DiscUtils/Compression/ZlibStream.cs diff --git a/DiscUtils/Core/DiscUtils.Core.csproj b/DiscUtils/Core/DiscUtils.Core.csproj deleted file mode 100644 index 8abd0ad..0000000 --- a/DiscUtils/Core/DiscUtils.Core.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Implementation of the ISO, UDF, FAT and NTFS file systems is now fairly stable. VHD, XVA, VMDK and VDI disk formats are implemented, as well as read/write Registry support. The library also includes a simple iSCSI initiator, for accessing disks via iSCSI and an NFS client implementation. - DiscUtils (for .NET and .NET Core), core library that supports parts of DiscUtils - Kenneth Bell;Quamotion;LordMike - DiscUtils;VHD;VDI;XVA;VMDK;ISO;NTFS;EXT2FS - - - - - - - diff --git a/DiscUtils/Core/CoreCompat/EncodingHelper.cs b/DiscUtils/CoreCompat/EncodingHelper.cs similarity index 100% rename from DiscUtils/Core/CoreCompat/EncodingHelper.cs rename to DiscUtils/CoreCompat/EncodingHelper.cs diff --git a/DiscUtils/Core/CoreCompat/ReflectionHelper.cs b/DiscUtils/CoreCompat/ReflectionHelper.cs similarity index 100% rename from DiscUtils/Core/CoreCompat/ReflectionHelper.cs rename to DiscUtils/CoreCompat/ReflectionHelper.cs diff --git a/DiscUtils/Core/CoreCompat/StringExtensions.cs b/DiscUtils/CoreCompat/StringExtensions.cs similarity index 100% rename from DiscUtils/Core/CoreCompat/StringExtensions.cs rename to DiscUtils/CoreCompat/StringExtensions.cs diff --git a/DiscUtils/Core/DiscDirectoryInfo.cs b/DiscUtils/DiscDirectoryInfo.cs similarity index 100% rename from DiscUtils/Core/DiscDirectoryInfo.cs rename to DiscUtils/DiscDirectoryInfo.cs diff --git a/DiscUtils/Core/DiscFileInfo.cs b/DiscUtils/DiscFileInfo.cs similarity index 100% rename from DiscUtils/Core/DiscFileInfo.cs rename to DiscUtils/DiscFileInfo.cs diff --git a/DiscUtils/Core/DiscFileLocator.cs b/DiscUtils/DiscFileLocator.cs similarity index 100% rename from DiscUtils/Core/DiscFileLocator.cs rename to DiscUtils/DiscFileLocator.cs diff --git a/DiscUtils/Core/DiscFileSystem.cs b/DiscUtils/DiscFileSystem.cs similarity index 100% rename from DiscUtils/Core/DiscFileSystem.cs rename to DiscUtils/DiscFileSystem.cs diff --git a/DiscUtils/Core/DiscFileSystemChecker.cs b/DiscUtils/DiscFileSystemChecker.cs similarity index 100% rename from DiscUtils/Core/DiscFileSystemChecker.cs rename to DiscUtils/DiscFileSystemChecker.cs diff --git a/DiscUtils/Core/DiscFileSystemInfo.cs b/DiscUtils/DiscFileSystemInfo.cs similarity index 100% rename from DiscUtils/Core/DiscFileSystemInfo.cs rename to DiscUtils/DiscFileSystemInfo.cs diff --git a/DiscUtils/Core/DiscFileSystemOptions.cs b/DiscUtils/DiscFileSystemOptions.cs similarity index 100% rename from DiscUtils/Core/DiscFileSystemOptions.cs rename to DiscUtils/DiscFileSystemOptions.cs diff --git a/DiscUtils/Core/DiskImageBuilder.cs b/DiscUtils/DiskImageBuilder.cs similarity index 100% rename from DiscUtils/Core/DiskImageBuilder.cs rename to DiscUtils/DiskImageBuilder.cs diff --git a/DiscUtils/Core/DiskImageFileSpecification.cs b/DiscUtils/DiskImageFileSpecification.cs similarity index 100% rename from DiscUtils/Core/DiskImageFileSpecification.cs rename to DiscUtils/DiskImageFileSpecification.cs diff --git a/DiscUtils/Core/FileLocator.cs b/DiscUtils/FileLocator.cs similarity index 100% rename from DiscUtils/Core/FileLocator.cs rename to DiscUtils/FileLocator.cs diff --git a/DiscUtils/Core/FileSystemInfo.cs b/DiscUtils/FileSystemInfo.cs similarity index 100% rename from DiscUtils/Core/FileSystemInfo.cs rename to DiscUtils/FileSystemInfo.cs diff --git a/DiscUtils/Core/FileSystemManager.cs b/DiscUtils/FileSystemManager.cs similarity index 100% rename from DiscUtils/Core/FileSystemManager.cs rename to DiscUtils/FileSystemManager.cs diff --git a/DiscUtils/Core/FileSystemParameters.cs b/DiscUtils/FileSystemParameters.cs similarity index 100% rename from DiscUtils/Core/FileSystemParameters.cs rename to DiscUtils/FileSystemParameters.cs diff --git a/DiscUtils/Core/FileTransport.cs b/DiscUtils/FileTransport.cs similarity index 100% rename from DiscUtils/Core/FileTransport.cs rename to DiscUtils/FileTransport.cs diff --git a/DiscUtils/Core/FloppyDiskType.cs b/DiscUtils/FloppyDiskType.cs similarity index 100% rename from DiscUtils/Core/FloppyDiskType.cs rename to DiscUtils/FloppyDiskType.cs diff --git a/DiscUtils/Core/GenericDiskAdapterType.cs b/DiscUtils/GenericDiskAdapterType.cs similarity index 100% rename from DiscUtils/Core/GenericDiskAdapterType.cs rename to DiscUtils/GenericDiskAdapterType.cs diff --git a/DiscUtils/Core/Geometry.cs b/DiscUtils/Geometry.cs similarity index 100% rename from DiscUtils/Core/Geometry.cs rename to DiscUtils/Geometry.cs diff --git a/DiscUtils/Core/GeometryCalculation.cs b/DiscUtils/GeometryCalculation.cs similarity index 100% rename from DiscUtils/Core/GeometryCalculation.cs rename to DiscUtils/GeometryCalculation.cs diff --git a/DiscUtils/Core/GeometryTranslation.cs b/DiscUtils/GeometryTranslation.cs similarity index 100% rename from DiscUtils/Core/GeometryTranslation.cs rename to DiscUtils/GeometryTranslation.cs diff --git a/DiscUtils/Core/IClusterBasedFileSystem.cs b/DiscUtils/IClusterBasedFileSystem.cs similarity index 100% rename from DiscUtils/Core/IClusterBasedFileSystem.cs rename to DiscUtils/IClusterBasedFileSystem.cs diff --git a/DiscUtils/Core/IDiagnosticTraceable.cs b/DiscUtils/IDiagnosticTraceable.cs similarity index 100% rename from DiscUtils/Core/IDiagnosticTraceable.cs rename to DiscUtils/IDiagnosticTraceable.cs diff --git a/DiscUtils/Core/IFileSystem.cs b/DiscUtils/IFileSystem.cs similarity index 100% rename from DiscUtils/Core/IFileSystem.cs rename to DiscUtils/IFileSystem.cs diff --git a/DiscUtils/Core/IUnixFileSystem.cs b/DiscUtils/IUnixFileSystem.cs similarity index 100% rename from DiscUtils/Core/IUnixFileSystem.cs rename to DiscUtils/IUnixFileSystem.cs diff --git a/DiscUtils/Core/IWindowsFileSystem.cs b/DiscUtils/IWindowsFileSystem.cs similarity index 100% rename from DiscUtils/Core/IWindowsFileSystem.cs rename to DiscUtils/IWindowsFileSystem.cs diff --git a/DiscUtils/Core/Internal/Crc32.cs b/DiscUtils/Internal/Crc32.cs similarity index 100% rename from DiscUtils/Core/Internal/Crc32.cs rename to DiscUtils/Internal/Crc32.cs diff --git a/DiscUtils/Core/Internal/Crc32Algorithm.cs b/DiscUtils/Internal/Crc32Algorithm.cs similarity index 100% rename from DiscUtils/Core/Internal/Crc32Algorithm.cs rename to DiscUtils/Internal/Crc32Algorithm.cs diff --git a/DiscUtils/Core/Internal/Crc32BigEndian.cs b/DiscUtils/Internal/Crc32BigEndian.cs similarity index 100% rename from DiscUtils/Core/Internal/Crc32BigEndian.cs rename to DiscUtils/Internal/Crc32BigEndian.cs diff --git a/DiscUtils/Core/Internal/Crc32LittleEndian.cs b/DiscUtils/Internal/Crc32LittleEndian.cs similarity index 100% rename from DiscUtils/Core/Internal/Crc32LittleEndian.cs rename to DiscUtils/Internal/Crc32LittleEndian.cs diff --git a/DiscUtils/Core/Internal/LocalFileLocator.cs b/DiscUtils/Internal/LocalFileLocator.cs similarity index 100% rename from DiscUtils/Core/Internal/LocalFileLocator.cs rename to DiscUtils/Internal/LocalFileLocator.cs diff --git a/DiscUtils/Core/Internal/LogicalVolumeFactory.cs b/DiscUtils/Internal/LogicalVolumeFactory.cs similarity index 100% rename from DiscUtils/Core/Internal/LogicalVolumeFactory.cs rename to DiscUtils/Internal/LogicalVolumeFactory.cs diff --git a/DiscUtils/Core/Internal/LogicalVolumeFactoryAttribute.cs b/DiscUtils/Internal/LogicalVolumeFactoryAttribute.cs similarity index 100% rename from DiscUtils/Core/Internal/LogicalVolumeFactoryAttribute.cs rename to DiscUtils/Internal/LogicalVolumeFactoryAttribute.cs diff --git a/DiscUtils/Core/Internal/ObjectCache.cs b/DiscUtils/Internal/ObjectCache.cs similarity index 100% rename from DiscUtils/Core/Internal/ObjectCache.cs rename to DiscUtils/Internal/ObjectCache.cs diff --git a/DiscUtils/Core/Internal/Utilities.cs b/DiscUtils/Internal/Utilities.cs similarity index 100% rename from DiscUtils/Core/Internal/Utilities.cs rename to DiscUtils/Internal/Utilities.cs diff --git a/DiscUtils/Core/Internal/VirtualDiskFactory.cs b/DiscUtils/Internal/VirtualDiskFactory.cs similarity index 100% rename from DiscUtils/Core/Internal/VirtualDiskFactory.cs rename to DiscUtils/Internal/VirtualDiskFactory.cs diff --git a/DiscUtils/Core/Internal/VirtualDiskFactoryAttribute.cs b/DiscUtils/Internal/VirtualDiskFactoryAttribute.cs similarity index 100% rename from DiscUtils/Core/Internal/VirtualDiskFactoryAttribute.cs rename to DiscUtils/Internal/VirtualDiskFactoryAttribute.cs diff --git a/DiscUtils/Core/Internal/VirtualDiskTransport.cs b/DiscUtils/Internal/VirtualDiskTransport.cs similarity index 100% rename from DiscUtils/Core/Internal/VirtualDiskTransport.cs rename to DiscUtils/Internal/VirtualDiskTransport.cs diff --git a/DiscUtils/Core/Internal/VirtualDiskTransportAttribute.cs b/DiscUtils/Internal/VirtualDiskTransportAttribute.cs similarity index 100% rename from DiscUtils/Core/Internal/VirtualDiskTransportAttribute.cs rename to DiscUtils/Internal/VirtualDiskTransportAttribute.cs diff --git a/DiscUtils/Core/InvalidFileSystemException.cs b/DiscUtils/InvalidFileSystemException.cs similarity index 100% rename from DiscUtils/Core/InvalidFileSystemException.cs rename to DiscUtils/InvalidFileSystemException.cs diff --git a/DiscUtils/Iso9660/BaseVolumeDescriptor.cs b/DiscUtils/Iso9660/BaseVolumeDescriptor.cs index 3aeb519..bab65d6 100644 --- a/DiscUtils/Iso9660/BaseVolumeDescriptor.cs +++ b/DiscUtils/Iso9660/BaseVolumeDescriptor.cs @@ -32,14 +32,12 @@ namespace DiscUtils.Iso9660 public readonly string StandardIdentifier; public readonly VolumeDescriptorType VolumeDescriptorType; public readonly byte VolumeDescriptorVersion; - private int SectorSize; - public BaseVolumeDescriptor(VolumeDescriptorType type, byte version, int sectorSize) + public BaseVolumeDescriptor(VolumeDescriptorType type, byte version) { VolumeDescriptorType = type; StandardIdentifier = "CD001"; VolumeDescriptorVersion = version; - SectorSize = sectorSize; } public BaseVolumeDescriptor(byte[] src, int offset) @@ -51,7 +49,7 @@ namespace DiscUtils.Iso9660 internal virtual void WriteTo(byte[] buffer, int offset) { - Array.Clear(buffer, offset, SectorSize); + Array.Clear(buffer, offset, IsoUtilities.SectorSize); buffer[offset] = (byte)VolumeDescriptorType; IsoUtilities.WriteAChars(buffer, offset + 1, 5, StandardIdentifier); buffer[offset + 6] = VolumeDescriptorVersion; diff --git a/DiscUtils/Iso9660/BootVolumeDescriptor.cs b/DiscUtils/Iso9660/BootVolumeDescriptor.cs index d32908a..335711b 100644 --- a/DiscUtils/Iso9660/BootVolumeDescriptor.cs +++ b/DiscUtils/Iso9660/BootVolumeDescriptor.cs @@ -28,8 +28,8 @@ namespace DiscUtils.Iso9660 { public const string ElToritoSystemIdentifier = "EL TORITO SPECIFICATION"; - public BootVolumeDescriptor(uint catalogSector, int sectorSize) - : base(VolumeDescriptorType.Boot, 1, sectorSize) + public BootVolumeDescriptor(uint catalogSector) + : base(VolumeDescriptorType.Boot, 1) { CatalogSector = catalogSector; } diff --git a/DiscUtils/Iso9660/BootVolumeDescriptorRegion.cs b/DiscUtils/Iso9660/BootVolumeDescriptorRegion.cs index 8e73eb0..f98d1b7 100644 --- a/DiscUtils/Iso9660/BootVolumeDescriptorRegion.cs +++ b/DiscUtils/Iso9660/BootVolumeDescriptorRegion.cs @@ -26,15 +26,15 @@ namespace DiscUtils.Iso9660 { private readonly BootVolumeDescriptor _descriptor; - public BootVolumeDescriptorRegion(BootVolumeDescriptor descriptor, long start, int sectorSize) - : base(start, sectorSize) + public BootVolumeDescriptorRegion(BootVolumeDescriptor descriptor, long start) + : base(start) { _descriptor = descriptor; } protected override byte[] GetBlockData() { - byte[] buffer = new byte[Length]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; _descriptor.WriteTo(buffer, 0); return buffer; } diff --git a/DiscUtils/Iso9660/BuildDirectoryInfo.cs b/DiscUtils/Iso9660/BuildDirectoryInfo.cs index 3212654..2fdc382 100644 --- a/DiscUtils/Iso9660/BuildDirectoryInfo.cs +++ b/DiscUtils/Iso9660/BuildDirectoryInfo.cs @@ -39,15 +39,13 @@ namespace DiscUtils.Iso9660 private readonly BuildDirectoryInfo _parent; private List _sortedMembers; - private int _sectorSize; - internal BuildDirectoryInfo(string name, BuildDirectoryInfo parent, int sectorSize) + internal BuildDirectoryInfo(string name, BuildDirectoryInfo parent) : base(name, MakeShortDirName(name, parent)) { _parent = parent == null ? this : parent; HierarchyDepth = parent == null ? 0 : parent.HierarchyDepth + 1; _members = new Dictionary(); - _sectorSize = sectorSize; } internal int HierarchyDepth { get; } @@ -89,16 +87,16 @@ namespace DiscUtils.Iso9660 // If this record would span a sector boundary, then the current sector is // zero-padded, and the record goes at the start of the next sector. - if (total % _sectorSize + recordSize > _sectorSize) + if (total % IsoUtilities.SectorSize + recordSize > IsoUtilities.SectorSize) { - long padLength = _sectorSize - total % _sectorSize; + long padLength = IsoUtilities.SectorSize - total % IsoUtilities.SectorSize; total += padLength; } total += recordSize; } - return MathUtilities.RoundUp(total, _sectorSize); + return MathUtilities.RoundUp(total, IsoUtilities.SectorSize); } internal uint GetPathTableEntrySize(Encoding enc) @@ -115,31 +113,31 @@ namespace DiscUtils.Iso9660 List sorted = GetSortedMembers(); // Two pseudo entries, effectively '.' and '..' - pos += WriteMember(this, "\0", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); - pos += WriteMember(_parent, "\x01", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); + pos += WriteMember(this, "\0", Encoding.ASCII, buffer, offset + pos, locationTable, enc); + pos += WriteMember(_parent, "\x01", Encoding.ASCII, buffer, offset + pos, locationTable, enc); foreach (BuildDirectoryMember m in sorted) { uint recordSize = m.GetDirectoryRecordSize(enc); - if (pos % _sectorSize + recordSize > _sectorSize) + if (pos % IsoUtilities.SectorSize + recordSize > IsoUtilities.SectorSize) { - int padLength = _sectorSize - pos % _sectorSize; + int padLength = IsoUtilities.SectorSize - pos % IsoUtilities.SectorSize; Array.Clear(buffer, offset + pos, padLength); pos += padLength; } - pos += WriteMember(m, null, enc, buffer, offset + pos, locationTable, enc, _sectorSize); + pos += WriteMember(m, null, enc, buffer, offset + pos, locationTable, enc); } // Ensure final padding data is zero'd - int finalPadLength = MathUtilities.RoundUp(pos, _sectorSize) - pos; + int finalPadLength = MathUtilities.RoundUp(pos, IsoUtilities.SectorSize) - pos; Array.Clear(buffer, offset + pos, finalPadLength); return pos + finalPadLength; } - private static int WriteMember(BuildDirectoryMember m, string nameOverride, Encoding nameEnc, byte[] buffer, int offset, Dictionary locationTable, Encoding dataEnc, int sectorSize) + private static int WriteMember(BuildDirectoryMember m, string nameOverride, Encoding nameEnc, byte[] buffer, int offset, Dictionary locationTable, Encoding dataEnc) { DirectoryRecord dr = new DirectoryRecord(); dr.FileIdentifier = m.PickName(nameOverride, nameEnc); diff --git a/DiscUtils/Iso9660/BuildParameters.cs b/DiscUtils/Iso9660/BuildParameters.cs index 63f7658..1ac3892 100644 --- a/DiscUtils/Iso9660/BuildParameters.cs +++ b/DiscUtils/Iso9660/BuildParameters.cs @@ -28,11 +28,8 @@ namespace DiscUtils.Iso9660 { VolumeIdentifier = string.Empty; UseJoliet = true; - SectorSize = 2048; } - public int SectorSize { get; set; } - public bool UseJoliet { get; set; } public string VolumeIdentifier { get; set; } diff --git a/DiscUtils/Iso9660/CDBuilder.cs b/DiscUtils/Iso9660/CDBuilder.cs index 0d7a275..7042d1e 100644 --- a/DiscUtils/Iso9660/CDBuilder.cs +++ b/DiscUtils/Iso9660/CDBuilder.cs @@ -51,7 +51,6 @@ namespace DiscUtils.Iso9660 private readonly List _files; private readonly BuildDirectoryInfo _rootDirectory; - private readonly int _sectorSize; /// /// Initializes a new instance of the CDBuilder class. @@ -60,24 +59,7 @@ namespace DiscUtils.Iso9660 { _files = new List(); _dirs = new List(); - _sectorSize = 2048; - _rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize); - _dirs.Add(_rootDirectory); - - _buildParams = new BuildParameters(); - _buildParams.UseJoliet = true; - } - - - /// - /// Initializes a new instance of the CDBuilder class. - /// - public CDBuilder(int sectorSize) - { - _files = new List(); - _dirs = new List(); - _sectorSize = sectorSize; - _rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize); + _rootDirectory = new BuildDirectoryInfo("\0", null); _dirs.Add(_rootDirectory); _buildParams = new BuildParameters(); @@ -270,10 +252,10 @@ namespace DiscUtils.Iso9660 Dictionary supplementaryLocationTable = new Dictionary(); - long focus = DiskStart + 3 * _buildParams.SectorSize; // Primary, Supplementary, End (fixed at end...) + long focus = DiskStart + 3 * IsoUtilities.SectorSize; // Primary, Supplementary, End (fixed at end...) if (_bootEntry != null) { - focus += _buildParams.SectorSize; + focus += IsoUtilities.SectorSize; } // #################################################################### @@ -283,21 +265,21 @@ namespace DiscUtils.Iso9660 if (_bootEntry != null) { long bootImagePos = focus; - Stream realBootImage = PatchBootImage(_bootImage, (uint)(DiskStart / _buildParams.SectorSize), - (uint)(bootImagePos / _buildParams.SectorSize)); + Stream realBootImage = PatchBootImage(_bootImage, (uint)(DiskStart / IsoUtilities.SectorSize), + (uint)(bootImagePos / IsoUtilities.SectorSize)); BuilderStreamExtent bootImageExtent = new BuilderStreamExtent(focus, realBootImage); fixedRegions.Add(bootImageExtent); - focus += MathUtilities.RoundUp(bootImageExtent.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(bootImageExtent.Length, IsoUtilities.SectorSize); bootCatalogPos = focus; - byte[] bootCatalog = new byte[_buildParams.SectorSize]; + byte[] bootCatalog = new byte[IsoUtilities.SectorSize]; BootValidationEntry bve = new BootValidationEntry(); bve.WriteTo(bootCatalog, 0x00); - _bootEntry.ImageStart = (uint)MathUtilities.Ceil(bootImagePos, _buildParams.SectorSize); + _bootEntry.ImageStart = (uint)MathUtilities.Ceil(bootImagePos, IsoUtilities.SectorSize); _bootEntry.SectorCount = (ushort)MathUtilities.Ceil(_bootImage.Length, Sizes.Sector); _bootEntry.WriteTo(bootCatalog, 0x20); fixedRegions.Add(new BuilderBufferExtent(bootCatalogPos, bootCatalog)); - focus += _buildParams.SectorSize; + focus += IsoUtilities.SectorSize; } // #################################################################### @@ -307,8 +289,8 @@ namespace DiscUtils.Iso9660 // Find end of the file data, fixing the files in place as we go foreach (BuildFileInfo fi in _files) { - primaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize)); - supplementaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize)); + primaryLocationTable.Add(fi, (uint)(focus / IsoUtilities.SectorSize)); + supplementaryLocationTable.Add(fi, (uint)(focus / IsoUtilities.SectorSize)); FileExtent extent = new FileExtent(fi, focus); // Only remember files of non-zero length (otherwise we'll stomp on a valid file) @@ -317,7 +299,7 @@ namespace DiscUtils.Iso9660 fixedRegions.Add(extent); } - focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(extent.Length, IsoUtilities.SectorSize); } // #################################################################### @@ -332,20 +314,20 @@ namespace DiscUtils.Iso9660 long startOfFirstDirData = focus; foreach (BuildDirectoryInfo di in _dirs) { - primaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize)); + primaryLocationTable.Add(di, (uint)(focus / IsoUtilities.SectorSize)); DirectoryExtent extent = new DirectoryExtent(di, primaryLocationTable, Encoding.ASCII, focus); fixedRegions.Add(extent); - focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(extent.Length, IsoUtilities.SectorSize); } // Find end of the second directory table, fixing supplementary directories in place. long startOfSecondDirData = focus; foreach (BuildDirectoryInfo di in _dirs) { - supplementaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize)); + supplementaryLocationTable.Add(di, (uint)(focus / IsoUtilities.SectorSize)); DirectoryExtent extent = new DirectoryExtent(di, supplementaryLocationTable, suppEncoding, focus); fixedRegions.Add(extent); - focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(extent.Length, IsoUtilities.SectorSize); } // #################################################################### @@ -362,24 +344,24 @@ namespace DiscUtils.Iso9660 long startOfFirstPathTable = focus; PathTable pathTable = new PathTable(false, Encoding.ASCII, _dirs, primaryLocationTable, focus); fixedRegions.Add(pathTable); - focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(pathTable.Length, IsoUtilities.SectorSize); long primaryPathTableLength = pathTable.Length; long startOfSecondPathTable = focus; pathTable = new PathTable(true, Encoding.ASCII, _dirs, primaryLocationTable, focus); fixedRegions.Add(pathTable); - focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(pathTable.Length, IsoUtilities.SectorSize); long startOfThirdPathTable = focus; pathTable = new PathTable(false, suppEncoding, _dirs, supplementaryLocationTable, focus); fixedRegions.Add(pathTable); - focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(pathTable.Length, IsoUtilities.SectorSize); long supplementaryPathTableLength = pathTable.Length; long startOfFourthPathTable = focus; pathTable = new PathTable(true, suppEncoding, _dirs, supplementaryLocationTable, focus); fixedRegions.Add(pathTable); - focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + focus += MathUtilities.RoundUp(pathTable.Length, IsoUtilities.SectorSize); // Find the end of the disk totalLength = focus; @@ -390,45 +372,43 @@ namespace DiscUtils.Iso9660 int regionIdx = 0; focus = DiskStart; PrimaryVolumeDescriptor pvDesc = new PrimaryVolumeDescriptor( - (uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize + (uint)(totalLength / IsoUtilities.SectorSize), // VolumeSpaceSize (uint)primaryPathTableLength, // PathTableSize - (uint)(startOfFirstPathTable / _buildParams.SectorSize), // TypeLPathTableLocation - (uint)(startOfSecondPathTable / _buildParams.SectorSize), // TypeMPathTableLocation - (uint)(startOfFirstDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent + (uint)(startOfFirstPathTable / IsoUtilities.SectorSize), // TypeLPathTableLocation + (uint)(startOfSecondPathTable / IsoUtilities.SectorSize), // TypeMPathTableLocation + (uint)(startOfFirstDirData / IsoUtilities.SectorSize), // RootDirectory.LocationOfExtent (uint)_rootDirectory.GetDataSize(Encoding.ASCII), // RootDirectory.DataLength - buildTime, - _sectorSize); + buildTime); pvDesc.VolumeIdentifier = _buildParams.VolumeIdentifier; - PrimaryVolumeDescriptorRegion pvdr = new PrimaryVolumeDescriptorRegion(pvDesc, focus, _sectorSize); + PrimaryVolumeDescriptorRegion pvdr = new PrimaryVolumeDescriptorRegion(pvDesc, focus); fixedRegions.Insert(regionIdx++, pvdr); - focus += _buildParams.SectorSize; + focus += IsoUtilities.SectorSize; if (_bootEntry != null) { BootVolumeDescriptor bvDesc = new BootVolumeDescriptor( - (uint)(bootCatalogPos / _buildParams.SectorSize), _buildParams.SectorSize); - BootVolumeDescriptorRegion bvdr = new BootVolumeDescriptorRegion(bvDesc, focus, _buildParams.SectorSize); + (uint)(bootCatalogPos / IsoUtilities.SectorSize)); + BootVolumeDescriptorRegion bvdr = new BootVolumeDescriptorRegion(bvDesc, focus); fixedRegions.Insert(regionIdx++, bvdr); - focus += _buildParams.SectorSize; + focus += IsoUtilities.SectorSize; } SupplementaryVolumeDescriptor svDesc = new SupplementaryVolumeDescriptor( - (uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize + (uint)(totalLength / IsoUtilities.SectorSize), // VolumeSpaceSize (uint)supplementaryPathTableLength, // PathTableSize - (uint)(startOfThirdPathTable / _buildParams.SectorSize), // TypeLPathTableLocation - (uint)(startOfFourthPathTable / _buildParams.SectorSize), // TypeMPathTableLocation - (uint)(startOfSecondDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent + (uint)(startOfThirdPathTable / IsoUtilities.SectorSize), // TypeLPathTableLocation + (uint)(startOfFourthPathTable / IsoUtilities.SectorSize), // TypeMPathTableLocation + (uint)(startOfSecondDirData / IsoUtilities.SectorSize), // RootDirectory.LocationOfExtent (uint)_rootDirectory.GetDataSize(suppEncoding), // RootDirectory.DataLength buildTime, - suppEncoding, - _sectorSize); + suppEncoding); svDesc.VolumeIdentifier = _buildParams.VolumeIdentifier; - SupplementaryVolumeDescriptorRegion svdr = new SupplementaryVolumeDescriptorRegion(svDesc, focus, _sectorSize); + SupplementaryVolumeDescriptorRegion svdr = new SupplementaryVolumeDescriptorRegion(svDesc, focus); fixedRegions.Insert(regionIdx++, svdr); - focus += _buildParams.SectorSize; + focus += IsoUtilities.SectorSize; - VolumeDescriptorSetTerminator evDesc = new VolumeDescriptorSetTerminator(_sectorSize); - VolumeDescriptorSetTerminatorRegion evdr = new VolumeDescriptorSetTerminatorRegion(evDesc, focus, _sectorSize); + VolumeDescriptorSetTerminator evDesc = new VolumeDescriptorSetTerminator(); + VolumeDescriptorSetTerminatorRegion evdr = new VolumeDescriptorSetTerminatorRegion(evDesc, focus); fixedRegions.Insert(regionIdx++, evdr); return fixedRegions; @@ -491,7 +471,7 @@ namespace DiscUtils.Iso9660 if (createMissing) { // This directory doesn't exist, create it... - BuildDirectoryInfo di = new BuildDirectoryInfo(path[i], focus, _sectorSize); + BuildDirectoryInfo di = new BuildDirectoryInfo(path[i], focus); focus.Add(di); _dirs.Add(di); focus = di; diff --git a/DiscUtils/Iso9660/CDReader.cs b/DiscUtils/Iso9660/CDReader.cs index 82bfe41..8a544ca 100644 --- a/DiscUtils/Iso9660/CDReader.cs +++ b/DiscUtils/Iso9660/CDReader.cs @@ -37,9 +37,7 @@ namespace DiscUtils.Iso9660 /// The stream to read the ISO image from. /// Whether to read Joliet extensions. public CDReader(Stream data, bool joliet) - : base(new VfsCDReader(data, joliet, false, 2048)) - { - } + : base(new VfsCDReader(data, joliet, false)) {} /// /// Initializes a new instance of the CDReader class. @@ -48,27 +46,7 @@ namespace DiscUtils.Iso9660 /// Whether to read Joliet extensions. /// Hides version numbers (e.g. ";1") from the end of files. public CDReader(Stream data, bool joliet, bool hideVersions) - : base(new VfsCDReader(data, joliet, hideVersions, 2048)) { } - - - /// - /// Initializes a new instance of the CDReader class. - /// - /// The stream to read the ISO image from. - /// Whether to read Joliet extensions. - /// The size of a sector - public CDReader(Stream data, bool joliet, int sectorSize) - : base(new VfsCDReader(data, joliet, false, sectorSize)) { - } - - /// - /// Initializes a new instance of the CDReader class. - /// - /// The stream to read the ISO image from. - /// Whether to read Joliet extensions. - /// Hides version numbers (e.g. ";1") from the end of files. - public CDReader(Stream data, bool joliet, bool hideVersions, int sectorSize) - : base(new VfsCDReader(data, joliet, hideVersions, sectorSize)) {} + : base(new VfsCDReader(data, joliet, hideVersions)) {} /// /// Gets which of the Iso9660 variants is being used. @@ -196,18 +174,18 @@ namespace DiscUtils.Iso9660 /// /// The stream to inspect. /// true if the stream contains an ISO file system, else false. - public static bool Detect(Stream data, int sectorSize) + public static bool Detect(Stream data) { - byte[] buffer = new byte[sectorSize]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; - if (data.Length < 0x8000 + sectorSize) + if (data.Length < 0x8000 + IsoUtilities.SectorSize) { return false; } data.Position = 0x8000; - int numRead = StreamUtilities.ReadMaximum(data, buffer, 0, sectorSize); - if (numRead != sectorSize) + int numRead = StreamUtilities.ReadMaximum(data, buffer, 0, IsoUtilities.SectorSize); + if (numRead != IsoUtilities.SectorSize) { return false; } diff --git a/DiscUtils/Iso9660/CommonVolumeDescriptor.cs b/DiscUtils/Iso9660/CommonVolumeDescriptor.cs index f4a4e0a..7ef3bf6 100644 --- a/DiscUtils/Iso9660/CommonVolumeDescriptor.cs +++ b/DiscUtils/Iso9660/CommonVolumeDescriptor.cs @@ -97,9 +97,8 @@ namespace DiscUtils.Iso9660 uint rootDirExtentLocation, uint rootDirDataLength, DateTime buildTime, - Encoding enc, - int sectorSize) - : base(type, version, sectorSize) + Encoding enc) + : base(type, version) { CharacterEncoding = enc; @@ -108,7 +107,7 @@ namespace DiscUtils.Iso9660 VolumeSpaceSize = volumeSpaceSize; VolumeSetSize = 1; VolumeSequenceNumber = 1; - LogicalBlockSize = (ushort)sectorSize; + LogicalBlockSize = IsoUtilities.SectorSize; PathTableSize = pathTableSize; TypeLPathTableLocation = typeLPathTableLocation; ////OptionalTypeLPathTableLocation = 0; diff --git a/DiscUtils/Iso9660/DiscUtils.Iso9660.csproj b/DiscUtils/Iso9660/DiscUtils.Iso9660.csproj deleted file mode 100644 index e9968cf..0000000 --- a/DiscUtils/Iso9660/DiscUtils.Iso9660.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - - DiscUtils Iso9660 - Kenneth Bell;LordMike - DiscUtils;Optical;Iso9660 - - - - - - - diff --git a/DiscUtils/Iso9660/ExtentStream.cs b/DiscUtils/Iso9660/ExtentStream.cs index 13ec3c2..e7d5aa4 100644 --- a/DiscUtils/Iso9660/ExtentStream.cs +++ b/DiscUtils/Iso9660/ExtentStream.cs @@ -35,17 +35,15 @@ namespace DiscUtils.Iso9660 private long _position; private readonly uint _startBlock; - private readonly int _sectorSize; public ExtentStream(Stream isoStream, uint startBlock, uint dataLength, byte fileUnitSize, - byte interleaveGapSize, int sectorSize) + byte interleaveGapSize) { _isoStream = isoStream; _startBlock = startBlock; _dataLength = dataLength; _fileUnitSize = fileUnitSize; _interleaveGapSize = interleaveGapSize; - _sectorSize = sectorSize; if (_fileUnitSize != 0 || _interleaveGapSize != 0) { @@ -90,7 +88,7 @@ namespace DiscUtils.Iso9660 int toRead = (int)Math.Min((uint)count, _dataLength - _position); - _isoStream.Position = _position + _startBlock * (long)_sectorSize; + _isoStream.Position = _position + _startBlock * (long)IsoUtilities.SectorSize; int numRead = _isoStream.Read(buffer, offset, toRead); _position += numRead; return numRead; diff --git a/DiscUtils/Iso9660/File.cs b/DiscUtils/Iso9660/File.cs index 81bbad4..0ca134a 100644 --- a/DiscUtils/Iso9660/File.cs +++ b/DiscUtils/Iso9660/File.cs @@ -111,7 +111,7 @@ namespace DiscUtils.Iso9660 get { ExtentStream es = new ExtentStream(_context.DataStream, _dirEntry.Record.LocationOfExtent, - _dirEntry.Record.DataLength, _dirEntry.Record.FileUnitSize, _dirEntry.Record.InterleaveGapSize, _context.SectorSize); + _dirEntry.Record.DataLength, _dirEntry.Record.FileUnitSize, _dirEntry.Record.InterleaveGapSize); return new StreamBuffer(es, Ownership.Dispose); } } diff --git a/DiscUtils/Iso9660/IsoContext.cs b/DiscUtils/Iso9660/IsoContext.cs index 5232f62..5ff6396 100644 --- a/DiscUtils/Iso9660/IsoContext.cs +++ b/DiscUtils/Iso9660/IsoContext.cs @@ -28,19 +28,12 @@ namespace DiscUtils.Iso9660 { internal class IsoContext : VfsContext { - public IsoContext(int sectorSize) - { - SectorSize = sectorSize; - } - public Stream DataStream { get; set; } public string RockRidgeIdentifier { get; set; } public bool SuspDetected { get; set; } - public int SectorSize { get; set; } - public List SuspExtensions { get; set; } public int SuspSkipBytes { get; set; } diff --git a/DiscUtils/Iso9660/IsoUtilities.cs b/DiscUtils/Iso9660/IsoUtilities.cs index 4327450..ac6e552 100644 --- a/DiscUtils/Iso9660/IsoUtilities.cs +++ b/DiscUtils/Iso9660/IsoUtilities.cs @@ -30,7 +30,7 @@ namespace DiscUtils.Iso9660 { internal static class IsoUtilities { - //public const int SectorSize = 2048; + public const int SectorSize = 2048; public static uint ToUInt32FromBoth(byte[] data, int offset) { diff --git a/DiscUtils/Iso9660/PrimaryVolumeDescriptor.cs b/DiscUtils/Iso9660/PrimaryVolumeDescriptor.cs index 565c2ea..1d3199d 100644 --- a/DiscUtils/Iso9660/PrimaryVolumeDescriptor.cs +++ b/DiscUtils/Iso9660/PrimaryVolumeDescriptor.cs @@ -38,11 +38,10 @@ namespace DiscUtils.Iso9660 uint typeMPathTableLocation, uint rootDirExtentLocation, uint rootDirDataLength, - DateTime buildTime, - int sectorSize) + DateTime buildTime) : base( VolumeDescriptorType.Primary, 1, volumeSpaceSize, pathTableSize, typeLPathTableLocation, - typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, Encoding.ASCII, sectorSize) {} + typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, Encoding.ASCII) {} internal override void WriteTo(byte[] buffer, int offset) { diff --git a/DiscUtils/Iso9660/PrimaryVolumeDescriptorRegion.cs b/DiscUtils/Iso9660/PrimaryVolumeDescriptorRegion.cs index dcc2125..0f0ae5d 100644 --- a/DiscUtils/Iso9660/PrimaryVolumeDescriptorRegion.cs +++ b/DiscUtils/Iso9660/PrimaryVolumeDescriptorRegion.cs @@ -26,15 +26,15 @@ namespace DiscUtils.Iso9660 { private readonly PrimaryVolumeDescriptor _descriptor; - public PrimaryVolumeDescriptorRegion(PrimaryVolumeDescriptor descriptor, long start, int sectorSize) - : base(start, sectorSize) + public PrimaryVolumeDescriptorRegion(PrimaryVolumeDescriptor descriptor, long start) + : base(start) { _descriptor = descriptor; } protected override byte[] GetBlockData() { - byte[] buffer = new byte[Length]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; _descriptor.WriteTo(buffer, 0); return buffer; } diff --git a/DiscUtils/Iso9660/ReaderDirectory.cs b/DiscUtils/Iso9660/ReaderDirectory.cs index 003436b..d88d312 100644 --- a/DiscUtils/Iso9660/ReaderDirectory.cs +++ b/DiscUtils/Iso9660/ReaderDirectory.cs @@ -37,8 +37,8 @@ namespace DiscUtils.Iso9660 public ReaderDirectory(IsoContext context, ReaderDirEntry dirEntry) : base(context, dirEntry) { - byte[] buffer = new byte[context.SectorSize]; - Stream extent = new ExtentStream(_context.DataStream, dirEntry.Record.LocationOfExtent, uint.MaxValue, 0, 0, context.SectorSize); + byte[] buffer = new byte[IsoUtilities.SectorSize]; + Stream extent = new ExtentStream(_context.DataStream, dirEntry.Record.LocationOfExtent, uint.MaxValue, 0, 0); _records = new List(); @@ -48,8 +48,6 @@ namespace DiscUtils.Iso9660 { int bytesRead = (int)Math.Min(buffer.Length, totalLength - totalRead); - extent.Seek(24, SeekOrigin.Current); - StreamUtilities.ReadExact(extent, buffer, 0, bytesRead); totalRead += (uint)bytesRead; diff --git a/DiscUtils/Iso9660/SupplementaryVolumeDescriptor.cs b/DiscUtils/Iso9660/SupplementaryVolumeDescriptor.cs index bb7c6ba..dfe5ebb 100644 --- a/DiscUtils/Iso9660/SupplementaryVolumeDescriptor.cs +++ b/DiscUtils/Iso9660/SupplementaryVolumeDescriptor.cs @@ -39,11 +39,10 @@ namespace DiscUtils.Iso9660 uint rootDirExtentLocation, uint rootDirDataLength, DateTime buildTime, - Encoding enc, - int sectorSize) + Encoding enc) : base( VolumeDescriptorType.Supplementary, 1, volumeSpaceSize, pathTableSize, typeLPathTableLocation, - typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, enc, sectorSize) {} + typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, enc) {} internal override void WriteTo(byte[] buffer, int offset) { diff --git a/DiscUtils/Iso9660/SupplementaryVolumeDescriptorRegion.cs b/DiscUtils/Iso9660/SupplementaryVolumeDescriptorRegion.cs index 07e756a..317ea7f 100644 --- a/DiscUtils/Iso9660/SupplementaryVolumeDescriptorRegion.cs +++ b/DiscUtils/Iso9660/SupplementaryVolumeDescriptorRegion.cs @@ -26,15 +26,15 @@ namespace DiscUtils.Iso9660 { private readonly SupplementaryVolumeDescriptor _descriptor; - public SupplementaryVolumeDescriptorRegion(SupplementaryVolumeDescriptor descriptor, long start, int sectorSize) - : base(start, sectorSize) + public SupplementaryVolumeDescriptorRegion(SupplementaryVolumeDescriptor descriptor, long start) + : base(start) { _descriptor = descriptor; } protected override byte[] GetBlockData() { - byte[] buffer = new byte[Length]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; _descriptor.WriteTo(buffer, 0); return buffer; } diff --git a/DiscUtils/Iso9660/VfsCDReader.cs b/DiscUtils/Iso9660/VfsCDReader.cs index f9207ed..9c628f2 100644 --- a/DiscUtils/Iso9660/VfsCDReader.cs +++ b/DiscUtils/Iso9660/VfsCDReader.cs @@ -40,7 +40,6 @@ namespace DiscUtils.Iso9660 private readonly Stream _data; private readonly bool _hideVersions; - protected readonly int _sectorSize; /// /// Initializes a new instance of the VfsCDReader class. @@ -48,9 +47,8 @@ namespace DiscUtils.Iso9660 /// The stream to read the ISO image from. /// Whether to read Joliet extensions. /// Hides version numbers (e.g. ";1") from the end of files. - public VfsCDReader(Stream data, bool joliet, bool hideVersions, int sectorSize) - : this(data, joliet ? DefaultVariantsWithJoliet : DefaultVariantsNoJoliet, hideVersions, sectorSize) { - } + public VfsCDReader(Stream data, bool joliet, bool hideVersions) + : this(data, joliet ? DefaultVariantsWithJoliet : DefaultVariantsNoJoliet, hideVersions) {} /// /// Initializes a new instance of the VfsCDReader class. @@ -70,16 +68,15 @@ namespace DiscUtils.Iso9660 /// The Iso9660 variant should normally be specified as the final entry in the list. Placing it earlier /// in the list will effectively mask later items and not including it may prevent some ISOs from being read. /// - public VfsCDReader(Stream data, Iso9660Variant[] variantPriorities, bool hideVersions, int sectorSize) + public VfsCDReader(Stream data, Iso9660Variant[] variantPriorities, bool hideVersions) : base(new DiscFileSystemOptions()) { _data = data; - _sectorSize = sectorSize; _hideVersions = hideVersions; - long vdpos = _sectorSize * 16; // Skip lead-in + long vdpos = 0x8000; // Skip lead-in - byte[] buffer = new byte[_sectorSize]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; long pvdPos = 0; long svdPos = 0; @@ -88,15 +85,13 @@ namespace DiscUtils.Iso9660 do { data.Position = vdpos; - int numRead = data.Read(buffer, 0, _sectorSize); - if (numRead != _sectorSize) + int numRead = data.Read(buffer, 0, IsoUtilities.SectorSize); + if (numRead != IsoUtilities.SectorSize) { break; } - var offset = 24; - - bvd = new BaseVolumeDescriptor(buffer, offset); + bvd = new BaseVolumeDescriptor(buffer, 0); if (bvd.StandardIdentifier != BaseVolumeDescriptor.Iso9660StandardIdentifier) { @@ -106,7 +101,7 @@ namespace DiscUtils.Iso9660 switch (bvd.VolumeDescriptorType) { case VolumeDescriptorType.Boot: - _bootVolDesc = new BootVolumeDescriptor(buffer, offset); + _bootVolDesc = new BootVolumeDescriptor(buffer, 0); if (_bootVolDesc.SystemId != BootVolumeDescriptor.ElToritoSystemIdentifier) { _bootVolDesc = null; @@ -128,7 +123,7 @@ namespace DiscUtils.Iso9660 break; } - vdpos += _sectorSize; + vdpos += IsoUtilities.SectorSize; } while (bvd.VolumeDescriptorType != VolumeDescriptorType.SetTerminator); ActiveVariant = Iso9660Variant.None; @@ -140,10 +135,10 @@ namespace DiscUtils.Iso9660 if (svdPos != 0) { data.Position = svdPos; - data.Read(buffer, 0, _sectorSize); + data.Read(buffer, 0, IsoUtilities.SectorSize); SupplementaryVolumeDescriptor volDesc = new SupplementaryVolumeDescriptor(buffer, 0); - Context = new IsoContext(_sectorSize) { VolumeDescriptor = volDesc, DataStream = _data }; + Context = new IsoContext { VolumeDescriptor = volDesc, DataStream = _data }; RootDirectory = new ReaderDirectory(Context, new ReaderDirEntry(Context, volDesc.RootDirectory)); ActiveVariant = Iso9660Variant.Iso9660; @@ -155,13 +150,11 @@ namespace DiscUtils.Iso9660 case Iso9660Variant.Iso9660: if (pvdPos != 0) { - data.Position = pvdPos + 24; - data.Read(buffer, 0, _sectorSize); + data.Position = pvdPos; + data.Read(buffer, 0, IsoUtilities.SectorSize); PrimaryVolumeDescriptor volDesc = new PrimaryVolumeDescriptor(buffer, 0); - volDesc.LogicalBlockSize = 2352; - - IsoContext context = new IsoContext(_sectorSize) { VolumeDescriptor = volDesc, DataStream = _data }; + IsoContext context = new IsoContext { VolumeDescriptor = volDesc, DataStream = _data }; DirectoryRecord rootSelfRecord = ReadRootSelfRecord(context); InitializeSusp(context, rootSelfRecord); @@ -215,7 +208,7 @@ namespace DiscUtils.Iso9660 BootInitialEntry initialEntry = GetBootInitialEntry(); if (initialEntry != null) { - return initialEntry.ImageStart * _sectorSize; + return initialEntry.ImageStart * IsoUtilities.SectorSize; } return 0; } @@ -273,7 +266,7 @@ namespace DiscUtils.Iso9660 public long ClusterSize { - get { return _sectorSize; } + get { return IsoUtilities.SectorSize; } } public long TotalClusters @@ -331,7 +324,7 @@ namespace DiscUtils.Iso9660 return new[] { new Range(entry.Record.LocationOfExtent, - MathUtilities.Ceil(entry.Record.DataLength, _sectorSize)) + MathUtilities.Ceil(entry.Record.DataLength, IsoUtilities.SectorSize)) }; } @@ -349,7 +342,7 @@ namespace DiscUtils.Iso9660 } return new[] - { new StreamExtent(entry.Record.LocationOfExtent * _sectorSize, entry.Record.DataLength) }; + { new StreamExtent(entry.Record.LocationOfExtent * IsoUtilities.SectorSize, entry.Record.DataLength) }; } public ClusterMap BuildClusterMap() @@ -386,7 +379,7 @@ namespace DiscUtils.Iso9660 throw new NotSupportedException("Non-contiguous extents not supported"); } - long clusters = MathUtilities.Ceil(entry.Record.DataLength, _sectorSize); + long clusters = MathUtilities.Ceil(entry.Record.DataLength, IsoUtilities.SectorSize); for (long i = 0; i < clusters; ++i) { clusterToRole[i + entry.Record.LocationOfExtent] = ClusterRoles.DataFile; @@ -408,7 +401,7 @@ namespace DiscUtils.Iso9660 BootInitialEntry initialEntry = GetBootInitialEntry(); if (initialEntry != null) { - return new SubStream(_data, initialEntry.ImageStart * _sectorSize, + return new SubStream(_data, initialEntry.ImageStart * IsoUtilities.SectorSize, initialEntry.SectorCount * Sizes.Sector); } throw new InvalidOperationException("No valid boot image"); @@ -491,7 +484,7 @@ namespace DiscUtils.Iso9660 private static DirectoryRecord ReadRootSelfRecord(IsoContext context) { context.DataStream.Position = context.VolumeDescriptor.RootDirectory.LocationOfExtent * - context.VolumeDescriptor.LogicalBlockSize + 24; + context.VolumeDescriptor.LogicalBlockSize; byte[] firstSector = StreamUtilities.ReadExact(context.DataStream, context.VolumeDescriptor.LogicalBlockSize); DirectoryRecord rootSelfRecord; @@ -520,8 +513,8 @@ namespace DiscUtils.Iso9660 { if (_bootCatalog == null && _bootVolDesc != null) { - _data.Position = _bootVolDesc.CatalogSector * _sectorSize; - _bootCatalog = StreamUtilities.ReadExact(_data, _sectorSize); + _data.Position = _bootVolDesc.CatalogSector * IsoUtilities.SectorSize; + _bootCatalog = StreamUtilities.ReadExact(_data, IsoUtilities.SectorSize); } return _bootCatalog; diff --git a/DiscUtils/Iso9660/VolumeDescriptorDiskRegion.cs b/DiscUtils/Iso9660/VolumeDescriptorDiskRegion.cs index ad89652..11546f6 100644 --- a/DiscUtils/Iso9660/VolumeDescriptorDiskRegion.cs +++ b/DiscUtils/Iso9660/VolumeDescriptorDiskRegion.cs @@ -29,8 +29,8 @@ namespace DiscUtils.Iso9660 { private byte[] _readCache; - public VolumeDescriptorDiskRegion(long start, int sectorSize) - : base(start, sectorSize) {} + public VolumeDescriptorDiskRegion(long start) + : base(start, IsoUtilities.SectorSize) {} public override void Dispose() {} diff --git a/DiscUtils/Iso9660/VolumeDescriptorSetTerminator.cs b/DiscUtils/Iso9660/VolumeDescriptorSetTerminator.cs index fb247d2..f7035ff 100644 --- a/DiscUtils/Iso9660/VolumeDescriptorSetTerminator.cs +++ b/DiscUtils/Iso9660/VolumeDescriptorSetTerminator.cs @@ -24,7 +24,7 @@ namespace DiscUtils.Iso9660 { internal class VolumeDescriptorSetTerminator : BaseVolumeDescriptor { - public VolumeDescriptorSetTerminator(int sectorSize) - : base(VolumeDescriptorType.SetTerminator, 1, sectorSize) {} + public VolumeDescriptorSetTerminator() + : base(VolumeDescriptorType.SetTerminator, 1) {} } } \ No newline at end of file diff --git a/DiscUtils/Iso9660/VolumeDescriptorSetTerminatorRegion.cs b/DiscUtils/Iso9660/VolumeDescriptorSetTerminatorRegion.cs index 743bfc4..f1e98d6 100644 --- a/DiscUtils/Iso9660/VolumeDescriptorSetTerminatorRegion.cs +++ b/DiscUtils/Iso9660/VolumeDescriptorSetTerminatorRegion.cs @@ -26,15 +26,15 @@ namespace DiscUtils.Iso9660 { private readonly VolumeDescriptorSetTerminator _descriptor; - public VolumeDescriptorSetTerminatorRegion(VolumeDescriptorSetTerminator descriptor, long start, int sectorSize) - : base(start, sectorSize) + public VolumeDescriptorSetTerminatorRegion(VolumeDescriptorSetTerminator descriptor, long start) + : base(start) { _descriptor = descriptor; } protected override byte[] GetBlockData() { - byte[] buffer = new byte[Length]; + byte[] buffer = new byte[IsoUtilities.SectorSize]; _descriptor.WriteTo(buffer, 0); return buffer; } diff --git a/DiscUtils/Iso9660Ps1/BaseVolumeDescriptor.cs b/DiscUtils/Iso9660Ps1/BaseVolumeDescriptor.cs new file mode 100644 index 0000000..f1f4feb --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BaseVolumeDescriptor.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BaseVolumeDescriptor + { + public const string Iso9660StandardIdentifier = "CD001"; + + public readonly string StandardIdentifier; + public readonly VolumeDescriptorType VolumeDescriptorType; + public readonly byte VolumeDescriptorVersion; + private int SectorSize; + + public BaseVolumeDescriptor(VolumeDescriptorType type, byte version, int sectorSize) + { + VolumeDescriptorType = type; + StandardIdentifier = "CD001"; + VolumeDescriptorVersion = version; + SectorSize = sectorSize; + } + + public BaseVolumeDescriptor(byte[] src, int offset) + { + VolumeDescriptorType = (VolumeDescriptorType)src[offset + 0]; + StandardIdentifier = Encoding.ASCII.GetString(src, offset + 1, 5); + VolumeDescriptorVersion = src[offset + 6]; + } + + internal virtual void WriteTo(byte[] buffer, int offset) + { + Array.Clear(buffer, offset, SectorSize); + buffer[offset] = (byte)VolumeDescriptorType; + IsoUtilities.WriteAChars(buffer, offset + 1, 5, StandardIdentifier); + buffer[offset + 6] = VolumeDescriptorVersion; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BootDeviceEmulation.cs b/DiscUtils/Iso9660Ps1/BootDeviceEmulation.cs new file mode 100644 index 0000000..553e468 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BootDeviceEmulation.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Enumeration of boot device emulation modes. + /// + public enum BootDeviceEmulation : byte + { + /// + /// No emulation, the boot image is just loaded and executed. + /// + NoEmulation = 0x0, + + /// + /// Emulates 1.2MB diskette image as drive A. + /// + Diskette1200KiB = 0x1, + + /// + /// Emulates 1.44MB diskette image as drive A. + /// + Diskette1440KiB = 0x2, + + /// + /// Emulates 2.88MB diskette image as drive A. + /// + Diskette2880KiB = 0x3, + + /// + /// Emulates hard disk image as drive C. + /// + HardDisk = 0x4 + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BootInitialEntry.cs b/DiscUtils/Iso9660Ps1/BootInitialEntry.cs new file mode 100644 index 0000000..0d09fba --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BootInitialEntry.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BootInitialEntry + { + public byte BootIndicator; + public BootDeviceEmulation BootMediaType; + public uint ImageStart; + public ushort LoadSegment; + public ushort SectorCount; + public byte SystemType; + + public BootInitialEntry() {} + + public BootInitialEntry(byte[] buffer, int offset) + { + BootIndicator = buffer[offset + 0x00]; + BootMediaType = (BootDeviceEmulation)buffer[offset + 0x01]; + LoadSegment = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 0x02); + SystemType = buffer[offset + 0x04]; + SectorCount = EndianUtilities.ToUInt16LittleEndian(buffer, offset + 0x06); + ImageStart = EndianUtilities.ToUInt32LittleEndian(buffer, offset + 0x08); + } + + internal void WriteTo(byte[] buffer, int offset) + { + Array.Clear(buffer, offset, 0x20); + buffer[offset + 0x00] = BootIndicator; + buffer[offset + 0x01] = (byte)BootMediaType; + EndianUtilities.WriteBytesLittleEndian(LoadSegment, buffer, offset + 0x02); + buffer[offset + 0x04] = SystemType; + EndianUtilities.WriteBytesLittleEndian(SectorCount, buffer, offset + 0x06); + EndianUtilities.WriteBytesLittleEndian(ImageStart, buffer, offset + 0x08); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BootValidationEntry.cs b/DiscUtils/Iso9660Ps1/BootValidationEntry.cs new file mode 100644 index 0000000..505c6b4 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BootValidationEntry.cs @@ -0,0 +1,88 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BootValidationEntry + { + private readonly byte[] _data; + public byte HeaderId; + public string ManfId; + public byte PlatformId; + + public BootValidationEntry() + { + HeaderId = 1; + PlatformId = 0; + ManfId = ".Net DiscUtils"; + } + + public BootValidationEntry(byte[] src, int offset) + { + _data = new byte[32]; + Array.Copy(src, offset, _data, 0, 32); + + HeaderId = _data[0]; + PlatformId = _data[1]; + ManfId = EndianUtilities.BytesToString(_data, 4, 24).TrimEnd('\0').TrimEnd(' '); + } + + public bool ChecksumValid + { + get + { + ushort total = 0; + for (int i = 0; i < 16; ++i) + { + total += EndianUtilities.ToUInt16LittleEndian(_data, i * 2); + } + + return total == 0; + } + } + + internal void WriteTo(byte[] buffer, int offset) + { + Array.Clear(buffer, offset, 0x20); + buffer[offset + 0x00] = HeaderId; + buffer[offset + 0x01] = PlatformId; + EndianUtilities.StringToBytes(ManfId, buffer, offset + 0x04, 24); + buffer[offset + 0x1E] = 0x55; + buffer[offset + 0x1F] = 0xAA; + EndianUtilities.WriteBytesLittleEndian(CalcChecksum(buffer, offset), buffer, offset + 0x1C); + } + + private static ushort CalcChecksum(byte[] buffer, int offset) + { + ushort total = 0; + for (int i = 0; i < 16; ++i) + { + total += EndianUtilities.ToUInt16LittleEndian(buffer, offset + i * 2); + } + + return (ushort)(0 - total); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BootVolumeDescriptor.cs b/DiscUtils/Iso9660Ps1/BootVolumeDescriptor.cs new file mode 100644 index 0000000..87f786a --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BootVolumeDescriptor.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BootVolumeDescriptor : BaseVolumeDescriptor + { + public const string ElToritoSystemIdentifier = "EL TORITO SPECIFICATION"; + + public BootVolumeDescriptor(uint catalogSector, int sectorSize) + : base(VolumeDescriptorType.Boot, 1, sectorSize) + { + CatalogSector = catalogSector; + } + + public BootVolumeDescriptor(byte[] src, int offset) + : base(src, offset) + { + SystemId = EndianUtilities.BytesToString(src, offset + 0x7, 0x20).TrimEnd('\0'); + CatalogSector = EndianUtilities.ToUInt32LittleEndian(src, offset + 0x47); + } + + public uint CatalogSector { get; } + + public string SystemId { get; } + + internal override void WriteTo(byte[] buffer, int offset) + { + base.WriteTo(buffer, offset); + + EndianUtilities.StringToBytes(ElToritoSystemIdentifier, buffer, offset + 7, 0x20); + EndianUtilities.WriteBytesLittleEndian(CatalogSector, buffer, offset + 0x47); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BootVolumeDescriptorRegion.cs b/DiscUtils/Iso9660Ps1/BootVolumeDescriptorRegion.cs new file mode 100644 index 0000000..61e1670 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BootVolumeDescriptorRegion.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BootVolumeDescriptorRegion : VolumeDescriptorDiskRegion + { + private readonly BootVolumeDescriptor _descriptor; + + public BootVolumeDescriptorRegion(BootVolumeDescriptor descriptor, long start, int sectorSize) + : base(start, sectorSize) + { + _descriptor = descriptor; + } + + protected override byte[] GetBlockData() + { + byte[] buffer = new byte[Length]; + _descriptor.WriteTo(buffer, 0); + return buffer; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BuildDirectoryInfo.cs b/DiscUtils/Iso9660Ps1/BuildDirectoryInfo.cs new file mode 100644 index 0000000..f021e83 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BuildDirectoryInfo.cs @@ -0,0 +1,219 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using DiscUtils.CoreCompat; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Represents a directory that will be built into the ISO image. + /// + public sealed class BuildDirectoryInfo : BuildDirectoryMember + { + internal static readonly Comparer PathTableSortComparison = new PathTableComparison(); + private readonly Dictionary _members; + + private readonly BuildDirectoryInfo _parent; + private List _sortedMembers; + private int _sectorSize; + + internal BuildDirectoryInfo(string name, BuildDirectoryInfo parent, int sectorSize) + : base(name, MakeShortDirName(name, parent)) + { + _parent = parent == null ? this : parent; + HierarchyDepth = parent == null ? 0 : parent.HierarchyDepth + 1; + _members = new Dictionary(); + _sectorSize = sectorSize; + } + + internal int HierarchyDepth { get; } + + /// + /// The parent directory, or null if none. + /// + public override BuildDirectoryInfo Parent + { + get { return _parent; } + } + + /// + /// Gets the specified child directory or file. + /// + /// The name of the file or directory to get. + /// The member found (or null). + /// true if the specified member was found. + internal bool TryGetMember(string name, out BuildDirectoryMember member) + { + return _members.TryGetValue(name, out member); + } + + internal void Add(BuildDirectoryMember member) + { + _members.Add(member.Name, member); + _sortedMembers = null; + } + + internal override long GetDataSize(Encoding enc) + { + List sorted = GetSortedMembers(); + + long total = 34 * 2; // Two pseudo entries (self & parent) + + foreach (BuildDirectoryMember m in sorted) + { + uint recordSize = m.GetDirectoryRecordSize(enc); + + // If this record would span a sector boundary, then the current sector is + // zero-padded, and the record goes at the start of the next sector. + if (total % _sectorSize + recordSize > _sectorSize) + { + long padLength = _sectorSize - total % _sectorSize; + total += padLength; + } + + total += recordSize; + } + + return MathUtilities.RoundUp(total, _sectorSize); + } + + internal uint GetPathTableEntrySize(Encoding enc) + { + int nameBytes = enc.GetByteCount(PickName(null, enc)); + + return (uint)(8 + nameBytes + ((nameBytes & 0x1) == 1 ? 1 : 0)); + } + + internal int Write(byte[] buffer, int offset, Dictionary locationTable, Encoding enc) + { + int pos = 0; + + List sorted = GetSortedMembers(); + + // Two pseudo entries, effectively '.' and '..' + pos += WriteMember(this, "\0", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); + pos += WriteMember(_parent, "\x01", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); + + foreach (BuildDirectoryMember m in sorted) + { + uint recordSize = m.GetDirectoryRecordSize(enc); + + if (pos % _sectorSize + recordSize > _sectorSize) + { + int padLength = _sectorSize - pos % _sectorSize; + Array.Clear(buffer, offset + pos, padLength); + pos += padLength; + } + + pos += WriteMember(m, null, enc, buffer, offset + pos, locationTable, enc, _sectorSize); + } + + // Ensure final padding data is zero'd + int finalPadLength = MathUtilities.RoundUp(pos, _sectorSize) - pos; + Array.Clear(buffer, offset + pos, finalPadLength); + + return pos + finalPadLength; + } + + private static int WriteMember(BuildDirectoryMember m, string nameOverride, Encoding nameEnc, byte[] buffer, int offset, Dictionary locationTable, Encoding dataEnc, int sectorSize) + { + DirectoryRecord dr = new DirectoryRecord(); + dr.FileIdentifier = m.PickName(nameOverride, nameEnc); + dr.LocationOfExtent = locationTable[m]; + dr.DataLength = (uint)m.GetDataSize(dataEnc); + dr.RecordingDateAndTime = m.CreationTime; + dr.Flags = m is BuildDirectoryInfo ? FileFlags.Directory : FileFlags.None; + return dr.WriteTo(buffer, offset, nameEnc); + } + + private static string MakeShortDirName(string longName, BuildDirectoryInfo dir) + { + if (IsoUtilities.IsValidDirectoryName(longName)) + { + return longName; + } + + char[] shortNameChars = longName.ToUpper(CultureInfo.InvariantCulture).ToCharArray(); + for (int i = 0; i < shortNameChars.Length; ++i) + { + if (!IsoUtilities.IsValidDChar(shortNameChars[i]) && shortNameChars[i] != '.' && shortNameChars[i] != ';') + { + shortNameChars[i] = '_'; + } + } + + return new string(shortNameChars); + } + + private List GetSortedMembers() + { + if (_sortedMembers == null) + { + List sorted = new List(_members.Values); + sorted.Sort(SortedComparison); + _sortedMembers = sorted; + } + + return _sortedMembers; + } + + private class PathTableComparison : Comparer + { + public override int Compare(BuildDirectoryInfo x, BuildDirectoryInfo y) + { + if (x.HierarchyDepth != y.HierarchyDepth) + { + return x.HierarchyDepth - y.HierarchyDepth; + } + + if (x.Parent != y.Parent) + { + return Compare(x.Parent, y.Parent); + } + + return CompareNames(x.Name, y.Name, ' '); + } + + private static int CompareNames(string x, string y, char padChar) + { + int max = Math.Max(x.Length, y.Length); + for (int i = 0; i < max; ++i) + { + char xChar = i < x.Length ? x[i] : padChar; + char yChar = i < y.Length ? y[i] : padChar; + + if (xChar != yChar) + { + return xChar - yChar; + } + } + + return 0; + } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BuildDirectoryMember.cs b/DiscUtils/Iso9660Ps1/BuildDirectoryMember.cs new file mode 100644 index 0000000..fdf135b --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BuildDirectoryMember.cs @@ -0,0 +1,155 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Provides the base class for and + /// objects that will be built into an + /// ISO image. + /// + /// Instances of this class have two names, a , + /// which is the full-length Joliet name and a , + /// which is the strictly compliant ISO 9660 name. + public abstract class BuildDirectoryMember + { + internal static readonly Comparer SortedComparison = new DirectorySortedComparison(); + + /// + /// Initializes a new instance of the BuildDirectoryMember class. + /// + /// The Joliet compliant name of the file or directory. + /// The ISO 9660 compliant name of the file or directory. + protected BuildDirectoryMember(string name, string shortName) + { + Name = name; + ShortName = shortName; + CreationTime = DateTime.UtcNow; + } + + /// + /// Gets or sets the creation date for the file or directory, in UTC. + /// + public DateTime CreationTime { get; set; } + + /// + /// Gets the Joliet compliant name of the file or directory. + /// + public string Name { get; } + + /// + /// Gets the parent directory, or null if this is the root directory. + /// + public abstract BuildDirectoryInfo Parent { get; } + + /// + /// Gets the ISO 9660 compliant name of the file or directory. + /// + public string ShortName { get; } + + internal string PickName(string nameOverride, Encoding enc) + { + if (nameOverride != null) + { + return nameOverride; + } + return enc == Encoding.ASCII ? ShortName : Name; + } + + internal abstract long GetDataSize(Encoding enc); + + internal uint GetDirectoryRecordSize(Encoding enc) + { + return DirectoryRecord.CalcLength(PickName(null, enc), enc); + } + + private class DirectorySortedComparison : Comparer + { + public override int Compare(BuildDirectoryMember x, BuildDirectoryMember y) + { + string[] xParts = x.Name.Split('.', ';'); + string[] yParts = y.Name.Split('.', ';'); + + string xPart; + string yPart; + + for (int i = 0; i < 2; ++i) + { + xPart = xParts.Length > i ? xParts[i] : string.Empty; + yPart = yParts.Length > i ? yParts[i] : string.Empty; + int val = ComparePart(xPart, yPart, ' '); + if (val != 0) + { + return val; + } + } + + xPart = xParts.Length > 2 ? xParts[2] : string.Empty; + yPart = yParts.Length > 2 ? yParts[2] : string.Empty; + return ComparePartBackwards(xPart, yPart, '0'); + } + + private static int ComparePart(string x, string y, char padChar) + { + int max = Math.Max(x.Length, y.Length); + for (int i = 0; i < max; ++i) + { + char xChar = i < x.Length ? x[i] : padChar; + char yChar = i < y.Length ? y[i] : padChar; + + if (xChar != yChar) + { + return xChar - yChar; + } + } + + return 0; + } + + private static int ComparePartBackwards(string x, string y, char padChar) + { + int max = Math.Max(x.Length, y.Length); + + int xPad = max - x.Length; + int yPad = max - y.Length; + + for (int i = 0; i < max; ++i) + { + char xChar = i >= xPad ? x[i - xPad] : padChar; + char yChar = i >= yPad ? y[i - yPad] : padChar; + + if (xChar != yChar) + { + // Note: Version numbers are in DESCENDING order! + return yChar - xChar; + } + } + + return 0; + } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BuildFileInfo.cs b/DiscUtils/Iso9660Ps1/BuildFileInfo.cs new file mode 100644 index 0000000..a826c2a --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BuildFileInfo.cs @@ -0,0 +1,136 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.IO; +using System.Text; +using DiscUtils.CoreCompat; +using DiscUtils.Internal; + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Represents a file that will be built into the ISO image. + /// + public sealed class BuildFileInfo : BuildDirectoryMember + { + private readonly byte[] _contentData; + private readonly string _contentPath; + private readonly long _contentSize; + private readonly Stream _contentStream; + + internal BuildFileInfo(string name, BuildDirectoryInfo parent, byte[] content) + : base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent)) + { + Parent = parent; + _contentData = content; + _contentSize = content.Length; + } + + internal BuildFileInfo(string name, BuildDirectoryInfo parent, string content) + : base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent)) + { + Parent = parent; + _contentPath = content; + _contentSize = new FileInfo(_contentPath).Length; + + CreationTime = new FileInfo(_contentPath).LastWriteTimeUtc; + } + + internal BuildFileInfo(string name, BuildDirectoryInfo parent, Stream source) + : base(IsoUtilities.NormalizeFileName(name), MakeShortFileName(name, parent)) + { + Parent = parent; + _contentStream = source; + _contentSize = _contentStream.Length; + } + + /// + /// The parent directory, or null if none. + /// + public override BuildDirectoryInfo Parent { get; } + + internal override long GetDataSize(Encoding enc) + { + return _contentSize; + } + + internal Stream OpenStream() + { + if (_contentData != null) + { + return new MemoryStream(_contentData, false); + } + if (_contentPath != null) + { + var locator = new LocalFileLocator(string.Empty); + return locator.Open(_contentPath, FileMode.Open, FileAccess.Read, FileShare.Read); + } + return _contentStream; + } + + internal void CloseStream(Stream s) + { + // Close and dispose the stream, unless it's one we were given to stream in + // from (we might need it again). + if (_contentStream != s) + { + s.Dispose(); + } + } + + private static string MakeShortFileName(string longName, BuildDirectoryInfo dir) + { + if (IsoUtilities.IsValidFileName(longName)) + { + return longName; + } + + char[] shortNameChars = longName.ToUpper(CultureInfo.InvariantCulture).ToCharArray(); + for (int i = 0; i < shortNameChars.Length; ++i) + { + if (!IsoUtilities.IsValidDChar(shortNameChars[i]) && shortNameChars[i] != '.' && shortNameChars[i] != ';') + { + shortNameChars[i] = '_'; + } + } + + string[] parts = IsoUtilities.SplitFileName(new string(shortNameChars)); + + if (parts[0].Length + parts[1].Length > 30) + { + parts[1] = parts[1].Substring(0, Math.Min(parts[1].Length, 3)); + } + + if (parts[0].Length + parts[1].Length > 30) + { + parts[0] = parts[0].Substring(0, 30 - parts[1].Length); + } + + string candidate = parts[0] + '.' + parts[1] + ';' + parts[2]; + + // TODO: Make unique + return candidate; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/BuildParameters.cs b/DiscUtils/Iso9660Ps1/BuildParameters.cs new file mode 100644 index 0000000..7f44cb7 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/BuildParameters.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class BuildParameters + { + public BuildParameters() + { + VolumeIdentifier = string.Empty; + UseJoliet = true; + SectorSize = 2048; + } + + public int SectorSize { get; set; } + + public bool UseJoliet { get; set; } + + public string VolumeIdentifier { get; set; } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/CDBuilder.cs b/DiscUtils/Iso9660Ps1/CDBuilder.cs new file mode 100644 index 0000000..b363661 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/CDBuilder.cs @@ -0,0 +1,518 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Class that creates ISO images. + /// + /// + /// + /// CDBuilder builder = new CDBuilder(); + /// builder.VolumeIdentifier = "MYISO"; + /// builder.UseJoliet = true; + /// builder.AddFile("Hello.txt", Encoding.ASCII.GetBytes("hello world!")); + /// builder.Build(@"C:\TEMP\myiso.iso"); + /// + /// + public sealed class CDBuilder : StreamBuilder + { + private const long DiskStart = 0x8000; + private BootInitialEntry _bootEntry; + private Stream _bootImage; + + private readonly BuildParameters _buildParams; + private readonly List _dirs; + + private readonly List _files; + private readonly BuildDirectoryInfo _rootDirectory; + private readonly int _sectorSize; + + /// + /// Initializes a new instance of the CDBuilder class. + /// + public CDBuilder() + { + _files = new List(); + _dirs = new List(); + _sectorSize = 2048; + _rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize); + _dirs.Add(_rootDirectory); + + _buildParams = new BuildParameters(); + _buildParams.UseJoliet = true; + } + + + /// + /// Initializes a new instance of the CDBuilder class. + /// + public CDBuilder(int sectorSize) + { + _files = new List(); + _dirs = new List(); + _sectorSize = sectorSize; + _rootDirectory = new BuildDirectoryInfo("\0", null, _sectorSize); + _dirs.Add(_rootDirectory); + + _buildParams = new BuildParameters(); + _buildParams.UseJoliet = true; + } + + /// + /// Gets or sets a value indicating whether to update the ISOLINUX info table at the + /// start of the boot image. Use with ISOLINUX only. + /// + /// + /// ISOLINUX has an 'information table' at the start of the boot loader that verifies + /// the CD has been loaded correctly by the BIOS. This table needs to be updated + /// to match the actual ISO. + /// + public bool UpdateIsolinuxBootTable { get; set; } + + /// + /// Gets or sets a value indicating whether Joliet file-system extensions should be used. + /// + public bool UseJoliet + { + get { return _buildParams.UseJoliet; } + set { _buildParams.UseJoliet = value; } + } + + /// + /// Gets or sets the Volume Identifier for the ISO file. + /// + /// + /// Must be a valid identifier, i.e. max 32 characters in the range A-Z, 0-9 or _. + /// Lower-case characters are not permitted. + /// + public string VolumeIdentifier + { + get { return _buildParams.VolumeIdentifier; } + + set + { + if (value.Length > 32) + { + throw new ArgumentException("Not a valid volume identifier"); + } + _buildParams.VolumeIdentifier = value; + } + } + + /// + /// Sets the boot image for the ISO image. + /// + /// Stream containing the boot image. + /// The type of emulation requested of the BIOS. + /// The memory segment to load the image to (0 for default). + public void SetBootImage(Stream image, BootDeviceEmulation emulation, int loadSegment) + { + if (_bootEntry != null) + { + throw new InvalidOperationException("Boot image already set"); + } + + _bootEntry = new BootInitialEntry(); + _bootEntry.BootIndicator = 0x88; + _bootEntry.BootMediaType = emulation; + _bootEntry.LoadSegment = (ushort)loadSegment; + _bootEntry.SystemType = 0; + _bootImage = image; + } + + /// + /// Adds a directory to the ISO image. + /// + /// The name of the directory on the ISO image. + /// The object representing this directory. + /// + /// The name is the full path to the directory, for example: + /// + /// builder.AddDirectory(@"DIRA\DIRB\DIRC"); + /// + /// + public BuildDirectoryInfo AddDirectory(string name) + { + string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + return GetDirectory(nameElements, nameElements.Length, true); + } + + /// + /// Adds a byte array to the ISO image as a file. + /// + /// The name of the file on the ISO image. + /// The contents of the file. + /// The object representing this file. + /// + /// The name is the full path to the file, for example: + /// + /// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", new byte[]{0,1,2}); + /// + /// Note the version number at the end of the file name is optional, if not + /// specified the default of 1 will be used. + /// + public BuildFileInfo AddFile(string name, byte[] content) + { + string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true); + + BuildDirectoryMember existing; + if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing)) + { + throw new IOException("File already exists"); + } + BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, content); + _files.Add(fi); + dir.Add(fi); + return fi; + } + + /// + /// Adds a disk file to the ISO image as a file. + /// + /// The name of the file on the ISO image. + /// The name of the file on disk. + /// The object representing this file. + /// + /// The name is the full path to the file, for example: + /// + /// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", @"C:\temp\tempfile.bin"); + /// + /// Note the version number at the end of the file name is optional, if not + /// specified the default of 1 will be used. + /// + public BuildFileInfo AddFile(string name, string sourcePath) + { + string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true); + + BuildDirectoryMember existing; + if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing)) + { + throw new IOException("File already exists"); + } + BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, sourcePath); + _files.Add(fi); + dir.Add(fi); + return fi; + } + + /// + /// Adds a stream to the ISO image as a file. + /// + /// The name of the file on the ISO image. + /// The contents of the file. + /// The object representing this file. + /// + /// The name is the full path to the file, for example: + /// + /// builder.AddFile(@"DIRA\DIRB\FILE.TXT;1", stream); + /// + /// Note the version number at the end of the file name is optional, if not + /// specified the default of 1 will be used. + /// + public BuildFileInfo AddFile(string name, Stream source) + { + if (!source.CanSeek) + { + throw new ArgumentException("source doesn't support seeking", nameof(source)); + } + + string[] nameElements = name.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + BuildDirectoryInfo dir = GetDirectory(nameElements, nameElements.Length - 1, true); + + BuildDirectoryMember existing; + if (dir.TryGetMember(nameElements[nameElements.Length - 1], out existing)) + { + throw new IOException("File already exists"); + } + BuildFileInfo fi = new BuildFileInfo(nameElements[nameElements.Length - 1], dir, source); + _files.Add(fi); + dir.Add(fi); + return fi; + } + + protected override List FixExtents(out long totalLength) + { + List fixedRegions = new List(); + + DateTime buildTime = DateTime.UtcNow; + + Encoding suppEncoding = _buildParams.UseJoliet ? Encoding.BigEndianUnicode : Encoding.ASCII; + + Dictionary primaryLocationTable = new Dictionary(); + Dictionary supplementaryLocationTable = + new Dictionary(); + + long focus = DiskStart + 3 * _buildParams.SectorSize; // Primary, Supplementary, End (fixed at end...) + if (_bootEntry != null) + { + focus += _buildParams.SectorSize; + } + + // #################################################################### + // # 0. Fix boot image location + // #################################################################### + long bootCatalogPos = 0; + if (_bootEntry != null) + { + long bootImagePos = focus; + Stream realBootImage = PatchBootImage(_bootImage, (uint)(DiskStart / _buildParams.SectorSize), + (uint)(bootImagePos / _buildParams.SectorSize)); + BuilderStreamExtent bootImageExtent = new BuilderStreamExtent(focus, realBootImage); + fixedRegions.Add(bootImageExtent); + focus += MathUtilities.RoundUp(bootImageExtent.Length, _buildParams.SectorSize); + + bootCatalogPos = focus; + byte[] bootCatalog = new byte[_buildParams.SectorSize]; + BootValidationEntry bve = new BootValidationEntry(); + bve.WriteTo(bootCatalog, 0x00); + _bootEntry.ImageStart = (uint)MathUtilities.Ceil(bootImagePos, _buildParams.SectorSize); + _bootEntry.SectorCount = (ushort)MathUtilities.Ceil(_bootImage.Length, Sizes.Sector); + _bootEntry.WriteTo(bootCatalog, 0x20); + fixedRegions.Add(new BuilderBufferExtent(bootCatalogPos, bootCatalog)); + focus += _buildParams.SectorSize; + } + + // #################################################################### + // # 1. Fix file locations + // #################################################################### + + // Find end of the file data, fixing the files in place as we go + foreach (BuildFileInfo fi in _files) + { + primaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize)); + supplementaryLocationTable.Add(fi, (uint)(focus / _buildParams.SectorSize)); + FileExtent extent = new FileExtent(fi, focus); + + // Only remember files of non-zero length (otherwise we'll stomp on a valid file) + if (extent.Length != 0) + { + fixedRegions.Add(extent); + } + + focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + } + + // #################################################################### + // # 2. Fix directory locations + // #################################################################### + + // There are two directory tables + // 1. Primary (std ISO9660) + // 2. Supplementary (Joliet) + + // Find start of the second set of directory data, fixing ASCII directories in place. + long startOfFirstDirData = focus; + foreach (BuildDirectoryInfo di in _dirs) + { + primaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize)); + DirectoryExtent extent = new DirectoryExtent(di, primaryLocationTable, Encoding.ASCII, focus); + fixedRegions.Add(extent); + focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + } + + // Find end of the second directory table, fixing supplementary directories in place. + long startOfSecondDirData = focus; + foreach (BuildDirectoryInfo di in _dirs) + { + supplementaryLocationTable.Add(di, (uint)(focus / _buildParams.SectorSize)); + DirectoryExtent extent = new DirectoryExtent(di, supplementaryLocationTable, suppEncoding, focus); + fixedRegions.Add(extent); + focus += MathUtilities.RoundUp(extent.Length, _buildParams.SectorSize); + } + + // #################################################################### + // # 3. Fix path tables + // #################################################################### + + // There are four path tables: + // 1. LE, ASCII + // 2. BE, ASCII + // 3. LE, Supp Encoding (Joliet) + // 4. BE, Supp Encoding (Joliet) + + // Find end of the path table + long startOfFirstPathTable = focus; + PathTable pathTable = new PathTable(false, Encoding.ASCII, _dirs, primaryLocationTable, focus); + fixedRegions.Add(pathTable); + focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + long primaryPathTableLength = pathTable.Length; + + long startOfSecondPathTable = focus; + pathTable = new PathTable(true, Encoding.ASCII, _dirs, primaryLocationTable, focus); + fixedRegions.Add(pathTable); + focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + + long startOfThirdPathTable = focus; + pathTable = new PathTable(false, suppEncoding, _dirs, supplementaryLocationTable, focus); + fixedRegions.Add(pathTable); + focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + long supplementaryPathTableLength = pathTable.Length; + + long startOfFourthPathTable = focus; + pathTable = new PathTable(true, suppEncoding, _dirs, supplementaryLocationTable, focus); + fixedRegions.Add(pathTable); + focus += MathUtilities.RoundUp(pathTable.Length, _buildParams.SectorSize); + + // Find the end of the disk + totalLength = focus; + + // #################################################################### + // # 4. Prepare volume descriptors now other structures are fixed + // #################################################################### + int regionIdx = 0; + focus = DiskStart; + PrimaryVolumeDescriptor pvDesc = new PrimaryVolumeDescriptor( + (uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize + (uint)primaryPathTableLength, // PathTableSize + (uint)(startOfFirstPathTable / _buildParams.SectorSize), // TypeLPathTableLocation + (uint)(startOfSecondPathTable / _buildParams.SectorSize), // TypeMPathTableLocation + (uint)(startOfFirstDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent + (uint)_rootDirectory.GetDataSize(Encoding.ASCII), // RootDirectory.DataLength + buildTime, + _sectorSize); + pvDesc.VolumeIdentifier = _buildParams.VolumeIdentifier; + PrimaryVolumeDescriptorRegion pvdr = new PrimaryVolumeDescriptorRegion(pvDesc, focus, _sectorSize); + fixedRegions.Insert(regionIdx++, pvdr); + focus += _buildParams.SectorSize; + + if (_bootEntry != null) + { + BootVolumeDescriptor bvDesc = new BootVolumeDescriptor( + (uint)(bootCatalogPos / _buildParams.SectorSize), _buildParams.SectorSize); + BootVolumeDescriptorRegion bvdr = new BootVolumeDescriptorRegion(bvDesc, focus, _buildParams.SectorSize); + fixedRegions.Insert(regionIdx++, bvdr); + focus += _buildParams.SectorSize; + } + + SupplementaryVolumeDescriptor svDesc = new SupplementaryVolumeDescriptor( + (uint)(totalLength / _buildParams.SectorSize), // VolumeSpaceSize + (uint)supplementaryPathTableLength, // PathTableSize + (uint)(startOfThirdPathTable / _buildParams.SectorSize), // TypeLPathTableLocation + (uint)(startOfFourthPathTable / _buildParams.SectorSize), // TypeMPathTableLocation + (uint)(startOfSecondDirData / _buildParams.SectorSize), // RootDirectory.LocationOfExtent + (uint)_rootDirectory.GetDataSize(suppEncoding), // RootDirectory.DataLength + buildTime, + suppEncoding, + _sectorSize); + svDesc.VolumeIdentifier = _buildParams.VolumeIdentifier; + SupplementaryVolumeDescriptorRegion svdr = new SupplementaryVolumeDescriptorRegion(svDesc, focus, _sectorSize); + fixedRegions.Insert(regionIdx++, svdr); + focus += _buildParams.SectorSize; + + VolumeDescriptorSetTerminator evDesc = new VolumeDescriptorSetTerminator(_sectorSize); + VolumeDescriptorSetTerminatorRegion evdr = new VolumeDescriptorSetTerminatorRegion(evDesc, focus, _sectorSize); + fixedRegions.Insert(regionIdx++, evdr); + + return fixedRegions; + } + + /// + /// Patches a boot image (esp. for ISOLINUX) before it is written to the disk. + /// + /// The original (master) boot image. + /// The logical block address of the primary volume descriptor. + /// The logical block address of the boot image itself. + /// A stream containing the patched boot image - does not need to be disposed. + private Stream PatchBootImage(Stream bootImage, uint pvdLba, uint bootImageLba) + { + // Early-exit if no patching to do... + if (!UpdateIsolinuxBootTable) + { + return bootImage; + } + + byte[] bootData = StreamUtilities.ReadExact(bootImage, (int)bootImage.Length); + + Array.Clear(bootData, 8, 56); + + uint checkSum = 0; + for (int i = 64; i < bootData.Length; i += 4) + { + checkSum += EndianUtilities.ToUInt32LittleEndian(bootData, i); + } + + EndianUtilities.WriteBytesLittleEndian(pvdLba, bootData, 8); + EndianUtilities.WriteBytesLittleEndian(bootImageLba, bootData, 12); + EndianUtilities.WriteBytesLittleEndian(bootData.Length, bootData, 16); + EndianUtilities.WriteBytesLittleEndian(checkSum, bootData, 20); + + return new MemoryStream(bootData, false); + } + + private BuildDirectoryInfo GetDirectory(string[] path, int pathLength, bool createMissing) + { + BuildDirectoryInfo di = TryGetDirectory(path, pathLength, createMissing); + + if (di == null) + { + throw new DirectoryNotFoundException("Directory not found"); + } + + return di; + } + + private BuildDirectoryInfo TryGetDirectory(string[] path, int pathLength, bool createMissing) + { + BuildDirectoryInfo focus = _rootDirectory; + + for (int i = 0; i < pathLength; ++i) + { + BuildDirectoryMember next; + if (!focus.TryGetMember(path[i], out next)) + { + if (createMissing) + { + // This directory doesn't exist, create it... + BuildDirectoryInfo di = new BuildDirectoryInfo(path[i], focus, _sectorSize); + focus.Add(di); + _dirs.Add(di); + focus = di; + } + else + { + return null; + } + } + else + { + BuildDirectoryInfo nextAsBuildDirectoryInfo = next as BuildDirectoryInfo; + if (nextAsBuildDirectoryInfo == null) + { + throw new IOException("File with conflicting name exists"); + } + focus = nextAsBuildDirectoryInfo; + } + } + + return focus; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/CDReader.cs b/DiscUtils/Iso9660Ps1/CDReader.cs new file mode 100644 index 0000000..033b3b6 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/CDReader.cs @@ -0,0 +1,229 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.IO; +using DiscUtils.Streams; +using DiscUtils.Vfs; + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Class for reading existing ISO images. + /// + public class CDReader : VfsFileSystemFacade, IClusterBasedFileSystem, IUnixFileSystem + { + /// + /// Initializes a new instance of the CDReader class. + /// + /// The stream to read the ISO image from. + /// Whether to read Joliet extensions. + public CDReader(Stream data, bool joliet) + : base(new VfsCDReader(data, joliet, false, 2048)) + { + } + + /// + /// Initializes a new instance of the CDReader class. + /// + /// The stream to read the ISO image from. + /// Whether to read Joliet extensions. + /// Hides version numbers (e.g. ";1") from the end of files. + public CDReader(Stream data, bool joliet, bool hideVersions) + : base(new VfsCDReader(data, joliet, hideVersions, 2048)) { } + + + + /// + /// Initializes a new instance of the CDReader class. + /// + /// The stream to read the ISO image from. + /// Whether to read Joliet extensions. + public CDReader(Stream data, bool joliet, int sectorSize) + : base(new VfsCDReader(data, joliet, false, sectorSize)) { + } + + /// + /// Initializes a new instance of the CDReader class. + /// + /// The stream to read the ISO image from. + /// Whether to read Joliet extensions. + /// Hides version numbers (e.g. ";1") from the end of files. + public CDReader(Stream data, bool joliet, bool hideVersions, int sectorSize) + : base(new VfsCDReader(data, joliet, hideVersions, sectorSize)) {} + + /// + /// Gets which of the Iso9660 variants is being used. + /// + public Iso9660Variant ActiveVariant + { + get { return GetRealFileSystem().ActiveVariant; } + } + + /// + /// Gets the emulation requested of BIOS when the image is loaded. + /// + public BootDeviceEmulation BootEmulation + { + get { return GetRealFileSystem().BootEmulation; } + } + + /// + /// Gets the absolute start position (in bytes) of the boot image, or zero if not found. + /// + public long BootImageStart + { + get { return GetRealFileSystem().BootImageStart; } + } + + /// + /// Gets the memory segment the image should be loaded into (0 for default). + /// + public int BootLoadSegment + { + get { return GetRealFileSystem().BootLoadSegment; } + } + + /// + /// Gets a value indicating whether a boot image is present. + /// + public bool HasBootImage + { + get { return GetRealFileSystem().HasBootImage; } + } + + /// + /// Gets the size (in bytes) of each cluster. + /// + public long ClusterSize + { + get { return GetRealFileSystem().ClusterSize; } + } + + /// + /// Gets the total number of clusters managed by the file system. + /// + public long TotalClusters + { + get { return GetRealFileSystem().TotalClusters; } + } + + /// + /// Converts a cluster (index) into an absolute byte position in the underlying stream. + /// + /// The cluster to convert. + /// The corresponding absolute byte position. + public long ClusterToOffset(long cluster) + { + return GetRealFileSystem().ClusterToOffset(cluster); + } + + /// + /// Converts an absolute byte position in the underlying stream to a cluster (index). + /// + /// The byte position to convert. + /// The cluster containing the specified byte. + public long OffsetToCluster(long offset) + { + return GetRealFileSystem().OffsetToCluster(offset); + } + + /// + /// Converts a file name to the list of clusters occupied by the file's data. + /// + /// The path to inspect. + /// The clusters. + /// Note that in some file systems, small files may not have dedicated + /// clusters. Only dedicated clusters will be returned. + public Range[] PathToClusters(string path) + { + return GetRealFileSystem().PathToClusters(path); + } + + /// + /// Converts a file name to the extents containing its data. + /// + /// The path to inspect. + /// The file extents, as absolute byte positions in the underlying stream. + /// Use this method with caution - not all file systems will store all bytes + /// directly in extents. Files may be compressed, sparse or encrypted. This method + /// merely indicates where file data is stored, not what's stored. + public StreamExtent[] PathToExtents(string path) + { + return GetRealFileSystem().PathToExtents(path); + } + + /// + /// Gets an object that can convert between clusters and files. + /// + /// The cluster map. + public ClusterMap BuildClusterMap() + { + return GetRealFileSystem().BuildClusterMap(); + } + + /// + /// Retrieves Unix-specific information about a file or directory. + /// + /// Path to the file or directory. + /// Information about the owner, group, permissions and type of the + /// file or directory. + public UnixFileSystemInfo GetUnixFileInfo(string path) + { + return GetRealFileSystem().GetUnixFileInfo(path); + } + + /// + /// Detects if a stream contains a valid ISO file system. + /// + /// The stream to inspect. + /// true if the stream contains an ISO file system, else false. + public static bool Detect(Stream data, int sectorSize) + { + byte[] buffer = new byte[sectorSize]; + + if (data.Length < 0x8000 + sectorSize) + { + return false; + } + + data.Position = 0x8000; + int numRead = StreamUtilities.ReadMaximum(data, buffer, 0, sectorSize); + if (numRead != sectorSize) + { + return false; + } + + BaseVolumeDescriptor bvd = new BaseVolumeDescriptor(buffer, 0); + + return bvd.StandardIdentifier == BaseVolumeDescriptor.Iso9660StandardIdentifier; + } + + /// + /// Opens a stream containing the boot image. + /// + /// The boot image as a stream. + public Stream OpenBootImage() + { + return GetRealFileSystem().OpenBootImage(); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/CommonVolumeDescriptor.cs b/DiscUtils/Iso9660Ps1/CommonVolumeDescriptor.cs new file mode 100644 index 0000000..fb7a571 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/CommonVolumeDescriptor.cs @@ -0,0 +1,141 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; +using DiscUtils.Internal; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class CommonVolumeDescriptor : BaseVolumeDescriptor + { + public string AbstractFileIdentifier; + public string ApplicationIdentifier; + public string BibliographicFileIdentifier; + public Encoding CharacterEncoding; + public string CopyrightFileIdentifier; + public DateTime CreationDateAndTime; + public string DataPreparerIdentifier; + public DateTime EffectiveDateAndTime; + public DateTime ExpirationDateAndTime; + public byte FileStructureVersion; + public ushort LogicalBlockSize; + public DateTime ModificationDateAndTime; + public uint OptionalTypeLPathTableLocation; + public uint OptionalTypeMPathTableLocation; + public uint PathTableSize; + public string PublisherIdentifier; + public DirectoryRecord RootDirectory; + + public string SystemIdentifier; + public uint TypeLPathTableLocation; + public uint TypeMPathTableLocation; + public string VolumeIdentifier; + public ushort VolumeSequenceNumber; + public string VolumeSetIdentifier; + public ushort VolumeSetSize; + public uint VolumeSpaceSize; + + public CommonVolumeDescriptor(byte[] src, int offset, Encoding enc) + : base(src, offset) + { + CharacterEncoding = enc; + + SystemIdentifier = IsoUtilities.ReadChars(src, offset + 8, 32, CharacterEncoding); + VolumeIdentifier = IsoUtilities.ReadChars(src, offset + 40, 32, CharacterEncoding); + VolumeSpaceSize = IsoUtilities.ToUInt32FromBoth(src, offset + 80); + VolumeSetSize = IsoUtilities.ToUInt16FromBoth(src, offset + 120); + VolumeSequenceNumber = IsoUtilities.ToUInt16FromBoth(src, offset + 124); + LogicalBlockSize = IsoUtilities.ToUInt16FromBoth(src, offset + 128); + PathTableSize = IsoUtilities.ToUInt32FromBoth(src, offset + 132); + TypeLPathTableLocation = EndianUtilities.ToUInt32LittleEndian(src, offset + 140); + OptionalTypeLPathTableLocation = EndianUtilities.ToUInt32LittleEndian(src, offset + 144); + TypeMPathTableLocation = Utilities.BitSwap(EndianUtilities.ToUInt32LittleEndian(src, offset + 148)); + OptionalTypeMPathTableLocation = Utilities.BitSwap(EndianUtilities.ToUInt32LittleEndian(src, offset + 152)); + DirectoryRecord.ReadFrom(src, offset + 156, CharacterEncoding, out RootDirectory); + VolumeSetIdentifier = IsoUtilities.ReadChars(src, offset + 190, 318 - 190, CharacterEncoding); + PublisherIdentifier = IsoUtilities.ReadChars(src, offset + 318, 446 - 318, CharacterEncoding); + DataPreparerIdentifier = IsoUtilities.ReadChars(src, offset + 446, 574 - 446, CharacterEncoding); + ApplicationIdentifier = IsoUtilities.ReadChars(src, offset + 574, 702 - 574, CharacterEncoding); + CopyrightFileIdentifier = IsoUtilities.ReadChars(src, offset + 702, 739 - 702, CharacterEncoding); + AbstractFileIdentifier = IsoUtilities.ReadChars(src, offset + 739, 776 - 739, CharacterEncoding); + BibliographicFileIdentifier = IsoUtilities.ReadChars(src, offset + 776, 813 - 776, CharacterEncoding); + CreationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 813); + ModificationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 830); + ExpirationDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 847); + EffectiveDateAndTime = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(src, offset + 864); + FileStructureVersion = src[offset + 881]; + } + + public CommonVolumeDescriptor( + VolumeDescriptorType type, + byte version, + uint volumeSpaceSize, + uint pathTableSize, + uint typeLPathTableLocation, + uint typeMPathTableLocation, + uint rootDirExtentLocation, + uint rootDirDataLength, + DateTime buildTime, + Encoding enc, + int sectorSize) + : base(type, version, sectorSize) + { + CharacterEncoding = enc; + + SystemIdentifier = string.Empty; + VolumeIdentifier = string.Empty; + VolumeSpaceSize = volumeSpaceSize; + VolumeSetSize = 1; + VolumeSequenceNumber = 1; + LogicalBlockSize = (ushort)sectorSize; + PathTableSize = pathTableSize; + TypeLPathTableLocation = typeLPathTableLocation; + ////OptionalTypeLPathTableLocation = 0; + TypeMPathTableLocation = typeMPathTableLocation; + ////OptionalTypeMPathTableLocation = 0; + RootDirectory = new DirectoryRecord(); + RootDirectory.ExtendedAttributeRecordLength = 0; + RootDirectory.LocationOfExtent = rootDirExtentLocation; + RootDirectory.DataLength = rootDirDataLength; + RootDirectory.RecordingDateAndTime = buildTime; + RootDirectory.Flags = FileFlags.Directory; + RootDirectory.FileUnitSize = 0; + RootDirectory.InterleaveGapSize = 0; + RootDirectory.VolumeSequenceNumber = 1; + RootDirectory.FileIdentifier = "\0"; + VolumeSetIdentifier = string.Empty; + PublisherIdentifier = string.Empty; + DataPreparerIdentifier = string.Empty; + ApplicationIdentifier = string.Empty; + CopyrightFileIdentifier = string.Empty; + AbstractFileIdentifier = string.Empty; + BibliographicFileIdentifier = string.Empty; + CreationDateAndTime = buildTime; + ModificationDateAndTime = buildTime; + ExpirationDateAndTime = DateTime.MinValue; + EffectiveDateAndTime = buildTime; + FileStructureVersion = 1; // V1 + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/DirectoryExtent.cs b/DiscUtils/Iso9660Ps1/DirectoryExtent.cs new file mode 100644 index 0000000..3c3b1f2 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/DirectoryExtent.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class DirectoryExtent : BuilderExtent + { + private readonly BuildDirectoryInfo _dirInfo; + private readonly Encoding _enc; + private readonly Dictionary _locationTable; + + private byte[] _readCache; + + public DirectoryExtent(BuildDirectoryInfo dirInfo, Dictionary locationTable, + Encoding enc, long start) + : base(start, dirInfo.GetDataSize(enc)) + { + _dirInfo = dirInfo; + _locationTable = locationTable; + _enc = enc; + } + + public override void Dispose() {} + + public override void PrepareForRead() + { + _readCache = new byte[Length]; + _dirInfo.Write(_readCache, 0, _locationTable, _enc); + } + + public override int Read(long diskOffset, byte[] buffer, int offset, int count) + { + long relPos = diskOffset - Start; + + int numRead = (int)Math.Min(count, _readCache.Length - relPos); + + Array.Copy(_readCache, (int)relPos, buffer, offset, numRead); + + return numRead; + } + + public override void DisposeReadState() + { + _readCache = null; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/DirectoryRecord.cs b/DiscUtils/Iso9660Ps1/DirectoryRecord.cs new file mode 100644 index 0000000..fb6c9ad --- /dev/null +++ b/DiscUtils/Iso9660Ps1/DirectoryRecord.cs @@ -0,0 +1,114 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class DirectoryRecord + { + public uint DataLength; + public byte ExtendedAttributeRecordLength; + public string FileIdentifier; + public byte FileUnitSize; + public FileFlags Flags; + public byte InterleaveGapSize; + public uint LocationOfExtent; + public DateTime RecordingDateAndTime; + public byte[] SystemUseData; + public ushort VolumeSequenceNumber; + + public static int ReadFrom(byte[] src, int offset, Encoding enc, out DirectoryRecord record) + { + int length = src[offset + 0]; + + record = new DirectoryRecord(); + record.ExtendedAttributeRecordLength = src[offset + 1]; + record.LocationOfExtent = IsoUtilities.ToUInt32FromBoth(src, offset + 2); + record.DataLength = IsoUtilities.ToUInt32FromBoth(src, offset + 10); + record.RecordingDateAndTime = IsoUtilities.ToUTCDateTimeFromDirectoryTime(src, offset + 18); + record.Flags = (FileFlags)src[offset + 25]; + record.FileUnitSize = src[offset + 26]; + record.InterleaveGapSize = src[offset + 27]; + record.VolumeSequenceNumber = IsoUtilities.ToUInt16FromBoth(src, offset + 28); + byte lengthOfFileIdentifier = src[offset + 32]; + record.FileIdentifier = IsoUtilities.ReadChars(src, offset + 33, lengthOfFileIdentifier, enc); + + int padding = (lengthOfFileIdentifier & 1) == 0 ? 1 : 0; + int startSystemArea = lengthOfFileIdentifier + padding + 33; + int lenSystemArea = length - startSystemArea; + if (lenSystemArea > 0) + { + record.SystemUseData = new byte[lenSystemArea]; + Array.Copy(src, offset + startSystemArea, record.SystemUseData, 0, lenSystemArea); + } + + return length; + } + + public static uint CalcLength(string name, Encoding enc) + { + int nameBytes; + if (name.Length == 1 && name[0] <= 1) + { + nameBytes = 1; + } + else + { + nameBytes = enc.GetByteCount(name); + } + + return (uint)(33 + nameBytes + ((nameBytes & 0x1) == 0 ? 1 : 0)); + } + + internal int WriteTo(byte[] buffer, int offset, Encoding enc) + { + uint length = CalcLength(FileIdentifier, enc); + buffer[offset] = (byte)length; + buffer[offset + 1] = ExtendedAttributeRecordLength; + IsoUtilities.ToBothFromUInt32(buffer, offset + 2, LocationOfExtent); + IsoUtilities.ToBothFromUInt32(buffer, offset + 10, DataLength); + IsoUtilities.ToDirectoryTimeFromUTC(buffer, offset + 18, RecordingDateAndTime); + buffer[offset + 25] = (byte)Flags; + buffer[offset + 26] = FileUnitSize; + buffer[offset + 27] = InterleaveGapSize; + IsoUtilities.ToBothFromUInt16(buffer, offset + 28, VolumeSequenceNumber); + byte lengthOfFileIdentifier; + + if (FileIdentifier.Length == 1 && FileIdentifier[0] <= 1) + { + buffer[offset + 33] = (byte)FileIdentifier[0]; + lengthOfFileIdentifier = 1; + } + else + { + lengthOfFileIdentifier = + (byte) + IsoUtilities.WriteString(buffer, offset + 33, (int)(length - 33), false, FileIdentifier, enc); + } + + buffer[offset + 32] = lengthOfFileIdentifier; + return (int)length; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/ExtentStream.cs b/DiscUtils/Iso9660Ps1/ExtentStream.cs new file mode 100644 index 0000000..80e4880 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/ExtentStream.cs @@ -0,0 +1,125 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class ExtentStream : Stream + { + private readonly uint _dataLength; + private readonly byte _fileUnitSize; + private readonly byte _interleaveGapSize; + + private readonly Stream _isoStream; + private long _position; + + private readonly uint _startBlock; + private readonly int _sectorSize; + + public ExtentStream(Stream isoStream, uint startBlock, uint dataLength, byte fileUnitSize, + byte interleaveGapSize, int sectorSize) + { + _isoStream = isoStream; + _startBlock = startBlock; + _dataLength = dataLength; + _fileUnitSize = fileUnitSize; + _interleaveGapSize = interleaveGapSize; + _sectorSize = sectorSize; + + if (_fileUnitSize != 0 || _interleaveGapSize != 0) + { + throw new NotSupportedException("Non-contiguous extents not supported"); + } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override long Length + { + get { return _dataLength; } + } + + public override long Position + { + get { return _position; } + set { _position = value; } + } + + public override void Flush() {} + + public override int Read(byte[] buffer, int offset, int count) + { + if (_position > _dataLength) + { + return 0; + } + + int toRead = (int)Math.Min((uint)count, _dataLength - _position); + + _isoStream.Position = _position + _startBlock * (long)_sectorSize; + int numRead = _isoStream.Read(buffer, offset, toRead); + _position += numRead; + return numRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + long newPos = offset; + if (origin == SeekOrigin.Current) + { + newPos += _position; + } + else if (origin == SeekOrigin.End) + { + newPos += _dataLength; + } + + _position = newPos; + return newPos; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/File.cs b/DiscUtils/Iso9660Ps1/File.cs new file mode 100644 index 0000000..32514fe --- /dev/null +++ b/DiscUtils/Iso9660Ps1/File.cs @@ -0,0 +1,119 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using DiscUtils.Vfs; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class File : IVfsFile + { + protected IsoContext _context; + protected ReaderDirEntry _dirEntry; + + public File(IsoContext context, ReaderDirEntry dirEntry) + { + _context = context; + _dirEntry = dirEntry; + } + + public virtual byte[] SystemUseData + { + get { return _dirEntry.Record.SystemUseData; } + } + + public UnixFileSystemInfo UnixFileInfo + { + get + { + if (!_context.SuspDetected || string.IsNullOrEmpty(_context.RockRidgeIdentifier)) + { + throw new InvalidOperationException("No RockRidge file information available"); + } + + SuspRecords suspRecords = new SuspRecords(_context, SystemUseData, 0); + + PosixFileInfoSystemUseEntry pfi = + suspRecords.GetEntry(_context.RockRidgeIdentifier, "PX"); + if (pfi != null) + { + return new UnixFileSystemInfo + { + FileType = (UnixFileType)((pfi.FileMode >> 12) & 0xff), + Permissions = (UnixFilePermissions)(pfi.FileMode & 0xfff), + UserId = (int)pfi.UserId, + GroupId = (int)pfi.GroupId, + Inode = pfi.Inode, + LinkCount = (int)pfi.NumLinks + }; + } + + throw new InvalidOperationException("No RockRidge file information available for this file"); + } + } + + public DateTime LastAccessTimeUtc + { + get { return _dirEntry.LastAccessTimeUtc; } + + set { throw new NotSupportedException(); } + } + + public DateTime LastWriteTimeUtc + { + get { return _dirEntry.LastWriteTimeUtc; } + + set { throw new NotSupportedException(); } + } + + public DateTime CreationTimeUtc + { + get { return _dirEntry.CreationTimeUtc; } + + set { throw new NotSupportedException(); } + } + + public FileAttributes FileAttributes + { + get { return _dirEntry.FileAttributes; } + + set { throw new NotSupportedException(); } + } + + public long FileLength + { + get { return _dirEntry.Record.DataLength; } + } + + public IBuffer FileContent + { + get + { + ExtentStream es = new ExtentStream(_context.DataStream, _dirEntry.Record.LocationOfExtent, + _dirEntry.Record.DataLength, _dirEntry.Record.FileUnitSize, _dirEntry.Record.InterleaveGapSize, _context.SectorSize); + return new StreamBuffer(es, Ownership.Dispose); + } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/FileExtent.cs b/DiscUtils/Iso9660Ps1/FileExtent.cs new file mode 100644 index 0000000..dbff57a --- /dev/null +++ b/DiscUtils/Iso9660Ps1/FileExtent.cs @@ -0,0 +1,85 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.IO; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class FileExtent : BuilderExtent + { + private readonly BuildFileInfo _fileInfo; + + private Stream _readStream; + + public FileExtent(BuildFileInfo fileInfo, long start) + : base(start, fileInfo.GetDataSize(Encoding.ASCII)) + { + _fileInfo = fileInfo; + } + + public override void Dispose() + { + if (_readStream != null) + { + _fileInfo.CloseStream(_readStream); + _readStream = null; + } + } + + public override void PrepareForRead() + { + _readStream = _fileInfo.OpenStream(); + } + + public override int Read(long diskOffset, byte[] block, int offset, int count) + { + long relPos = diskOffset - Start; + int totalRead = 0; + + // Don't arbitrarily set position, just in case stream implementation is + // non-seeking, and we're doing sequential reads + if (_readStream.Position != relPos) + { + _readStream.Position = relPos; + } + + // Read up to EOF + int numRead = _readStream.Read(block, offset, count); + totalRead += numRead; + while (numRead > 0 && totalRead < count) + { + numRead = _readStream.Read(block, offset + totalRead, count - totalRead); + totalRead += numRead; + } + + return totalRead; + } + + public override void DisposeReadState() + { + _fileInfo.CloseStream(_readStream); + _readStream = null; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/FileFlags.cs b/DiscUtils/Iso9660Ps1/FileFlags.cs new file mode 100644 index 0000000..b350c27 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/FileFlags.cs @@ -0,0 +1,16 @@ +using System; + +namespace DiscUtils.Iso9660Ps1 +{ + [Flags] + internal enum FileFlags : byte + { + None = 0x00, + Hidden = 0x01, + Directory = 0x02, + AssociatedFile = 0x04, + Record = 0x08, + Protection = 0x10, + MultiExtent = 0x80 + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Iso9660Variant.cs b/DiscUtils/Iso9660Ps1/Iso9660Variant.cs new file mode 100644 index 0000000..fd76c2f --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Iso9660Variant.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + /// + /// Enumeration of known file system variants. + /// + /// + /// ISO9660 has a number of significant limitations, and over time + /// multiple schemes have been devised for extending the standard + /// to support the richer file system semantics typical of most modern + /// operating systems. These variants differ functionally and (in the + /// case of RockRidge) may represent a logically different directory + /// hierarchy to that encoded in the vanilla iso9660 standard. + /// Use this enum to control which variants to honour / prefer + /// when accessing an ISO image. + /// + public enum Iso9660Variant + { + /// + /// No known variant. + /// + None, + + /// + /// Vanilla ISO9660. + /// + Iso9660, + + /// + /// Joliet file system (Windows). + /// + Joliet, + + /// + /// Rock Ridge (Unix). + /// + RockRidge + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/IsoContext.cs b/DiscUtils/Iso9660Ps1/IsoContext.cs new file mode 100644 index 0000000..318f207 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/IsoContext.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.IO; +using DiscUtils.Vfs; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class IsoContext : VfsContext + { + public IsoContext(int sectorSize) + { + SectorSize = sectorSize; + } + + public Stream DataStream { get; set; } + + public string RockRidgeIdentifier { get; set; } + + public bool SuspDetected { get; set; } + + public int SectorSize { get; set; } + + public List SuspExtensions { get; set; } + + public int SuspSkipBytes { get; set; } + public CommonVolumeDescriptor VolumeDescriptor { get; set; } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/IsoUtilities.cs b/DiscUtils/Iso9660Ps1/IsoUtilities.cs new file mode 100644 index 0000000..05e4398 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/IsoUtilities.cs @@ -0,0 +1,467 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Globalization; +using System.IO; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal static class IsoUtilities + { + //public const int SectorSize = 2048; + + public static uint ToUInt32FromBoth(byte[] data, int offset) + { + return EndianUtilities.ToUInt32LittleEndian(data, offset); + } + + public static ushort ToUInt16FromBoth(byte[] data, int offset) + { + return EndianUtilities.ToUInt16LittleEndian(data, offset); + } + + internal static void ToBothFromUInt32(byte[] buffer, int offset, uint value) + { + EndianUtilities.WriteBytesLittleEndian(value, buffer, offset); + EndianUtilities.WriteBytesBigEndian(value, buffer, offset + 4); + } + + internal static void ToBothFromUInt16(byte[] buffer, int offset, ushort value) + { + EndianUtilities.WriteBytesLittleEndian(value, buffer, offset); + EndianUtilities.WriteBytesBigEndian(value, buffer, offset + 2); + } + + internal static void ToBytesFromUInt32(byte[] buffer, int offset, uint value) + { + EndianUtilities.WriteBytesLittleEndian(value, buffer, offset); + } + + internal static void ToBytesFromUInt16(byte[] buffer, int offset, ushort value) + { + EndianUtilities.WriteBytesLittleEndian(value, buffer, offset); + } + + internal static void WriteAChars(byte[] buffer, int offset, int numBytes, string str) + { + // Validate string + if (!IsValidAString(str)) + { + throw new IOException("Attempt to write string with invalid a-characters"); + } + + ////WriteASCII(buffer, offset, numBytes, true, str); + WriteString(buffer, offset, numBytes, true, str, Encoding.ASCII); + } + + internal static void WriteDChars(byte[] buffer, int offset, int numBytes, string str) + { + // Validate string + if (!IsValidDString(str)) + { + throw new IOException("Attempt to write string with invalid d-characters"); + } + + ////WriteASCII(buffer, offset, numBytes, true, str); + WriteString(buffer, offset, numBytes, true, str, Encoding.ASCII); + } + + internal static void WriteA1Chars(byte[] buffer, int offset, int numBytes, string str, Encoding enc) + { + // Validate string + if (!IsValidAString(str)) + { + throw new IOException("Attempt to write string with invalid a-characters"); + } + + WriteString(buffer, offset, numBytes, true, str, enc); + } + + internal static void WriteD1Chars(byte[] buffer, int offset, int numBytes, string str, Encoding enc) + { + // Validate string + if (!IsValidDString(str)) + { + throw new IOException("Attempt to write string with invalid d-characters"); + } + + WriteString(buffer, offset, numBytes, true, str, enc); + } + + internal static string ReadChars(byte[] buffer, int offset, int numBytes, Encoding enc) + { + char[] chars; + + // Special handling for 'magic' names '\x00' and '\x01', which indicate root and parent, respectively + if (numBytes == 1) + { + chars = new char[1]; + chars[0] = (char)buffer[offset]; + } + else + { + Decoder decoder = enc.GetDecoder(); + chars = new char[decoder.GetCharCount(buffer, offset, numBytes, false)]; + decoder.GetChars(buffer, offset, numBytes, chars, 0, false); + } + + return new string(chars).TrimEnd(' '); + } + +#if false + public static byte WriteFileName(byte[] buffer, int offset, int numBytes, string str, Encoding enc) + { + if (numBytes > 255 || numBytes < 0) + { + throw new ArgumentOutOfRangeException("numBytes", "Attempt to write overlength or underlength file name"); + } + + // Validate string + if (!isValidFileName(str)) + { + throw new IOException("Attempt to write string with invalid file name characters"); + } + + return (byte)WriteString(buffer, offset, numBytes, false, str, enc); + } + + public static byte WriteDirectoryName(byte[] buffer, int offset, int numBytes, string str, Encoding enc) + { + if (numBytes > 255 || numBytes < 0) + { + throw new ArgumentOutOfRangeException("numBytes", "Attempt to write overlength or underlength directory name"); + } + + // Validate string + if (!isValidDirectoryName(str)) + { + throw new IOException("Attempt to write string with invalid directory name characters"); + } + + return (byte)WriteString(buffer, offset, numBytes, false, str, enc); + } +#endif + + internal static int WriteString(byte[] buffer, int offset, int numBytes, bool pad, string str, Encoding enc) + { + return WriteString(buffer, offset, numBytes, pad, str, enc, false); + } + + internal static int WriteString(byte[] buffer, int offset, int numBytes, bool pad, string str, Encoding enc, + bool canTruncate) + { + Encoder encoder = enc.GetEncoder(); + + string paddedString = pad ? str + new string(' ', numBytes) : str; + + // Assumption: never less than one byte per character + + int charsUsed; + int bytesUsed; + bool completed; + encoder.Convert(paddedString.ToCharArray(), 0, paddedString.Length, buffer, offset, numBytes, false, + out charsUsed, out bytesUsed, out completed); + + if (!canTruncate && charsUsed < str.Length) + { + throw new IOException("Failed to write entire string"); + } + + return bytesUsed; + } + + internal static bool IsValidAString(string str) + { + for (int i = 0; i < str.Length; ++i) + { + if (!( + (str[i] >= ' ' && str[i] <= '\"') + || (str[i] >= '%' && str[i] <= '/') + || (str[i] >= ':' && str[i] <= '?') + || (str[i] >= '0' && str[i] <= '9') + || (str[i] >= 'A' && str[i] <= 'Z') + || (str[i] == '_'))) + { + return false; + } + } + + return true; + } + + internal static bool IsValidDString(string str) + { + for (int i = 0; i < str.Length; ++i) + { + if (!IsValidDChar(str[i])) + { + return false; + } + } + + return true; + } + + internal static bool IsValidDChar(char ch) + { + return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch == '_'); + } + + internal static bool IsValidFileName(string str) + { + for (int i = 0; i < str.Length; ++i) + { + if ( + !((str[i] >= '0' && str[i] <= '9') || (str[i] >= 'A' && str[i] <= 'Z') || (str[i] == '_') || + (str[i] == '.') || (str[i] == ';'))) + { + return false; + } + } + + return true; + } + + internal static bool IsValidDirectoryName(string str) + { + if (str.Length == 1 && (str[0] == 0 || str[0] == 1)) + { + return true; + } + return IsValidDString(str); + } + + internal static string NormalizeFileName(string name) + { + string[] parts = SplitFileName(name); + return parts[0] + '.' + parts[1] + ';' + parts[2]; + } + + internal static string[] SplitFileName(string name) + { + string[] parts = { name, string.Empty, "1" }; + + if (name.Contains(".")) + { + int endOfFilePart = name.IndexOf('.'); + parts[0] = name.Substring(0, endOfFilePart); + if (name.Contains(";")) + { + int verSep = name.IndexOf(';', endOfFilePart + 1); + parts[1] = name.Substring(endOfFilePart + 1, verSep - (endOfFilePart + 1)); + parts[2] = name.Substring(verSep + 1); + } + else + { + parts[1] = name.Substring(endOfFilePart + 1); + } + } + else + { + if (name.Contains(";")) + { + int verSep = name.IndexOf(';'); + parts[0] = name.Substring(0, verSep); + parts[2] = name.Substring(verSep + 1); + } + } + + ushort ver; + if (!ushort.TryParse(parts[2], out ver) || ver > 32767 || ver < 1) + { + ver = 1; + } + + parts[2] = string.Format(CultureInfo.InvariantCulture, "{0}", ver); + + return parts; + } + + /// + /// Converts a DirectoryRecord time to UTC. + /// + /// Buffer containing the time data. + /// Offset in buffer of the time data. + /// The time in UTC. + internal static DateTime ToUTCDateTimeFromDirectoryTime(byte[] data, int offset) + { + try + { + DateTime relTime = new DateTime( + 1900 + data[offset], + data[offset + 1], + data[offset + 2], + data[offset + 3], + data[offset + 4], + data[offset + 5], + DateTimeKind.Utc); + return relTime - TimeSpan.FromMinutes(15 * (sbyte)data[offset + 6]); + } + catch (ArgumentOutOfRangeException) + { + // In case the ISO has a bad date encoded, we'll just fall back to using a fixed date + return DateTime.MinValue; + } + } + + internal static void ToDirectoryTimeFromUTC(byte[] data, int offset, DateTime dateTime) + { + if (dateTime == DateTime.MinValue) + { + Array.Clear(data, offset, 7); + } + else + { + if (dateTime.Year < 1900) + { + throw new IOException("Year is out of range"); + } + + data[offset] = (byte)(dateTime.Year - 1900); + data[offset + 1] = (byte)dateTime.Month; + data[offset + 2] = (byte)dateTime.Day; + data[offset + 3] = (byte)dateTime.Hour; + data[offset + 4] = (byte)dateTime.Minute; + data[offset + 5] = (byte)dateTime.Second; + data[offset + 6] = 0; + } + } + + internal static DateTime ToDateTimeFromVolumeDescriptorTime(byte[] data, int offset) + { + bool allNull = true; + for (int i = 0; i < 16; ++i) + { + if (data[offset + i] != (byte)'0' && data[offset + i] != 0) + { + allNull = false; + break; + } + } + + if (allNull) + { + return DateTime.MinValue; + } + + string strForm = Encoding.ASCII.GetString(data, offset, 16); + + // Work around bugs in burning software that may use zero bytes (rather than '0' characters) + strForm = strForm.Replace('\0', '0'); + + int year = SafeParseInt(1, 9999, strForm.Substring(0, 4)); + int month = SafeParseInt(1, 12, strForm.Substring(4, 2)); + int day = SafeParseInt(1, 31, strForm.Substring(6, 2)); + int hour = SafeParseInt(0, 23, strForm.Substring(8, 2)); + int min = SafeParseInt(0, 59, strForm.Substring(10, 2)); + int sec = SafeParseInt(0, 59, strForm.Substring(12, 2)); + int hundredths = SafeParseInt(0, 99, strForm.Substring(14, 2)); + + try + { + DateTime time = new DateTime(year, month, day, hour, min, sec, hundredths * 10, DateTimeKind.Utc); + return time - TimeSpan.FromMinutes(15 * (sbyte)data[offset + 16]); + } + catch (ArgumentOutOfRangeException) + { + return DateTime.MinValue; + } + } + + internal static void ToVolumeDescriptorTimeFromUTC(byte[] buffer, int offset, DateTime dateTime) + { + if (dateTime == DateTime.MinValue) + { + for (int i = offset; i < offset + 16; ++i) + { + buffer[i] = (byte)'0'; + } + + buffer[offset + 16] = 0; + return; + } + + string strForm = dateTime.ToString("yyyyMMddHHmmssff", CultureInfo.InvariantCulture); + EndianUtilities.StringToBytes(strForm, buffer, offset, 16); + buffer[offset + 16] = 0; + } + + internal static void EncodingToBytes(Encoding enc, byte[] data, int offset) + { + Array.Clear(data, offset, 32); + if (enc == Encoding.ASCII) + { + // Nothing to do + } + else if (enc == Encoding.BigEndianUnicode) + { + data[offset + 0] = 0x25; + data[offset + 1] = 0x2F; + data[offset + 2] = 0x45; + } + else + { + throw new ArgumentException("Unrecognized character encoding"); + } + } + + internal static Encoding EncodingFromBytes(byte[] data, int offset) + { + Encoding enc = Encoding.ASCII; + if (data[offset + 0] == 0x25 && data[offset + 1] == 0x2F + && (data[offset + 2] == 0x40 || data[offset + 2] == 0x43 || data[offset + 2] == 0x45)) + { + // I.e. this is a joliet disc! + enc = Encoding.BigEndianUnicode; + } + + return enc; + } + + internal static bool IsSpecialDirectory(DirectoryRecord r) + { + return r.FileIdentifier == "\0" || r.FileIdentifier == "\x01"; + } + + private static int SafeParseInt(int minVal, int maxVal, string str) + { + int val; + if (!int.TryParse(str, out val)) + { + return minVal; + } + + if (val < minVal) + { + return minVal; + } + if (val > maxVal) + { + return maxVal; + } + return val; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/PathTable.cs b/DiscUtils/Iso9660Ps1/PathTable.cs new file mode 100644 index 0000000..e314cd4 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/PathTable.cs @@ -0,0 +1,100 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class PathTable : BuilderExtent + { + private readonly bool _byteSwap; + private readonly List _dirs; + private readonly Encoding _enc; + private readonly Dictionary _locations; + + private byte[] _readCache; + + public PathTable(bool byteSwap, Encoding enc, List dirs, + Dictionary locations, long start) + : base(start, CalcLength(enc, dirs)) + { + _byteSwap = byteSwap; + _enc = enc; + _dirs = dirs; + _locations = locations; + } + + public override void Dispose() {} + + public override void PrepareForRead() + { + _readCache = new byte[Length]; + int pos = 0; + + List sortedList = new List(_dirs); + sortedList.Sort(BuildDirectoryInfo.PathTableSortComparison); + + Dictionary dirNumbers = new Dictionary(_dirs.Count); + ushort i = 1; + foreach (BuildDirectoryInfo di in sortedList) + { + dirNumbers[di] = i++; + PathTableRecord ptr = new PathTableRecord(); + ptr.DirectoryIdentifier = di.PickName(null, _enc); + ptr.LocationOfExtent = _locations[di]; + ptr.ParentDirectoryNumber = dirNumbers[di.Parent]; + + pos += ptr.Write(_byteSwap, _enc, _readCache, pos); + } + } + + public override int Read(long diskOffset, byte[] buffer, int offset, int count) + { + long relPos = diskOffset - Start; + + int numRead = (int)Math.Min(count, _readCache.Length - relPos); + + Array.Copy(_readCache, (int)relPos, buffer, offset, numRead); + + return numRead; + } + + public override void DisposeReadState() + { + _readCache = null; + } + + private static uint CalcLength(Encoding enc, List dirs) + { + uint length = 0; + foreach (BuildDirectoryInfo di in dirs) + { + length += di.GetPathTableEntrySize(enc); + } + + return length; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/PathTableRecord.cs b/DiscUtils/Iso9660Ps1/PathTableRecord.cs new file mode 100644 index 0000000..0480e18 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/PathTableRecord.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Text; +using DiscUtils.Internal; + +namespace DiscUtils.Iso9660Ps1 +{ + internal struct PathTableRecord + { + ////public byte ExtendedAttributeRecordLength; + public uint LocationOfExtent; + public ushort ParentDirectoryNumber; + public string DirectoryIdentifier; + + ////public static int ReadFrom(byte[] src, int offset, bool byteSwap, Encoding enc, out PathTableRecord record) + ////{ + //// byte directoryIdentifierLength = src[offset + 0]; + //// record.ExtendedAttributeRecordLength = src[offset + 1]; + //// record.LocationOfExtent = EndianUtilities.ToUInt32LittleEndian(src, offset + 2); + //// record.ParentDirectoryNumber = EndianUtilities.ToUInt16LittleEndian(src, offset + 6); + //// record.DirectoryIdentifier = IsoUtilities.ReadChars(src, offset + 8, directoryIdentifierLength, enc); + //// + //// if (byteSwap) + //// { + //// record.LocationOfExtent = Utilities.BitSwap(record.LocationOfExtent); + //// record.ParentDirectoryNumber = Utilities.BitSwap(record.ParentDirectoryNumber); + //// } + //// + //// return directoryIdentifierLength + 8 + (((directoryIdentifierLength & 1) == 1) ? 1 : 0); + ////} + + internal int Write(bool byteSwap, Encoding enc, byte[] buffer, int offset) + { + int nameBytes = enc.GetByteCount(DirectoryIdentifier); + + buffer[offset + 0] = (byte)nameBytes; + buffer[offset + 1] = 0; // ExtendedAttributeRecordLength; + IsoUtilities.ToBytesFromUInt32(buffer, offset + 2, + byteSwap ? Utilities.BitSwap(LocationOfExtent) : LocationOfExtent); + IsoUtilities.ToBytesFromUInt16(buffer, offset + 6, + byteSwap ? Utilities.BitSwap(ParentDirectoryNumber) : ParentDirectoryNumber); + IsoUtilities.WriteString(buffer, offset + 8, nameBytes, false, DirectoryIdentifier, enc); + if ((nameBytes & 1) == 1) + { + buffer[offset + 8 + nameBytes] = 0; + } + + return 8 + nameBytes + ((nameBytes & 0x1) == 1 ? 1 : 0); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptor.cs b/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptor.cs new file mode 100644 index 0000000..75be21b --- /dev/null +++ b/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptor.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; +using DiscUtils.Internal; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class PrimaryVolumeDescriptor : CommonVolumeDescriptor + { + public PrimaryVolumeDescriptor(byte[] src, int offset) + : base(src, offset, Encoding.ASCII) {} + + public PrimaryVolumeDescriptor( + uint volumeSpaceSize, + uint pathTableSize, + uint typeLPathTableLocation, + uint typeMPathTableLocation, + uint rootDirExtentLocation, + uint rootDirDataLength, + DateTime buildTime, + int sectorSize) + : base( + VolumeDescriptorType.Primary, 1, volumeSpaceSize, pathTableSize, typeLPathTableLocation, + typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, Encoding.ASCII, sectorSize) {} + + internal override void WriteTo(byte[] buffer, int offset) + { + base.WriteTo(buffer, offset); + IsoUtilities.WriteAChars(buffer, offset + 8, 32, SystemIdentifier); + IsoUtilities.WriteString(buffer, offset + 40, 32, true, VolumeIdentifier, Encoding.ASCII, true); + IsoUtilities.ToBothFromUInt32(buffer, offset + 80, VolumeSpaceSize); + IsoUtilities.ToBothFromUInt16(buffer, offset + 120, VolumeSetSize); + IsoUtilities.ToBothFromUInt16(buffer, offset + 124, VolumeSequenceNumber); + IsoUtilities.ToBothFromUInt16(buffer, offset + 128, LogicalBlockSize); + IsoUtilities.ToBothFromUInt32(buffer, offset + 132, PathTableSize); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 140, TypeLPathTableLocation); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 144, OptionalTypeLPathTableLocation); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 148, Utilities.BitSwap(TypeMPathTableLocation)); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 152, Utilities.BitSwap(OptionalTypeMPathTableLocation)); + RootDirectory.WriteTo(buffer, offset + 156, Encoding.ASCII); + IsoUtilities.WriteDChars(buffer, offset + 190, 129, VolumeSetIdentifier); + IsoUtilities.WriteAChars(buffer, offset + 318, 129, PublisherIdentifier); + IsoUtilities.WriteAChars(buffer, offset + 446, 129, DataPreparerIdentifier); + IsoUtilities.WriteAChars(buffer, offset + 574, 129, ApplicationIdentifier); + IsoUtilities.WriteDChars(buffer, offset + 702, 37, CopyrightFileIdentifier); // FIXME!! + IsoUtilities.WriteDChars(buffer, offset + 739, 37, AbstractFileIdentifier); // FIXME!! + IsoUtilities.WriteDChars(buffer, offset + 776, 37, BibliographicFileIdentifier); // FIXME!! + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 813, CreationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 830, ModificationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 847, ExpirationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 864, EffectiveDateAndTime); + buffer[offset + 881] = FileStructureVersion; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptorRegion.cs b/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptorRegion.cs new file mode 100644 index 0000000..d327ae3 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/PrimaryVolumeDescriptorRegion.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class PrimaryVolumeDescriptorRegion : VolumeDescriptorDiskRegion + { + private readonly PrimaryVolumeDescriptor _descriptor; + + public PrimaryVolumeDescriptorRegion(PrimaryVolumeDescriptor descriptor, long start, int sectorSize) + : base(start, sectorSize) + { + _descriptor = descriptor; + } + + protected override byte[] GetBlockData() + { + byte[] buffer = new byte[Length]; + _descriptor.WriteTo(buffer, 0); + return buffer; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/ReaderDirEntry.cs b/DiscUtils/Iso9660Ps1/ReaderDirEntry.cs new file mode 100644 index 0000000..3678b95 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/ReaderDirEntry.cs @@ -0,0 +1,195 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using DiscUtils.Internal; +using DiscUtils.Streams; +using DiscUtils.Vfs; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class ReaderDirEntry : VfsDirEntry + { + private readonly IsoContext _context; + private readonly string _fileName; + private readonly DirectoryRecord _record; + + public ReaderDirEntry(IsoContext context, DirectoryRecord dirRecord) + { + _context = context; + _record = dirRecord; + _fileName = _record.FileIdentifier; + + bool rockRidge = !string.IsNullOrEmpty(_context.RockRidgeIdentifier); + + if (context.SuspDetected && _record.SystemUseData != null) + { + SuspRecords = new SuspRecords(_context, _record.SystemUseData, 0); + } + + if (rockRidge && SuspRecords != null) + { + // The full name is taken from this record, even if it's a child-link record + List nameEntries = SuspRecords.GetEntries(_context.RockRidgeIdentifier, "NM"); + StringBuilder rrName = new StringBuilder(); + if (nameEntries != null && nameEntries.Count > 0) + { + foreach (PosixNameSystemUseEntry nameEntry in nameEntries) + { + rrName.Append(nameEntry.NameData); + } + + _fileName = rrName.ToString(); + } + + // If this is a Rock Ridge child link, replace the dir record with that from the 'self' record + // in the child directory. + ChildLinkSystemUseEntry clEntry = + SuspRecords.GetEntry(_context.RockRidgeIdentifier, "CL"); + if (clEntry != null) + { + _context.DataStream.Position = clEntry.ChildDirLocation * _context.VolumeDescriptor.LogicalBlockSize; + byte[] firstSector = StreamUtilities.ReadExact(_context.DataStream, + _context.VolumeDescriptor.LogicalBlockSize); + + DirectoryRecord.ReadFrom(firstSector, 0, _context.VolumeDescriptor.CharacterEncoding, out _record); + if (_record.SystemUseData != null) + { + SuspRecords = new SuspRecords(_context, _record.SystemUseData, 0); + } + } + } + + LastAccessTimeUtc = _record.RecordingDateAndTime; + LastWriteTimeUtc = _record.RecordingDateAndTime; + CreationTimeUtc = _record.RecordingDateAndTime; + + if (rockRidge && SuspRecords != null) + { + FileTimeSystemUseEntry tfEntry = + SuspRecords.GetEntry(_context.RockRidgeIdentifier, "TF"); + + if (tfEntry != null) + { + if ((tfEntry.TimestampsPresent & FileTimeSystemUseEntry.Timestamps.Access) != 0) + { + LastAccessTimeUtc = tfEntry.AccessTime; + } + + if ((tfEntry.TimestampsPresent & FileTimeSystemUseEntry.Timestamps.Modify) != 0) + { + LastWriteTimeUtc = tfEntry.ModifyTime; + } + + if ((tfEntry.TimestampsPresent & FileTimeSystemUseEntry.Timestamps.Creation) != 0) + { + CreationTimeUtc = tfEntry.CreationTime; + } + } + } + } + + public override DateTime CreationTimeUtc { get; } + + public override FileAttributes FileAttributes + { + get + { + FileAttributes attrs = 0; + + if (!string.IsNullOrEmpty(_context.RockRidgeIdentifier)) + { + // If Rock Ridge PX info is present, derive the attributes from the RR info. + PosixFileInfoSystemUseEntry pfi = + SuspRecords.GetEntry(_context.RockRidgeIdentifier, "PX"); + if (pfi != null) + { + attrs = Utilities.FileAttributesFromUnixFileType((UnixFileType)((pfi.FileMode >> 12) & 0xF)); + } + + if (_fileName.StartsWith(".", StringComparison.Ordinal)) + { + attrs |= FileAttributes.Hidden; + } + } + + attrs |= FileAttributes.ReadOnly; + + if ((_record.Flags & FileFlags.Directory) != 0) + { + attrs |= FileAttributes.Directory; + } + + if ((_record.Flags & FileFlags.Hidden) != 0) + { + attrs |= FileAttributes.Hidden; + } + + return attrs; + } + } + + public override string FileName + { + get { return _fileName; } + } + + public override bool HasVfsFileAttributes + { + get { return true; } + } + + public override bool HasVfsTimeInfo + { + get { return true; } + } + + public override bool IsDirectory + { + get { return (_record.Flags & FileFlags.Directory) != 0; } + } + + public override bool IsSymlink + { + get { return false; } + } + + public override DateTime LastAccessTimeUtc { get; } + + public override DateTime LastWriteTimeUtc { get; } + + public DirectoryRecord Record + { + get { return _record; } + } + + public SuspRecords SuspRecords { get; } + + public override long UniqueCacheId + { + get { return ((long)_record.LocationOfExtent << 32) | _record.DataLength; } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/ReaderDirectory.cs b/DiscUtils/Iso9660Ps1/ReaderDirectory.cs new file mode 100644 index 0000000..1e43f49 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/ReaderDirectory.cs @@ -0,0 +1,130 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using DiscUtils.CoreCompat; +using DiscUtils.Streams; +using DiscUtils.Vfs; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class ReaderDirectory : File, IVfsDirectory + { + private readonly List _records; + + public ReaderDirectory(IsoContext context, ReaderDirEntry dirEntry) + : base(context, dirEntry) + { + byte[] buffer = new byte[context.SectorSize]; + Stream extent = new ExtentStream(_context.DataStream, dirEntry.Record.LocationOfExtent, uint.MaxValue, 0, 0, context.SectorSize); + + _records = new List(); + + uint totalLength = dirEntry.Record.DataLength; + uint totalRead = 0; + while (totalRead < totalLength) + { + int bytesRead = (int)Math.Min(buffer.Length, totalLength - totalRead); + + extent.Seek(24, SeekOrigin.Current); + + StreamUtilities.ReadExact(extent, buffer, 0, bytesRead); + totalRead += (uint)bytesRead; + + uint pos = 0; + while (pos < bytesRead && buffer[pos] != 0) + { + DirectoryRecord dr; + uint length = (uint)DirectoryRecord.ReadFrom(buffer, (int)pos, context.VolumeDescriptor.CharacterEncoding, out dr); + + if (!IsoUtilities.IsSpecialDirectory(dr)) + { + ReaderDirEntry childDirEntry = new ReaderDirEntry(_context, dr); + + if (context.SuspDetected && !string.IsNullOrEmpty(context.RockRidgeIdentifier)) + { + if (childDirEntry.SuspRecords == null || !childDirEntry.SuspRecords.HasEntry(context.RockRidgeIdentifier, "RE")) + { + _records.Add(childDirEntry); + } + } + else + { + _records.Add(childDirEntry); + } + } + else if (dr.FileIdentifier == "\0") + { + Self = new ReaderDirEntry(_context, dr); + } + + pos += length; + } + } + } + + public override byte[] SystemUseData + { + get { return Self.Record.SystemUseData; } + } + + public ICollection AllEntries + { + get { return _records; } + } + + public ReaderDirEntry Self { get; } + + public ReaderDirEntry GetEntryByName(string name) + { + bool anyVerMatch = name.IndexOf(';') < 0; + string normName = IsoUtilities.NormalizeFileName(name).ToUpper(CultureInfo.InvariantCulture); + if (anyVerMatch) + { + normName = normName.Substring(0, normName.LastIndexOf(';') + 1); + } + + foreach (ReaderDirEntry r in _records) + { + string toComp = IsoUtilities.NormalizeFileName(r.FileName).ToUpper(CultureInfo.InvariantCulture); + if (!anyVerMatch && toComp == normName) + { + return r; + } + if (anyVerMatch && toComp.StartsWith(normName, StringComparison.Ordinal)) + { + return r; + } + } + + return null; + } + + public ReaderDirEntry CreateNewFile(string name) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/RockRidge/ChildLinkSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/RockRidge/ChildLinkSystemUseEntry.cs new file mode 100644 index 0000000..5806eee --- /dev/null +++ b/DiscUtils/Iso9660Ps1/RockRidge/ChildLinkSystemUseEntry.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class ChildLinkSystemUseEntry : SystemUseEntry + { + public uint ChildDirLocation; + + public ChildLinkSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 12, 1); + + ChildDirLocation = IsoUtilities.ToUInt32FromBoth(data, offset + 4); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/RockRidge/FileTimeSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/RockRidge/FileTimeSystemUseEntry.cs new file mode 100644 index 0000000..d8e9780 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/RockRidge/FileTimeSystemUseEntry.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class FileTimeSystemUseEntry : SystemUseEntry + { + [Flags] + public enum Timestamps : byte + { + None = 0x00, + Creation = 0x01, + Modify = 0x02, + Access = 0x04, + Attributes = 0x08, + Backup = 0x10, + Expiration = 0x20, + Effective = 0x40 + } + + public DateTime AccessTime; + public DateTime AttributesTime; + public DateTime BackupTime; + public DateTime CreationTime; + public DateTime EffectiveTime; + public DateTime ExpirationTime; + public DateTime ModifyTime; + public Timestamps TimestampsPresent = Timestamps.None; + + public FileTimeSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 5, 1); + + byte flags = data[offset + 4]; + + bool longForm = (flags & 0x80) != 0; + int fieldLen = longForm ? 17 : 7; + + TimestampsPresent = (Timestamps)(flags & 0x7F); + + int pos = offset + 5; + + CreationTime = ReadTimestamp(Timestamps.Creation, data, longForm, ref pos); + ModifyTime = ReadTimestamp(Timestamps.Modify, data, longForm, ref pos); + AccessTime = ReadTimestamp(Timestamps.Access, data, longForm, ref pos); + AttributesTime = ReadTimestamp(Timestamps.Attributes, data, longForm, ref pos); + BackupTime = ReadTimestamp(Timestamps.Backup, data, longForm, ref pos); + ExpirationTime = ReadTimestamp(Timestamps.Expiration, data, longForm, ref pos); + EffectiveTime = ReadTimestamp(Timestamps.Effective, data, longForm, ref pos); + } + + private DateTime ReadTimestamp(Timestamps timestamp, byte[] data, bool longForm, ref int pos) + { + DateTime result = DateTime.MinValue; + + if ((TimestampsPresent & timestamp) != 0) + { + if (longForm) + { + result = IsoUtilities.ToDateTimeFromVolumeDescriptorTime(data, pos); + pos += 17; + } + else + { + result = IsoUtilities.ToUTCDateTimeFromDirectoryTime(data, pos); + pos += 7; + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/RockRidge/PosixFileInfoSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/RockRidge/PosixFileInfoSystemUseEntry.cs new file mode 100644 index 0000000..aa9cbd4 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/RockRidge/PosixFileInfoSystemUseEntry.cs @@ -0,0 +1,48 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class PosixFileInfoSystemUseEntry : SystemUseEntry + { + public uint FileMode; + public uint GroupId; + public uint Inode; + public uint NumLinks; + public uint UserId; + + public PosixFileInfoSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 36, 1); + + FileMode = IsoUtilities.ToUInt32FromBoth(data, offset + 4); + NumLinks = IsoUtilities.ToUInt32FromBoth(data, offset + 12); + UserId = IsoUtilities.ToUInt32FromBoth(data, offset + 20); + GroupId = IsoUtilities.ToUInt32FromBoth(data, offset + 28); + Inode = 0; + if (length >= 44) + { + Inode = IsoUtilities.ToUInt32FromBoth(data, offset + 36); + } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/RockRidge/PosixNameSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/RockRidge/PosixNameSystemUseEntry.cs new file mode 100644 index 0000000..ba32771 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/RockRidge/PosixNameSystemUseEntry.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class PosixNameSystemUseEntry : SystemUseEntry + { + public byte Flags; + public string NameData; + + public PosixNameSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 5, 1); + + Flags = data[offset + 4]; + NameData = EndianUtilities.BytesToString(data, offset + 5, length - 5); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/RockRidge/RockRidgeExtension.cs b/DiscUtils/Iso9660Ps1/RockRidge/RockRidgeExtension.cs new file mode 100644 index 0000000..9c2f422 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/RockRidge/RockRidgeExtension.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class RockRidgeExtension : SuspExtension + { + public RockRidgeExtension(string identifier) + { + Identifier = identifier; + } + + public override string Identifier { get; } + + public override SystemUseEntry Parse(string name, byte length, byte version, byte[] data, int offset, Encoding encoding) + { + switch (name) + { + case "PX": + return new PosixFileInfoSystemUseEntry(name, length, version, data, offset); + + case "NM": + return new PosixNameSystemUseEntry(name, length, version, data, offset); + + case "CL": + return new ChildLinkSystemUseEntry(name, length, version, data, offset); + + case "TF": + return new FileTimeSystemUseEntry(name, length, version, data, offset); + + default: + return new GenericSystemUseEntry(name, length, version, data, offset); + } + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptor.cs b/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptor.cs new file mode 100644 index 0000000..719b70b --- /dev/null +++ b/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptor.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Text; +using DiscUtils.Internal; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class SupplementaryVolumeDescriptor : CommonVolumeDescriptor + { + public SupplementaryVolumeDescriptor(byte[] src, int offset) + : base(src, offset, IsoUtilities.EncodingFromBytes(src, offset + 88)) {} + + public SupplementaryVolumeDescriptor( + uint volumeSpaceSize, + uint pathTableSize, + uint typeLPathTableLocation, + uint typeMPathTableLocation, + uint rootDirExtentLocation, + uint rootDirDataLength, + DateTime buildTime, + Encoding enc, + int sectorSize) + : base( + VolumeDescriptorType.Supplementary, 1, volumeSpaceSize, pathTableSize, typeLPathTableLocation, + typeMPathTableLocation, rootDirExtentLocation, rootDirDataLength, buildTime, enc, sectorSize) {} + + internal override void WriteTo(byte[] buffer, int offset) + { + base.WriteTo(buffer, offset); + IsoUtilities.WriteA1Chars(buffer, offset + 8, 32, SystemIdentifier, CharacterEncoding); + IsoUtilities.WriteString(buffer, offset + 40, 32, true, VolumeIdentifier, CharacterEncoding, true); + IsoUtilities.ToBothFromUInt32(buffer, offset + 80, VolumeSpaceSize); + IsoUtilities.EncodingToBytes(CharacterEncoding, buffer, offset + 88); + IsoUtilities.ToBothFromUInt16(buffer, offset + 120, VolumeSetSize); + IsoUtilities.ToBothFromUInt16(buffer, offset + 124, VolumeSequenceNumber); + IsoUtilities.ToBothFromUInt16(buffer, offset + 128, LogicalBlockSize); + IsoUtilities.ToBothFromUInt32(buffer, offset + 132, PathTableSize); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 140, TypeLPathTableLocation); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 144, OptionalTypeLPathTableLocation); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 148, Utilities.BitSwap(TypeMPathTableLocation)); + IsoUtilities.ToBytesFromUInt32(buffer, offset + 152, Utilities.BitSwap(OptionalTypeMPathTableLocation)); + RootDirectory.WriteTo(buffer, offset + 156, CharacterEncoding); + IsoUtilities.WriteD1Chars(buffer, offset + 190, 129, VolumeSetIdentifier, CharacterEncoding); + IsoUtilities.WriteA1Chars(buffer, offset + 318, 129, PublisherIdentifier, CharacterEncoding); + IsoUtilities.WriteA1Chars(buffer, offset + 446, 129, DataPreparerIdentifier, CharacterEncoding); + IsoUtilities.WriteA1Chars(buffer, offset + 574, 129, ApplicationIdentifier, CharacterEncoding); + IsoUtilities.WriteD1Chars(buffer, offset + 702, 37, CopyrightFileIdentifier, CharacterEncoding); // FIXME!! + IsoUtilities.WriteD1Chars(buffer, offset + 739, 37, AbstractFileIdentifier, CharacterEncoding); // FIXME!! + IsoUtilities.WriteD1Chars(buffer, offset + 776, 37, BibliographicFileIdentifier, CharacterEncoding); + + // FIXME!! + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 813, CreationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 830, ModificationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 847, ExpirationDateAndTime); + IsoUtilities.ToVolumeDescriptorTimeFromUTC(buffer, offset + 864, EffectiveDateAndTime); + buffer[offset + 881] = FileStructureVersion; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptorRegion.cs b/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptorRegion.cs new file mode 100644 index 0000000..329f0f5 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/SupplementaryVolumeDescriptorRegion.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class SupplementaryVolumeDescriptorRegion : VolumeDescriptorDiskRegion + { + private readonly SupplementaryVolumeDescriptor _descriptor; + + public SupplementaryVolumeDescriptorRegion(SupplementaryVolumeDescriptor descriptor, long start, int sectorSize) + : base(start, sectorSize) + { + _descriptor = descriptor; + } + + protected override byte[] GetBlockData() + { + byte[] buffer = new byte[Length]; + _descriptor.WriteTo(buffer, 0); + return buffer; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/ContinuationSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/ContinuationSystemUseEntry.cs new file mode 100644 index 0000000..23ea48f --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/ContinuationSystemUseEntry.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class ContinuationSystemUseEntry : SystemUseEntry + { + public uint Block; + public uint BlockOffset; + public uint Length; + + public ContinuationSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 28, 1); + + Block = IsoUtilities.ToUInt32FromBoth(data, offset + 4); + BlockOffset = IsoUtilities.ToUInt32FromBoth(data, offset + 12); + Length = IsoUtilities.ToUInt32FromBoth(data, offset + 20); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/ExtensionSelectSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/ExtensionSelectSystemUseEntry.cs new file mode 100644 index 0000000..9001da4 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/ExtensionSelectSystemUseEntry.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class ExtensionSelectSystemUseEntry : SystemUseEntry + { + public byte SelectedExtension; + + public ExtensionSelectSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 5, 1); + + SelectedExtension = data[offset + 4]; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/ExtensionSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/ExtensionSystemUseEntry.cs new file mode 100644 index 0000000..dc6d382 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/ExtensionSystemUseEntry.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.IO; +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class ExtensionSystemUseEntry : SystemUseEntry + { + public string ExtensionDescriptor; + public string ExtensionIdentifier; + public string ExtensionSource; + public byte ExtensionVersion; + + public ExtensionSystemUseEntry(string name, byte length, byte version, byte[] data, int offset, Encoding encoding) + { + CheckAndSetCommonProperties(name, length, version, 8, 1); + + int lenId = data[offset + 4]; + int lenDescriptor = data[offset + 5]; + int lenSource = data[offset + 6]; + + ExtensionVersion = data[offset + 7]; + + if (length < 8 + lenId + lenDescriptor + lenSource) + { + throw new InvalidDataException("Invalid SUSP ER entry - too short, only " + length + " bytes - expected: " + + (8 + lenId + lenDescriptor + lenSource)); + } + + ExtensionIdentifier = IsoUtilities.ReadChars(data, offset + 8, lenId, encoding); + ExtensionDescriptor = IsoUtilities.ReadChars(data, offset + 8 + lenId, lenDescriptor, encoding); + ExtensionSource = IsoUtilities.ReadChars(data, offset + 8 + lenId + lenDescriptor, lenSource, encoding); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/GenericSuspExtension.cs b/DiscUtils/Iso9660Ps1/Susp/GenericSuspExtension.cs new file mode 100644 index 0000000..0b2af9c --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/GenericSuspExtension.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class GenericSuspExtension : SuspExtension + { + public GenericSuspExtension(string identifier) + { + Identifier = identifier; + } + + public override string Identifier { get; } + + public override SystemUseEntry Parse(string name, byte length, byte version, byte[] data, int offset, Encoding encoding) + { + return new GenericSystemUseEntry(name, length, version, data, offset); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/GenericSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/GenericSystemUseEntry.cs new file mode 100644 index 0000000..e1d95b5 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/GenericSystemUseEntry.cs @@ -0,0 +1,39 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class GenericSystemUseEntry : SystemUseEntry + { + public byte[] Data; + + public GenericSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 4, 0xFF); + + Data = new byte[length - 4]; + Array.Copy(data, offset + 4, Data, 0, length - 4); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/PaddingSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/PaddingSystemUseEntry.cs new file mode 100644 index 0000000..49c2134 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/PaddingSystemUseEntry.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class PaddingSystemUseEntry : SystemUseEntry + { + public PaddingSystemUseEntry(string name, byte length, byte version) + { + CheckAndSetCommonProperties(name, length, version, 4, 1); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/SharingProtocolSystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/SharingProtocolSystemUseEntry.cs new file mode 100644 index 0000000..b35520d --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/SharingProtocolSystemUseEntry.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.IO; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class SharingProtocolSystemUseEntry : SystemUseEntry + { + public byte SystemAreaSkip; + + public SharingProtocolSystemUseEntry(string name, byte length, byte version, byte[] data, int offset) + { + CheckAndSetCommonProperties(name, length, version, 7, 1); + + if (data[offset + 4] != 0xBE || data[offset + 5] != 0xEF) + { + throw new InvalidDataException("Invalid SUSP SP entry - invalid checksum bytes"); + } + + SystemAreaSkip = data[offset + 6]; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/SuspExtension.cs b/DiscUtils/Iso9660Ps1/Susp/SuspExtension.cs new file mode 100644 index 0000000..fd220b2 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/SuspExtension.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Text; + +namespace DiscUtils.Iso9660Ps1 +{ + internal abstract class SuspExtension + { + public abstract string Identifier { get; } + + public abstract SystemUseEntry Parse(string name, byte length, byte version, byte[] data, int offset, Encoding encoding); + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/SuspRecords.cs b/DiscUtils/Iso9660Ps1/Susp/SuspRecords.cs new file mode 100644 index 0000000..174ee40 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/SuspRecords.cs @@ -0,0 +1,182 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal sealed class SuspRecords + { + private readonly Dictionary>> _records; + + public SuspRecords(IsoContext context, byte[] data, int offset) + { + _records = new Dictionary>>(); + + ContinuationSystemUseEntry contEntry = Parse(context, data, offset + context.SuspSkipBytes); + while (contEntry != null) + { + context.DataStream.Position = contEntry.Block * (long)context.VolumeDescriptor.LogicalBlockSize + + contEntry.BlockOffset; + byte[] contData = StreamUtilities.ReadExact(context.DataStream, (int)contEntry.Length); + + contEntry = Parse(context, contData, 0); + } + } + + public static bool DetectSharingProtocol(byte[] data, int offset) + { + if (data == null || data.Length - offset < 7) + { + return false; + } + + return data[offset] == 83 + && data[offset + 1] == 80 + && data[offset + 2] == 7 + && data[offset + 3] == 1 + && data[offset + 4] == 0xBE + && data[offset + 5] == 0xEF; + } + + public List GetEntries(string extension, string name) + { + if (string.IsNullOrEmpty(extension)) + { + extension = string.Empty; + } + + Dictionary> extensionData; + if (!_records.TryGetValue(extension, out extensionData)) + { + return null; + } + + List result; + if (extensionData.TryGetValue(name, out result)) + { + return result; + } + + return null; + } + + public T GetEntry(string extension, string name) + where T : SystemUseEntry + { + List entries = GetEntries(extension, name); + if (entries == null) + { + return null; + } + + foreach (T entry in entries) + { + return entry; + } + + return null; + } + + public bool HasEntry(string extension, string name) + { + List entries = GetEntries(extension, name); + return entries != null && entries.Count != 0; + } + + private ContinuationSystemUseEntry Parse(IsoContext context, byte[] data, int offset) + { + ContinuationSystemUseEntry contEntry = null; + SuspExtension extension = null; + + if (context.SuspExtensions != null && context.SuspExtensions.Count > 0) + { + extension = context.SuspExtensions[0]; + } + + int pos = offset; + while (data.Length - pos > 4) + { + byte len; + SystemUseEntry entry = SystemUseEntry.Parse(data, pos, context.VolumeDescriptor.CharacterEncoding, + extension, out len); + pos += len; + + if (entry == null) + { + // A null entry indicates SUSP parsing must terminate. + // This will occur if a termination record is found, + // or if there is a problem with the SUSP data. + return contEntry; + } + + switch (entry.Name) + { + case "CE": + contEntry = (ContinuationSystemUseEntry)entry; + break; + + case "ES": + ExtensionSelectSystemUseEntry esEntry = (ExtensionSelectSystemUseEntry)entry; + extension = context.SuspExtensions[esEntry.SelectedExtension]; + break; + + case "PD": + break; + + case "SP": + case "ER": + StoreEntry(null, entry); + break; + + default: + StoreEntry(extension, entry); + break; + } + } + + return contEntry; + } + + private void StoreEntry(SuspExtension extension, SystemUseEntry entry) + { + string extensionId = extension == null ? string.Empty : extension.Identifier; + + Dictionary> extensionEntries; + if (!_records.TryGetValue(extensionId, out extensionEntries)) + { + extensionEntries = new Dictionary>(); + _records.Add(extensionId, extensionEntries); + } + + List entries; + if (!extensionEntries.TryGetValue(entry.Name, out entries)) + { + entries = new List(); + extensionEntries.Add(entry.Name, entries); + } + + entries.Add(entry); + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/Susp/SystemUseEntry.cs b/DiscUtils/Iso9660Ps1/Susp/SystemUseEntry.cs new file mode 100644 index 0000000..91877ca --- /dev/null +++ b/DiscUtils/Iso9660Ps1/Susp/SystemUseEntry.cs @@ -0,0 +1,105 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Text; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal abstract class SystemUseEntry + { + public string Name; + public byte Version; + + public static SystemUseEntry Parse(byte[] data, int offset, Encoding encoding, SuspExtension extension, + out byte length) + { + if (data[offset] == 0) + { + // A zero-byte here is invalid and indicates an incorrectly written SUSP field. + // Return null to indicate to the caller that SUSP parsing is terminated. + length = 0; + + return null; + } + + string name = EndianUtilities.BytesToString(data, offset, 2); + length = data[offset + 2]; + byte version = data[offset + 3]; + + switch (name) + { + case "CE": + return new ContinuationSystemUseEntry(name, length, version, data, offset); + + case "PD": + return new PaddingSystemUseEntry(name, length, version); + + case "SP": + return new SharingProtocolSystemUseEntry(name, length, version, data, offset); + + case "ST": + // Termination entry. There's no point in storing or validating this one. + // Return null to indicate to the caller that SUSP parsing is terminated. + return null; + + case "ER": + return new ExtensionSystemUseEntry(name, length, version, data, offset, encoding); + + case "ES": + return new ExtensionSelectSystemUseEntry(name, length, version, data, offset); + + case "AA": + case "AB": + case "AS": + // Placeholder support for Apple and Amiga extension records. + return new GenericSystemUseEntry(name, length, version, data, offset); + + default: + if (extension == null) + { + return new GenericSystemUseEntry(name, length, version, data, offset); + } + + return extension.Parse(name, length, version, data, offset, encoding); + } + } + + protected void CheckAndSetCommonProperties(string name, byte length, byte version, byte minLength, byte maxVersion) + { + if (length < minLength) + { + throw new InvalidDataException("Invalid SUSP " + Name + " entry - too short, only " + length + " bytes"); + } + + if (version > maxVersion || version == 0) + { + throw new NotSupportedException("Unknown SUSP " + Name + " entry version: " + version); + } + + Name = name; + Version = version; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/VfsCDReader.cs b/DiscUtils/Iso9660Ps1/VfsCDReader.cs new file mode 100644 index 0000000..c694ec3 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/VfsCDReader.cs @@ -0,0 +1,530 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using DiscUtils.Streams; +using DiscUtils.Vfs; + +namespace DiscUtils.Iso9660Ps1 +{ + internal class VfsCDReader : VfsReadOnlyFileSystem, + IClusterBasedFileSystem, IUnixFileSystem + { + private static readonly Iso9660Variant[] DefaultVariantsNoJoliet = { Iso9660Variant.RockRidge, Iso9660Variant.Iso9660 }; + + private static readonly Iso9660Variant[] DefaultVariantsWithJoliet = { Iso9660Variant.Joliet, Iso9660Variant.RockRidge, Iso9660Variant.Iso9660 }; + + private byte[] _bootCatalog; + private readonly BootVolumeDescriptor _bootVolDesc; + + private readonly Stream _data; + private readonly bool _hideVersions; + protected readonly int _sectorSize; + + /// + /// Initializes a new instance of the VfsCDReader class. + /// + /// The stream to read the ISO image from. + /// Whether to read Joliet extensions. + /// Hides version numbers (e.g. ";1") from the end of files. + public VfsCDReader(Stream data, bool joliet, bool hideVersions, int sectorSize) + : this(data, joliet ? DefaultVariantsWithJoliet : DefaultVariantsNoJoliet, hideVersions, sectorSize) { + } + + /// + /// Initializes a new instance of the VfsCDReader class. + /// + /// The stream to read the ISO image from. + /// Which possible file system variants to use, and with which priority. + /// Hides version numbers (e.g. ";1") from the end of files. + /// + /// + /// The implementation considers each of the file system variants in variantProperties and selects + /// the first which is determined to be present. In this example Joliet, then Rock Ridge, then vanilla + /// Iso9660 will be considered: + /// + /// + /// VfsCDReader(stream, new Iso9660Variant[] {Joliet, RockRidge, Iso9660}, true); + /// + /// The Iso9660 variant should normally be specified as the final entry in the list. Placing it earlier + /// in the list will effectively mask later items and not including it may prevent some ISOs from being read. + /// + public VfsCDReader(Stream data, Iso9660Variant[] variantPriorities, bool hideVersions, int sectorSize) + : base(new DiscFileSystemOptions()) + { + _data = data; + _sectorSize = sectorSize; + _hideVersions = hideVersions; + + long vdpos = _sectorSize * 16; // Skip lead-in + + byte[] buffer = new byte[_sectorSize]; + + long pvdPos = 0; + long svdPos = 0; + + BaseVolumeDescriptor bvd; + do + { + data.Position = vdpos; + int numRead = data.Read(buffer, 0, _sectorSize); + if (numRead != _sectorSize) + { + break; + } + + var offset = 24; + + bvd = new BaseVolumeDescriptor(buffer, offset); + + if (bvd.StandardIdentifier != BaseVolumeDescriptor.Iso9660StandardIdentifier) + { + throw new InvalidFileSystemException("Volume is not ISO-9660"); + } + + switch (bvd.VolumeDescriptorType) + { + case VolumeDescriptorType.Boot: + _bootVolDesc = new BootVolumeDescriptor(buffer, offset); + if (_bootVolDesc.SystemId != BootVolumeDescriptor.ElToritoSystemIdentifier) + { + _bootVolDesc = null; + } + + break; + + case VolumeDescriptorType.Primary: // Primary Vol Descriptor + pvdPos = vdpos; + break; + + case VolumeDescriptorType.Supplementary: // Supplementary Vol Descriptor + svdPos = vdpos; + break; + + case VolumeDescriptorType.Partition: // Volume Partition Descriptor + break; + case VolumeDescriptorType.SetTerminator: // Volume Descriptor Set Terminator + break; + } + + vdpos += _sectorSize; + } while (bvd.VolumeDescriptorType != VolumeDescriptorType.SetTerminator); + + ActiveVariant = Iso9660Variant.None; + foreach (Iso9660Variant variant in variantPriorities) + { + switch (variant) + { + case Iso9660Variant.Joliet: + if (svdPos != 0) + { + data.Position = svdPos; + data.Read(buffer, 0, _sectorSize); + SupplementaryVolumeDescriptor volDesc = new SupplementaryVolumeDescriptor(buffer, 0); + + Context = new IsoContext(_sectorSize) { VolumeDescriptor = volDesc, DataStream = _data }; + RootDirectory = new ReaderDirectory(Context, + new ReaderDirEntry(Context, volDesc.RootDirectory)); + ActiveVariant = Iso9660Variant.Iso9660; + } + + break; + + case Iso9660Variant.RockRidge: + case Iso9660Variant.Iso9660: + if (pvdPos != 0) + { + data.Position = pvdPos + 24; + data.Read(buffer, 0, _sectorSize); + PrimaryVolumeDescriptor volDesc = new PrimaryVolumeDescriptor(buffer, 0); + + volDesc.LogicalBlockSize = 2352; + + IsoContext context = new IsoContext(_sectorSize) { VolumeDescriptor = volDesc, DataStream = _data }; + DirectoryRecord rootSelfRecord = ReadRootSelfRecord(context); + + InitializeSusp(context, rootSelfRecord); + + if (variant == Iso9660Variant.Iso9660 + || + (variant == Iso9660Variant.RockRidge && + !string.IsNullOrEmpty(context.RockRidgeIdentifier))) + { + Context = context; + RootDirectory = new ReaderDirectory(context, new ReaderDirEntry(context, rootSelfRecord)); + ActiveVariant = variant; + } + } + + break; + } + + if (ActiveVariant != Iso9660Variant.None) + { + break; + } + } + + if (ActiveVariant == Iso9660Variant.None) + { + throw new IOException("None of the permitted ISO9660 file system variants was detected"); + } + } + + public Iso9660Variant ActiveVariant { get; } + + public BootDeviceEmulation BootEmulation + { + get + { + BootInitialEntry initialEntry = GetBootInitialEntry(); + if (initialEntry != null) + { + return initialEntry.BootMediaType; + } + + return BootDeviceEmulation.NoEmulation; + } + } + + public long BootImageStart + { + get + { + BootInitialEntry initialEntry = GetBootInitialEntry(); + if (initialEntry != null) + { + return initialEntry.ImageStart * _sectorSize; + } + return 0; + } + } + + public int BootLoadSegment + { + get + { + BootInitialEntry initialEntry = GetBootInitialEntry(); + if (initialEntry != null) + { + return initialEntry.LoadSegment; + } + + return 0; + } + } + + /// + /// Provides the friendly name for the CD filesystem. + /// + public override string FriendlyName + { + get { return "ISO 9660 (CD-ROM)"; } + } + + public bool HasBootImage + { + get + { + if (_bootVolDesc == null) + { + return false; + } + + byte[] bootCatalog = GetBootCatalog(); + if (bootCatalog == null) + { + return false; + } + + BootValidationEntry entry = new BootValidationEntry(bootCatalog, 0); + return entry.ChecksumValid; + } + } + + /// + /// Gets the Volume Identifier. + /// + public override string VolumeLabel + { + get { return Context.VolumeDescriptor.VolumeIdentifier; } + } + + public long ClusterSize + { + get { return _sectorSize; } + } + + public long TotalClusters + { + get { return Context.VolumeDescriptor.VolumeSpaceSize; } + } + + public long ClusterToOffset(long cluster) + { + return cluster * ClusterSize; + } + + public long OffsetToCluster(long offset) + { + return offset / ClusterSize; + } + + /// + /// Size of the Filesystem in bytes + /// + public override long Size + { + get { throw new NotSupportedException("Filesystem size is not (yet) supported"); } + } + + /// + /// Used space of the Filesystem in bytes + /// + public override long UsedSpace + { + get { throw new NotSupportedException("Filesystem size is not (yet) supported"); } + } + + /// + /// Available space of the Filesystem in bytes + /// + public override long AvailableSpace + { + get { throw new NotSupportedException("Filesystem size is not (yet) supported"); } + } + + public Range[] PathToClusters(string path) + { + ReaderDirEntry entry = GetDirectoryEntry(path); + if (entry == null) + { + throw new FileNotFoundException("File not found", path); + } + + if (entry.Record.FileUnitSize != 0 || entry.Record.InterleaveGapSize != 0) + { + throw new NotSupportedException("Non-contiguous extents not supported"); + } + + return new[] + { + new Range(entry.Record.LocationOfExtent, + MathUtilities.Ceil(entry.Record.DataLength, _sectorSize)) + }; + } + + public StreamExtent[] PathToExtents(string path) + { + ReaderDirEntry entry = GetDirectoryEntry(path); + if (entry == null) + { + throw new FileNotFoundException("File not found", path); + } + + if (entry.Record.FileUnitSize != 0 || entry.Record.InterleaveGapSize != 0) + { + throw new NotSupportedException("Non-contiguous extents not supported"); + } + + return new[] + { new StreamExtent(entry.Record.LocationOfExtent * _sectorSize, entry.Record.DataLength) }; + } + + public ClusterMap BuildClusterMap() + { + long totalClusters = TotalClusters; + ClusterRoles[] clusterToRole = new ClusterRoles[totalClusters]; + object[] clusterToFileId = new object[totalClusters]; + Dictionary fileIdToPaths = new Dictionary(); + + ForAllDirEntries( + string.Empty, + (path, entry) => + { + string[] paths = null; + if (fileIdToPaths.ContainsKey(entry.UniqueCacheId)) + { + paths = fileIdToPaths[entry.UniqueCacheId]; + } + + if (paths == null) + { + fileIdToPaths[entry.UniqueCacheId] = new[] { path }; + } + else + { + string[] newPaths = new string[paths.Length + 1]; + Array.Copy(paths, newPaths, paths.Length); + newPaths[paths.Length] = path; + fileIdToPaths[entry.UniqueCacheId] = newPaths; + } + + if (entry.Record.FileUnitSize != 0 || entry.Record.InterleaveGapSize != 0) + { + throw new NotSupportedException("Non-contiguous extents not supported"); + } + + long clusters = MathUtilities.Ceil(entry.Record.DataLength, _sectorSize); + for (long i = 0; i < clusters; ++i) + { + clusterToRole[i + entry.Record.LocationOfExtent] = ClusterRoles.DataFile; + clusterToFileId[i + entry.Record.LocationOfExtent] = entry.UniqueCacheId; + } + }); + + return new ClusterMap(clusterToRole, clusterToFileId, fileIdToPaths); + } + + public UnixFileSystemInfo GetUnixFileInfo(string path) + { + File file = GetFile(path); + return file.UnixFileInfo; + } + + public Stream OpenBootImage() + { + BootInitialEntry initialEntry = GetBootInitialEntry(); + if (initialEntry != null) + { + return new SubStream(_data, initialEntry.ImageStart * _sectorSize, + initialEntry.SectorCount * Sizes.Sector); + } + throw new InvalidOperationException("No valid boot image"); + } + + protected override File ConvertDirEntryToFile(ReaderDirEntry dirEntry) + { + if (dirEntry.IsDirectory) + { + return new ReaderDirectory(Context, dirEntry); + } + return new File(Context, dirEntry); + } + + protected override string FormatFileName(string name) + { + if (_hideVersions) + { + int pos = name.LastIndexOf(';'); + if (pos > 0) + { + return name.Substring(0, pos); + } + } + + return name; + } + + private static void InitializeSusp(IsoContext context, DirectoryRecord rootSelfRecord) + { + // Stage 1 - SUSP present? + List extensions = new List(); + if (!SuspRecords.DetectSharingProtocol(rootSelfRecord.SystemUseData, 0)) + { + context.SuspExtensions = new List(); + context.SuspDetected = false; + return; + } + context.SuspDetected = true; + + SuspRecords suspRecords = new SuspRecords(context, rootSelfRecord.SystemUseData, 0); + + // Stage 2 - Init general SUSP params + SharingProtocolSystemUseEntry spEntry = + (SharingProtocolSystemUseEntry)suspRecords.GetEntries(null, "SP")[0]; + context.SuspSkipBytes = spEntry.SystemAreaSkip; + + // Stage 3 - Init extensions + List extensionEntries = suspRecords.GetEntries(null, "ER"); + if (extensionEntries != null) + { + foreach (ExtensionSystemUseEntry extension in extensionEntries) + { + switch (extension.ExtensionIdentifier) + { + case "RRIP_1991A": + case "IEEE_P1282": + case "IEEE_1282": + extensions.Add(new RockRidgeExtension(extension.ExtensionIdentifier)); + context.RockRidgeIdentifier = extension.ExtensionIdentifier; + break; + + default: + extensions.Add(new GenericSuspExtension(extension.ExtensionIdentifier)); + break; + } + } + } + else if (suspRecords.GetEntries(null, "RR") != null) + { + // Some ISO creators don't add the 'ER' record for RockRidge, but write the (legacy) + // RR record anyway + extensions.Add(new RockRidgeExtension("RRIP_1991A")); + context.RockRidgeIdentifier = "RRIP_1991A"; + } + + context.SuspExtensions = extensions; + } + + private static DirectoryRecord ReadRootSelfRecord(IsoContext context) + { + context.DataStream.Position = context.VolumeDescriptor.RootDirectory.LocationOfExtent * + context.VolumeDescriptor.LogicalBlockSize + 24; + byte[] firstSector = StreamUtilities.ReadExact(context.DataStream, context.VolumeDescriptor.LogicalBlockSize); + + DirectoryRecord rootSelfRecord; + DirectoryRecord.ReadFrom(firstSector, 0, context.VolumeDescriptor.CharacterEncoding, out rootSelfRecord); + return rootSelfRecord; + } + + private BootInitialEntry GetBootInitialEntry() + { + byte[] bootCatalog = GetBootCatalog(); + if (bootCatalog == null) + { + return null; + } + + BootValidationEntry validationEntry = new BootValidationEntry(bootCatalog, 0); + if (!validationEntry.ChecksumValid) + { + return null; + } + + return new BootInitialEntry(bootCatalog, 0x20); + } + + private byte[] GetBootCatalog() + { + if (_bootCatalog == null && _bootVolDesc != null) + { + _data.Position = _bootVolDesc.CatalogSector * _sectorSize; + _bootCatalog = StreamUtilities.ReadExact(_data, _sectorSize); + } + + return _bootCatalog; + } + } +} diff --git a/DiscUtils/Iso9660Ps1/VolumeDescriptorDiskRegion.cs b/DiscUtils/Iso9660Ps1/VolumeDescriptorDiskRegion.cs new file mode 100644 index 0000000..4be6e93 --- /dev/null +++ b/DiscUtils/Iso9660Ps1/VolumeDescriptorDiskRegion.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +using System; +using DiscUtils.Streams; + +namespace DiscUtils.Iso9660Ps1 +{ + internal abstract class VolumeDescriptorDiskRegion : BuilderExtent + { + private byte[] _readCache; + + public VolumeDescriptorDiskRegion(long start, int sectorSize) + : base(start, sectorSize) {} + + public override void Dispose() {} + + public override void PrepareForRead() + { + _readCache = GetBlockData(); + } + + public override int Read(long diskOffset, byte[] buffer, int offset, int count) + { + long relPos = diskOffset - Start; + + int numRead = (int)Math.Min(count, _readCache.Length - relPos); + + Array.Copy(_readCache, (int)relPos, buffer, offset, numRead); + + return numRead; + } + + public override void DisposeReadState() + { + _readCache = null; + } + + protected abstract byte[] GetBlockData(); + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminator.cs b/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminator.cs new file mode 100644 index 0000000..98ed18e --- /dev/null +++ b/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminator.cs @@ -0,0 +1,30 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class VolumeDescriptorSetTerminator : BaseVolumeDescriptor + { + public VolumeDescriptorSetTerminator(int sectorSize) + : base(VolumeDescriptorType.SetTerminator, 1, sectorSize) {} + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminatorRegion.cs b/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminatorRegion.cs new file mode 100644 index 0000000..22fdaec --- /dev/null +++ b/DiscUtils/Iso9660Ps1/VolumeDescriptorSetTerminatorRegion.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) 2008-2011, Kenneth Bell +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + +namespace DiscUtils.Iso9660Ps1 +{ + internal class VolumeDescriptorSetTerminatorRegion : VolumeDescriptorDiskRegion + { + private readonly VolumeDescriptorSetTerminator _descriptor; + + public VolumeDescriptorSetTerminatorRegion(VolumeDescriptorSetTerminator descriptor, long start, int sectorSize) + : base(start, sectorSize) + { + _descriptor = descriptor; + } + + protected override byte[] GetBlockData() + { + byte[] buffer = new byte[Length]; + _descriptor.WriteTo(buffer, 0); + return buffer; + } + } +} \ No newline at end of file diff --git a/DiscUtils/Iso9660Ps1/VolumeDescriptorType.cs b/DiscUtils/Iso9660Ps1/VolumeDescriptorType.cs new file mode 100644 index 0000000..1505f7e --- /dev/null +++ b/DiscUtils/Iso9660Ps1/VolumeDescriptorType.cs @@ -0,0 +1,11 @@ +namespace DiscUtils.Iso9660Ps1 +{ + internal enum VolumeDescriptorType : byte + { + Boot = 0, + Primary = 1, + Supplementary = 2, + Partition = 3, + SetTerminator = 255 + } +} \ No newline at end of file diff --git a/DiscUtils/Core/LogicalDiskManager/ComponentRecord.cs b/DiscUtils/LogicalDiskManager/ComponentRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/ComponentRecord.cs rename to DiscUtils/LogicalDiskManager/ComponentRecord.cs diff --git a/DiscUtils/Core/LogicalDiskManager/Database.cs b/DiscUtils/LogicalDiskManager/Database.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/Database.cs rename to DiscUtils/LogicalDiskManager/Database.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DatabaseHeader.cs b/DiscUtils/LogicalDiskManager/DatabaseHeader.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DatabaseHeader.cs rename to DiscUtils/LogicalDiskManager/DatabaseHeader.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DatabaseRecord.cs b/DiscUtils/LogicalDiskManager/DatabaseRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DatabaseRecord.cs rename to DiscUtils/LogicalDiskManager/DatabaseRecord.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DiskGroupRecord.cs b/DiscUtils/LogicalDiskManager/DiskGroupRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DiskGroupRecord.cs rename to DiscUtils/LogicalDiskManager/DiskGroupRecord.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DiskRecord.cs b/DiscUtils/LogicalDiskManager/DiskRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DiskRecord.cs rename to DiscUtils/LogicalDiskManager/DiskRecord.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DynamicDisk.cs b/DiscUtils/LogicalDiskManager/DynamicDisk.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DynamicDisk.cs rename to DiscUtils/LogicalDiskManager/DynamicDisk.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DynamicDiskGroup.cs b/DiscUtils/LogicalDiskManager/DynamicDiskGroup.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DynamicDiskGroup.cs rename to DiscUtils/LogicalDiskManager/DynamicDiskGroup.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DynamicDiskManager.cs b/DiscUtils/LogicalDiskManager/DynamicDiskManager.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DynamicDiskManager.cs rename to DiscUtils/LogicalDiskManager/DynamicDiskManager.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DynamicDiskManagerFactory.cs b/DiscUtils/LogicalDiskManager/DynamicDiskManagerFactory.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DynamicDiskManagerFactory.cs rename to DiscUtils/LogicalDiskManager/DynamicDiskManagerFactory.cs diff --git a/DiscUtils/Core/LogicalDiskManager/DynamicVolume.cs b/DiscUtils/LogicalDiskManager/DynamicVolume.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/DynamicVolume.cs rename to DiscUtils/LogicalDiskManager/DynamicVolume.cs diff --git a/DiscUtils/Core/LogicalDiskManager/ExtentMergeType.cs b/DiscUtils/LogicalDiskManager/ExtentMergeType.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/ExtentMergeType.cs rename to DiscUtils/LogicalDiskManager/ExtentMergeType.cs diff --git a/DiscUtils/Core/LogicalDiskManager/ExtentRecord.cs b/DiscUtils/LogicalDiskManager/ExtentRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/ExtentRecord.cs rename to DiscUtils/LogicalDiskManager/ExtentRecord.cs diff --git a/DiscUtils/Core/LogicalDiskManager/PrivateHeader.cs b/DiscUtils/LogicalDiskManager/PrivateHeader.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/PrivateHeader.cs rename to DiscUtils/LogicalDiskManager/PrivateHeader.cs diff --git a/DiscUtils/Core/LogicalDiskManager/RecordType.cs b/DiscUtils/LogicalDiskManager/RecordType.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/RecordType.cs rename to DiscUtils/LogicalDiskManager/RecordType.cs diff --git a/DiscUtils/Core/LogicalDiskManager/TocBlock.cs b/DiscUtils/LogicalDiskManager/TocBlock.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/TocBlock.cs rename to DiscUtils/LogicalDiskManager/TocBlock.cs diff --git a/DiscUtils/Core/LogicalDiskManager/VolumeRecord.cs b/DiscUtils/LogicalDiskManager/VolumeRecord.cs similarity index 100% rename from DiscUtils/Core/LogicalDiskManager/VolumeRecord.cs rename to DiscUtils/LogicalDiskManager/VolumeRecord.cs diff --git a/DiscUtils/Core/LogicalVolumeInfo.cs b/DiscUtils/LogicalVolumeInfo.cs similarity index 100% rename from DiscUtils/Core/LogicalVolumeInfo.cs rename to DiscUtils/LogicalVolumeInfo.cs diff --git a/DiscUtils/Core/LogicalVolumeStatus.cs b/DiscUtils/LogicalVolumeStatus.cs similarity index 100% rename from DiscUtils/Core/LogicalVolumeStatus.cs rename to DiscUtils/LogicalVolumeStatus.cs diff --git a/DiscUtils/Core/NativeFileSystem.cs b/DiscUtils/NativeFileSystem.cs similarity index 100% rename from DiscUtils/Core/NativeFileSystem.cs rename to DiscUtils/NativeFileSystem.cs diff --git a/DiscUtils/Core/Partitions/BiosExtendedPartitionTable.cs b/DiscUtils/Partitions/BiosExtendedPartitionTable.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosExtendedPartitionTable.cs rename to DiscUtils/Partitions/BiosExtendedPartitionTable.cs diff --git a/DiscUtils/Core/Partitions/BiosPartitionInfo.cs b/DiscUtils/Partitions/BiosPartitionInfo.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosPartitionInfo.cs rename to DiscUtils/Partitions/BiosPartitionInfo.cs diff --git a/DiscUtils/Core/Partitions/BiosPartitionRecord.cs b/DiscUtils/Partitions/BiosPartitionRecord.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosPartitionRecord.cs rename to DiscUtils/Partitions/BiosPartitionRecord.cs diff --git a/DiscUtils/Core/Partitions/BiosPartitionTable.cs b/DiscUtils/Partitions/BiosPartitionTable.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosPartitionTable.cs rename to DiscUtils/Partitions/BiosPartitionTable.cs diff --git a/DiscUtils/Core/Partitions/BiosPartitionTypes.cs b/DiscUtils/Partitions/BiosPartitionTypes.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosPartitionTypes.cs rename to DiscUtils/Partitions/BiosPartitionTypes.cs diff --git a/DiscUtils/Core/Partitions/BiosPartitionedDiskBuilder.cs b/DiscUtils/Partitions/BiosPartitionedDiskBuilder.cs similarity index 100% rename from DiscUtils/Core/Partitions/BiosPartitionedDiskBuilder.cs rename to DiscUtils/Partitions/BiosPartitionedDiskBuilder.cs diff --git a/DiscUtils/Core/Partitions/DefaultPartitionTableFactory.cs b/DiscUtils/Partitions/DefaultPartitionTableFactory.cs similarity index 100% rename from DiscUtils/Core/Partitions/DefaultPartitionTableFactory.cs rename to DiscUtils/Partitions/DefaultPartitionTableFactory.cs diff --git a/DiscUtils/Core/Partitions/GptEntry.cs b/DiscUtils/Partitions/GptEntry.cs similarity index 100% rename from DiscUtils/Core/Partitions/GptEntry.cs rename to DiscUtils/Partitions/GptEntry.cs diff --git a/DiscUtils/Core/Partitions/GptHeader.cs b/DiscUtils/Partitions/GptHeader.cs similarity index 100% rename from DiscUtils/Core/Partitions/GptHeader.cs rename to DiscUtils/Partitions/GptHeader.cs diff --git a/DiscUtils/Core/Partitions/GuidPartitionInfo.cs b/DiscUtils/Partitions/GuidPartitionInfo.cs similarity index 100% rename from DiscUtils/Core/Partitions/GuidPartitionInfo.cs rename to DiscUtils/Partitions/GuidPartitionInfo.cs diff --git a/DiscUtils/Core/Partitions/GuidPartitionTable.cs b/DiscUtils/Partitions/GuidPartitionTable.cs similarity index 100% rename from DiscUtils/Core/Partitions/GuidPartitionTable.cs rename to DiscUtils/Partitions/GuidPartitionTable.cs diff --git a/DiscUtils/Core/Partitions/GuidPartitionTypes.cs b/DiscUtils/Partitions/GuidPartitionTypes.cs similarity index 100% rename from DiscUtils/Core/Partitions/GuidPartitionTypes.cs rename to DiscUtils/Partitions/GuidPartitionTypes.cs diff --git a/DiscUtils/Core/Partitions/PartitionInfo.cs b/DiscUtils/Partitions/PartitionInfo.cs similarity index 100% rename from DiscUtils/Core/Partitions/PartitionInfo.cs rename to DiscUtils/Partitions/PartitionInfo.cs diff --git a/DiscUtils/Core/Partitions/PartitionTable.cs b/DiscUtils/Partitions/PartitionTable.cs similarity index 100% rename from DiscUtils/Core/Partitions/PartitionTable.cs rename to DiscUtils/Partitions/PartitionTable.cs diff --git a/DiscUtils/Core/Partitions/PartitionTableFactory.cs b/DiscUtils/Partitions/PartitionTableFactory.cs similarity index 100% rename from DiscUtils/Core/Partitions/PartitionTableFactory.cs rename to DiscUtils/Partitions/PartitionTableFactory.cs diff --git a/DiscUtils/Core/Partitions/PartitionTableFactoryAttribute.cs b/DiscUtils/Partitions/PartitionTableFactoryAttribute.cs similarity index 100% rename from DiscUtils/Core/Partitions/PartitionTableFactoryAttribute.cs rename to DiscUtils/Partitions/PartitionTableFactoryAttribute.cs diff --git a/DiscUtils/Core/Partitions/WellKnownPartitionType.cs b/DiscUtils/Partitions/WellKnownPartitionType.cs similarity index 100% rename from DiscUtils/Core/Partitions/WellKnownPartitionType.cs rename to DiscUtils/Partitions/WellKnownPartitionType.cs diff --git a/DiscUtils/Core/PhysicalVolumeInfo.cs b/DiscUtils/PhysicalVolumeInfo.cs similarity index 100% rename from DiscUtils/Core/PhysicalVolumeInfo.cs rename to DiscUtils/PhysicalVolumeInfo.cs diff --git a/DiscUtils/Core/PhysicalVolumeType.cs b/DiscUtils/PhysicalVolumeType.cs similarity index 100% rename from DiscUtils/Core/PhysicalVolumeType.cs rename to DiscUtils/PhysicalVolumeType.cs diff --git a/DiscUtils/Core/Plist.cs b/DiscUtils/Plist.cs similarity index 100% rename from DiscUtils/Core/Plist.cs rename to DiscUtils/Plist.cs diff --git a/DiscUtils/Core/Raw/Disk.cs b/DiscUtils/Raw/Disk.cs similarity index 100% rename from DiscUtils/Core/Raw/Disk.cs rename to DiscUtils/Raw/Disk.cs diff --git a/DiscUtils/Core/Raw/DiskFactory.cs b/DiscUtils/Raw/DiskFactory.cs similarity index 100% rename from DiscUtils/Core/Raw/DiskFactory.cs rename to DiscUtils/Raw/DiskFactory.cs diff --git a/DiscUtils/Core/Raw/DiskImageFile.cs b/DiscUtils/Raw/DiskImageFile.cs similarity index 100% rename from DiscUtils/Core/Raw/DiskImageFile.cs rename to DiscUtils/Raw/DiskImageFile.cs diff --git a/DiscUtils/Core/ReadOnlyDiscFileSystem.cs b/DiscUtils/ReadOnlyDiscFileSystem.cs similarity index 100% rename from DiscUtils/Core/ReadOnlyDiscFileSystem.cs rename to DiscUtils/ReadOnlyDiscFileSystem.cs diff --git a/DiscUtils/Core/ReparsePoint.cs b/DiscUtils/ReparsePoint.cs similarity index 100% rename from DiscUtils/Core/ReparsePoint.cs rename to DiscUtils/ReparsePoint.cs diff --git a/DiscUtils/Core/ReportLevels.cs b/DiscUtils/ReportLevels.cs similarity index 100% rename from DiscUtils/Core/ReportLevels.cs rename to DiscUtils/ReportLevels.cs diff --git a/DiscUtils/Core/Setup/FileOpenEventArgs.cs b/DiscUtils/Setup/FileOpenEventArgs.cs similarity index 100% rename from DiscUtils/Core/Setup/FileOpenEventArgs.cs rename to DiscUtils/Setup/FileOpenEventArgs.cs diff --git a/DiscUtils/Core/Setup/SetupHelper.cs b/DiscUtils/Setup/SetupHelper.cs similarity index 100% rename from DiscUtils/Core/Setup/SetupHelper.cs rename to DiscUtils/Setup/SetupHelper.cs diff --git a/DiscUtils/Streams/DiscUtils.Streams.csproj b/DiscUtils/Streams/DiscUtils.Streams.csproj deleted file mode 100644 index 0083bf2..0000000 --- a/DiscUtils/Streams/DiscUtils.Streams.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - - DiscUtils Streams - Kenneth Bell;LordMike;Bianco Veigel - DiscUtils;Streams - - - diff --git a/DiscUtils/Core/System/DateTimeOffsetExtensions.cs b/DiscUtils/System/DateTimeOffsetExtensions.cs similarity index 100% rename from DiscUtils/Core/System/DateTimeOffsetExtensions.cs rename to DiscUtils/System/DateTimeOffsetExtensions.cs diff --git a/DiscUtils/Core/System/ExtensionAttribute.cs b/DiscUtils/System/ExtensionAttribute.cs similarity index 100% rename from DiscUtils/Core/System/ExtensionAttribute.cs rename to DiscUtils/System/ExtensionAttribute.cs diff --git a/DiscUtils/Core/System/HashSet.cs b/DiscUtils/System/HashSet.cs similarity index 100% rename from DiscUtils/Core/System/HashSet.cs rename to DiscUtils/System/HashSet.cs diff --git a/DiscUtils/Core/System/Tuple.cs b/DiscUtils/System/Tuple.cs similarity index 100% rename from DiscUtils/Core/System/Tuple.cs rename to DiscUtils/System/Tuple.cs diff --git a/DiscUtils/Core/TimeConverter.cs b/DiscUtils/TimeConverter.cs similarity index 100% rename from DiscUtils/Core/TimeConverter.cs rename to DiscUtils/TimeConverter.cs diff --git a/DiscUtils/Core/UnixFilePermissions.cs b/DiscUtils/UnixFilePermissions.cs similarity index 100% rename from DiscUtils/Core/UnixFilePermissions.cs rename to DiscUtils/UnixFilePermissions.cs diff --git a/DiscUtils/Core/UnixFileSystemInfo.cs b/DiscUtils/UnixFileSystemInfo.cs similarity index 100% rename from DiscUtils/Core/UnixFileSystemInfo.cs rename to DiscUtils/UnixFileSystemInfo.cs diff --git a/DiscUtils/Core/UnixFileType.cs b/DiscUtils/UnixFileType.cs similarity index 100% rename from DiscUtils/Core/UnixFileType.cs rename to DiscUtils/UnixFileType.cs diff --git a/DiscUtils/Core/Vfs/IVfsDirectory.cs b/DiscUtils/Vfs/IVfsDirectory.cs similarity index 100% rename from DiscUtils/Core/Vfs/IVfsDirectory.cs rename to DiscUtils/Vfs/IVfsDirectory.cs diff --git a/DiscUtils/Core/Vfs/IVfsFile.cs b/DiscUtils/Vfs/IVfsFile.cs similarity index 100% rename from DiscUtils/Core/Vfs/IVfsFile.cs rename to DiscUtils/Vfs/IVfsFile.cs diff --git a/DiscUtils/Core/Vfs/IVfsFileWithStreams.cs b/DiscUtils/Vfs/IVfsFileWithStreams.cs similarity index 100% rename from DiscUtils/Core/Vfs/IVfsFileWithStreams.cs rename to DiscUtils/Vfs/IVfsFileWithStreams.cs diff --git a/DiscUtils/Core/Vfs/IVfsSymlink.cs b/DiscUtils/Vfs/IVfsSymlink.cs similarity index 100% rename from DiscUtils/Core/Vfs/IVfsSymlink.cs rename to DiscUtils/Vfs/IVfsSymlink.cs diff --git a/DiscUtils/Core/Vfs/VfsContext.cs b/DiscUtils/Vfs/VfsContext.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsContext.cs rename to DiscUtils/Vfs/VfsContext.cs diff --git a/DiscUtils/Core/Vfs/VfsDirEntry.cs b/DiscUtils/Vfs/VfsDirEntry.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsDirEntry.cs rename to DiscUtils/Vfs/VfsDirEntry.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystem.cs b/DiscUtils/Vfs/VfsFileSystem.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystem.cs rename to DiscUtils/Vfs/VfsFileSystem.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystemFacade.cs b/DiscUtils/Vfs/VfsFileSystemFacade.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystemFacade.cs rename to DiscUtils/Vfs/VfsFileSystemFacade.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystemFactory.cs b/DiscUtils/Vfs/VfsFileSystemFactory.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystemFactory.cs rename to DiscUtils/Vfs/VfsFileSystemFactory.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystemFactoryAttribute.cs b/DiscUtils/Vfs/VfsFileSystemFactoryAttribute.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystemFactoryAttribute.cs rename to DiscUtils/Vfs/VfsFileSystemFactoryAttribute.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystemInfo.cs b/DiscUtils/Vfs/VfsFileSystemInfo.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystemInfo.cs rename to DiscUtils/Vfs/VfsFileSystemInfo.cs diff --git a/DiscUtils/Core/Vfs/VfsFileSystemOpener.cs b/DiscUtils/Vfs/VfsFileSystemOpener.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsFileSystemOpener.cs rename to DiscUtils/Vfs/VfsFileSystemOpener.cs diff --git a/DiscUtils/Core/Vfs/VfsReadOnlyFileSystem.cs b/DiscUtils/Vfs/VfsReadOnlyFileSystem.cs similarity index 100% rename from DiscUtils/Core/Vfs/VfsReadOnlyFileSystem.cs rename to DiscUtils/Vfs/VfsReadOnlyFileSystem.cs diff --git a/DiscUtils/Core/VirtualDisk.cs b/DiscUtils/VirtualDisk.cs similarity index 100% rename from DiscUtils/Core/VirtualDisk.cs rename to DiscUtils/VirtualDisk.cs diff --git a/DiscUtils/Core/VirtualDiskClass.cs b/DiscUtils/VirtualDiskClass.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskClass.cs rename to DiscUtils/VirtualDiskClass.cs diff --git a/DiscUtils/Core/VirtualDiskExtent.cs b/DiscUtils/VirtualDiskExtent.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskExtent.cs rename to DiscUtils/VirtualDiskExtent.cs diff --git a/DiscUtils/Core/VirtualDiskLayer.cs b/DiscUtils/VirtualDiskLayer.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskLayer.cs rename to DiscUtils/VirtualDiskLayer.cs diff --git a/DiscUtils/Core/VirtualDiskManager.cs b/DiscUtils/VirtualDiskManager.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskManager.cs rename to DiscUtils/VirtualDiskManager.cs diff --git a/DiscUtils/Core/VirtualDiskParameters.cs b/DiscUtils/VirtualDiskParameters.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskParameters.cs rename to DiscUtils/VirtualDiskParameters.cs diff --git a/DiscUtils/Core/VirtualDiskTypeInfo.cs b/DiscUtils/VirtualDiskTypeInfo.cs similarity index 100% rename from DiscUtils/Core/VirtualDiskTypeInfo.cs rename to DiscUtils/VirtualDiskTypeInfo.cs diff --git a/DiscUtils/Core/VolumeInfo.cs b/DiscUtils/VolumeInfo.cs similarity index 100% rename from DiscUtils/Core/VolumeInfo.cs rename to DiscUtils/VolumeInfo.cs diff --git a/DiscUtils/Core/VolumeManager.cs b/DiscUtils/VolumeManager.cs similarity index 100% rename from DiscUtils/Core/VolumeManager.cs rename to DiscUtils/VolumeManager.cs diff --git a/DiscUtils/Core/WindowsFileInformation.cs b/DiscUtils/WindowsFileInformation.cs similarity index 100% rename from DiscUtils/Core/WindowsFileInformation.cs rename to DiscUtils/WindowsFileInformation.cs diff --git a/PopsBuilder/Pops/DiscInfo.cs b/PopsBuilder/Pops/DiscInfo.cs index 84b601f..ab8233b 100644 --- a/PopsBuilder/Pops/DiscInfo.cs +++ b/PopsBuilder/Pops/DiscInfo.cs @@ -1,4 +1,4 @@ -using DiscUtils.Iso9660; +using DiscUtils.Iso9660Ps1; using DiscUtils.Streams; using GameBuilder.Cue; using System; diff --git a/PopsBuilder/Pops/PsIsoImg.cs b/PopsBuilder/Pops/PsIsoImg.cs index 740a3c7..a80c0a9 100644 --- a/PopsBuilder/Pops/PsIsoImg.cs +++ b/PopsBuilder/Pops/PsIsoImg.cs @@ -1,12 +1,9 @@ -using Org.BouncyCastle.Crypto.Paddings; -using GameBuilder.Atrac3; +using GameBuilder.Atrac3; using GameBuilder.Cue; using GameBuilder.Psp; using PspCrypto; using System; using System.Net; -using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; using GameBuilder.Progress; namespace GameBuilder.Pops diff --git a/PopsBuilder/Psp/UmdInfo.cs b/PopsBuilder/Psp/UmdInfo.cs index 012a2da..2962b0b 100644 --- a/PopsBuilder/Psp/UmdInfo.cs +++ b/PopsBuilder/Psp/UmdInfo.cs @@ -25,7 +25,7 @@ namespace GameBuilder.Psp { this.IsoFile = isoFile; this.IsoStream = File.OpenRead(isoFile); - using (CDReader cdReader = new CDReader(this.IsoStream, true, true, 2048)) + using (CDReader cdReader = new CDReader(this.IsoStream, true, true)) { foreach (string file in filesList) {