Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
tbejos committed Oct 25, 2024
2 parents ad95833 + e4848ad commit a3e4c7f
Show file tree
Hide file tree
Showing 14 changed files with 599 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ target_sources(redumper
"cd/cdrom.ixx"
"cd/ecc.ixx"
"cd/edc.ixx"
"cd/fix_msf.ixx"
"cd/offset_manager.ixx"
"cd/protection.ixx"
"cd/scrambler.ixx"
Expand Down Expand Up @@ -148,6 +149,7 @@ target_sources(redumper
"systems/securom.ixx"
"systems/s_cdrom.ixx"
"systems/s_iso.ixx"
"systems/dc.ixx"
"systems/mcd.ixx"
"systems/psx.ixx"
"systems/ps2.ixx"
Expand Down
2 changes: 1 addition & 1 deletion cd/cd_dump.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ export bool redumper_dump_cd(Context &ctx, const Options &options, bool refine)
// PLEXTOR: multisession lead-out overread
// usually there are couple of slow sectors before SCSI error is generated
// some models (PX-708UF) exit on I/O semaphore timeout on such slow sectors
if(ctx.drive_config.type == DriveConfig::Type::PLEXTOR && slow && inside_range(lba, error_ranges) != nullptr)
if((ctx.drive_config.type == DriveConfig::Type::PLEXTOR || drive_is_plextor4824(ctx.drive_config)) && slow && inside_range(lba, error_ranges) != nullptr)
{
// skip sector in refine mode
// lba_next = lba + 1; //FIXME:
Expand Down
16 changes: 9 additions & 7 deletions cd/cdrom.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export struct Sector
uint8_t q_parity[104];
};

struct SubHeader
{
uint8_t file_number;
uint8_t channel;
uint8_t submode;
uint8_t coding_info;
};

uint8_t sync[12];

struct Header
Expand All @@ -64,13 +72,7 @@ export struct Sector

struct
{
struct SubHeader
{
uint8_t file_number;
uint8_t channel;
uint8_t submode;
uint8_t coding_info;
} sub_header;
SubHeader sub_header;
SubHeader sub_header_copy;

union
Expand Down
192 changes: 192 additions & 0 deletions cd/fix_msf.ixx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
module;

#include <filesystem>
#include <fstream>
#include <list>
#include <optional>
#include <sstream>
#include <string>
#include <utility>
#include "systems/system.hh"
#include "throw_line.hh"

export module cd.fix_msf;

import cd.cd;
import cd.cdrom;
import cd.ecc;
import cd.edc;
import dump;
import filesystem.iso9660;
import options;
import readers.image_bin_form1_reader;
import readers.image_iso_form1_reader;
import readers.image_raw_reader;
import readers.sector_reader;
import systems.systems;
import utils.hex_bin;
import utils.logger;
import utils.misc;
import utils.strings;



namespace gpsxre
{

enum class TrackType
{
DATA,
AUDIO,
ISO
};


void regenerate_data_sector(Sector &sector, int32_t lba)
{
std::copy_n(CD_DATA_SYNC, sizeof(CD_DATA_SYNC), sector.sync);
sector.header.address = LBA_to_BCDMSF(lba);

if(sector.header.mode == 1)
{
std::fill_n(sector.mode1.intermediate, sizeof(sector.mode1.intermediate), 0x00);

Sector::ECC ecc = ECC().Generate((uint8_t *)&sector.header);
std::copy_n(ecc.p_parity, sizeof(ecc.p_parity), sector.mode1.ecc.p_parity);
std::copy_n(ecc.q_parity, sizeof(ecc.q_parity), sector.mode1.ecc.q_parity);

sector.mode1.edc = EDC().update((uint8_t *)&sector, offsetof(Sector, mode1.edc)).final();
}
else if(sector.header.mode == 2)
{
sector.mode2.xa.sub_header_copy = sector.mode2.xa.sub_header;

// Form2
if(sector.mode2.xa.sub_header.submode & (uint8_t)CDXAMode::FORM2)
{
// can be zeroed, regenerate only if it was set
if(sector.mode2.xa.form2.edc)
sector.mode2.xa.form2.edc = EDC().update((uint8_t *)&sector.mode2.xa.sub_header, offsetof(Sector, mode2.xa.form2.edc) - offsetof(Sector, mode2.xa.sub_header)).final();
}
// Form1
else
{
sector.mode2.xa.form1.edc = EDC().update((uint8_t *)&sector.mode2.xa.sub_header, offsetof(Sector, mode2.xa.form1.edc) - offsetof(Sector, mode2.xa.sub_header)).final();

// modifies sector, make sure sector data is not used after ECC calculation, otherwise header has to be restored
Sector::Header header = sector.header;
std::fill_n((uint8_t *)&sector.header, sizeof(sector.header), 0x00);

Sector::ECC ecc = ECC().Generate((uint8_t *)&sector.header);
std::copy_n(ecc.p_parity, sizeof(ecc.p_parity), sector.mode2.xa.form1.ecc.p_parity);
std::copy_n(ecc.q_parity, sizeof(ecc.q_parity), sector.mode2.xa.form1.ecc.q_parity);

// restore modified sector header
sector.header = header;
}
}
}


std::pair<uint8_t *, uint32_t> data_sector_get_data(Sector &sector)
{
if(sector.header.mode == 0)
return std::pair(sector.mode2.user_data, MODE0_DATA_SIZE);
else if(sector.header.mode == 1)
return std::pair(sector.mode1.user_data, FORM1_DATA_SIZE);
else if(sector.header.mode == 2)
{
if(sector.mode2.xa.sub_header.submode & (uint8_t)CDXAMode::FORM2)
return std::pair(sector.mode2.xa.form2.user_data, FORM2_DATA_SIZE);
else
return std::pair(sector.mode2.xa.form1.user_data, FORM1_DATA_SIZE);
}

return std::pair(nullptr, 0);
}


void fill_data_sector_data(Sector &sector, uint8_t fill_byte)
{
auto d = data_sector_get_data(sector);
std::fill_n(d.first, d.second, fill_byte);
}


bool data_sector_is_dummy(Sector &sector)
{
auto d = data_sector_get_data(sector);
return std::all_of(d.first, d.first + d.second, [](uint8_t value) { return value == 0x55; });
}


export void redumper_fix_msf(Context &ctx, Options &options)
{
image_check_empty(options);

auto image_prefix = (std::filesystem::path(options.image_path) / options.image_name).string();

std::list<std::filesystem::path> tracks;
if(std::filesystem::exists(image_prefix + ".cue"))
for(auto const &t : cue_get_entries(image_prefix + ".cue"))
if(t.second)
tracks.push_back(std::filesystem::path(options.image_path) / t.first);

if(tracks.empty())
throw_line("no files to process");

uint32_t errors = 0;

for(auto const &t : tracks)
{
uint32_t sectors_count = std::filesystem::file_size(t) / sizeof(Sector);
std::fstream fs(t, std::fstream::binary | std::fstream::in | std::fstream::out);

std::optional<int32_t> lba_base;
Sector sector_last;

for(uint32_t s = 0; s < sectors_count; ++s)
{
Sector sector;
fs.read((char *)&sector, sizeof(sector));
if(fs.fail())
throw_line("read failed");

bool sector_correct = true;
if(memcmp(sector.sync, CD_DATA_SYNC, sizeof(CD_DATA_SYNC)))
sector_correct = false;
if(lba_base && *lba_base + s != BCDMSF_to_LBA(sector.header.address))
sector_correct = false;

if(sector_correct)
{
// store base LBA address once
if(!lba_base)
lba_base = BCDMSF_to_LBA(sector.header.address);

// always cache last good sector to be used as a repair template
if(!data_sector_is_dummy(sector))
sector_last = sector;
}
else if(lba_base)
{
fill_data_sector_data(sector_last, 0x00);
regenerate_data_sector(sector_last, *lba_base + s);

fs.seekp(s * sizeof(Sector));
if(fs.fail())
throw_line("read failed");
fs.write((char *)&sector_last, sizeof(sector_last));
if(fs.fail())
throw_line("write failed");
fs << std::flush;

++errors;
}
}
}

LOG("corrected {} sectors", errors);
}

}
4 changes: 4 additions & 0 deletions cd/split.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ int32_t byte_offset_by_magic(int32_t lba_start, int32_t lba_end, std::fstream &s
{
int32_t write_offset = std::numeric_limits<int32_t>::max();

if(lba_start > lba_end)
{
return write_offset;
}
const uint32_t sectors_to_check = lba_end - lba_start;

std::vector<uint8_t> data(sectors_to_check * CD_DATA_SIZE);
Expand Down
8 changes: 7 additions & 1 deletion dump.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,20 @@ export TOC choose_toc(const std::vector<uint8_t> &toc_buffer, const std::vector<
}


export bool drive_is_plextor4824(const DriveConfig &drive_config)
{
return drive_config.vendor_id == "PLEXTOR" && drive_config.product_id == "CD-R PX-W4824A";
}


export bool toc_enable_cdtext(const Context &ctx, const TOC &toc, const Options &options)
{
if(options.disable_cdtext)
return false;
else if(options.force_cdtext_reading)
return true;
else
return !(ctx.drive_config.vendor_id == "PLEXTOR" && ctx.drive_config.product_id == "CD-R PX-W4824A") || toc.sessions.size() <= 1;
return !drive_is_plextor4824(ctx.drive_config) || toc.sessions.size() <= 1;
}


Expand Down
6 changes: 6 additions & 0 deletions options.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export struct Options
bool help;
bool version;
bool verbose;
bool auto_eject;
bool debug;

std::string image_path;
Expand Down Expand Up @@ -70,6 +71,7 @@ export struct Options
: help(false)
, version(false)
, verbose(false)
, auto_eject(false)
, debug(false)
, overwrite(false)
, force_split(false)
Expand Down Expand Up @@ -129,6 +131,8 @@ export struct Options
version = true;
else if(key == "--verbose")
verbose = true;
else if(key == "--auto-eject")
auto_eject = true;
else if(key == "--debug")
debug = true;
else if(key == "--image-path")
Expand Down Expand Up @@ -286,13 +290,15 @@ export struct Options
LOG("\tsplit \tgenerates BIN/CUE track split from dump files");
LOG("\thash \toutputs XML DAT hash entries (CUE/BIN or ISO)");
LOG("\tinfo \toutputs basic image information (CUE/BIN or ISO)");
LOG("\tskeleton \tgenerates image file with zeroed content");
LOG("");

LOG("OPTIONS:");
LOG("\t(general)");
LOG("\t--help,-h \tprint usage");
LOG("\t--version \tprint version");
LOG("\t--verbose \tverbose output");
LOG("\t--auto-eject \tauto eject after dump");
LOG("\t--drive=VALUE \tdrive to use, first available drive with disc, if not provided");
LOG("\t--speed=VALUE \tdrive read speed, optimal drive speed will be used if not provided");
LOG("\t--retries=VALUE \tnumber of sector retries in case of SCSI/C2 error (default: {})", retries);
Expand Down
Loading

0 comments on commit a3e4c7f

Please sign in to comment.