From 56c83c48e414c1e279e0e7b6e17dd3323dfbd897 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:39:45 +0900 Subject: [PATCH 1/5] Print error summary --- cd/cd_dump.ixx | 20 ++++++++++++++ debug.ixx | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ dvd/dvd_dump.ixx | 12 +++++++++ interval_set.ixx | 15 +++++++++++ redumper.ixx | 1 + 5 files changed, 118 insertions(+) diff --git a/cd/cd_dump.ixx b/cd/cd_dump.ixx index 755f873a..066bed52 100644 --- a/cd/cd_dump.ixx +++ b/cd/cd_dump.ixx @@ -20,6 +20,7 @@ import cd.subcode; import cd.toc; import common; import drive; +import interval_set; import options; import range; import scsi.cmd; @@ -338,6 +339,9 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool dump) int32_t subcode_shift = 0; uint32_t subcode_byte_desync_counter = 0; + IntervalSet scsi_error_intervals; + IntervalSet c2_error_intervals; + SignalINT signal; int32_t lba_overread = lba_end; @@ -432,7 +436,10 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool dump) if(session_gap_range == nullptr && lba < lba_end) { if(dump) + { errors.scsi += CD_DATA_SIZE_SAMPLES; + scsi_error_intervals.add(lba); + } if(options.verbose) LOGC_R("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); @@ -478,7 +485,10 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool dump) if(c2_samples) { if(dump) + { errors.c2 += c2_samples; + c2_error_intervals.add(lba); + } if(options.verbose) { @@ -575,6 +585,16 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool dump) LOG(" SCSI: {} samples", errors.scsi); LOG(" C2: {} samples", errors.c2); LOG(" Q: {}", errors.q); + + if(!scsi_error_intervals.empty() || !c2_error_intervals.empty()) + { + LOG(""); + LOG("LBA error ranges: "); + if(!scsi_error_intervals.empty()) + LOG(" SCSI: {}", scsi_error_intervals.to_string()); + if(!c2_error_intervals.empty()) + LOG(" C2: {}", c2_error_intervals.to_string()); + } } else { diff --git a/debug.ixx b/debug.ixx index 89ef7f01..4a5db09b 100644 --- a/debug.ixx +++ b/debug.ixx @@ -13,6 +13,7 @@ module; export module debug; +import bd; import cd.cd; import cd.common; import cd.subcode; @@ -20,6 +21,7 @@ import cd.toc; import common; import drive; import drive.mediatek; +import dvd; import dvd.dump; import dvd.xbox; import options; @@ -414,6 +416,74 @@ export int redumper_debug(Context &ctx, Options &options) return exit_code; } +export int redumper_state(Context &ctx, Options &options) +{ + int exit_code = 0; + + std::string image_prefix = (std::filesystem::path(options.image_path) / options.image_name).string(); + + std::filesystem::path state_path(image_prefix + ".state"); + if(!std::filesystem::exists(state_path)) + throw_line("state file not found ({})", state_path.filename().string()); + + bool cd = std::filesystem::exists(image_prefix + ".scram"); + if(!cd && options.disc_type && *options.disc_type == "CD") + cd = true; + uint64_t entries_count = std::filesystem::file_size(state_path) / sizeof(State); + + std::fstream state_fs(state_path, std::fstream::in | std::fstream::binary); + if(!state_fs.is_open()) + throw_line("unable to open file ({})", state_path.filename().string()); + + int32_t offset = 0; + if(cd) + offset = LBA_START * CD_DATA_SIZE_SAMPLES; + else if(std::filesystem::exists(image_prefix + ".sdram")) + offset = dvd::LBA_START; + else if(std::filesystem::exists(image_prefix + ".sbram")) + offset = bd::LBA_START; + + static const char *STATE_NAME[] = { "Unread", "Read Error", "Success (Plextor lead-in)", "Success (MediaTek lead-out)", "Success" }; + + LOG("state file (unit: {}): ", cd ? "samples" : "sectors"); + + constexpr uint32_t CHUNK = 4096; + std::vector buffer(CHUNK); + State current = State::ERROR_SKIP; + int64_t range_start = offset; + + auto print_range = [&](int64_t range_end) + { + if(range_start > range_end) + return; + + if(cd) + LOG(" {}-{} (LBA {}-{}): {}", range_start, range_end, range_start / CD_DATA_SIZE_SAMPLES, range_end / CD_DATA_SIZE_SAMPLES, STATE_NAME[(uint8_t)current]); + else + LOG(" {}-{}: {}", range_start, range_end, STATE_NAME[(uint8_t)current]); + }; + + for(uint64_t i = 0; i < entries_count; i += CHUNK) + { + uint32_t count = std::min((uint64_t)CHUNK, entries_count - i); + state_fs.read((char *)buffer.data(), count * sizeof(State)); + + for(uint32_t j = 0; j < count; ++j) + { + if(buffer[j] != current) + { + print_range(offset + (int64_t)(i + j) - 1); + current = buffer[j]; + range_start = offset + (int64_t)(i + j); + } + } + } + + print_range(offset + (int64_t)entries_count - 1); + + return exit_code; +} + export int redumper_flip(Context &ctx, Options &options) { int exit_code = 0; diff --git a/dvd/dvd_dump.ixx b/dvd/dvd_dump.ixx index 9e34ac36..a6811c72 100644 --- a/dvd/dvd_dump.ixx +++ b/dvd/dvd_dump.ixx @@ -1378,12 +1378,24 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, bool dump) LOG("media errors: "); LOG(" SCSI: {}", errors.scsi); LOG(" EDC: {}", errors.edc); + + if(!error_intervals.empty()) + { + LOG(""); + LOG("LBA error ranges: {}", error_intervals.to_string()); + } } else { LOG("correction statistics: "); LOG(" SCSI: {}", errors_initial.scsi - errors.scsi); LOG(" EDC: {}", errors_initial.edc - errors.edc); + + if(!intervals.empty()) + { + LOG(""); + LOG("LBA error ranges: {}", intervals.to_string()); + } } if(signal.interrupt()) diff --git a/interval_set.ixx b/interval_set.ixx index 950cd5a9..4b9e0ad9 100644 --- a/interval_set.ixx +++ b/interval_set.ixx @@ -1,7 +1,9 @@ module; #include #include +#include #include +#include #include #include @@ -213,6 +215,19 @@ public: return std::nullopt; } + + std::string to_string() const + { + std::string str; + for(auto &[start, end] : _ranges) + { + if(!str.empty()) + str += ", "; + str += end - start == 1 ? std::format("{}", start) : std::format("{}-{}", start, end - 1); + } + return str; + } + private: std::vector _ranges; }; diff --git a/redumper.ixx b/redumper.ixx index 101c3325..d8ec02d8 100644 --- a/redumper.ixx +++ b/redumper.ixx @@ -144,6 +144,7 @@ const std::map COMMANDS{ { "debug", { false, false, false, false, false, redumper_debug } }, { "fixmsf", { false, false, false, true, false, redumper_fix_msf } }, { "debug::flip", { false, false, false, true, false, redumper_flip } }, + { "debug::state", { false, false, false, true, false, redumper_state } }, { "drive::test", { true, true, true, false, false, redumper_drive_test } }, }; From 6d1df69977df0fca83bec45b19f16e76ef2a9c94 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:44:30 +0900 Subject: [PATCH 2/5] Use .. instead of - for range separator --- debug.ixx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug.ixx b/debug.ixx index 4a5db09b..84edff5c 100644 --- a/debug.ixx +++ b/debug.ixx @@ -458,9 +458,9 @@ export int redumper_state(Context &ctx, Options &options) return; if(cd) - LOG(" {}-{} (LBA {}-{}): {}", range_start, range_end, range_start / CD_DATA_SIZE_SAMPLES, range_end / CD_DATA_SIZE_SAMPLES, STATE_NAME[(uint8_t)current]); + LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, range_start / CD_DATA_SIZE_SAMPLES, range_end / CD_DATA_SIZE_SAMPLES, STATE_NAME[(uint8_t)current]); else - LOG(" {}-{}: {}", range_start, range_end, STATE_NAME[(uint8_t)current]); + LOG(" {}..{}: {}", range_start, range_end, STATE_NAME[(uint8_t)current]); }; for(uint64_t i = 0; i < entries_count; i += CHUNK) From 45daaadef9416ec47ced5db4ca3982dde0d5af74 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Thu, 25 Jun 2026 21:07:04 +0900 Subject: [PATCH 3/5] Error summary is verbose --- cd/cd_dump.ixx | 2 +- debug.ixx | 5 +++-- dvd/dvd_dump.ixx | 8 +------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/cd/cd_dump.ixx b/cd/cd_dump.ixx index 066bed52..745e5376 100644 --- a/cd/cd_dump.ixx +++ b/cd/cd_dump.ixx @@ -586,7 +586,7 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool dump) LOG(" C2: {} samples", errors.c2); LOG(" Q: {}", errors.q); - if(!scsi_error_intervals.empty() || !c2_error_intervals.empty()) + if(options.verbose && (!scsi_error_intervals.empty() || !c2_error_intervals.empty())) { LOG(""); LOG("LBA error ranges: "); diff --git a/debug.ixx b/debug.ixx index 84edff5c..77b78a70 100644 --- a/debug.ixx +++ b/debug.ixx @@ -457,10 +457,11 @@ export int redumper_state(Context &ctx, Options &options) if(range_start > range_end) return; + const char *state_name = (uint8_t)current < std::size(STATE_NAME) ? STATE_NAME[(uint8_t)current] : "Unknown"; if(cd) - LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, range_start / CD_DATA_SIZE_SAMPLES, range_end / CD_DATA_SIZE_SAMPLES, STATE_NAME[(uint8_t)current]); + LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, sample_to_lba(range_start, 0), sample_to_lba(range_end, 0), state_name); else - LOG(" {}..{}: {}", range_start, range_end, STATE_NAME[(uint8_t)current]); + LOG(" {}..{}: {}", range_start, range_end, state_name); }; for(uint64_t i = 0; i < entries_count; i += CHUNK) diff --git a/dvd/dvd_dump.ixx b/dvd/dvd_dump.ixx index a6811c72..0c1dac21 100644 --- a/dvd/dvd_dump.ixx +++ b/dvd/dvd_dump.ixx @@ -1379,7 +1379,7 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, bool dump) LOG(" SCSI: {}", errors.scsi); LOG(" EDC: {}", errors.edc); - if(!error_intervals.empty()) + if(options.verbose && !error_intervals.empty()) { LOG(""); LOG("LBA error ranges: {}", error_intervals.to_string()); @@ -1390,12 +1390,6 @@ export bool redumper_dump_dvd(Context &ctx, const Options &options, bool dump) LOG("correction statistics: "); LOG(" SCSI: {}", errors_initial.scsi - errors.scsi); LOG(" EDC: {}", errors_initial.edc - errors.edc); - - if(!intervals.empty()) - { - LOG(""); - LOG("LBA error ranges: {}", intervals.to_string()); - } } if(signal.interrupt()) From 965a42108447f50723b6146b7183c8b1996d4634 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:47:43 +0900 Subject: [PATCH 4/5] Review --- debug.ixx | 48 +++++++++++++++++++++++------------------------- interval_set.ixx | 4 ++-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/debug.ixx b/debug.ixx index 77b78a70..64d1a73d 100644 --- a/debug.ixx +++ b/debug.ixx @@ -443,45 +443,43 @@ export int redumper_state(Context &ctx, Options &options) else if(std::filesystem::exists(image_prefix + ".sbram")) offset = bd::LBA_START; - static const char *STATE_NAME[] = { "Unread", "Read Error", "Success (Plextor lead-in)", "Success (MediaTek lead-out)", "Success" }; + static const char *STATE_NAME[] = { "Unread", "Read Error", "Success (No confirmed C2 data)", "Success (No SCSI state)", "Success" }; LOG("state file (unit: {}): ", cd ? "samples" : "sectors"); - constexpr uint32_t CHUNK = 4096; - std::vector buffer(CHUNK); - State current = State::ERROR_SKIP; + std::vector buffer(CHUNK_1KB + 1); + State current = (State)0xFF; int64_t range_start = offset; - auto print_range = [&](int64_t range_end) + for (uint64_t i = 0; i < entries_count; i += CHUNK_1KB) { - if(range_start > range_end) - return; - - const char *state_name = (uint8_t)current < std::size(STATE_NAME) ? STATE_NAME[(uint8_t)current] : "Unknown"; - if(cd) - LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, sample_to_lba(range_start, 0), sample_to_lba(range_end, 0), state_name); - else - LOG(" {}..{}: {}", range_start, range_end, state_name); - }; - - for(uint64_t i = 0; i < entries_count; i += CHUNK) - { - uint32_t count = std::min((uint64_t)CHUNK, entries_count - i); + uint32_t count = std::min((uint64_t)CHUNK_1KB, entries_count - i); state_fs.read((char *)buffer.data(), count * sizeof(State)); - for(uint32_t j = 0; j < count; ++j) + // create state change at end of file to trigger final print + if (i + count >= entries_count) + buffer[count++] = (State)~(uint8_t)current; + + for (uint32_t j = 0; j < count; ++j) { - if(buffer[j] != current) + if (buffer[j] == current) + continue; + + int64_t range_end = offset + (int64_t)(i + j) - 1; + if (range_start <= range_end) { - print_range(offset + (int64_t)(i + j) - 1); - current = buffer[j]; - range_start = offset + (int64_t)(i + j); + const char *state_name = (uint8_t)current < std::size(STATE_NAME) ? STATE_NAME[(uint8_t)current] : "Unknown"; + if (cd) + LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, sample_to_lba(range_start, 0), sample_to_lba(range_end, 0), state_name); + else + LOG(" {}..{}: {}", range_start, range_end, state_name); } + + current = buffer[j]; + range_start = offset + (int64_t)(i + j); } } - print_range(offset + (int64_t)entries_count - 1); - return exit_code; } diff --git a/interval_set.ixx b/interval_set.ixx index 4b9e0ad9..132f10de 100644 --- a/interval_set.ixx +++ b/interval_set.ixx @@ -219,11 +219,11 @@ public: std::string to_string() const { std::string str; - for(auto &[start, end] : _ranges) + for(auto &r : _ranges) { if(!str.empty()) str += ", "; - str += end - start == 1 ? std::format("{}", start) : std::format("{}-{}", start, end - 1); + str += (r.second - r.first == 1) ? std::format("{}", r.first) : std::format("{}-{}", r.first, r.second - 1); } return str; } From 16d8e62edcc60c839ea88f08392a94b1cd263b29 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:50:42 +0900 Subject: [PATCH 5/5] clang format --- debug.ixx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/debug.ixx b/debug.ixx index 64d1a73d..a448e420 100644 --- a/debug.ixx +++ b/debug.ixx @@ -451,25 +451,25 @@ export int redumper_state(Context &ctx, Options &options) State current = (State)0xFF; int64_t range_start = offset; - for (uint64_t i = 0; i < entries_count; i += CHUNK_1KB) + for(uint64_t i = 0; i < entries_count; i += CHUNK_1KB) { uint32_t count = std::min((uint64_t)CHUNK_1KB, entries_count - i); state_fs.read((char *)buffer.data(), count * sizeof(State)); // create state change at end of file to trigger final print - if (i + count >= entries_count) - buffer[count++] = (State)~(uint8_t)current; + if(i + count >= entries_count) + buffer[count++] = (State) ~(uint8_t)current; - for (uint32_t j = 0; j < count; ++j) + for(uint32_t j = 0; j < count; ++j) { - if (buffer[j] == current) + if(buffer[j] == current) continue; int64_t range_end = offset + (int64_t)(i + j) - 1; - if (range_start <= range_end) + if(range_start <= range_end) { const char *state_name = (uint8_t)current < std::size(STATE_NAME) ? STATE_NAME[(uint8_t)current] : "Unknown"; - if (cd) + if(cd) LOG(" {}..{} (LBA {}..{}): {}", range_start, range_end, sample_to_lba(range_start, 0), sample_to_lba(range_end, 0), state_name); else LOG(" {}..{}: {}", range_start, range_end, state_name);