Skip to content

Read ISO 9660 directories in volume order and add a path lookup - #99

Open
gmipf wants to merge 2 commits into
SabreTools:mainfrom
gmipf:iso-elevator-lookup
Open

gmipf wants to merge 2 commits into
SabreTools:mainfrom
gmipf:iso-elevator-lookup

Conversation

@gmipf

@gmipf gmipf commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

ParseDirectoryDescriptors now reads the directory extents of all volume descriptors from one list sorted by offset: always the next extent at or after the last one read, wrapping around at the end, and each extent only once. Before, it walked the primary tree depth-first and then the Joliet tree, which costs a long seek for almost every directory on a disc with scattered directories. The record loop of ParseDirectory moved into a helper that both use.

FindDirectoryRecords returns the directory records of one path and reads only the directories on that path, so a caller that needs a few known files does not have to parse the whole tree.

Measured on a DVD drive (Gun, 2376 directory extents, cache dropped before every run, two runs each):

  • full tree: 53.8 s before, 32.7 / 34.1 s after, kernel mount + find 33.1 s; same parsed model
  • one file seven directories deep: FindDirectoryRecords + read 0.8 s, kernel mount + read 0.6 / 0.7 s, both 19 read requests
  • FindDirectoryRecords returns the same records as the full parse for every file: 6772 paths on the drive, 1728 on 8 Redump images (PS1 to PS5, three PC discs), where the parsed model is also identical

Tests: one for the read order (fails without the change), four for the lookup.

Prepared with AI assistance (Claude Opus 5.5) and reviewed before submission.

gmipf and others added 2 commits October 15, 2026 12:27
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>
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>
@mnadareski
mnadareski requested a review from Deterous October 15, 2026 12:47
QueueExtent(data, extent.BlockLength, record, pending);
}
}
else if (dr.ExtendedAttributeRecordLength > 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been a while since i've written the code, but could you explain why the extended attribute record must be present here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It keeps what the old code did. ParseDirectory was called for every record, and for a file it only added something to the dictionary when ExtendedAttributeRecordLength > 0: it parsed the Extended Attribute Record at the start of the file's extent into a FileExtent (the file data itself is not read). For a file without one it added nothing, so the queue leaves those files out. The parsed model is the same as before on Gun and on the 8 images.

Generated with Claude Opus 5.5.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the file extent isn't being added to the directory object if it doesn't have extended file attributes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and that is the same on main: the TryAdd is inside the if (dr.ExtendedAttributeRecordLength > 0) block (ISO9660.cs#L751-L770), so DirectoryDescriptors holds the directories plus only the files that have an Extended Attribute Record. The PR keeps that. If every file should get an entry, that would not need a read (without an Extended Attribute Record the FileExtent stays empty); I can add it here or leave it for a separate change.

Generated with Claude Opus 5.5.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what it affects but it should probably be added in both places. Though perhaps the file extents without EAR are empty objects anyway and that's why I never added them, nothing to process. Which makes sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, without an Extended Attribute Record the entry would be an empty FileExtent, so I kept it as on main. If you want an entry for every file anyway, I can do that in both places in a follow-up PR after this one.

Generated with Claude Opus 5.5.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants