Skip to content

Commit

Permalink
Add split output bin support
Browse files Browse the repository at this point in the history
  • Loading branch information
987123879113 committed Mar 26, 2024
1 parent 94c15a0 commit f2d5660
Showing 1 changed file with 112 additions and 90 deletions.
202 changes: 112 additions & 90 deletions src/tools/chdman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
#include <iostream>
#include <limits>
#include <memory>
#include <optional>
#include <new>
#include <optional>
#include <regex>
#include <string_view>
#include <tuple>
#include <unordered_map>
Expand Down Expand Up @@ -96,6 +97,7 @@ constexpr int MODE_GDI = 2;
#define OPTION_INPUT "input"
#define OPTION_OUTPUT "output"
#define OPTION_OUTPUT_BIN "outputbin"
#define OPTION_OUTPUT_SPLIT "outputsplit"
#define OPTION_OUTPUT_FORCE "force"
#define OPTION_INPUT_START_BYTE "inputstartbyte"
#define OPTION_INPUT_START_HUNK "inputstarthunk"
Expand Down Expand Up @@ -628,6 +630,7 @@ static const option_description s_options[] =
{ OPTION_INPUT_PARENT, "ip", true, " <filename>: parent file name for input CHD" },
{ OPTION_OUTPUT, "o", true, " <filename>: output file name" },
{ OPTION_OUTPUT_BIN, "ob", true, " <filename>: output file name for binary data" },
{ OPTION_OUTPUT_SPLIT, "os", false, ": output one binary file per track" },
{ OPTION_OUTPUT_FORCE, "f", false, ": force overwriting an existing file" },
{ OPTION_OUTPUT_PARENT, "op", true, " <filename>: parent file name for output CHD" },
{ OPTION_INPUT_START_BYTE, "isb", true, " <offset>: starting byte offset within the input" },
Expand Down Expand Up @@ -782,6 +785,7 @@ static const command_description s_commands[] =
{
REQUIRED OPTION_OUTPUT,
OPTION_OUTPUT_BIN,
OPTION_OUTPUT_SPLIT,
OPTION_OUTPUT_FORCE,
REQUIRED OPTION_INPUT,
OPTION_INPUT_PARENT,
Expand Down Expand Up @@ -1462,62 +1466,19 @@ static void compress_common(chd_file_compressor &chd)
// to a CUE file
//-------------------------------------------------

void output_track_metadata(int mode, util::core_file &file, int tracknum, const cdrom_file::track_info &info, const std::string &filename, uint32_t frameoffs, uint64_t discoffs)
void output_track_metadata(int mode, util::core_file &file, int tracknum, const cdrom_file::track_info &info, const std::string &filename, uint32_t frameoffs, uint64_t discoffs, bool is_new_file)
{
if (mode == MODE_GDI)
{
int mode = 0, size = 2048;

switch (info.trktype)
{
case cdrom_file::CD_TRACK_MODE1:
mode = 4;
size = 2048;
break;

case cdrom_file::CD_TRACK_MODE1_RAW:
mode = 4;
size = 2352;
break;

case cdrom_file::CD_TRACK_MODE2:
mode = 4;
size = 2336;
break;

case cdrom_file::CD_TRACK_MODE2_FORM1:
mode = 4;
size = 2048;
break;

case cdrom_file::CD_TRACK_MODE2_FORM2:
mode = 4;
size = 2324;
break;

case cdrom_file::CD_TRACK_MODE2_FORM_MIX:
mode = 4;
size = 2336;
break;

case cdrom_file::CD_TRACK_MODE2_RAW:
mode = 4;
size = 2352;
break;

case cdrom_file::CD_TRACK_AUDIO:
mode = 0;
size = 2352;
break;
}
const int tracktype = info.trktype == cdrom_file::CD_TRACK_AUDIO ? 0 : 4;
const bool needquote = filename.find(' ') != std::string::npos;
const char *const quotestr = needquote ? "\"" : "";
file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, mode, size, quotestr, filename, quotestr, discoffs);
file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, tracktype, info.datasize, quotestr, filename, quotestr, discoffs);
}
else if (mode == MODE_CUEBIN)
{
// first track specifies the file
if (tracknum == 0)
if (is_new_file)
file.printf("FILE \"%s\" BINARY\n", filename);

// determine submode
Expand Down Expand Up @@ -1593,10 +1554,10 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const
file.printf("ZERO %s %s\n", modesubmode, msf_string_from_frames(info.pregap));

// all tracks but the first one have a file offset
if (tracknum > 0)
file.printf("DATAFILE \"%s\" #%d %s // length in bytes: %d\n", filename, uint32_t(discoffs), msf_string_from_frames(info.frames), info.frames * (info.datasize + info.subsize));
else
if (is_new_file)
file.printf("DATAFILE \"%s\" %s // length in bytes: %d\n", filename, msf_string_from_frames(info.frames), info.frames * (info.datasize + info.subsize));
else
file.printf("DATAFILE \"%s\" #%d %s // length in bytes: %d\n", filename, uint32_t(discoffs), msf_string_from_frames(info.frames), info.frames * (info.datasize + info.subsize));

// tracks with pregaps get a START marker too
if (info.pregap > 0)
Expand Down Expand Up @@ -2569,7 +2530,6 @@ static void do_extract_raw(parameters_map &params)
}
}


