Skip to content

Commit a9c3bee

Browse files
committed
Address CodeRabbit second review round on PR 2013
- ISO9660Reader: malformed descriptor after the PVD now fails the reader instead of returning early with a stale m_vdEnd default. - readerListDir: guard against failed readers up front, matching readerFindGaps. - readerFindGaps: emit a trailing gap from the last occupied extent to the end of the disc (PVD VolumeSpaceSize) so appended/orphaned content is visible to Lua callers. - Flat view: same trailing-gap addition so the GUI doesn't hide content past the last known file. - Flat view selection highlight: check isGap() || isHidden() to match how m_selectedIsGap is set, so M1/M2F1/M2F2 rows keep their selected state. - Gap scanning: moved to a Coroutine<> with progress bar; yields every 50ms so large images or trailing gaps no longer freeze the UI. The scan coroutine is reset on ISO change. Signed-off-by: Nicolas 'Pixel' Noble <nicolas@nobis-crew.org>
1 parent d45e683 commit a9c3bee

4 files changed

Lines changed: 138 additions & 86 deletions

File tree

src/cdrom/iso9660-reader.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ PCSX::ISO9660Reader::ISO9660Reader(std::shared_ptr<CDRIso> iso) : m_iso(iso) {
3939
uint8_t vd[7];
4040
pvdFile->readAt(vd, 7, 0);
4141
if ((vd[1] != 'C') || (vd[2] != 'D') || (vd[3] != '0') || (vd[4] != '0') || (vd[5] != '1') || (vd[6] != 1)) {
42-
if (!foundPVD) m_failed = true;
42+
// Malformed descriptor set: even if we already have the PVD, the
43+
// set is broken so we can't trust m_vdEnd.
44+
m_failed = true;
4345
return;
4446
}
4547

src/core/luaiso.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct DirEntries {
6363
};
6464

6565
DirEntries* readerListDir(PCSX::ISO9660Reader* reader, const char* path) {
66+
if (reader->failed()) return new DirEntries{};
6667
auto root = reader->getRootDirEntry();
6768
if (path == nullptr || path[0] == '\0') {
6869
return new DirEntries{reader->listAllEntriesFrom(root)};
@@ -177,6 +178,11 @@ GapList* readerFindGaps(PCSX::ISO9660Reader* reader) {
177178
uint32_t end = lba + sectors;
178179
if (end > nextExpected) nextExpected = end;
179180
}
181+
// Trailing gap: anything between the last occupied extent and the disc end.
182+
uint32_t volumeSpaceSize = pvd.get<PCSX::ISO9660LowLevel::PVD_VolumeSpaceSize>();
183+
if (volumeSpaceSize > nextExpected) {
184+
result->gaps.push_back({nextExpected, volumeSpaceSize - nextExpected});
185+
}
180186
return result;
181187
}
182188

src/gui/widgets/isobrowser.cc

Lines changed: 126 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -149,88 +149,127 @@ void PCSX::Widgets::IsoBrowser::collectFlatEntries(const ISO9660LowLevel::DirEnt
149149
}
150150
}
151151

152-
void PCSX::Widgets::IsoBrowser::scanGapSectors(std::vector<FlatEntry>& out, uint32_t startLBA, uint32_t sectorCount,
153-
std::shared_ptr<CDRIso> iso) {
154-
uint32_t lba = startLBA;
155-
uint32_t end = startLBA + sectorCount;
156-
157-
while (lba < end) {
158-
uint8_t sector[2352];
159-
if (iso->readSectors(lba, sector, 1) != 1) {
160-
// Read failed, treat rest as gap
161-
uint32_t remaining = end - lba;
162-
auto label = fmt::format(f_("<gap {} sectors>"), remaining);
163-
out.push_back({label, lba, remaining * 2352, remaining, FlatEntry::Gap, {}});
164-
break;
152+
PCSX::Coroutine<> PCSX::Widgets::IsoBrowser::scanAllGaps(std::shared_ptr<CDRIso> iso) {
153+
auto time = std::chrono::steady_clock::now();
154+
std::vector<FlatEntry> scanned;
155+
156+
// Count total gap sectors to scan for progress reporting.
157+
uint32_t totalGapSectors = 0;
158+
for (auto& entry : m_flatEntries) {
159+
if (entry.isGap()) totalGapSectors += entry.sectors;
160+
}
161+
uint32_t scannedSectors = 0;
162+
163+
for (auto& entry : m_flatEntries) {
164+
if (!entry.isGap()) {
165+
scanned.push_back(entry);
166+
continue;
165167
}
166168

167-
uint8_t mode = sector[15];
169+
uint32_t lba = entry.lba;
170+
uint32_t end = entry.lba + entry.sectors;
171+
172+
while (lba < end) {
173+
uint8_t sector[2352];
174+
if (iso->readSectors(lba, sector, 1) != 1) {
175+
uint32_t remaining = end - lba;
176+
auto label = fmt::format(f_("<gap {} sectors>"), remaining);
177+
scanned.push_back({label, lba, remaining * 2352, remaining, FlatEntry::Gap, {}});
178+
scannedSectors += remaining;
179+
break;
180+
}
168181

169-
if (mode == 0) {
170-
// Mode 0: true gap. Accumulate consecutive mode 0 sectors.
171-
uint32_t gapStart = lba;
172-
while (lba < end) {
173-
if (lba != gapStart) {
174-
if (iso->readSectors(lba, sector, 1) != 1) break;
175-
if (sector[15] != 0) break;
182+
uint8_t mode = sector[15];
183+
184+
if (mode == 0) {
185+
uint32_t gapStart = lba;
186+
while (lba < end) {
187+
if (lba != gapStart) {
188+
if (iso->readSectors(lba, sector, 1) != 1) break;
189+
if (sector[15] != 0) break;
190+
}
191+
lba++;
192+
scannedSectors++;
193+
if (std::chrono::steady_clock::now() - time > std::chrono::milliseconds(50)) {
194+
m_gapScanProgress =
195+
totalGapSectors > 0 ? (float)scannedSectors / (float)totalGapSectors : 1.0f;
196+
co_yield m_gapScanCoroutine.awaiter();
197+
time = std::chrono::steady_clock::now();
198+
}
176199
}
177-
lba++;
200+
uint32_t count = lba - gapStart;
201+
auto label = fmt::format(f_("<gap {} sectors>"), count);
202+
scanned.push_back({label, gapStart, count * 2352, count, FlatEntry::Gap, {}});
203+
continue;
178204
}
179-
uint32_t count = lba - gapStart;
180-
auto label = fmt::format(f_("<gap {} sectors>"), count);
181-
out.push_back({label, gapStart, count * 2352, count, FlatEntry::Gap, {}});
182-
continue;
183-
}
184205

185-
if (mode == 1) {
186-
// Mode 1 hidden data. Accumulate until mode changes or gap ends.
187-
uint32_t fileStart = lba;
188-
lba++;
189-
while (lba < end) {
190-
if (iso->readSectors(lba, sector, 1) != 1) break;
191-
if (sector[15] != 1) break;
206+
if (mode == 1) {
207+
uint32_t fileStart = lba;
192208
lba++;
209+
scannedSectors++;
210+
while (lba < end) {
211+
if (iso->readSectors(lba, sector, 1) != 1) break;
212+
if (sector[15] != 1) break;
213+
lba++;
214+
scannedSectors++;
215+
if (std::chrono::steady_clock::now() - time > std::chrono::milliseconds(50)) {
216+
m_gapScanProgress =
217+
totalGapSectors > 0 ? (float)scannedSectors / (float)totalGapSectors : 1.0f;
218+
co_yield m_gapScanCoroutine.awaiter();
219+
time = std::chrono::steady_clock::now();
220+
}
221+
}
222+
uint32_t count = lba - fileStart;
223+
auto label = fmt::format(f_("<hidden M1 {} sectors>"), count);
224+
scanned.push_back({label, fileStart, count * 2048, count, FlatEntry::HiddenM1, {}});
225+
continue;
193226
}
194-
uint32_t count = lba - fileStart;
195-
auto label = fmt::format(f_("<hidden M1 {} sectors>"), count);
196-
out.push_back({label, fileStart, count * 2048, count, FlatEntry::HiddenM1, {}});
197-
continue;
198-
}
199227

200-
if (mode == 2) {
201-
// Mode 2: parse subheader for form and file boundaries
202-
uint8_t* sub = sector + 16;
203-
uint8_t fileNum = sub[0];
204-
uint8_t channelNum = sub[1];
205-
uint8_t submode = sub[2];
206-
bool isForm2 = (submode & 0x20) != 0;
207-
auto type = isForm2 ? FlatEntry::HiddenM2F2 : FlatEntry::HiddenM2F1;
208-
209-
uint32_t fileStart = lba;
210-
bool hitEof = (submode & 0x80) != 0;
211-
lba++;
228+
if (mode == 2) {
229+
uint8_t* sub = sector + 16;
230+
uint8_t fileNum = sub[0];
231+
uint8_t channelNum = sub[1];
232+
uint8_t submode = sub[2];
233+
bool isForm2 = (submode & 0x20) != 0;
234+
auto type = isForm2 ? FlatEntry::HiddenM2F2 : FlatEntry::HiddenM2F1;
212235

213-
while (lba < end && !hitEof) {
214-
if (iso->readSectors(lba, sector, 1) != 1) break;
215-
if (sector[15] != 2) break;
216-
sub = sector + 16;
217-
// Different file/channel = new subfile
218-
if (sub[0] != fileNum || sub[1] != channelNum) break;
219-
hitEof = (sub[2] & 0x80) != 0;
236+
uint32_t fileStart = lba;
237+
bool hitEof = (submode & 0x80) != 0;
220238
lba++;
239+
scannedSectors++;
240+
241+
while (lba < end && !hitEof) {
242+
if (iso->readSectors(lba, sector, 1) != 1) break;
243+
if (sector[15] != 2) break;
244+
sub = sector + 16;
245+
if (sub[0] != fileNum || sub[1] != channelNum) break;
246+
hitEof = (sub[2] & 0x80) != 0;
247+
lba++;
248+
scannedSectors++;
249+
if (std::chrono::steady_clock::now() - time > std::chrono::milliseconds(50)) {
250+
m_gapScanProgress =
251+
totalGapSectors > 0 ? (float)scannedSectors / (float)totalGapSectors : 1.0f;
252+
co_yield m_gapScanCoroutine.awaiter();
253+
time = std::chrono::steady_clock::now();
254+
}
255+
}
256+
257+
uint32_t count = lba - fileStart;
258+
uint32_t dataSize = isForm2 ? count * 2324 : count * 2048;
259+
auto label = fmt::format(f_("<hidden {} f={} ch={} {} sectors>"),
260+
isForm2 ? "M2F2" : "M2F1", fileNum, channelNum, count);
261+
scanned.push_back({label, fileStart, dataSize, count, type, {}});
262+
continue;
221263
}
222264

223-
uint32_t count = lba - fileStart;
224-
uint32_t dataSize = isForm2 ? count * 2324 : count * 2048;
225-
auto label = fmt::format(f_("<hidden {} f={} ch={} {} sectors>"),
226-
isForm2 ? "M2F2" : "M2F1", fileNum, channelNum, count);
227-
out.push_back({label, fileStart, dataSize, count, type, {}});
228-
continue;
265+
lba++;
266+
scannedSectors++;
229267
}
230-
231-
// Unknown mode, skip sector
232-
lba++;
233268
}
269+
270+
m_flatEntries = std::move(scanned);
271+
m_gapsScanned = true;
272+
m_gapScanProgress = 1.0f;
234273
}
235274

236275
void PCSX::Widgets::IsoBrowser::drawFilesystemFlat() {
@@ -287,32 +326,35 @@ void PCSX::Widgets::IsoBrowser::drawFilesystemFlat() {
287326
uint32_t end = entry.lba + entry.sectors;
288327
if (end > nextExpected) nextExpected = end;
289328
}
329+
// Trailing gap: append any unreferenced sectors at the end of the image.
330+
uint32_t discEnd = pvd.get<ISO9660LowLevel::PVD_VolumeSpaceSize>();
331+
if (discEnd > nextExpected) {
332+
uint32_t gapSectors = discEnd - nextExpected;
333+
auto label = fmt::format(f_("<gap {} sectors>"), gapSectors);
334+
withGaps.push_back({label, nextExpected, gapSectors * 2352, gapSectors, FlatEntry::Gap, {}});
335+
}
290336
m_flatEntries = std::move(withGaps);
291337
m_flatEntriesDirty = false;
292338
m_gapsScanned = false;
293339
}
294340

295341
if (!m_gapsScanned) {
296-
if (ImGui::Button(_("Scan gaps for hidden files"))) {
297-
auto iso = m_cachedIso.lock();
298-
if (iso) {
299-
std::vector<FlatEntry> scanned;
300-
for (auto& entry : m_flatEntries) {
301-
if (entry.isGap()) {
302-
uint32_t sectorCount = entry.size / 2352;
303-
scanGapSectors(scanned, entry.lba, sectorCount, iso);
304-
} else {
305-
scanned.push_back(entry);
306-
}
342+
if (m_gapScanCoroutine.done()) {
343+
if (ImGui::Button(_("Scan gaps for hidden files"))) {
344+
auto iso = m_cachedIso.lock();
345+
if (iso) {
346+
m_gapScanProgress = 0.0f;
347+
m_gapScanCoroutine = scanAllGaps(iso);
307348
}
308-
m_flatEntries = std::move(scanned);
309-
m_gapsScanned = true;
310349
}
311-
}
312-
ImGuiHelpers::ShowHelpMarker(_(R"(Reads sector headers in gap regions to detect
350+
ImGuiHelpers::ShowHelpMarker(_(R"(Reads sector headers in gap regions to detect
313351
hidden files that were removed from the ISO9660
314352
directory but still have intact Mode 1/2 sector
315353
headers and subheader file boundary markers.)"));
354+
} else {
355+
ImGui::ProgressBar(m_gapScanProgress);
356+
m_gapScanCoroutine.resume();
357+
}
316358
}
317359

318360
if (ImGui::BeginTable("FilesystemFlat", 4,
@@ -329,7 +371,7 @@ headers and subheader file boundary markers.)"));
329371
ImGui::TableNextRow();
330372
ImGui::TableSetColumnIndex(0);
331373
bool selected = m_hasSelection && m_selectedLBA == entry.lba &&
332-
m_selectedIsGap == entry.isGap();
374+
m_selectedIsGap == (entry.isGap() || entry.isHidden());
333375
auto id = fmt::format("{}##{}", entry.path, i);
334376
if (ImGui::Selectable(id.c_str(), selected,
335377
ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
@@ -520,6 +562,7 @@ significantly by caching the files beforehand.)"));
520562
m_hasSelection = false;
521563
m_selectedPath.clear();
522564
m_flatEntriesDirty = true;
565+
m_gapScanCoroutine = {};
523566
if (currentIso && !currentIso->failed()) {
524567
m_reader = std::make_unique<ISO9660Reader>(currentIso);
525568
if (m_reader->failed()) m_reader.reset();

src/gui/widgets/isobrowser.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,13 @@ class IsoBrowser {
9292
std::vector<FlatEntry> m_flatEntries;
9393
bool m_flatEntriesDirty = true;
9494
bool m_gapsScanned = false;
95+
Coroutine<> m_gapScanCoroutine;
96+
float m_gapScanProgress = 0.0f;
9597

9698
void drawFilesystemTree(const ISO9660LowLevel::DirEntry& entry, const std::string& path);
9799
void drawFilesystemFlat();
98100
void collectFlatEntries(const ISO9660LowLevel::DirEntry& entry, const std::string& path);
99-
void scanGapSectors(std::vector<FlatEntry>& out, uint32_t startLBA, uint32_t sectorCount,
100-
std::shared_ptr<CDRIso> iso);
101+
Coroutine<> scanAllGaps(std::shared_ptr<CDRIso> iso);
101102

102103
FileDialog<> m_openIsoFileDialog;
103104
FileDialog<FileDialogMode::Save> m_saveFileDialog;

0 commit comments

Comments
 (0)