* Add a UDF reader
Reads the Volume Recognition Sequence, the Anchor Volume Descriptor
Pointer (sector 256, then the end of the volume), the Main or Reserve
Volume Descriptor Sequence, the File Set Descriptor and the directory
hierarchy. Type 1 and Metadata Partitions are mapped, Virtual and
Sparable partitions are not.
File Entries, Allocation Extent Descriptors and directories are read in
ascending sector order with wrap-around, adjacent sectors merged into
one request. FindFileEntry follows one path and reads only the
directories on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add a UDF wrapper
Prints the volume and file set structures and the directory hierarchy,
and extracts the files. CreateDiscImageWrapper tries UDF after ISO 9660
for a stream at offset 0, so discs with both file systems still open as
ISO 9660.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Treat the stream position as the start of the UDF volume
The UDF reader addresses logical sectors from the start of the stream,
so the wrapper hands it a view that starts at the current position and
keeps that offset, like the other wrappers. CreateDiscImageWrapper no
longer skips UDF for a stream that is not at offset 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Address review comments on the UDF code
- Move the descriptor tag check to UDFExtensions, with tests
- Parse Volume Structure Descriptors in their own method
- Use the existing extensions for numbers, the partition type identifier
and printing entity identifiers
- Use Array.Exists in IsSafeName
- Make Extract non-virtual and remove the printing test
- Use collection expressions and string interpolation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Parse File Identifier Descriptors in their own method
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add a TODO to use the CRC from Hashing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Read directory extents in volume order
ParseDirectoryDescriptors now keeps one worklist of directory extents for
all volume descriptors, sorted by offset, and always reads the next
extent at or after the last one, wrapping around at the end. Each extent
is read once. The record loop moves to a shared helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add a path lookup for ISO 9660 directory records
FindDirectoryRecords follows a path from the root directory of one
volume descriptor and reads only the directories on that path. Names
are compared case-insensitively, without the version suffix or a
trailing period. A file recorded in several extents returns all of its
records.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Locate the PKZIP file data instead of reading it into the model
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep only the offset of the PKZIP file data
The length is the compressed size from the local file header or the ZIP64
extended field, which the printer already shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep the file data property as a comment for documentation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The DAT writers and the filter formatted Refresh and Freq with the
current culture, so a German or French system wrote "123,450000".
SabreTools.IO parses these values with the invariant culture.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ParseDirectory parsed the big-endian extent when the extent location
was valid (both values equal) instead of when it was ambiguous, so
every directory was parsed again, once per directory level.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>