//-------------------------------------------------
// do_extract_cd - extract a CD file from a
// CHD image
Expand All @@ -2591,43 +2551,61 @@ static void do_extract_cd(parameters_map &params)
if (output_file_str != params.end())
check_existing_output_file(params, *output_file_str->second);

// verify output BIN file doesn't exist
// determine output type based on the specified file extension
int mode = MODE_NORMAL;
if (output_file_str->second->find(".cue") != -1)
mode = MODE_CUEBIN;
else if (output_file_str->second->find(".gdi") != -1)
mode = MODE_GDI;

// determine the output bin filename based on provided input parameters
auto output_bin_file_fnd = params.find(OPTION_OUTPUT_BIN);
std::string default_name(*output_file_str->second);

// split path and extension
int chop = default_name.find_last_of('.');
if (chop != -1)
default_name.erase(chop, default_name.size());
std::string basename = default_name;
default_name.append(".bin");

// GDIs will always output as split bin
bool is_splitbin = mode == MODE_GDI || params.find(OPTION_OUTPUT_SPLIT) != params.end();
if (is_splitbin)
{
if (mode == MODE_GDI)
default_name = default_name + "%02t";
else
default_name = default_name + " (Track %t)";
}

std::string output_bin_file_ext = ".bin";
std::string *output_bin_file_str;
if (output_bin_file_fnd == params.end())
{
output_bin_file_str = &default_name;
}
else
{
output_bin_file_str = output_bin_file_fnd->second;

check_existing_output_file(params, *output_bin_file_str);
chop = (*output_bin_file_str).find_last_of('.');
if (chop != -1)
{
output_bin_file_ext = (*output_bin_file_str).substr(chop, (*output_bin_file_str).size() - chop);
(*output_bin_file_str).erase(chop, (*output_bin_file_str).size());
}
}

// print some info
util::stream_format(std::cout, "Output TOC: %s\n", *output_file_str->second);
util::stream_format(std::cout, "Output Data: %s\n", *output_bin_file_str);
util::stream_format(std::cout, "Output Data: %s\n", *output_bin_file_str + output_bin_file_ext);
util::stream_format(std::cout, "Input CHD: %s\n", *params.find(OPTION_INPUT)->second);

// catch errors so we can close & delete the output file
util::core_file::ptr output_bin_file;
util::core_file::ptr output_toc_file;
std::string trackbin_name;
try
{
int mode = MODE_NORMAL;

if (output_file_str->second->find(".cue") != -1)
{
mode = MODE_CUEBIN;
}
else if (output_file_str->second->find(".gdi") != -1)
{
mode = MODE_GDI;
}

if (cdrom->is_gdrom() && (mode == MODE_CUEBIN))
{
util::stream_format(std::cout, "Warning: extracting GD-ROM CHDs as bin/cue is not fully supported and will result in an unusable CD-ROM cue file.\n");
Expand All @@ -2638,14 +2616,6 @@ static void do_extract_cd(parameters_map &params)
if (filerr)
report_error(1, "Unable to open file (%s): %s", *output_file_str->second, filerr.message());

// process output BIN file
if (mode != MODE_GDI)
{
filerr = util::core_file::open(*output_bin_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file);
if (filerr)
report_error(1, "Unable to open file (%s): %s", *output_bin_file_str, filerr.message());
}

// determine total frames
uint64_t total_bytes = 0;
for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
Expand Down Expand Up @@ -2697,37 +2667,88 @@ static void do_extract_cd(parameters_map &params)
uint64_t outputoffs = 0;
uint32_t discoffs = 0;
std::vector<uint8_t> buffer;
bool is_new_file = false;

// process output BIN file
for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
{
std::string trackbin_name = basename;
std::string new_trackbin_name = *output_bin_file_str;

if (mode == MODE_GDI)
{
trackbin_name += util::string_format("%02d", tracknum+1);
if (toc.tracks[tracknum].trktype == cdrom_file::CD_TRACK_AUDIO)
trackbin_name += ".raw";
new_trackbin_name += ".raw";
else
trackbin_name += ".bin";
new_trackbin_name += output_bin_file_ext;
}
else
{
new_trackbin_name += output_bin_file_ext;
}

if (is_splitbin)
{
outputoffs = 0;
}

if (new_trackbin_name != trackbin_name)
{
is_new_file = true;
output_bin_file.reset();

// variable replacement in output filename
const std::regex variables_regex("%([+-]?\\d+)?([a-zA-Z])");
std::string::const_iterator new_trackbin_name_itr = new_trackbin_name.begin();
std::string::const_iterator new_trackbin_name_end = new_trackbin_name.end();
std::string filename_formatted = new_trackbin_name;
std::smatch variable_matches;

while (std::regex_search(new_trackbin_name_itr, new_trackbin_name_end, variable_matches, variables_regex))
{
const std::string full_match = variable_matches[0].str();
const std::string format_part = variable_matches[1].str();
const std::string format_type = variable_matches[2].str();
std::string replacement;

if (format_type == "t") // track
{
replacement = util::string_format("%" + format_part + "d", tracknum+1);

if (!is_splitbin)
{
util::stream_format(std::cout, "Warning: --%s not specified but track format string detected, enabling automatically\n", OPTION_OUTPUT_SPLIT);
is_splitbin = true;
}
}
else
util::stream_format(std::cout, "Warning: found unknown format value '%s'\n", format_type);

if (!replacement.empty())
{
size_t index = std::string::npos;
while ((index = filename_formatted.find(full_match)) != std::string::npos)
filename_formatted.replace(index, full_match.size(), replacement);
}

new_trackbin_name_itr = variable_matches.suffix().first; // move past match for next loop
}

trackbin_name = filename_formatted;

// verify output BIN file doesn't exist
check_existing_output_file(params, trackbin_name);

filerr = util::core_file::open(trackbin_name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file);
if (filerr)
report_error(1, "Unable to open file (%s): %s", trackbin_name, filerr.message());

outputoffs = 0;
util::stream_format(std::cout, "Current output data file: %s\n", trackbin_name);
}

// output the metadata about the track to the TOC file
const cdrom_file::track_info &trackinfo = toc.tracks[tracknum];
if (mode == MODE_GDI)
{
output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(trackbin_name)), discoffs, outputoffs);
}
else
{
output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(*output_bin_file_str)), discoffs, outputoffs);
}
output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(trackbin_name)), discoffs, outputoffs, is_new_file);
is_new_file = false;

// If this is bin/cue output and the CHD contains subdata, warn the user and don't include
// the subdata size in the buffer calculation.
Expand Down Expand Up @@ -2818,7 +2839,8 @@ static void do_extract_cd(parameters_map &params)
// delete the output files
output_bin_file.reset();
output_toc_file.reset();
osd_file::remove(*output_bin_file_str);
if (!trackbin_name.empty())
osd_file::remove(trackbin_name);
osd_file::remove(*output_file_str->second);
throw;
}
Expand Down

0 comments on commit f2d5660

Please sign in to comment.