summaryrefslogtreecommitdiffstatshomepage
path: root/src/tools/chdman.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/chdman.cpp')
-rw-r--r--src/tools/chdman.cpp2008
1 files changed, 1241 insertions, 767 deletions
diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp
index 153a4e34157..353ac88ce6b 100644
--- a/src/tools/chdman.cpp
+++ b/src/tools/chdman.cpp
@@ -5,39 +5,51 @@
CHD compression frontend
****************************************************************************/
-#include <stdio.h> // must be here otherwise issues with I64FMT in MINGW
-#include <assert.h>
+#include <stdio.h> // must be stdio.h and here otherwise issues with I64FMT in MINGW
-#include "osdcore.h"
-#include "corefile.h"
-#include "chdcd.h"
-#include "aviio.h"
+// lib/util
#include "avhuff.h"
+#include "aviio.h"
#include "bitmap.h"
+#include "cdrom.h"
+#include "corefile.h"
+#include "coretmpl.h"
+#include "hashing.h"
#include "md5.h"
-#include "sha1.h"
+#include "multibyte.h"
+#include "osdcore.h"
+#include "path.h"
+#include "strformat.h"
#include "vbiparse.h"
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <ctype.h>
-#include <iostream>
+#include <array>
#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
#include <cstring>
+#include <ctime>
+#include <iostream>
#include <limits>
#include <memory>
#include <new>
+#include <optional>
+#include <regex>
+#include <string_view>
+#include <tuple>
#include <unordered_map>
+#include <utility>
+#include <vector>
+
+using util::string_format;
//**************************************************************************
// CONSTANTS & DEFINES
//**************************************************************************
-/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 */
-#if defined(WIN32)
+// MINGW has adopted the MSVC formatting for 64-bit ints as of GCC 4.4 and deprecated it as of GCC 9.3
+#if defined(_WIN32) && defined(__GNUC__) && ((__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3)))
#define I64FMT "I64"
#elif !defined(__APPLE__) && defined(__LP64__)
#define I64FMT "l"
@@ -45,16 +57,23 @@
#define I64FMT "ll"
#endif
+// hunk size limits
+constexpr uint32_t HUNK_SIZE_MIN = 16;
+constexpr uint32_t HUNK_SIZE_MAX = 1024 * 1024;
+
// default hard disk sector size
-const uint32_t IDE_SECTOR_SIZE = 512;
+constexpr uint32_t IDE_SECTOR_SIZE = 512;
// temporary input buffer size
-const uint32_t TEMP_BUFFER_SIZE = 32 * 1024 * 1024;
+constexpr uint32_t TEMP_BUFFER_SIZE = 32 * 1024 * 1024;
// modes
-const int MODE_NORMAL = 0;
-const int MODE_CUEBIN = 1;
-const int MODE_GDI = 2;
+constexpr int MODE_NORMAL = 0;
+constexpr int MODE_CUEBIN = 1;
+constexpr int MODE_GDI = 2;
+
+// osd printf verbosity
+constexpr bool OSD_PRINTF_VERBOSE = false;
// command modifier
#define REQUIRED "~"
@@ -66,10 +85,12 @@ const int MODE_GDI = 2;
#define COMMAND_CREATE_RAW "createraw"
#define COMMAND_CREATE_HD "createhd"
#define COMMAND_CREATE_CD "createcd"
+#define COMMAND_CREATE_DVD "createdvd"
#define COMMAND_CREATE_LD "createld"
#define COMMAND_EXTRACT_RAW "extractraw"
#define COMMAND_EXTRACT_HD "extracthd"
#define COMMAND_EXTRACT_CD "extractcd"
+#define COMMAND_EXTRACT_DVD "extractdvd"
#define COMMAND_EXTRACT_LD "extractld"
#define COMMAND_COPY "copy"
#define COMMAND_ADD_METADATA "addmeta"
@@ -81,6 +102,7 @@ const int MODE_GDI = 2;
#define OPTION_INPUT "input"
#define OPTION_OUTPUT "output"
#define OPTION_OUTPUT_BIN "outputbin"
+#define OPTION_OUTPUT_SPLITBIN "splitbin"
#define OPTION_OUTPUT_FORCE "force"
#define OPTION_INPUT_START_BYTE "inputstartbyte"
#define OPTION_INPUT_START_HUNK "inputstarthunk"
@@ -112,23 +134,26 @@ const int MODE_GDI = 2;
// FUNCTION PROTOTYPES
//**************************************************************************
-typedef std::unordered_map<std::string,std::string *> parameters_t;
+typedef std::unordered_map<std::string, std::string *> parameters_map;
+
+template <typename Format, typename... Params>
+[[noreturn]] static void report_error(int error, Format &&fmt, Params &&...args);
-template <typename Format, typename... Params> static void report_error(int error, Format &&fmt, Params &&...args);
-static void do_info(parameters_t &params);
-static void do_verify(parameters_t &params);
-static void do_create_raw(parameters_t &params);
-static void do_create_hd(parameters_t &params);
-static void do_create_cd(parameters_t &params);
-static void do_create_ld(parameters_t &params);
-static void do_copy(parameters_t &params);
-static void do_extract_raw(parameters_t &params);
-static void do_extract_cd(parameters_t &params);
-static void do_extract_ld(parameters_t &params);
-static void do_add_metadata(parameters_t &params);
-static void do_del_metadata(parameters_t &params);
-static void do_dump_metadata(parameters_t &params);
-static void do_list_templates(parameters_t &params);
+static void do_info(parameters_map &params);
+static void do_verify(parameters_map &params);
+static void do_create_raw(parameters_map &params);
+static void do_create_hd(parameters_map &params);
+static void do_create_cd(parameters_map &params);
+static void do_create_dvd(parameters_map &params);
+static void do_create_ld(parameters_map &params);
+static void do_copy(parameters_map &params);
+static void do_extract_raw(parameters_map &params);
+static void do_extract_cd(parameters_map &params);
+static void do_extract_ld(parameters_map &params);
+static void do_add_metadata(parameters_map &params);
+static void do_del_metadata(parameters_map &params);
+static void do_dump_metadata(parameters_map &params);
+static void do_list_templates(parameters_map &params);
@@ -136,6 +161,46 @@ static void do_list_templates(parameters_t &params);
// TYPE DEFINITIONS
//**************************************************************************
+// Allow chdman to show osd_printf_X messages
+class chdman_osd_output : public osd_output
+{
+public:
+ chdman_osd_output() {
+ osd_output::push(this);
+ }
+
+ ~chdman_osd_output() {
+ osd_output::pop(this);
+ }
+
+ void output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args);
+};
+
+void chdman_osd_output::output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args)
+{
+ switch (channel)
+ {
+ case OSD_OUTPUT_CHANNEL_ERROR:
+ case OSD_OUTPUT_CHANNEL_WARNING:
+ util::stream_format(std::cerr, args);
+ break;
+ case OSD_OUTPUT_CHANNEL_INFO:
+ case OSD_OUTPUT_CHANNEL_LOG:
+ util::stream_format(std::cout, args);
+ break;
+ case OSD_OUTPUT_CHANNEL_VERBOSE:
+ if (OSD_PRINTF_VERBOSE) util::stream_format(std::cout, args);
+ break;
+ case OSD_OUTPUT_CHANNEL_DEBUG:
+#ifdef MAME_DEBUG
+ util::stream_format(std::cout, args);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
// ======================> option_description
struct option_description
@@ -152,7 +217,7 @@ struct option_description
struct command_description
{
const char *name;
- void (*handler)(parameters_t &);
+ void (*handler)(parameters_map &);
const char *description;
const char *valid_options[16];
};
@@ -198,8 +263,7 @@ struct metadata_index_info
class fatal_error : public std::exception
{
public:
- fatal_error(int error)
- : m_error(error) { }
+ fatal_error(int error) : m_error(error) { }
int error() const { return m_error; }
@@ -245,11 +309,16 @@ class chd_rawfile_compressor : public chd_file_compressor
{
public:
// construction/destruction
- chd_rawfile_compressor(util::core_file &file, std::uint64_t offset = 0, std::uint64_t maxoffset = std::numeric_limits<std::uint64_t>::max())
+ chd_rawfile_compressor(util::random_read &file, std::uint64_t offset = 0, std::uint64_t maxoffset = std::numeric_limits<std::uint64_t>::max())
: m_file(file)
, m_offset(offset)
- , m_maxoffset((std::min)(maxoffset, file.size()))
{
+ // TODO: what to do about error getting file size?
+ std::uint64_t filelen;
+ if (!file.length(filelen))
+ m_maxoffset = (std::min)(maxoffset, filelen);
+ else
+ m_maxoffset = maxoffset;
}
// read interface
@@ -260,13 +329,15 @@ public:
return 0;
if (offset + length > m_maxoffset)
length = m_maxoffset - offset;
- m_file.seek(offset, SEEK_SET);
- return m_file.read(dest, length);
+ if (m_file.seek(offset, SEEK_SET)) // FIXME: better error reporting?
+ return 0;
+ auto const [err, actual] = read(m_file, dest, length); // FIXME: check for error return
+ return actual;
}
private:
// internal state
- util::core_file & m_file;
+ util::random_read & m_file;
std::uint64_t m_offset;
std::uint64_t m_maxoffset;
};
@@ -279,10 +350,12 @@ class chd_chdfile_compressor : public chd_file_compressor
public:
// construction/destruction
chd_chdfile_compressor(chd_file &file, uint64_t offset = 0, uint64_t maxoffset = ~0)
- : m_toc(nullptr),
- m_file(file),
- m_offset(offset),
- m_maxoffset(std::min(maxoffset, file.logical_bytes())) { }
+ : m_toc(nullptr)
+ , m_file(file)
+ , m_offset(offset)
+ , m_maxoffset(std::min(maxoffset, file.logical_bytes()))
+ {
+ }
// read interface
virtual uint32_t read_data(void *dest, uint64_t offset, uint32_t length)
@@ -292,18 +365,18 @@ public:
return 0;
if (offset + length > m_maxoffset)
length = m_maxoffset - offset;
- chd_error err = m_file.read_bytes(offset, dest, length);
- if (err != CHDERR_NONE)
+ std::error_condition err = m_file.read_bytes(offset, dest, length);
+ if (err)
throw err;
// if we have TOC - detect audio sectors and swap data
if (m_toc)
{
- assert(offset % CD_FRAME_SIZE == 0);
- assert(length % CD_FRAME_SIZE == 0);
+ assert(offset % cdrom_file::FRAME_SIZE == 0);
+ assert(length % cdrom_file::FRAME_SIZE == 0);
- int startlba = offset / CD_FRAME_SIZE;
- int lenlba = length / CD_FRAME_SIZE;
+ int startlba = offset / cdrom_file::FRAME_SIZE;
+ int lenlba = length / cdrom_file::FRAME_SIZE;
uint8_t *_dest = reinterpret_cast<uint8_t *>(dest);
for (int chdlba = 0; chdlba < lenlba; chdlba++)
@@ -317,11 +390,11 @@ public:
break;
}
// is it audio ?
- if (m_toc->tracks[tracknum].trktype != CD_TRACK_AUDIO)
+ if (m_toc->tracks[tracknum].trktype != cdrom_file::CD_TRACK_AUDIO)
continue;
// byteswap if yes
- int dataoffset = chdlba * CD_FRAME_SIZE;
- for (uint32_t swapindex = dataoffset; swapindex < (dataoffset + CD_MAX_SECTOR_DATA); swapindex += 2)
+ int dataoffset = chdlba * cdrom_file::FRAME_SIZE;
+ for (uint32_t swapindex = dataoffset; swapindex < (dataoffset + cdrom_file::MAX_SECTOR_DATA); swapindex += 2)
{
uint8_t temp = _dest[swapindex];
_dest[swapindex] = _dest[swapindex + 1];
@@ -333,7 +406,7 @@ public:
return length;
}
-const cdrom_toc * m_toc;
+const cdrom_file::toc * m_toc;
private:
// internal state
@@ -349,10 +422,12 @@ class chd_cd_compressor : public chd_file_compressor
{
public:
// construction/destruction
- chd_cd_compressor(cdrom_toc &toc, chdcd_track_input_info &info)
- : m_file(),
- m_toc(toc),
- m_info(info) { }
+ chd_cd_compressor(cdrom_file::toc &toc, cdrom_file::track_input_info &info)
+ : m_file()
+ , m_toc(toc)
+ , m_info(info)
+ {
+ }
~chd_cd_compressor()
{
@@ -362,8 +437,8 @@ public:
virtual uint32_t read_data(void *_dest, uint64_t offset, uint32_t length)
{
// verify assumptions made below
- assert(offset % CD_FRAME_SIZE == 0);
- assert(length % CD_FRAME_SIZE == 0);
+ assert(offset % cdrom_file::FRAME_SIZE == 0);
+ assert(length % cdrom_file::FRAME_SIZE == 0);
// initialize destination to 0 so that unused areas are filled
uint8_t *dest = reinterpret_cast<uint8_t *>(_dest);
@@ -374,8 +449,9 @@ public:
uint32_t length_remaining = length;
for (int tracknum = 0; tracknum < m_toc.numtrks; tracknum++)
{
- const cdrom_track_info &trackinfo = m_toc.tracks[tracknum];
- uint64_t endoffs = startoffs + (uint64_t)(trackinfo.frames + trackinfo.extraframes) * CD_FRAME_SIZE;
+ const cdrom_file::track_info &trackinfo = m_toc.tracks[tracknum];
+ uint64_t endoffs = startoffs + (uint64_t)(trackinfo.frames + trackinfo.extraframes) * cdrom_file::FRAME_SIZE;
+
if (offset >= startoffs && offset < endoffs)
{
// if we don't already have this file open, open it now
@@ -383,33 +459,56 @@ public:
{
m_file.reset();
m_lastfile = m_info.track[tracknum].fname;
- osd_file::error filerr = util::core_file::open(m_lastfile, OPEN_FLAG_READ, m_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Error opening input file (%s)'", m_lastfile.c_str());
+ std::error_condition const filerr = util::core_file::open(m_lastfile, OPEN_FLAG_READ, m_file);
+ if (filerr)
+ report_error(1, "Error opening input file (%s): %s", m_lastfile, filerr.message());
}
// iterate over frames
uint64_t bytesperframe = trackinfo.datasize + trackinfo.subsize;
uint64_t src_track_start = m_info.track[tracknum].offset;
uint64_t src_track_end = src_track_start + bytesperframe * (uint64_t)trackinfo.frames;
- uint64_t pad_track_start = src_track_end - ((uint64_t)m_toc.tracks[tracknum].padframes * bytesperframe);
+ uint64_t split_track_start = src_track_end - ((uint64_t)trackinfo.splitframes * bytesperframe);
+ uint64_t pad_track_start = split_track_start - ((uint64_t)trackinfo.padframes * bytesperframe);
+
+ // dont split when split-bin read not required
+ if ((uint64_t)trackinfo.splitframes == 0L)
+ split_track_start = UINT64_MAX;
+
while (length_remaining != 0 && offset < endoffs)
{
// determine start of current frame
- uint64_t src_frame_start = src_track_start + ((offset - startoffs) / CD_FRAME_SIZE) * bytesperframe;
+ uint64_t src_frame_start = src_track_start + ((offset - startoffs) / cdrom_file::FRAME_SIZE) * bytesperframe;
+
+ // auto-advance next track for split-bin read
+ if (src_frame_start >= split_track_start && src_frame_start < src_track_end && m_lastfile.compare(m_info.track[tracknum+1].fname)!=0)
+ {
+ m_file.reset();
+ m_lastfile = m_info.track[tracknum+1].fname;
+ std::error_condition const filerr = util::core_file::open(m_lastfile, OPEN_FLAG_READ, m_file);
+ if (filerr)
+ report_error(1, "Error opening input file (%s): %s", m_lastfile, filerr.message());
+ }
+
if (src_frame_start < src_track_end)
{
// read it in, or pad if we're into the padframes
- if (src_frame_start >= pad_track_start)
+ if (src_frame_start >= pad_track_start && src_frame_start < split_track_start)
{
memset(dest, 0, bytesperframe);
}
else
{
- m_file->seek(src_frame_start, SEEK_SET);
- uint32_t count = m_file->read(dest, bytesperframe);
- if (count != bytesperframe)
- report_error(1, "Error reading input file (%s)'", m_lastfile.c_str());
+ std::error_condition err = m_file->seek(
+ (src_frame_start >= split_track_start)
+ ? src_frame_start - split_track_start
+ : src_frame_start,
+ SEEK_SET);
+ std::size_t count = 0;
+ if (!err)
+ std::tie(err, count) = read(*m_file, dest, bytesperframe);
+ if (err || (count != bytesperframe))
+ report_error(1, "Error reading input file (%s)'", m_lastfile);
}
// swap if appropriate
@@ -423,9 +522,9 @@ public:
}
// advance
- offset += CD_FRAME_SIZE;
- dest += CD_FRAME_SIZE;
- length_remaining -= CD_FRAME_SIZE;
+ offset += cdrom_file::FRAME_SIZE;
+ dest += cdrom_file::FRAME_SIZE;
+ length_remaining -= cdrom_file::FRAME_SIZE;
if (length_remaining == 0)
break;
}
@@ -439,10 +538,10 @@ public:
private:
// internal state
- std::string m_lastfile;
- util::core_file::ptr m_file;
- cdrom_toc & m_toc;
- chdcd_track_input_info & m_info;
+ std::string m_lastfile;
+ util::core_file::ptr m_file;
+ cdrom_file::toc & m_toc;
+ cdrom_file::track_input_info &m_info;
};
@@ -453,13 +552,15 @@ class chd_avi_compressor : public chd_file_compressor
public:
// construction/destruction
chd_avi_compressor(avi_file &file, avi_info &info, uint32_t first_frame, uint32_t num_frames)
- : m_file(file),
- m_info(info),
- m_bitmap(info.width, info.height * (info.interlaced ? 2 : 1)),
- m_start_frame(first_frame),
- m_frame_count(num_frames),
- m_ldframedata(num_frames * VBI_PACKED_BYTES),
- m_rawdata(info.bytes_per_frame) { }
+ : m_file(file)
+ , m_info(info)
+ , m_bitmap(info.width, info.height * (info.interlaced ? 2 : 1))
+ , m_start_frame(first_frame)
+ , m_frame_count(num_frames)
+ , m_ldframedata(num_frames * VBI_PACKED_BYTES)
+ , m_rawdata(info.bytes_per_frame)
+ {
+ }
// getters
const std::vector<uint8_t> &ldframedata() const { return m_ldframedata; }
@@ -475,6 +576,7 @@ public:
int32_t start_frame = offset / m_info.bytes_per_frame;
int32_t end_frame = (offset + length - 1) / m_info.bytes_per_frame;
for (int32_t framenum = start_frame; framenum <= end_frame; framenum++)
+ {
if (framenum < m_frame_count)
{
// determine effective frame number and first/last samples
@@ -483,7 +585,7 @@ public:
uint32_t samples = (uint64_t(m_info.rate) * uint64_t(effframe + 1) * uint64_t(1000000) + m_info.fps_times_1million - 1) / uint64_t(m_info.fps_times_1million) - first_sample;
// loop over channels and read the samples
- int channels = unsigned((std::min<std::size_t>)(m_info.channels, ARRAY_LENGTH(m_audio)));
+ int channels = unsigned(std::min<std::size_t>(m_info.channels, std::size(m_audio)));
EQUIVALENT_ARRAY(m_audio, int16_t *) samplesptr;
for (int chnum = 0; chnum < channels; chnum++)
{
@@ -505,7 +607,7 @@ public:
if (m_info.height == 524/2 || m_info.height == 624/2)
{
vbi_metadata vbi;
- vbi_parse_all(&subbitmap.pix16(0), subbitmap.rowpixels(), subbitmap.width(), 8, &vbi);
+ vbi_parse_all(&subbitmap.pix(0), subbitmap.rowpixels(), subbitmap.width(), 8, &vbi);
vbi_metadata_pack(&m_ldframedata[framenum * VBI_PACKED_BYTES], framenum, &vbi);
}
@@ -531,6 +633,7 @@ public:
dest += bytes_to_copy;
length_remaining -= bytes_to_copy;
}
+ }
return length;
}
@@ -540,11 +643,11 @@ private:
avi_file & m_file;
avi_info & m_info;
bitmap_yuy16 m_bitmap;
- uint32_t m_start_frame;
- uint32_t m_frame_count;
+ uint32_t m_start_frame;
+ uint32_t m_frame_count;
std::vector<int16_t> m_audio[8];
- std::vector<uint8_t> m_ldframedata;
- std::vector<uint8_t> m_rawdata;
+ std::vector<uint8_t> m_ldframedata;
+ std::vector<uint8_t> m_rawdata;
};
@@ -558,10 +661,11 @@ static clock_t lastprogress = 0;
// default compressors
-static const chd_codec_type s_default_raw_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
-static const chd_codec_type s_default_hd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
-static const chd_codec_type s_default_cd_compression[4] = { CHD_CODEC_CD_LZMA, CHD_CODEC_CD_ZLIB, CHD_CODEC_CD_FLAC };
-static const chd_codec_type s_default_ld_compression[4] = { CHD_CODEC_AVHUFF };
+static const std::array<chd_codec_type, 4> s_no_compression = { CHD_CODEC_NONE, CHD_CODEC_NONE, CHD_CODEC_NONE, CHD_CODEC_NONE };
+static const std::array<chd_codec_type, 4> s_default_raw_compression = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
+static const std::array<chd_codec_type, 4> s_default_hd_compression = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
+static const std::array<chd_codec_type, 4> s_default_cd_compression = { CHD_CODEC_CD_LZMA, CHD_CODEC_CD_ZLIB, CHD_CODEC_CD_FLAC };
+static const std::array<chd_codec_type, 4> s_default_ld_compression = { CHD_CODEC_AVHUFF };
// descriptions for each option
@@ -571,6 +675,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_SPLITBIN, "sb", 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" },
@@ -583,7 +688,7 @@ static const option_description s_options[] =
{ OPTION_UNIT_SIZE, "us", true, " <bytes>: size of each unit, in bytes" },
{ OPTION_COMPRESSION, "c", true, " <none|type1[,type2[,...]]>: which compression codecs to use (up to 4)" },
{ OPTION_IDENT, "id", true, " <filename>: name of ident file to provide CHS information" },
- { OPTION_CHS, "chs", true, " <cylinders,heads,sectors>: specifies CHS values directly" },
+ { OPTION_CHS, "chs", true, " <cylinders,heads,sectors>: specifies CHS geometry directly" },
{ OPTION_SECTOR_SIZE, "ss", true, " <bytes>: size of each hard disk sector" },
{ OPTION_TAG, "t", true, " <tag>: 4-character tag for metadata" },
{ OPTION_INDEX, "ix", true, " <index>: indexed instance of this metadata tag" },
@@ -611,7 +716,8 @@ static const command_description s_commands[] =
{ COMMAND_VERIFY, do_verify, ": verifies a CHD's integrity",
{
REQUIRED OPTION_INPUT,
- OPTION_INPUT_PARENT
+ OPTION_INPUT_PARENT,
+ OPTION_FIX
}
},
@@ -625,8 +731,8 @@ static const command_description s_commands[] =
OPTION_INPUT_START_HUNK,
OPTION_INPUT_LENGTH_BYTES,
OPTION_INPUT_LENGTH_HUNKS,
- REQUIRED OPTION_HUNK_SIZE,
- REQUIRED OPTION_UNIT_SIZE,
+ OPTION_HUNK_SIZE,
+ OPTION_UNIT_SIZE,
OPTION_COMPRESSION,
OPTION_NUMPROCESSORS
}
@@ -664,6 +770,21 @@ static const command_description s_commands[] =
OPTION_NUMPROCESSORS
}
},
+ { COMMAND_CREATE_DVD, do_create_dvd, ": create a DVD CHD from the input file",
+ {
+ REQUIRED OPTION_OUTPUT,
+ OPTION_OUTPUT_PARENT,
+ OPTION_OUTPUT_FORCE,
+ REQUIRED OPTION_INPUT,
+ OPTION_INPUT_START_BYTE,
+ OPTION_INPUT_START_HUNK,
+ OPTION_INPUT_LENGTH_BYTES,
+ OPTION_INPUT_LENGTH_HUNKS,
+ OPTION_HUNK_SIZE,
+ OPTION_COMPRESSION,
+ OPTION_NUMPROCESSORS
+ }
+ },
{ COMMAND_CREATE_LD, do_create_ld, ": create a laserdisc CHD from the input file",
{
@@ -709,12 +830,26 @@ static const command_description s_commands[] =
{
REQUIRED OPTION_OUTPUT,
OPTION_OUTPUT_BIN,
+ OPTION_OUTPUT_SPLITBIN,
OPTION_OUTPUT_FORCE,
REQUIRED OPTION_INPUT,
OPTION_INPUT_PARENT,
}
},
+ { COMMAND_EXTRACT_DVD, do_extract_raw, ": extract DVD file from a CHD input file",
+ {
+ REQUIRED OPTION_OUTPUT,
+ OPTION_OUTPUT_FORCE,
+ REQUIRED OPTION_INPUT,
+ OPTION_INPUT_PARENT,
+ OPTION_INPUT_START_BYTE,
+ OPTION_INPUT_START_HUNK,
+ OPTION_INPUT_LENGTH_BYTES,
+ OPTION_INPUT_LENGTH_HUNKS
+ }
+ },
+
{ COMMAND_EXTRACT_LD, do_extract_ld, ": extract laserdisc AVI from a CHD input file",
{
REQUIRED OPTION_OUTPUT,
@@ -782,11 +917,19 @@ static const command_description s_commands[] =
// hard disk templates
static const hd_template s_hd_templates[] =
{
- { "Conner", "CFA170A", 332, 16, 63, 512 }, // 163 MB
- { "Rodime", "R0201", 321, 2, 16, 512 }, // 5 MB
- { "Rodime", "R0202", 321, 4, 16, 512 }, // 10 MB
- { "Rodime", "R0203", 321, 6, 16, 512 }, // 15 MB
- { "Rodime", "R0204", 321, 8, 16, 512 }, // 20 MB
+ { "Conner", "CFA170A", 332, 16, 63, 512 }, // 163 MB
+ { "Rodime", "R0201", 321, 2, 16, 512 }, // 5 MB
+ { "Rodime", "R0202", 321, 4, 16, 512 }, // 10 MB
+ { "Rodime", "R0203", 321, 6, 16, 512 }, // 15 MB
+ { "Rodime", "R0204", 321, 8, 16, 512 }, // 20 MB
+ { "Seagate", "ST-213", 615, 2, 17, 512 }, // 10 MB
+ { "Seagate", "ST-225", 615, 4, 17, 512 }, // 20 MB
+ { "Seagate", "ST-251", 820, 6, 17, 512 }, // 40 MB
+ { "Seagate", "ST-3600N", 1877, 7, 76, 512 }, // 525 MB
+ { "Maxtor", "LXT-213S", 1314, 7, 53, 512 }, // 200 MB
+ { "Maxtor", "LXT-340S", 1574, 7, 70, 512 }, // 340 MB
+ { "Maxtor", "MXT-540SL", 2466, 7, 87, 512 }, // 540 MB
+ { "Micropolis", "1528", 2094, 15, 83, 512 }, // 1342 MB
};
@@ -799,7 +942,8 @@ static const hd_template s_hd_templates[] =
// report_error - report an error
//-------------------------------------------------
-template <typename Format, typename... Params> static void report_error(int error, Format &&fmt, Params &&...args)
+template <typename Format, typename... Params>
+static void report_error(int error, Format &&fmt, Params &&...args)
{
// output to stderr
util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...);
@@ -815,7 +959,8 @@ template <typename Format, typename... Params> static void report_error(int erro
// progress - generic progress callback
//-------------------------------------------------
-template <typename Format, typename... Params> static void progress(bool forceit, Format &&fmt, Params &&...args)
+template <typename Format, typename... Params>
+static void progress(bool forceit, Format &&fmt, Params &&...args)
{
// skip if it hasn't been long enough
clock_t curtime = clock();
@@ -833,20 +978,20 @@ template <typename Format, typename... Params> static void progress(bool forceit
// print_help - print help for all the commands
//-------------------------------------------------
-static int print_help(const char *argv0, const char *error = nullptr)
+static int print_help(const std::string &argv0, const char *error = nullptr)
{
// print the error message first
- if (error != nullptr)
- fprintf(stderr, "Error: %s\n\n", error);
+ if (error)
+ util::stream_format(std::cerr, "Error: %s\n\n", error);
// print a summary of each command
- printf("Usage:\n");
+ util::stream_format(std::cout, "Usage:\n");
for (auto & desc : s_commands)
{
- printf(" %s %s%s\n", argv0, desc.name, desc.description);
+ util::stream_format(std::cout, " %s %s%s\n", argv0, desc.name, desc.description);
}
- printf("\nFor help with any command, run:\n");
- printf(" %s %s <command>\n", argv0, COMMAND_HELP);
+ util::stream_format(std::cout, "\nFor help with any command, run:\n");
+ util::stream_format(std::cout, " %s %s <command>\n", argv0, COMMAND_HELP);
return 1;
}
@@ -856,16 +1001,16 @@ static int print_help(const char *argv0, const char *error = nullptr)
// command
//-------------------------------------------------
-static int print_help(const char *argv0, const command_description &desc, const char *error = nullptr)
+static int print_help(const std::string &argv0, const command_description &desc, const char *error = nullptr)
{
// print the error message first
if (error != nullptr)
- fprintf(stderr, "Error: %s\n\n", error);
+ util::stream_format(std::cerr, "Error: %s\n\n", error);
// print usage for this command
- printf("Usage:\n");
- printf(" %s %s [options], where valid options are:\n", argv0, desc.name);
- for (int valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
+ util::stream_format(std::cout, "Usage:\n");
+ util::stream_format(std::cout, " %s %s [options], where valid options are:\n", argv0, desc.name);
+ for (int valid = 0; valid < std::size(desc.valid_options); valid++)
{
// determine whether we are required
const char *option = desc.valid_options[valid];
@@ -880,10 +1025,10 @@ static int print_help(const char *argv0, const command_description &desc, const
if (strcmp(option, s_option.name) == 0)
{
const option_description &odesc = s_option;
- printf(" --%s", odesc.name);
+ util::stream_format(std::cout, " --%s", odesc.name);
if (odesc.shortname != nullptr)
- printf(", -%s", odesc.shortname);
- printf("%s%s\n", odesc.description, required ? " (required)" : "");
+ util::stream_format(std::cout, ", -%s", odesc.shortname);
+ util::stream_format(std::cout, "%s%s\n", odesc.description, required ? " (required)" : "");
}
}
return 1;
@@ -967,10 +1112,17 @@ uint64_t parse_number(const char *string)
// compute a best guess CHS value set
//-------------------------------------------------
-static void guess_chs(std::string *filename, uint64_t filesize, int sectorsize, uint32_t &cylinders, uint32_t &heads, uint32_t &sectors, uint32_t &bps)
+static void guess_chs(
+ const std::string *filename,
+ uint64_t filesize,
+ int sectorsize,
+ uint32_t &cylinders,
+ uint32_t &heads,
+ uint32_t &sectors,
+ uint32_t &bps)
{
// if this is a direct physical drive read, handle it specially
- if (filename != nullptr && osd_get_physical_drive_geometry(filename->c_str(), &cylinders, &heads, &sectors, &bps))
+ if (filename && osd_get_physical_drive_geometry(filename->c_str(), &cylinders, &heads, &sectors, &bps))
return;
// if we have no length to work with, we can't guess
@@ -979,11 +1131,14 @@ static void guess_chs(std::string *filename, uint64_t filesize, int sectorsize,
// now find a valid value
for (uint32_t totalsectors = filesize / sectorsize; ; totalsectors++)
+ {
for (uint32_t cursectors = 63; cursectors > 1; cursectors--)
+ {
if (totalsectors % cursectors == 0)
{
uint32_t totalheads = totalsectors / cursectors;
for (uint32_t curheads = 16; curheads > 1; curheads--)
+ {
if (totalheads % curheads == 0)
{
cylinders = totalheads / curheads;
@@ -991,7 +1146,10 @@ static void guess_chs(std::string *filename, uint64_t filesize, int sectorsize,
sectors = cursectors;
return;
}
+ }
}
+ }
+ }
}
@@ -1000,24 +1158,24 @@ static void guess_chs(std::string *filename, uint64_t filesize, int sectorsize,
// standard set of input CHD parameters
//-------------------------------------------------
-static void parse_input_chd_parameters(const parameters_t &params, chd_file &input_chd, chd_file &input_parent_chd, bool writeable = false)
+static void parse_input_chd_parameters(const parameters_map &params, chd_file &input_chd, chd_file &input_parent_chd, bool writeable = false)
{
// process input parent file
auto input_chd_parent_str = params.find(OPTION_INPUT_PARENT);
if (input_chd_parent_str != params.end())
{
- chd_error err = input_parent_chd.open(input_chd_parent_str->second->c_str());
- if (err != CHDERR_NONE)
- report_error(1, "Error opening parent CHD file (%s): %s", input_chd_parent_str->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = input_parent_chd.open(*input_chd_parent_str->second);
+ if (err)
+ report_error(1, "Error opening parent CHD file (%s): %s", *input_chd_parent_str->second, err.message());
}
// process input file
auto input_chd_str = params.find(OPTION_INPUT);
if (input_chd_str != params.end())
{
- chd_error err = input_chd.open(input_chd_str->second->c_str(), writeable, input_parent_chd.opened() ? &input_parent_chd : nullptr);
- if (err != CHDERR_NONE)
- report_error(1, "Error opening CHD file (%s): %s", input_chd_str->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = input_chd.open(*input_chd_str->second, writeable, input_parent_chd.opened() ? &input_parent_chd : nullptr);
+ if (err)
+ report_error(1, "Error opening CHD file (%s): %s", *input_chd_str->second, err.message());
}
}
@@ -1027,38 +1185,80 @@ static void parse_input_chd_parameters(const parameters_t &params, chd_file &inp
// parameters in a standard way
//-------------------------------------------------
-static void parse_input_start_end(const parameters_t &params, uint64_t logical_size, uint32_t hunkbytes, uint32_t framebytes, uint64_t &input_start, uint64_t &input_end)
+static std::pair<uint64_t, uint64_t> parse_input_start_end(
+ const parameters_map &params,
+ uint64_t logical_size,
+ uint32_t hunkbytes,
+ uint32_t framebytes)
{
- // process start/end if we were provided an input CHD
- input_start = 0;
- input_end = logical_size;
-
// process input start
- auto input_start_byte_str = params.find(OPTION_INPUT_START_BYTE);
- auto input_start_hunk_str = params.find(OPTION_INPUT_START_HUNK);
- auto input_start_frame_str = params.find(OPTION_INPUT_START_FRAME);
+ const auto input_start_byte_str = params.find(OPTION_INPUT_START_BYTE);
+ const auto input_start_hunk_str = params.find(OPTION_INPUT_START_HUNK);
+ const auto input_start_frame_str = params.find(OPTION_INPUT_START_FRAME);
+ uint64_t input_start = 0;
if (input_start_byte_str != params.end())
+ {
+ if (input_start_hunk_str != params.end())
+ report_error(1, "Start offset cannot be specified in both bytes and hunks");
+ if (input_start_frame_str != params.end())
+ report_error(1, "Start offset cannot be specified in both bytes and frames");
input_start = parse_number(input_start_byte_str->second->c_str());
- if (input_start_hunk_str != params.end())
+ }
+ else if (input_start_hunk_str != params.end())
+ {
+ if (input_start_frame_str != params.end())
+ report_error(1, "Start offset cannot be specified in both hunks and frames");
input_start = parse_number(input_start_hunk_str->second->c_str()) * hunkbytes;
- if (input_start_frame_str != params.end())
+ }
+ else if (input_start_frame_str != params.end())
+ {
input_start = parse_number(input_start_frame_str->second->c_str()) * framebytes;
- if (input_start >= input_end)
- report_error(1, "Input start offset greater than input file size");
+ }
// process input length
- auto input_length_bytes_str = params.find(OPTION_INPUT_LENGTH_BYTES);
+ const auto input_length_bytes_str = params.find(OPTION_INPUT_LENGTH_BYTES);
auto input_length_hunks_str = params.find(OPTION_INPUT_LENGTH_HUNKS);
auto input_length_frames_str = params.find(OPTION_INPUT_LENGTH_FRAMES);
- uint64_t input_length = input_end;
+ std::optional<uint64_t> input_length;
if (input_length_bytes_str != params.end())
+ {
+ if (input_length_hunks_str != params.end())
+ report_error(1, "Length cannot be specified in both bytes and hunks");
+ if (input_length_frames_str != params.end())
+ report_error(1, "Length cannot be specified in both bytes and frames");
input_length = parse_number(input_length_bytes_str->second->c_str());
- if (input_length_hunks_str != params.end())
+ }
+ else if (input_length_hunks_str != params.end())
+ {
+ if (input_length_frames_str != params.end())
+ report_error(1, "Length cannot be specified in both hunks and frames");
input_length = parse_number(input_length_hunks_str->second->c_str()) * hunkbytes;
- if (input_length_frames_str != params.end())
+ }
+ else if (input_length_frames_str != params.end())
+ {
input_length = parse_number(input_length_frames_str->second->c_str()) * framebytes;
- if (input_start + input_length < input_end)
- input_end = input_start + input_length;
+ }
+
+ // check that offsets are within input
+ if (input_start >= logical_size)
+ report_error(1, "Input start offset is beyond end of input");
+ if (input_length && ((input_start + *input_length) > logical_size))
+ report_error(1, "Input length is larger than available input from start offset");
+ return std::make_pair(input_start, input_length ? (input_start + *input_length) : logical_size);
+}
+
+static std::tuple<uint64_t, uint64_t, uint64_t> parse_input_start_end(
+ const parameters_map &params,
+ util::random_read &input_file,
+ uint32_t hunkbytes,
+ uint32_t framebytes)
+{
+ uint64_t input_size = 0;
+ const std::error_condition err = input_file.length(input_size);
+ if (err)
+ report_error(1, "Error getting size of input file: %s", err.message());
+ const auto [input_start, input_end] = parse_input_start_end(params, input_size, hunkbytes, framebytes);
+ return std::make_tuple(input_size, input_start, input_end);
}
@@ -1068,13 +1268,13 @@ static void parse_input_start_end(const parameters_t &params, uint64_t logical_s
// unless --force is specified
//-------------------------------------------------
-static void check_existing_output_file(const parameters_t &params, const char *filename)
+static void check_existing_output_file(const parameters_map &params, std::string_view filename)
{
if (params.find(OPTION_OUTPUT_FORCE) == params.end())
{
util::core_file::ptr file;
- osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ, file);
- if (filerr == osd_file::error::NONE)
+ std::error_condition const filerr = util::core_file::open(filename, OPEN_FLAG_READ, file);
+ if (!filerr)
{
file.reset();
report_error(1, "Error: file already exists (%s)\nUse --force (or -f) to force overwriting", filename);
@@ -1088,22 +1288,23 @@ static void check_existing_output_file(const parameters_t &params, const char *f
// standard set of output CHD parameters
//-------------------------------------------------
-static std::string *parse_output_chd_parameters(const parameters_t &params, chd_file &output_parent_chd)
+static const std::string *parse_output_chd_parameters(const parameters_map &params, chd_file &output_parent_chd)
{
// process output parent file
- auto output_chd_parent_str = params.find(OPTION_OUTPUT_PARENT);
+ const auto output_chd_parent_str = params.find(OPTION_OUTPUT_PARENT);
if (output_chd_parent_str != params.end())
{
- chd_error err = output_parent_chd.open(output_chd_parent_str->second->c_str());
- if (err != CHDERR_NONE)
- report_error(1, "Error opening parent CHD file (%s): %s", output_chd_parent_str->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = output_parent_chd.open(*output_chd_parent_str->second);
+ if (err)
+ report_error(1, "Error opening parent CHD file (%s): %s", *output_chd_parent_str->second, err.message());
}
// process output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- check_existing_output_file(params, output_chd_str->second->c_str());
- return (output_chd_str != params.end()) ? output_chd_str->second : nullptr;
+ const auto output_chd_str = params.find(OPTION_OUTPUT);
+ if (output_chd_str == params.end())
+ return nullptr;
+ check_existing_output_file(params, *output_chd_str->second);
+ return output_chd_str->second;
}
@@ -1112,17 +1313,32 @@ static std::string *parse_output_chd_parameters(const parameters_t &params, chd_
// parameter in a standard way
//-------------------------------------------------
-static void parse_hunk_size(const parameters_t &params, uint32_t required_granularity, uint32_t &hunk_size)
+static uint32_t parse_hunk_size(
+ const parameters_map &params,
+ const chd_file &output_parent,
+ uint32_t required_granularity,
+ uint32_t default_size)
{
- auto hunk_size_str = params.find(OPTION_HUNK_SIZE);
+ const auto hunk_size_str = params.find(OPTION_HUNK_SIZE);
+ uint32_t hunk_size = default_size;
if (hunk_size_str != params.end())
{
hunk_size = parse_number(hunk_size_str->second->c_str());
- if (hunk_size < 16 || hunk_size > 1024 * 1024)
- report_error(1, "Invalid hunk size");
- if (hunk_size % required_granularity != 0)
- report_error(1, "Hunk size is not an even multiple of %d", required_granularity);
+ if (output_parent.opened() && (output_parent.hunk_bytes() != hunk_size))
+ report_error(1, "Specified hunk size %u bytes does not match output parent CHD hunk size %u bytes", hunk_size, output_parent.hunk_bytes());
+ if (hunk_size < HUNK_SIZE_MIN)
+ report_error(1, "Invalid hunk size (minimum %u)", HUNK_SIZE_MIN);
+ if (hunk_size > HUNK_SIZE_MAX)
+ report_error(1, "Invalid hunk size (maximum %u)", HUNK_SIZE_MAX);
}
+ else if (output_parent.opened())
+ {
+ hunk_size = output_parent.hunk_bytes();
+ }
+
+ if (hunk_size % required_granularity)
+ report_error(1, "Hunk size %u bytes is not a whole multiple of %u", hunk_size, required_granularity);
+ return hunk_size;
}
@@ -1131,15 +1347,18 @@ static void parse_hunk_size(const parameters_t &params, uint32_t required_granul
// compression parameter string
//-------------------------------------------------
-static void parse_compression(const parameters_t &params, chd_codec_type compression[4])
+static void parse_compression(const parameters_map &params, const std::array<chd_codec_type, 4> &defaults, const chd_file &output_parent, chd_codec_type compression[4])
{
+ // TODO: should we default to the same compression as the output parent?
+ std::copy(std::begin(defaults), std::end(defaults), compression);
+
// see if anything was specified
- auto compression_str = params.find(OPTION_COMPRESSION);
+ const auto compression_str = params.find(OPTION_COMPRESSION);
if (compression_str == params.end())
return;
// special case: 'none'
- if (compression_str->second->compare("none")==0)
+ if (*compression_str->second == "none")
{
compression[0] = compression[1] = compression[2] = compression[3] = CHD_CODEC_NONE;
return;
@@ -1151,10 +1370,10 @@ static void parse_compression(const parameters_t &params, chd_codec_type compres
{
std::string name(*compression_str->second, start, (end == -1) ? -1 : end - start);
if (name.length() != 4)
- report_error(1, "Invalid compressor '%s' specified", name.c_str());
+ report_error(1, "Invalid compressor '%s' specified", name);
chd_codec_type type = CHD_MAKE_TAG(name[0], name[1], name[2], name[3]);
if (!chd_codec_list::codec_exists(type))
- report_error(1, "Invalid compressor '%s' specified", name.c_str());
+ report_error(1, "Invalid compressor '%s' specified", name);
compression[index++] = type;
if (end == -1)
break;
@@ -1172,7 +1391,7 @@ static void parse_compression(const parameters_t &params, chd_codec_type compres
// command
//-------------------------------------------------
-static void parse_numprocessors(const parameters_t &params)
+static void parse_numprocessors(const parameters_map &params)
{
auto numprocessors_str = params.find(OPTION_NUMPROCESSORS);
if (numprocessors_str == params.end())
@@ -1218,6 +1437,49 @@ static std::string compression_string(chd_codec_type compression[4])
//-------------------------------------------------
+// open_input_file - open input file if specified
+//-------------------------------------------------
+
+static std::pair<util::core_file::ptr, const std::string *> open_input_file(const parameters_map &params)
+{
+ const auto path = params.find(OPTION_INPUT);
+ if (path == params.end())
+ return std::make_pair(nullptr, nullptr);
+
+ util::core_file::ptr file;
+ const std::error_condition err = util::core_file::open(*path->second, OPEN_FLAG_READ, file);
+ if (err)
+ report_error(1, "Unable to open input file (%s): %s", *path->second, err.message());
+
+ return std::make_pair(std::move(file), path->second);
+}
+
+
+//-------------------------------------------------
+// create_output_chd - open output CHD with or
+// without parent CHD file
+//-------------------------------------------------
+
+static void create_output_chd(
+ chd_file_compressor &compressor,
+ std::string_view path,
+ uint64_t logical_size,
+ uint32_t hunk_size,
+ uint32_t unit_size,
+ const chd_codec_type (&compression)[4],
+ chd_file &parent)
+{
+ std::error_condition err;
+ if (parent.opened())
+ err = compressor.create(path, logical_size, hunk_size, compression, parent);
+ else
+ err = compressor.create(path, logical_size, hunk_size, unit_size, compression);
+ if (err)
+ report_error(1, "Error creating CHD file (%s): %s", path, err.message());
+}
+
+
+//-------------------------------------------------
// compress_common - standard compression loop
//-------------------------------------------------
@@ -1228,16 +1490,16 @@ static void compress_common(chd_file_compressor &chd)
// loop until done
double complete, ratio;
- chd_error err;
- while ((err = chd.compress_continue(complete, ratio)) == CHDERR_WALKING_PARENT || err == CHDERR_COMPRESSING)
- if (err == CHDERR_WALKING_PARENT)
+ std::error_condition err;
+ while ((err = chd.compress_continue(complete, ratio)) == chd_file::error::WALKING_PARENT || err == chd_file::error::COMPRESSING)
+ if (err == chd_file::error::WALKING_PARENT)
progress(false, "Examining parent, %.1f%% complete... \r", 100.0 * complete);
else
progress(false, "Compressing, %.1f%% complete... (ratio=%.1f%%) \r", 100.0 * complete, 100.0 * ratio);
// handle errors
- if (err != CHDERR_NONE)
- report_error(1, "Error during compression: %-40s", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error during compression: %-40s", err.message());
// final progress update
progress(true, "Compression complete ... final ratio = %.1f%% \n", 100.0 * ratio);
@@ -1249,81 +1511,39 @@ 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_track_info &info, const char *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 outputoffs)
{
if (mode == MODE_GDI)
{
- int mode = 0, size = 2048;
-
- switch (info.trktype)
- {
- case CD_TRACK_MODE1:
- mode = 4;
- size = 2048;
- break;
-
- case CD_TRACK_MODE1_RAW:
- mode = 4;
- size = 2352;
- break;
-
- case CD_TRACK_MODE2:
- mode = 4;
- size = 2336;
- break;
-
- case CD_TRACK_MODE2_FORM1:
- mode = 4;
- size = 2048;
- break;
-
- case CD_TRACK_MODE2_FORM2:
- mode = 4;
- size = 2324;
- break;
-
- case CD_TRACK_MODE2_FORM_MIX:
- mode = 4;
- size = 2336;
- break;
-
- case CD_TRACK_MODE2_RAW:
- mode = 4;
- size = 2352;
- break;
-
- case CD_TRACK_AUDIO:
- mode = 0;
- size = 2352;
- break;
- }
- bool needquote = strchr(filename, ' ') != nullptr;
- file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, mode, size, needquote?"\"":"", filename, needquote?"\"":"", discoffs);
+ 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, tracktype, info.datasize, quotestr, filename, quotestr, outputoffs);
}
else if (mode == MODE_CUEBIN)
{
- // first track specifies the file
- if (tracknum == 0)
+ // specify a new file when writing to the beginning of a file
+ if (outputoffs == 0)
file.printf("FILE \"%s\" BINARY\n", filename);
// determine submode
std::string tempstr;
switch (info.trktype)
{
- case CD_TRACK_MODE1:
- case CD_TRACK_MODE1_RAW:
+ case cdrom_file::CD_TRACK_MODE1:
+ case cdrom_file::CD_TRACK_MODE1_RAW:
tempstr = string_format("MODE1/%04d", info.datasize);
break;
- case CD_TRACK_MODE2:
- case CD_TRACK_MODE2_FORM1:
- case CD_TRACK_MODE2_FORM2:
- case CD_TRACK_MODE2_FORM_MIX:
- case CD_TRACK_MODE2_RAW:
+ case cdrom_file::CD_TRACK_MODE2:
+ case cdrom_file::CD_TRACK_MODE2_FORM1:
+ case cdrom_file::CD_TRACK_MODE2_FORM2:
+ case cdrom_file::CD_TRACK_MODE2_FORM_MIX:
+ case cdrom_file::CD_TRACK_MODE2_RAW:
tempstr = string_format("MODE2/%04d", info.datasize);
break;
- case CD_TRACK_AUDIO:
+ case cdrom_file::CD_TRACK_AUDIO:
tempstr.assign("AUDIO");
break;
}
@@ -1356,22 +1576,19 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const
// non-CUE mode
else if (mode == MODE_NORMAL)
{
- // header on the first track
- if (tracknum == 0)
- file.printf("CD_ROM\n\n\n");
file.printf("// Track %d\n", tracknum + 1);
// write out the track type
std::string modesubmode;
- if (info.subtype != CD_SUB_NONE)
- modesubmode = string_format("%s %s", cdrom_get_type_string(info.trktype), cdrom_get_subtype_string(info.subtype));
+ if (info.subtype != cdrom_file::CD_SUB_NONE)
+ modesubmode = string_format("%s %s", cdrom_file::get_type_string(info.trktype), cdrom_file::get_subtype_string(info.subtype));
else
- modesubmode = string_format("%s", cdrom_get_type_string(info.trktype));
+ modesubmode = string_format("%s", cdrom_file::get_type_string(info.trktype));
file.printf("TRACK %s\n", modesubmode);
// write out the attributes
file.printf("NO COPY\n");
- if (info.trktype == CD_TRACK_AUDIO)
+ if (info.trktype == cdrom_file::CD_TRACK_AUDIO)
{
file.printf("NO PRE_EMPHASIS\n");
file.printf("TWO_CHANNEL_AUDIO\n");
@@ -1381,11 +1598,10 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const
if (info.pregap > 0)
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 (outputoffs == 0)
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(outputoffs), msf_string_from_frames(info.frames), info.frames * (info.datasize + info.subsize));
// tracks with pregaps get a START marker too
if (info.pregap > 0)
@@ -1401,7 +1617,7 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const
// a drive image
//-------------------------------------------------
-static void do_info(parameters_t &params)
+static void do_info(parameters_map &params)
{
bool verbose = params.find(OPTION_VERBOSE) != params.end();
// parse out input files
@@ -1410,34 +1626,36 @@ static void do_info(parameters_t &params)
parse_input_chd_parameters(params, input_chd, input_parent_chd);
// print filename and version
- printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
- printf("File Version: %d\n", input_chd.version());
+ util::stream_format(std::cout, "Input file: %s\n", *params.find(OPTION_INPUT)->second);
+ util::stream_format(std::cout, "File Version: %d\n", input_chd.version());
if (input_chd.version() < 3)
report_error(1, "Unsupported version (%d); use an older chdman to upgrade to version 3 or later", input_chd.version());
// output cmpression and size information
chd_codec_type compression[4] = { input_chd.compression(0), input_chd.compression(1), input_chd.compression(2), input_chd.compression(3) };
- printf("Logical size: %s bytes\n", big_int_string(input_chd.logical_bytes()).c_str());
- printf("Hunk Size: %s bytes\n", big_int_string(input_chd.hunk_bytes()).c_str());
- printf("Total Hunks: %s\n", big_int_string(input_chd.hunk_count()).c_str());
- printf("Unit Size: %s bytes\n", big_int_string(input_chd.unit_bytes()).c_str());
- printf("Total Units: %s\n", big_int_string(input_chd.unit_count()).c_str());
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("CHD size: %s bytes\n", big_int_string(static_cast<util::core_file &>(input_chd).size()).c_str());
+ uint64_t filesize = 0;
+ input_chd.file().length(filesize);
+ util::stream_format(std::cout, "Logical size: %s bytes\n", big_int_string(input_chd.logical_bytes()));
+ util::stream_format(std::cout, "Hunk Size: %s bytes\n", big_int_string(input_chd.hunk_bytes()));
+ util::stream_format(std::cout, "Total Hunks: %s\n", big_int_string(input_chd.hunk_count()));
+ util::stream_format(std::cout, "Unit Size: %s bytes\n", big_int_string(input_chd.unit_bytes()));
+ util::stream_format(std::cout, "Total Units: %s\n", big_int_string(input_chd.unit_count()));
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "CHD size: %s bytes\n", big_int_string(filesize));
if (compression[0] != CHD_CODEC_NONE)
- printf("Ratio: %.1f%%\n", 100.0 * double(static_cast<util::core_file &>(input_chd).size()) / double(input_chd.logical_bytes()));
+ util::stream_format(std::cout, "Ratio: %.1f%%\n", 100.0 * double(filesize) / double(input_chd.logical_bytes()));
- // add SHA1 output
+ // add SHA-1 output
util::sha1_t overall = input_chd.sha1();
if (overall != util::sha1_t::null)
{
- printf("SHA1: %s\n", overall.as_string().c_str());
+ util::stream_format(std::cout, "SHA1: %s\n", overall.as_string());
if (input_chd.version() >= 4)
- printf("Data SHA1: %s\n", input_chd.raw_sha1().as_string().c_str());
+ util::stream_format(std::cout, "Data SHA1: %s\n", input_chd.raw_sha1().as_string());
}
util::sha1_t parent = input_chd.parent_sha1();
if (parent != util::sha1_t::null)
- printf("Parent SHA1: %s\n", parent.as_string().c_str());
+ util::stream_format(std::cout, "Parent SHA1: %s\n", parent.as_string());
// print out metadata
std::vector<uint8_t> buffer;
@@ -1447,8 +1665,8 @@ static void do_info(parameters_t &params)
// get the indexed metadata item; stop when we hit an error
chd_metadata_tag metatag;
uint8_t metaflags;
- chd_error err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index, buffer, metatag, metaflags);
- if (err != CHDERR_NONE)
+ std::error_condition err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index, buffer, metatag, metaflags);
+ if (err)
break;
// determine our index
@@ -1470,18 +1688,18 @@ static void do_info(parameters_t &params)
// print either a string representation or a hex representation of the tag
if (isprint((metatag >> 24) & 0xff) && isprint((metatag >> 16) & 0xff) && isprint((metatag >> 8) & 0xff) && isprint(metatag & 0xff))
- printf("Metadata: Tag='%c%c%c%c' Index=%d Length=%d bytes\n", (metatag >> 24) & 0xff, (metatag >> 16) & 0xff, (metatag >> 8) & 0xff, metatag & 0xff, metaindex, int(buffer.size()));
+ util::stream_format(std::cout, "Metadata: Tag='%c%c%c%c' Index=%d Length=%d bytes\n", (metatag >> 24) & 0xff, (metatag >> 16) & 0xff, (metatag >> 8) & 0xff, metatag & 0xff, metaindex, buffer.size());
else
- printf("Metadata: Tag=%08x Index=%d Length=%d bytes\n", metatag, metaindex, int(buffer.size()));
- printf(" ");
+ util::stream_format(std::cout, "Metadata: Tag=%08x Index=%d Length=%d bytes\n", metatag, metaindex, buffer.size());
+ util::stream_format(std::cout, " ");
uint32_t count = buffer.size();
// limit output to 60 characters of metadata if not verbose
if (!verbose)
count = std::min(60U, count);
for (int chnum = 0; chnum < count; chnum++)
- printf("%c", isprint(uint8_t(buffer[chnum])) ? buffer[chnum] : '.');
- printf("\n");
+ util::stream_format(std::cout, "%c", isprint(uint8_t(buffer[chnum])) ? buffer[chnum] : '.');
+ util::stream_format(std::cout, "\n");
}
// print compression stats if verbose
@@ -1493,9 +1711,9 @@ static void do_info(parameters_t &params)
// get info on this hunk
chd_codec_type codec;
uint32_t compbytes;
- chd_error err = input_chd.hunk_info(hunknum, codec, compbytes);
- if (err != CHDERR_NONE)
- report_error(1, "Error getting info on hunk %d: %s", hunknum, chd_file::error_string(err));
+ std::error_condition err = input_chd.hunk_info(hunknum, codec, compbytes);
+ if (err)
+ report_error(1, "Error getting info on hunk %d: %s", hunknum, err.message());
// decode into our data
if (codec > CHD_CODEC_MINI)
@@ -1505,18 +1723,18 @@ static void do_info(parameters_t &params)
codec = CHD_CODEC_MINI + 1 + comptype;
break;
}
- if (codec > ARRAY_LENGTH(compression_types))
- codec = ARRAY_LENGTH(compression_types) - 1;
+ if (codec >= std::size(compression_types))
+ codec = std::size(compression_types) - 1;
// count stats
compression_types[codec]++;
}
// output the stats
- printf("\n");
- printf(" Hunks Percent Name\n");
- printf("---------- ------- ------------------------------------\n");
- for (int comptype = 0; comptype < ARRAY_LENGTH(compression_types); comptype++)
+ util::stream_format(std::cout, "\n");
+ util::stream_format(std::cout, " Hunks Percent Name\n");
+ util::stream_format(std::cout, "---------- ------- ------------------------------------\n");
+ for (int comptype = 0; comptype < std::size(compression_types); comptype++)
if (compression_types[comptype] != 0)
{
// determine the name
@@ -1535,8 +1753,8 @@ static void do_info(parameters_t &params)
}
// output the stats
- printf("%10s %5.1f%% %-40s\n",
- big_int_string(compression_types[comptype]).c_str(),
+ util::stream_format(std::cout, "%10s %5.1f%% %-40s\n",
+ big_int_string(compression_types[comptype]),
100.0 * double(compression_types[comptype]) / double(input_chd.hunk_count()),
name);
}
@@ -1545,17 +1763,17 @@ static void do_info(parameters_t &params)
//-------------------------------------------------
-// do_verify - validate the SHA1 on a CHD
+// do_verify - validate the SHA-1 on a CHD
//-------------------------------------------------
-static void do_verify(parameters_t &params)
+static void do_verify(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
chd_file input_chd;
parse_input_chd_parameters(params, input_chd, input_parent_chd);
- // only makes sense for compressed CHDs with valid SHA1's
+ // only makes sense for compressed CHDs with valid SHA-1's
if (!input_chd.compressed())
report_error(0, "No verification to be done; CHD is uncompressed");
util::sha1_t raw_sha1 = (input_chd.version() <= 3) ? input_chd.sha1() : input_chd.raw_sha1();
@@ -1573,9 +1791,9 @@ static void do_verify(parameters_t &params)
// determine how much to read
uint32_t bytes_to_read = (std::min<uint64_t>)(buffer.size(), input_chd.logical_bytes() - offset);
- chd_error err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
- if (err != CHDERR_NONE)
- report_error(1, "Error reading CHD file (%s): %s", params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
+ if (err)
+ report_error(1, "Error reading CHD file (%s): %s", *params.find(OPTION_INPUT)->second, err.message());
// add to the checksum
rawsha1.append(&buffer[0], bytes_to_read);
@@ -1586,36 +1804,41 @@ static void do_verify(parameters_t &params)
// finish up
if (raw_sha1 != computed_sha1)
{
- fprintf(stderr, "Error: Raw SHA1 in header = %s\n", raw_sha1.as_string().c_str());
- fprintf(stderr, " actual SHA1 = %s\n", computed_sha1.as_string().c_str());
+ util::stream_format(std::cerr, "Error: Raw SHA1 in header = %s\n", raw_sha1.as_string());
+ util::stream_format(std::cerr, " actual SHA1 = %s\n", computed_sha1.as_string());
// fix it if requested; this also fixes the overall one so we don't need to do any more
if (params.find(OPTION_FIX) != params.end())
{
- input_chd.set_raw_sha1(computed_sha1);
- printf("SHA-1 updated to correct value in input CHD\n");
+ std::error_condition err = input_chd.set_raw_sha1(computed_sha1);
+ if (err)
+ report_error(1, "Error updating SHA1: %s", err.message());
+ util::stream_format(std::cout, "SHA1 updated to correct value in input CHD\n");
}
}
else
{
- printf("Raw SHA1 verification successful!\n");
+ util::stream_format(std::cout, "Raw SHA1 verification successful!\n");
// now include the metadata for >= v4
if (input_chd.version() >= 4)
{
util::sha1_t computed_overall_sha1 = input_chd.compute_overall_sha1(computed_sha1);
if (input_chd.sha1() == computed_overall_sha1)
- printf("Overall SHA1 verification successful!\n");
+ util::stream_format(std::cout, "Overall SHA1 verification successful!\n");
else
{
- fprintf(stderr, "Error: Overall SHA1 in header = %s\n", input_chd.sha1().as_string().c_str());
- fprintf(stderr, " actual SHA1 = %s\n", computed_overall_sha1.as_string().c_str());
+ util::stream_format(std::cerr, "Error: Overall SHA1 in header = %s\n", input_chd.sha1().as_string());
+ util::stream_format(std::cerr, " actual SHA1 = %s\n", computed_overall_sha1.as_string());
// fix it if requested
if (params.find(OPTION_FIX) != params.end())
{
input_chd.set_raw_sha1(computed_sha1);
- printf("SHA-1 updated to correct value in input CHD\n");
+ std::error_condition err = input_chd.set_raw_sha1(computed_sha1);
+ if (err)
+ report_error(1, "Error updating SHA1: %s", err.message());
+ util::stream_format(std::cout, "SHA1 updated to correct value in input CHD\n");
}
}
}
@@ -1628,25 +1851,14 @@ static void do_verify(parameters_t &params)
// image from a raw file
//-------------------------------------------------
-static void do_create_raw(parameters_t &params)
+static void do_create_raw(parameters_map &params)
{
// process input file
- util::core_file::ptr input_file;
- auto input_file_str = params.find(OPTION_INPUT);
- if (input_file_str != params.end())
- {
- osd_file::error filerr = util::core_file::open(*input_file_str->second, OPEN_FLAG_READ, input_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", input_file_str->second->c_str());
- }
+ auto [input_file, input_file_str] = open_input_file(params);
// process output CHD
chd_file output_parent;
- std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
-
- // process hunk size
- uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : 0;
- parse_hunk_size(params, 1, hunk_size);
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
// process unit size
uint32_t unit_size = output_parent.opened() ? output_parent.unit_bytes() : 0;
@@ -1654,49 +1866,49 @@ static void do_create_raw(parameters_t &params)
if (unit_size_str != params.end())
{
unit_size = parse_number(unit_size_str->second->c_str());
- if (hunk_size % unit_size != 0)
- report_error(1, "Unit size is not an even divisor of the hunk size");
+ if (output_parent.opened() && (output_parent.unit_bytes() != unit_size))
+ report_error(1, "Specified unit size %u bytes does not match output parent CHD unit size %u bytes", unit_size, output_parent.unit_bytes());
+ }
+ else if (!output_parent.opened())
+ {
+ report_error(1, "Unit size must be specified if no output parent CHD is supplied");
}
+ // process hunk size
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, unit_size, std::max((4096 / unit_size) * unit_size, unit_size));
+
// process input start/end (needs to know hunk_size)
- uint64_t input_start;
- uint64_t input_end;
- parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
+ const auto [input_size, input_start, input_end] = parse_input_start_end(params, *input_file, hunk_size, hunk_size);
+ if ((input_end - input_start) % unit_size)
+ report_error(1, "Data size %s is not divisible by unit size %d", big_int_string(input_end - input_start), unit_size);
// process compression
chd_codec_type compression[4];
- memcpy(compression, s_default_raw_compression, sizeof(compression));
- parse_compression(params, compression);
+ parse_compression(params, s_default_raw_compression, output_parent, compression);
// process numprocessors
parse_numprocessors(params);
// print some info
- printf("Output CHD: %s\n", output_chd_str->c_str());
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
if (output_parent.opened())
- printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
- printf("Input file: %s\n", input_file_str->second->c_str());
- if (input_start != 0 || input_end != input_file->size())
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
+ util::stream_format(std::cout, "Input file: %s\n", *input_file_str);
+ if (input_start != 0 || input_end != input_size)
{
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(input_end - input_start));
}
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
- printf("Logical size: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "Hunk size: %s\n", big_int_string(hunk_size));
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(input_end - input_start));
// catch errors so we can close & delete the output file
try
{
// create the new CHD
- std::unique_ptr<chd_file_compressor> chd(new chd_rawfile_compressor(*input_file, input_start, input_end));
- chd_error err;
- if (output_parent.opened())
- err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, compression, output_parent);
- else
- err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, unit_size, compression);
- if (err != CHDERR_NONE)
- report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
+ auto chd = std::make_unique<chd_rawfile_compressor>(*input_file, input_start, input_end);
+ create_output_chd(*chd, *output_chd_str, input_end - input_start, hunk_size, unit_size, compression, output_parent);
// if we have a parent, copy forward all the metadata
if (output_parent.opened())
@@ -1708,9 +1920,7 @@ static void do_create_raw(parameters_t &params)
catch (...)
{
// delete the output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- osd_file::remove(*output_chd_str->second);
+ osd_file::remove(*output_chd_str);
throw;
}
}
@@ -1721,80 +1931,96 @@ static void do_create_raw(parameters_t &params)
// disk image from a raw file
//-------------------------------------------------
-static void do_create_hd(parameters_t &params)
+static void do_create_hd(parameters_map &params)
{
// process input file
- util::core_file::ptr input_file;
- auto input_file_str = params.find(OPTION_INPUT);
- if (input_file_str != params.end())
- {
- osd_file::error filerr = util::core_file::open(*input_file_str->second, OPEN_FLAG_READ, input_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", input_file_str->second->c_str());
- }
+ auto [input_file, input_file_str] = open_input_file(params);
// process output CHD
chd_file output_parent;
- std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
- // process sectorsize
+ // process template
+ uint32_t cylinders = 0;
+ uint32_t heads = 0;
+ uint32_t sectors = 0;
uint32_t sector_size = output_parent.opened() ? output_parent.unit_bytes() : IDE_SECTOR_SIZE;
+ const auto template_str = params.find(OPTION_TEMPLATE);
+ uint32_t template_id = 0;
+ if (template_str != params.end())
+ {
+ if (output_parent.opened())
+ report_error(1, "Template cannot be used when a parent CHD is supplied");
+ template_id = parse_number(template_str->second->c_str());
+ if (template_id >= std::size(s_hd_templates))
+ report_error(1, "Template '%s' is invalid\n", *template_str->second);
+
+ cylinders = s_hd_templates[template_id].cylinders;
+ heads = s_hd_templates[template_id].heads;
+ sectors = s_hd_templates[template_id].sectors;
+ sector_size = s_hd_templates[template_id].sector_size;
+ }
+
+ // process CHS
+ const auto chs_str = params.find(OPTION_CHS);
+ if (chs_str != params.end())
+ {
+ if (template_str != params.end())
+ report_error(1, "CHS geometry cannot be specified separately when a template is specified");
+ if (output_parent.opened())
+ report_error(1, "CHS geometry cannot be specified when a parent CHD is supplied");
+ if (sscanf(chs_str->second->c_str(), "%u,%u,%u", &cylinders, &heads, &sectors) != 3)
+ report_error(1, "Invalid CHS string; must be of the form <cylinders>,<heads>,<sectors>");
+ }
+
+ // process sector size
auto sectorsize_str = params.find(OPTION_SECTOR_SIZE);
if (sectorsize_str != params.end())
{
- if (output_parent.opened())
- report_error(1, "Sector size does not apply when creating a diff from the parent");
+ if (template_str != params.end())
+ report_error(1, "Sector size cannot be specified separately when a template is specified");
sector_size = parse_number(sectorsize_str->second->c_str());
}
+ if (output_parent.opened() && (output_parent.unit_bytes() != sector_size))
+ report_error(1, "Sector size %u bytes does not match output parent CHD sector size %u bytes", sector_size, output_parent.unit_bytes());
// process hunk size (needs to know sector_size)
- uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : std::max((4096 / sector_size) * sector_size, sector_size);
- parse_hunk_size(params, sector_size, hunk_size);
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, sector_size, std::max((4096 / sector_size) * sector_size, sector_size));
// process input start/end (needs to know hunk_size)
uint64_t filesize = 0;
+ uint64_t input_size = 0;
uint64_t input_start = 0;
uint64_t input_end = 0;
if (input_file)
{
- parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
+ std::tie(input_size, input_start, input_end) = parse_input_start_end(params, *input_file, hunk_size, hunk_size);
filesize = input_end - input_start;
+ if (params.find(OPTION_SIZE) != params.end())
+ report_error(1, "Size cannot be specified when an input file is supplied");
}
else
{
- auto size_str = params.find(OPTION_SIZE);
+ const auto size_str = params.find(OPTION_SIZE);
if (size_str != params.end())
{
- if (sscanf(size_str->second->c_str(), "%" I64FMT"d", &filesize) != 1)
- report_error(1, "Invalid size string");
+ if (sscanf(size_str->second->c_str(), "%" I64FMT"u", &filesize) != 1)
+ report_error(1, "Invalid size specified");
}
+ if ((params.find(OPTION_INPUT_START_BYTE) != params.end()) || (params.find(OPTION_INPUT_START_HUNK) != params.end()) ||
+ (params.find(OPTION_INPUT_LENGTH_BYTES) != params.end()) || (params.find(OPTION_INPUT_LENGTH_HUNKS) != params.end()))
+ report_error(1, "Input start/length cannot be specified when no input file is supplied");
}
// process compression
chd_codec_type compression[4];
- memcpy(compression, s_default_hd_compression, sizeof(compression));
- if (!input_file)
- compression[0] = compression[1] = compression[2] = compression[3] = CHD_CODEC_NONE;
- parse_compression(params, compression);
- if (!input_file && compression[0] != CHD_CODEC_NONE)
- report_error(1, "Blank hard disks must be uncompressed");
+ parse_compression(params, input_file ? s_default_hd_compression : s_no_compression, output_parent, compression);
+ if (!input_file && (compression[0] != CHD_CODEC_NONE))
+ report_error(1, "Blank hard disk images must be uncompressed");
// process numprocessors
parse_numprocessors(params);
- // process chs
- uint32_t cylinders = 0;
- uint32_t heads = 0;
- uint32_t sectors = 0;
- auto chs_str = params.find(OPTION_CHS);
- if (chs_str != params.end())
- {
- if (output_parent.opened())
- report_error(1, "CHS does not apply when creating a diff from the parent");
- if (sscanf(chs_str->second->c_str(), "%d,%d,%d", &cylinders, &heads, &sectors) != 3)
- report_error(1, "Invalid CHS string; must be of the form <cylinders>,<heads>,<sectors>");
- }
-
// process ident
std::vector<uint8_t> identdata;
if (output_parent.opened())
@@ -1803,110 +2029,92 @@ static void do_create_hd(parameters_t &params)
if (ident_str != params.end())
{
// load the file
- osd_file::error filerr = util::core_file::load(ident_str->second->c_str(), identdata);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Error reading ident file (%s)", ident_str->second->c_str());
+ std::error_condition const filerr = util::core_file::load(*ident_str->second, identdata);
+ if (filerr)
+ report_error(1, "Error reading ident file (%s): %s", *ident_str->second, filerr.message());
// must be at least 14 bytes; extract CHS data from there
if (identdata.size() < 14)
- report_error(1, "Ident file '%s' is invalid (too short)", ident_str->second->c_str());
- cylinders = (identdata[3] << 8) | identdata[2];
- heads = (identdata[7] << 8) | identdata[6];
- sectors = (identdata[13] << 8) | identdata[12];
+ report_error(1, "Ident file '%s' is invalid (too short)", *ident_str->second);
+ cylinders = get_u16le(&identdata[2]);
+ heads = get_u16le(&identdata[6]);
+ sectors = get_u16le(&identdata[12]);
// ignore CHS for > 8GB drives
- if (cylinders * heads * sectors >= 16514064)
+ if (cylinders * heads * sectors >= 16'514'064)
cylinders = 0;
}
- // process template
- auto template_str = params.find(OPTION_TEMPLATE);
- if (template_str != params.end())
- {
- uint32_t id = parse_number(template_str->second->c_str());
-
- if (id >= ARRAY_LENGTH(s_hd_templates))
- report_error(1, "Template '%d' is invalid\n", id);
-
- cylinders = s_hd_templates[id].cylinders;
- heads = s_hd_templates[id].heads;
- sectors = s_hd_templates[id].sectors;
- sector_size = s_hd_templates[id].sector_size;
-
- printf("Template: %s %s\n", s_hd_templates[id].manufacturer, s_hd_templates[id].model);
- }
-
// extract geometry from the parent if we have one
if (output_parent.opened() && cylinders == 0)
{
std::string metadata;
- if (output_parent.read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) != CHDERR_NONE)
+ if (output_parent.read_metadata(HARD_DISK_METADATA_TAG, 0, metadata))
report_error(1, "Unable to find hard disk metadata in parent CHD");
if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &cylinders, &heads, &sectors, &sector_size) != 4)
report_error(1, "Error parsing hard disk metadata in parent CHD");
}
// validate the size
- if (filesize % sector_size != 0)
- report_error(1, "Data size is not divisible by sector size %d", sector_size);
+ if (filesize % sector_size)
+ report_error(1, "Data size %s is not divisible by sector size %u", big_int_string(input_end - input_start), sector_size);
// if no CHS values, try to guess them
if (cylinders == 0)
{
if (!input_file && filesize == 0)
- report_error(1, "Blank hard drives must specify either a length or a set of CHS values");
- guess_chs((input_file_str != params.end()) ? input_file_str->second : nullptr, filesize, sector_size, cylinders, heads, sectors, sector_size);
+ report_error(1, "Length or CHS geometry must be specified when creating a blank hard disk image");
+ guess_chs(input_file_str, filesize, sector_size, cylinders, heads, sectors, sector_size);
}
uint32_t totalsectors = cylinders * heads * sectors;
// print some info
- printf("Output CHD: %s\n", output_chd_str->c_str());
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
if (output_parent.opened())
- printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
if (input_file)
{
- printf("Input file: %s\n", input_file_str->second->c_str());
- if (input_start != 0 || input_end != input_file->size())
+ util::stream_format(std::cout, "Input file: %s\n", *input_file_str);
+ if (input_start != 0 || input_end != input_size)
{
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s\n", big_int_string(filesize).c_str());
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(filesize));
}
}
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("Cylinders: %d\n", cylinders);
- printf("Heads: %d\n", heads);
- printf("Sectors: %d\n", sectors);
- printf("Bytes/sector: %d\n", sector_size);
- printf("Sectors/hunk: %d\n", hunk_size / sector_size);
- printf("Logical size: %s\n", big_int_string(uint64_t(totalsectors) * uint64_t(sector_size)).c_str());
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ if (template_str != params.end())
+ util::stream_format(std::cout, "Template: %s %s\n", s_hd_templates[template_id].manufacturer, s_hd_templates[template_id].model);
+ util::stream_format(std::cout, "Cylinders: %u\n", cylinders);
+ util::stream_format(std::cout, "Heads: %u\n", heads);
+ util::stream_format(std::cout, "Sectors: %u\n", sectors);
+ util::stream_format(std::cout, "Bytes/sector: %u\n", sector_size);
+ util::stream_format(std::cout, "Sectors/hunk: %u\n", hunk_size / sector_size);
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(uint64_t(totalsectors) * uint64_t(sector_size)));
// catch errors so we can close & delete the output file
try
{
// create the new hard drive
std::unique_ptr<chd_file_compressor> chd;
- if (input_file) chd.reset(new chd_rawfile_compressor(*input_file, input_start, input_end));
- else chd.reset(new chd_zero_compressor(input_start, input_end));
- chd_error err;
- if (output_parent.opened())
- err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(sector_size), hunk_size, compression, output_parent);
+ if (input_file)
+ chd.reset(new chd_rawfile_compressor(*input_file, input_start, input_end));
else
- err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(sector_size), hunk_size, sector_size, compression);
- if (err != CHDERR_NONE)
- report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
+ chd.reset(new chd_zero_compressor(input_start, input_end));
+ create_output_chd(*chd, *output_chd_str, uint64_t(totalsectors) * sector_size, hunk_size, sector_size, compression, output_parent);
// add the standard hard disk metadata
- std::string metadata = string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sector_size);
+ std::string metadata = string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sector_size);
+ std::error_condition err;
err = chd->write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding hard disk metadata: %s", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error adding hard disk metadata: %s", err.message());
// write the ident if present
if (!identdata.empty())
{
err = chd->write_metadata(HARD_DISK_IDENT_METADATA_TAG, 0, identdata);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding hard disk metadata: %s", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error adding hard disk metadata: %s", err.message());
}
// compress it generically
@@ -1916,9 +2124,7 @@ static void do_create_hd(parameters_t &params)
catch (...)
{
// delete the output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- osd_file::remove(*output_chd_str->second);
+ osd_file::remove(*output_chd_str);
throw;
}
}
@@ -1929,87 +2135,144 @@ static void do_create_hd(parameters_t &params)
// image from a raw file
//-------------------------------------------------
-static void do_create_cd(parameters_t &params)
+static void do_create_cd(parameters_map &params)
{
// process input file
- chdcd_track_input_info track_info;
- cdrom_toc toc = { 0 };
+ cdrom_file::track_input_info track_info;
+ cdrom_file::toc toc = { 0 };
auto input_file_str = params.find(OPTION_INPUT);
if (input_file_str != params.end())
{
- chd_error err = chdcd_parse_toc(input_file_str->second->c_str(), toc, track_info);
- if (err != CHDERR_NONE)
- report_error(1, "Error parsing input file (%s: %s)\n", input_file_str->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = cdrom_file::parse_toc(*input_file_str->second, toc, track_info);
+ if (err)
+ report_error(1, "Error parsing input file (%s: %s)\n", *input_file_str->second, err.message());
}
// process output CHD
chd_file output_parent;
- std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
// process hunk size
- uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : CD_FRAMES_PER_HUNK * CD_FRAME_SIZE;
- parse_hunk_size(params, CD_FRAME_SIZE, hunk_size);
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, cdrom_file::FRAME_SIZE, cdrom_file::FRAMES_PER_HUNK * cdrom_file::FRAME_SIZE);
+ if (output_parent.opened() && (output_parent.unit_bytes() != cdrom_file::FRAME_SIZE))
+ report_error(1, "Output parent CHD sector size %u bytes does not match CD-ROM frame size %u bytes", output_parent.unit_bytes(), cdrom_file::FRAME_SIZE);
// process compression
chd_codec_type compression[4];
- memcpy(compression, s_default_cd_compression, sizeof(compression));
- parse_compression(params, compression);
+ parse_compression(params, s_default_cd_compression, output_parent, compression);
// process numprocessors
parse_numprocessors(params);
- // pad each track to a 4-frame boundary. cdrom.c will deal with this on the read side
+ // pad each track to a 4-frame boundary. cdrom.cpp will deal with this on the read side
uint32_t origtotalsectors = 0;
uint32_t totalsectors = 0;
for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
{
- cdrom_track_info &trackinfo = toc.tracks[tracknum];
- int padded = (trackinfo.frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
- trackinfo.extraframes = padded * CD_TRACK_PADDING - trackinfo.frames;
+ cdrom_file::track_info &trackinfo = toc.tracks[tracknum];
+ int padded = (trackinfo.frames + cdrom_file::TRACK_PADDING - 1) / cdrom_file::TRACK_PADDING;
+ trackinfo.extraframes = padded * cdrom_file::TRACK_PADDING - trackinfo.frames;
origtotalsectors += trackinfo.frames;
totalsectors += trackinfo.frames + trackinfo.extraframes;
}
// print some info
- printf("Output CHD: %s\n", output_chd_str->c_str());
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
if (output_parent.opened())
- printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
- printf("Input file: %s\n", input_file_str->second->c_str());
- printf("Input tracks: %d\n", toc.numtrks);
- printf("Input length: %s\n", msf_string_from_frames(origtotalsectors).c_str());
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("Logical size: %s\n", big_int_string(uint64_t(totalsectors) * CD_FRAME_SIZE).c_str());
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
+ util::stream_format(std::cout, "Input file: %s\n", *input_file_str->second);
+ util::stream_format(std::cout, "Input tracks: %d\n", toc.numtrks);
+ util::stream_format(std::cout, "Input length: %s\n", msf_string_from_frames(origtotalsectors));
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(uint64_t(totalsectors) * cdrom_file::FRAME_SIZE));
// catch errors so we can close & delete the output file
- chd_cd_compressor *chd = nullptr;
try
{
// create the new CD
- chd = new chd_cd_compressor(toc, track_info);
- chd_error err;
- if (output_parent.opened())
- err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(CD_FRAME_SIZE), hunk_size, compression, output_parent);
- else
- err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(CD_FRAME_SIZE), hunk_size, CD_FRAME_SIZE, compression);
- if (err != CHDERR_NONE)
- report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
+ auto chd = std::make_unique<chd_cd_compressor>(toc, track_info);
+ create_output_chd(*chd, *output_chd_str, uint64_t(totalsectors) * cdrom_file::FRAME_SIZE, hunk_size, cdrom_file::FRAME_SIZE, compression, output_parent);
// add the standard CD metadata; we do this even if we have a parent because it might be different
- err = cdrom_write_metadata(chd, &toc);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding CD metadata: %s", chd_file::error_string(err));
+ const std::error_condition err = cdrom_file::write_metadata(chd.get(), toc);
+ if (err)
+ report_error(1, "Error adding CD metadata: %s", err.message());
// compress it generically
compress_common(*chd);
- delete chd;
}
catch (...)
{
- delete chd;
// delete the output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- osd_file::remove(*output_chd_str->second);
+ osd_file::remove(*output_chd_str);
+ throw;
+ }
+}
+
+
+//-------------------------------------------------
+// do_create_dvd - create a new compressed dvd
+// image from a raw file
+//-------------------------------------------------
+
+static void do_create_dvd(parameters_map &params)
+{
+ // process input file
+ auto [input_file, input_file_str] = open_input_file(params);
+
+ // process output CHD
+ chd_file output_parent;
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
+
+ // process hunk size
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, 2048, 2 * 2048);
+ if (output_parent.opened() && (output_parent.unit_bytes() != 2048))
+ report_error(1, "Output parent CHD sector size %u bytes does not match DVD-ROM sector size 2048 bytes", output_parent.unit_bytes());
+
+ // process input start/end
+ const auto [input_size, input_start, input_end] = parse_input_start_end(params, *input_file, hunk_size, hunk_size);
+ if ((input_end - input_start) % 2048)
+ report_error(1, "Data size %s is not divisible by sector size 2048", big_int_string(input_end - input_start));
+
+ // process compression
+ chd_codec_type compression[4];
+ parse_compression(params, s_default_hd_compression, output_parent, compression); // No reason to be different than HD for compression
+
+ // process numprocessors
+ parse_numprocessors(params);
+
+ // print some info
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
+ if (output_parent.opened())
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
+ util::stream_format(std::cout, "Input file: %s\n", *input_file_str);
+ if (input_start != 0 || input_end != input_size)
+ {
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(input_end - input_start));
+ }
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(input_end - input_start));
+
+ // catch errors so we can close & delete the output file
+ try
+ {
+ // create the new DVD
+ auto chd = std::make_unique<chd_rawfile_compressor>(*input_file, input_start, input_end);
+ create_output_chd(*chd, *output_chd_str, input_end - input_start, hunk_size, 2048, compression, output_parent);
+
+ // add the standard DVD type tag
+ const std::error_condition err = chd->write_metadata(DVD_METADATA_TAG, 0, "");
+ if (err)
+ report_error(1, "Error adding DVD metadata: %s", err.message());
+
+ // compress it generically
+ compress_common(*chd);
+ }
+ catch (...)
+ {
+ // delete the output file
+ osd_file::remove(*output_chd_str);
throw;
}
}
@@ -2020,7 +2283,7 @@ static void do_create_cd(parameters_t &params)
// input AVI file and metadata
//-------------------------------------------------
-static void do_create_ld(parameters_t &params)
+static void do_create_ld(parameters_map &params)
{
// process input file
avi_file::ptr input_file;
@@ -2029,18 +2292,16 @@ static void do_create_ld(parameters_t &params)
{
avi_file::error avierr = avi_file::open(*input_file_str->second, input_file);
if (avierr != avi_file::error::NONE)
- report_error(1, "Error opening AVI file (%s): %s\n", input_file_str->second->c_str(), avi_file::error_string(avierr));
+ report_error(1, "Error opening AVI file (%s): %s\n", *input_file_str->second, avi_file::error_string(avierr));
}
const avi_file::movie_info &aviinfo = input_file->get_movie_info();
// process output CHD
chd_file output_parent;
- std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
// process input start/end
- uint64_t input_start;
- uint64_t input_end;
- parse_input_start_end(params, aviinfo.video_numsamples, 0, 1, input_start, input_end);
+ auto [input_start, input_end] = parse_input_start_end(params, aviinfo.video_numsamples, 0, 1);
// determine parameters of the incoming video stream
avi_info info;
@@ -2065,13 +2326,11 @@ static void do_create_ld(parameters_t &params)
info.bytes_per_frame = avhuff_encoder::raw_data_size(info.width, info.height, info.channels, info.max_samples_per_frame);
// process hunk size
- uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : info.bytes_per_frame;
- parse_hunk_size(params, info.bytes_per_frame, hunk_size);
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, info.bytes_per_frame, info.bytes_per_frame);
// process compression
chd_codec_type compression[4];
- memcpy(compression, s_default_ld_compression, sizeof(compression));
- parse_compression(params, compression);
+ parse_compression(params, s_default_ld_compression, output_parent, compression);
// disable support for uncompressed ones until the extraction code can handle it
if (compression[0] == CHD_CODEC_NONE)
report_error(1, "Uncompressed is not supported");
@@ -2080,42 +2339,36 @@ static void do_create_ld(parameters_t &params)
parse_numprocessors(params);
// print some info
- printf("Output CHD: %s\n", output_chd_str->c_str());
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
if (output_parent.opened())
- printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
- printf("Input file: %s\n", input_file_str->second->c_str());
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
+ util::stream_format(std::cout, "Input file: %s\n", *input_file_str->second);
if (input_start != 0 && input_end != aviinfo.video_numsamples)
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s (%02d:%02d:%02d)\n", big_int_string(input_end - input_start).c_str(),
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s (%02d:%02d:%02d)\n", big_int_string(input_end - input_start),
uint32_t((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million / 60 / 60)),
uint32_t(((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million / 60) % 60)),
uint32_t(((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million) % 60)));
- printf("Frame rate: %d.%06d\n", info.fps_times_1million / 1000000, info.fps_times_1million % 1000000);
- printf("Frame size: %d x %d %s\n", info.width, info.height * (info.interlaced ? 2 : 1), info.interlaced ? "interlaced" : "non-interlaced");
- printf("Audio: %d channels at %d Hz\n", info.channels, info.rate);
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
- printf("Logical size: %s\n", big_int_string(uint64_t(input_end - input_start) * hunk_size).c_str());
+ util::stream_format(std::cout, "Frame rate: %d.%06d\n", info.fps_times_1million / 1000000, info.fps_times_1million % 1000000);
+ util::stream_format(std::cout, "Frame size: %d x %d %s\n", info.width, info.height * (info.interlaced ? 2 : 1), info.interlaced ? "interlaced" : "non-interlaced");
+ util::stream_format(std::cout, "Audio: %d channels at %d Hz\n", info.channels, info.rate);
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "Hunk size: %s\n", big_int_string(hunk_size));
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(uint64_t(input_end - input_start) * hunk_size));
// catch errors so we can close & delete the output file
- chd_avi_compressor *chd = nullptr;
try
{
// create the new CHD
- chd = new chd_avi_compressor(*input_file, info, input_start, input_end);
- chd_error err;
- if (output_parent.opened())
- err = chd->create(output_chd_str->c_str(), uint64_t(input_end - input_start) * hunk_size, hunk_size, compression, output_parent);
- else
- err = chd->create(output_chd_str->c_str(), uint64_t(input_end - input_start) * hunk_size, hunk_size, info.bytes_per_frame, compression);
- if (err != CHDERR_NONE)
- report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
+ auto chd = std::make_unique<chd_avi_compressor>(*input_file, info, input_start, input_end);
+ create_output_chd(*chd, *output_chd_str, uint64_t(input_end - input_start) * hunk_size, hunk_size, info.bytes_per_frame, compression, output_parent);
// write the core A/V metadata
std::string metadata = string_format(AV_METADATA_FORMAT, info.fps_times_1million / 1000000, info.fps_times_1million % 1000000, info.width, info.height, info.interlaced, info.channels, info.rate);
+ std::error_condition err;
err = chd->write_metadata(AV_METADATA_TAG, 0, metadata);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding AV metadata: %s\n", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error adding AV metadata: %s\n", err.message());
// create the compressor and then run it generically
compress_common(*chd);
@@ -2124,29 +2377,58 @@ static void do_create_ld(parameters_t &params)
if (info.height == 524/2 || info.height == 624/2)
{
err = chd->write_metadata(AV_LD_METADATA_TAG, 0, chd->ldframedata(), 0);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding AVLD metadata: %s\n", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error adding AVLD metadata: %s\n", err.message());
}
- delete chd;
}
catch (...)
{
- delete chd;
// delete the output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- osd_file::remove(*output_chd_str->second);
+ osd_file::remove(*output_chd_str);
throw;
}
}
//-------------------------------------------------
+// get_compression_defaults - use CHD metadata to
+// pick the preferred type
+//-------------------------------------------------
+
+static const std::array<chd_codec_type, 4> &get_compression_defaults(chd_file &input_chd)
+{
+ std::error_condition err = input_chd.check_is_hd();
+ if (err == chd_file::error::METADATA_NOT_FOUND)
+ err = input_chd.check_is_dvd();
+ if (!err)
+ return s_default_hd_compression;
+ if (err != chd_file::error::METADATA_NOT_FOUND)
+ throw err;
+
+ err = input_chd.check_is_av();
+ if (!err)
+ return s_default_ld_compression;
+ if (err != chd_file::error::METADATA_NOT_FOUND)
+ throw err;
+
+ err = input_chd.check_is_cd();
+ if (err == chd_file::error::METADATA_NOT_FOUND)
+ err = input_chd.check_is_gd();
+ if (!err)
+ return s_default_cd_compression;
+ if (err != chd_file::error::METADATA_NOT_FOUND)
+ throw err;
+
+ return s_default_raw_compression;
+}
+
+
+//-------------------------------------------------
// do_copy - create a new CHD with data from
// another CHD
//-------------------------------------------------
-static void do_copy(parameters_t &params)
+static void do_copy(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2154,78 +2436,56 @@ static void do_copy(parameters_t &params)
parse_input_chd_parameters(params, input_chd, input_parent_chd);
// parse out input start/end
- uint64_t input_start;
- uint64_t input_end;
- parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes(), input_start, input_end);
+ const auto [input_start, input_end] = parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes());
+ // TODO: should we check that the input range is aligned to the unit size?
// process output CHD
chd_file output_parent;
- std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
+ const auto output_chd_str = parse_output_chd_parameters(params, output_parent);
// process hunk size
- uint32_t hunk_size = input_chd.hunk_bytes();
- parse_hunk_size(params, 1, hunk_size);
- if (hunk_size % input_chd.hunk_bytes() != 0 && input_chd.hunk_bytes() % hunk_size != 0)
- report_error(1, "Hunk size is not an even multiple or divisor of input hunk size");
+ const uint32_t hunk_size = parse_hunk_size(params, output_parent, input_chd.unit_bytes(), input_chd.hunk_bytes());
+ if ((hunk_size % input_chd.hunk_bytes()) && (input_chd.hunk_bytes() % hunk_size)) // TODO: is this check still necessary, or is the unit size check enough?
+ report_error(1, "Hunk size is not a whole multiple or factor of input hunk size");
// process compression; we default to our current preferences using metadata to pick the type
chd_codec_type compression[4];
- {
- std::vector<uint8_t> metadata;
- if (input_chd.read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
- memcpy(compression, s_default_hd_compression, sizeof(compression));
- else if (input_chd.read_metadata(AV_METADATA_TAG, 0, metadata) == CHDERR_NONE)
- memcpy(compression, s_default_ld_compression, sizeof(compression));
- else if (input_chd.read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- input_chd.read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- input_chd.read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE ||
- input_chd.read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- input_chd.read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
- memcpy(compression, s_default_cd_compression, sizeof(compression));
- else
- memcpy(compression, s_default_raw_compression, sizeof(compression));
- }
- parse_compression(params, compression);
+ parse_compression(params, get_compression_defaults(input_chd), output_parent, compression);
// process numprocessors
parse_numprocessors(params);
// print some info
- printf("Output CHD: %s\n", output_chd_str->c_str());
+ util::stream_format(std::cout, "Output CHD: %s\n", *output_chd_str);
if (output_parent.opened())
- printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
- printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
+ util::stream_format(std::cout, "Parent CHD: %s\n", *params.find(OPTION_OUTPUT_PARENT)->second);
+ util::stream_format(std::cout, "Input CHD: %s\n", *params.find(OPTION_INPUT)->second);
if (input_start != 0 || input_end != input_chd.logical_bytes())
{
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(input_end - input_start));
}
- printf("Compression: %s\n", compression_string(compression).c_str());
- printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
- printf("Logical size: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Compression: %s\n", compression_string(compression));
+ util::stream_format(std::cout, "Hunk size: %s\n", big_int_string(hunk_size));
+ util::stream_format(std::cout, "Logical size: %s\n", big_int_string(input_end - input_start));
// catch errors so we can close & delete the output file
- chd_chdfile_compressor *chd = nullptr;
try
{
// create the new CHD
- chd = new chd_chdfile_compressor(input_chd, input_start, input_end);
- chd_error err;
- if (output_parent.opened())
- err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, compression, output_parent);
- else
- err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, input_chd.unit_bytes(), compression);
- if (err != CHDERR_NONE)
- report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
+ std::unique_ptr<cdrom_file> cdrom; // want this to be unwound after chd
+ auto chd = std::make_unique<chd_chdfile_compressor>(input_chd, input_start, input_end);
+ create_output_chd(*chd, *output_chd_str, input_end - input_start, hunk_size, input_chd.unit_bytes(), compression, output_parent);
// clone all the metadata, upgrading where appropriate
+ std::error_condition err;
std::vector<uint8_t> metadata;
chd_metadata_tag metatag;
uint8_t metaflags;
uint32_t index = 0;
bool redo_cd = false;
bool cdda_swap = false;
- for (err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index++, metadata, metatag, metaflags); err == CHDERR_NONE; err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index++, metadata, metatag, metaflags))
+ for (err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index++, metadata, metatag, metaflags); !err; err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index++, metadata, metatag, metaflags))
{
// if this is an old CD-CHD tag, note that we want to re-do it
if (metatag == CDROM_OLD_METADATA_TAG || metatag == CDROM_TRACK_METADATA_TAG)
@@ -2242,35 +2502,29 @@ static void do_copy(parameters_t &params)
// otherwise, clone it
err = chd->write_metadata(metatag, CHDMETAINDEX_APPEND, metadata, metaflags);
- if (err != CHDERR_NONE)
- report_error(1, "Error writing cloned metadata: %s", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error writing cloned metadata: %s", err.message());
}
// if we need to re-do the CD metadata, do it now
if (redo_cd)
{
- cdrom_file *cdrom = cdrom_open(&input_chd);
- if (cdrom == nullptr)
- report_error(1, "Error upgrading CD metadata");
- const cdrom_toc *toc = cdrom_get_toc(cdrom);
- err = cdrom_write_metadata(chd, toc);
- if (err != CHDERR_NONE)
- report_error(1, "Error writing upgraded CD metadata: %s", chd_file::error_string(err));
+ cdrom = std::make_unique<cdrom_file>(&input_chd);
+ const cdrom_file::toc &toc = cdrom->get_toc();
+ err = cdrom_file::write_metadata(chd.get(), toc);
+ if (err)
+ report_error(1, "Error writing upgraded CD metadata: %s", err.message());
if (cdda_swap)
- chd->m_toc = toc;
+ chd->m_toc = &toc;
}
// compress it generically
compress_common(*chd);
- delete chd;
}
catch (...)
{
- delete chd;
// delete the output file
- auto output_chd_str = params.find(OPTION_OUTPUT);
- if (output_chd_str != params.end())
- osd_file::remove(*output_chd_str->second);
+ osd_file::remove(*output_chd_str);
throw;
}
}
@@ -2281,7 +2535,7 @@ static void do_copy(parameters_t &params)
// CHD image
//-------------------------------------------------
-static void do_extract_raw(parameters_t &params)
+static void do_extract_raw(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2289,22 +2543,20 @@ static void do_extract_raw(parameters_t &params)
parse_input_chd_parameters(params, input_chd, input_parent_chd);
// parse out input start/end
- uint64_t input_start;
- uint64_t input_end;
- parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes(), input_start, input_end);
+ const auto [input_start, input_end] = parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes());
// verify output file doesn't exist
auto output_file_str = params.find(OPTION_OUTPUT);
if (output_file_str != params.end())
- check_existing_output_file(params, output_file_str->second->c_str());
+ check_existing_output_file(params, *output_file_str->second);
// print some info
- printf("Output File: %s\n", output_file_str->second->c_str());
- printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
+ util::stream_format(std::cout, "Output File: %s\n", *output_file_str->second);
+ util::stream_format(std::cout, "Input CHD: %s\n", *params.find(OPTION_INPUT)->second);
if (input_start != 0 || input_end != input_chd.logical_bytes())
{
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(input_end - input_start));
}
// catch errors so we can close & delete the output file
@@ -2312,9 +2564,9 @@ static void do_extract_raw(parameters_t &params)
try
{
// process output file
- osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
+ std::error_condition const filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
+ if (filerr)
+ report_error(1, "Unable to open file (%s): %s", *output_file_str->second, filerr.message());
// copy all data
std::vector<uint8_t> buffer((TEMP_BUFFER_SIZE / input_chd.hunk_bytes()) * input_chd.hunk_bytes());
@@ -2324,14 +2576,14 @@ static void do_extract_raw(parameters_t &params)
// determine how much to read
uint32_t bytes_to_read = (std::min<uint64_t>)(buffer.size(), input_end - offset);
- chd_error err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
- if (err != CHDERR_NONE)
- report_error(1, "Error reading CHD file (%s): %s", params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err));
+ std::error_condition err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
+ if (err)
+ report_error(1, "Error reading CHD file (%s): %s", *params.find(OPTION_INPUT)->second, err.message());
// write to the output
- uint32_t count = output_file->write(&buffer[0], bytes_to_read);
- if (count != bytes_to_read)
- report_error(1, "Error writing to file; check disk space (%s)", output_file_str->second->c_str());
+ auto const [writerr, count] = write(*output_file, &buffer[0], bytes_to_read);
+ if (writerr)
+ report_error(1, "Error writing to file; check disk space (%s)", *output_file_str->second);
// advance
offset += bytes_to_read;
@@ -2339,7 +2591,7 @@ static void do_extract_raw(parameters_t &params)
// finish up
output_file.reset();
- printf("Extraction complete \n");
+ util::stream_format(std::cout, "Extraction complete \n");
}
catch (...)
{
@@ -2359,7 +2611,7 @@ static void do_extract_raw(parameters_t &params)
// CHD image
//-------------------------------------------------
-static void do_extract_cd(parameters_t &params)
+static void do_extract_cd(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2367,123 +2619,320 @@ static void do_extract_cd(parameters_t &params)
parse_input_chd_parameters(params, input_chd, input_parent_chd);
// further process input file
- cdrom_file *cdrom = cdrom_open(&input_chd);
- if (cdrom == nullptr)
- report_error(1, "Unable to recognize CHD file as a CD");
- const cdrom_toc *toc = cdrom_get_toc(cdrom);
+ cdrom_file *cdrom = new cdrom_file(&input_chd);
+ const cdrom_file::toc &toc = cdrom->get_toc();
// verify output file doesn't exist
auto output_file_str = params.find(OPTION_OUTPUT);
if (output_file_str != params.end())
- check_existing_output_file(params, output_file_str->second->c_str());
+ 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 (core_filename_ends_with(*output_file_str->second, ".cue"))
+ mode = MODE_CUEBIN;
+ else if (core_filename_ends_with(*output_file_str->second, ".gdi"))
+ 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)
+ if (chop != std::string::npos)
default_name.erase(chop, default_name.size());
- char basename[128];
- strncpy(basename, default_name.c_str(), 127);
- default_name.append(".bin");
+
+ // GDIs will always output as split bin
+ bool is_splitbin = mode == MODE_GDI || params.find(OPTION_OUTPUT_SPLITBIN) != params.end();
+ if (!is_splitbin && cdrom->is_gdrom() && mode == MODE_CUEBIN)
+ {
+ // GD-ROM cue/bin is in Redump format which should always be split by tracks
+ util::stream_format(std::cout, "Warning: --%s is required for this specific combination of input disc type and output format, enabling automatically\n", OPTION_OUTPUT_SPLITBIN);
+ is_splitbin = true;
+ }
+
+ if (is_splitbin)
+ {
+ if (mode == MODE_GDI)
+ {
+ default_name += "%02t";
+ }
+ else
+ {
+ const std::string format = toc.numtrks >= 10 ? "%02t" : "%t";
+ default_name += " (Track " + format + ")";
+ }
+ }
+
+ 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->c_str());
+ chop = output_bin_file_str->find_last_of('.');
+ if (chop != std::string::npos)
+ {
+ 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());
+ }
+ }
+
+ if (output_bin_file_str->find('"') != std::string::npos || output_bin_file_ext.find('"') != std::string::npos)
+ report_error(1, "Output bin filename (%s%s) must not contain quotation marks", *output_bin_file_str, output_bin_file_ext);
// print some info
- printf("Output TOC: %s\n", output_file_str->second->c_str());
- printf("Output Data: %s\n", output_bin_file_str->c_str());
- printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
+ util::stream_format(std::cout, "Input CHD: %s\n", *params.find(OPTION_INPUT)->second);
+ util::stream_format(std::cout, "Output TOC: %s\n", *output_file_str->second);
// catch errors so we can close & delete the output file
+ std::vector<std::string> track_filenames;
util::core_file::ptr output_bin_file;
util::core_file::ptr output_toc_file;
+ std::vector<std::string> output_bin_filenames;
+ std::string trackbin_name;
try
{
- int mode = MODE_NORMAL;
+ // process output file
+ std::error_condition filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, output_toc_file);
+ if (filerr)
+ report_error(1, "Unable to open file (%s): %s", *output_file_str->second, filerr.message());
- if (output_file_str->second->find(".cue") != -1)
+ uint64_t total_bytes = 0;
+ for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
{
- mode = MODE_CUEBIN;
+ // determine total frames
+ total_bytes += toc.tracks[tracknum].frames * (toc.tracks[tracknum].datasize + toc.tracks[tracknum].subsize);
+
+ // generate output filename for each track
+ std::string new_trackbin_name = *output_bin_file_str;
+
+ if (mode == MODE_GDI && toc.tracks[tracknum].trktype == cdrom_file::CD_TRACK_AUDIO)
+ new_trackbin_name += ".raw";
+ else
+ new_trackbin_name += output_bin_file_ext;
+
+ // 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;
+ bool found_track_variable = false;
+
+ while (std::regex_search(new_trackbin_name_itr, new_trackbin_name_end, variable_matches, variables_regex))
+ {
+ // full_match will always have one leading %, so if leading_escape has an even number of %s then
+ // we can know that we're working on an unescaped %
+ const std::string leading_escape = variable_matches[1].str();
+ const std::string full_match = variable_matches[2].str();
+ const std::string format_part = variable_matches[3].str();
+ const std::string format_type = variable_matches[4].str();
+
+ if ((leading_escape.size() % 2) == 0)
+ {
+ std::string replacement;
+
+ if (format_type == "t")
+ {
+ // track number
+ if (is_splitbin)
+ {
+ replacement = util::string_format("%" + format_part + "d", tracknum+1);
+ found_track_variable = true;
+ }
+ }
+ else
+ {
+ util::stream_format(std::cout, "Warning: encountered unknown format value '%s', ignoring\n", format_type);
+ }
+
+ if (!replacement.empty())
+ {
+ // replace all instances of encountered full format token
+ 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
+ }
+
+ if (is_splitbin && !found_track_variable)
+ {
+ report_error(1, "A track number variable (%%t) must be specified in the output bin filename when --%s is enabled\n", OPTION_OUTPUT_SPLITBIN);
+ }
+
+ // verify output BIN file doesn't exist
+ check_existing_output_file(params, filename_formatted);
+
+ // display all new filenames to the user
+ if (track_filenames.empty() || filename_formatted != track_filenames.back())
+ util::stream_format(std::cout, "Output Data: %s\n", filename_formatted);
+
+ track_filenames.push_back(filename_formatted);
}
- else if (output_file_str->second->find(".gdi") != -1)
+
+ // GDI must start with the # of tracks
+ if (mode == MODE_GDI)
{
- mode = MODE_GDI;
+ output_toc_file->printf("%d\n", toc.numtrks);
}
+ else if (mode == MODE_NORMAL)
+ {
+ bool mode1 = false;
+ bool mode2 = false;
+ bool cdda = false;
- // process output file
- osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, output_toc_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
+ for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
+ {
+ switch (toc.tracks[tracknum].trktype)
+ {
+ case cdrom_file::CD_TRACK_MODE1:
+ case cdrom_file::CD_TRACK_MODE1_RAW:
+ mode1 = true;
+ break;
- // 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 != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", output_bin_file_str->c_str());
- }
+ case cdrom_file::CD_TRACK_MODE2:
+ case cdrom_file::CD_TRACK_MODE2_FORM1:
+ case cdrom_file::CD_TRACK_MODE2_FORM2:
+ case cdrom_file::CD_TRACK_MODE2_FORM_MIX:
+ case cdrom_file::CD_TRACK_MODE2_RAW:
+ mode2 = true;
+ break;
- // determine total frames
- uint64_t total_bytes = 0;
- for (int tracknum = 0; tracknum < toc->numtrks; tracknum++)
- total_bytes += toc->tracks[tracknum].frames * (toc->tracks[tracknum].datasize + toc->tracks[tracknum].subsize);
+ case cdrom_file::CD_TRACK_AUDIO:
+ cdda = true;
+ break;
+ }
+ }
- // GDI must start with the # of tracks
- if (mode == MODE_GDI)
+ if (mode2)
+ output_toc_file->printf("CD_ROM_XA\n\n\n");
+ else if (cdda && !mode1)
+ output_toc_file->printf("CD_DA\n\n\n");
+ else
+ output_toc_file->printf("CD_ROM\n\n\n");
+ }
+
+ if (cdrom->is_gdrom() && mode == MODE_CUEBIN)
{
- output_toc_file->printf("%d\n", toc->numtrks);
+ // modify TOC to match Redump cue/bin format as best as possible
+ cdrom_file::toc *trackinfo = (cdrom_file::toc*)&toc;
+
+ // TOSEC GDI-based CHDs have the padframes field set to non-0 where the pregaps for the next track would be
+ const bool has_physical_pregap = trackinfo->tracks[0].padframes == 0;
+
+ for (int tracknum = 1; tracknum < toc.numtrks; tracknum++)
+ {
+ // pgdatasize should never be set in GD-ROMs currently, so if it is set then assume the TOC has proper pregap values
+ if (trackinfo->tracks[tracknum].pgdatasize != 0)
+ break;
+
+ // don't adjust the first track of the single-density and high-density areas
+ if (toc.tracks[tracknum].physframeofs == 45000)
+ continue;
+
+ if (!has_physical_pregap)
+ {
+ // NOTE: This will generate a cue with PREGAP commands instead of INDEX 00 because the pregap data isn't baked into the bins
+ trackinfo->tracks[tracknum].pregap += trackinfo->tracks[tracknum-1].padframes;
+
+ // "type 1" (only one data track in high-density area) and "type 2" (1 data and then the rest of the tracks being audio tracks in high-density area) don't require any adjustments
+ if (tracknum + 1 >= toc.numtrks && toc.tracks[tracknum].trktype != cdrom_file::CD_TRACK_AUDIO)
+ {
+ if (toc.tracks[tracknum-1].trktype != cdrom_file::CD_TRACK_AUDIO)
+ {
+ // "type 3" where the high-density area is just two data tracks
+ // there shouldn't be any pregap in the padframes from the previous track in this case, and the full 3s pregap is baked into the previous track
+ // Only known to be used by Shenmue II JP's discs 2, 3, 4 and Virtua Fighter History & VF4
+ trackinfo->tracks[tracknum-1].padframes += 225;
+
+ trackinfo->tracks[tracknum].pregap += 225;
+ trackinfo->tracks[tracknum].splitframes = 225;
+ trackinfo->tracks[tracknum].pgdatasize = trackinfo->tracks[tracknum].datasize;
+ trackinfo->tracks[tracknum].pgtype = trackinfo->tracks[tracknum].trktype;
+ }
+ else
+ {
+ // "type 3 split" where the first track and last of the high-density area are data tracks and in between is audio tracks
+ // TODO: These 75 frames are actually included at the end of the previous track so should be written
+ // It's currently not possible to format it as expected without hacky code because the 150 pregap for the last track
+ // is sandwiched between these 75 frames and the actual track data.
+ // The 75 frames seems to normally be 0s so this should be ok for now until a use case is found.
+ trackinfo->tracks[tracknum-1].frames -= 75;
+ trackinfo->tracks[tracknum].pregap += 75;
+ }
+ }
+ }
+ else
+ {
+ int curextra = 150; // 00:02:00
+ if (tracknum + 1 >= toc.numtrks && toc.tracks[tracknum].trktype != cdrom_file::CD_TRACK_AUDIO)
+ curextra += 75; // 00:01:00, special case when last track is data
+
+ trackinfo->tracks[tracknum-1].padframes = curextra;
+
+ trackinfo->tracks[tracknum].pregap += curextra;
+ trackinfo->tracks[tracknum].splitframes = curextra;
+ trackinfo->tracks[tracknum].pgdatasize = trackinfo->tracks[tracknum].datasize;
+ trackinfo->tracks[tracknum].pgtype = trackinfo->tracks[tracknum].trktype;
+ }
+ }
}
// iterate over tracks and copy all data
+ uint64_t totaloutputoffs = 0;
uint64_t outputoffs = 0;
uint32_t discoffs = 0;
std::vector<uint8_t> buffer;
- for (int tracknum = 0; tracknum < toc->numtrks; tracknum++)
- {
- std::string trackbin_name(basename);
- if (mode == MODE_GDI)
+ for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
+ {
+ if (track_filenames[tracknum] != trackbin_name)
{
- char temp[11];
- sprintf(temp, "%02d", tracknum+1);
- trackbin_name.append(temp);
- if (toc->tracks[tracknum].trktype == CD_TRACK_AUDIO)
- trackbin_name.append(".raw");
- else
- trackbin_name.append(".bin");
+ totaloutputoffs += outputoffs;
+ outputoffs = 0;
+
+ if (mode != MODE_GDI)
+ discoffs = 0;
output_bin_file.reset();
+ trackbin_name = track_filenames[tracknum];
+
filerr = util::core_file::open(trackbin_name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", trackbin_name.c_str());
+ if (filerr)
+ report_error(1, "Unable to open file (%s): %s", trackbin_name, filerr.message());
- outputoffs = 0;
+ output_bin_filenames.push_back(trackbin_name);
}
- // output the metadata about the track to the TOC file
- const cdrom_track_info &trackinfo = toc->tracks[tracknum];
- if (mode == MODE_GDI)
- {
- output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, core_filename_extract_base(trackbin_name).c_str(), discoffs, outputoffs);
- }
- else
+ if (cdrom->is_gdrom() && mode == MODE_CUEBIN)
{
- output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, core_filename_extract_base(*output_bin_file_str).c_str(), discoffs, outputoffs);
+ if (tracknum == 0)
+ output_toc_file->printf("REM SINGLE-DENSITY AREA\n");
+ else if (toc.tracks[tracknum].physframeofs == 45000)
+ output_toc_file->printf("REM HIGH-DENSITY AREA\n");
}
+ // output the metadata about the track to the TOC file
+ const cdrom_file::track_info &trackinfo = toc.tracks[tracknum];
+ output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(trackbin_name)), discoffs, outputoffs);
+
// 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.
- uint32_t output_frame_size = trackinfo.datasize + ((trackinfo.subtype != CD_SUB_NONE) ? trackinfo.subsize : 0);
- if (trackinfo.subtype != CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI)))
+ uint32_t output_frame_size = trackinfo.datasize + ((trackinfo.subtype != cdrom_file::CD_SUB_NONE) ? trackinfo.subsize : 0);
+ if (trackinfo.subtype != cdrom_file::CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI)))
{
- printf("Warning: Track %d has subcode data. bin/cue and gdi formats cannot contain subcode data and it will be omitted.\n", tracknum+1);
- printf(" : This may affect usage of the output image. Use bin/toc output to keep all data.\n");
+ util::stream_format(std::cout, "Warning: Track %d has subcode data. bin/cue and gdi formats cannot contain subcode data and it will be omitted.\n", tracknum+1);
+ util::stream_format(std::cout, " : This may affect usage of the output image. Use bin/toc output to keep all data.\n");
output_frame_size = trackinfo.datasize;
}
@@ -2492,40 +2941,53 @@ static void do_extract_cd(parameters_t &params)
// now read and output the actual data
uint32_t bufferoffs = 0;
- uint32_t actualframes = trackinfo.frames - trackinfo.padframes;
+ uint32_t actualframes = trackinfo.frames - trackinfo.padframes + trackinfo.splitframes;
for (uint32_t frame = 0; frame < actualframes; frame++)
{
- progress(false, "Extracting, %.1f%% complete... \r", 100.0 * double(outputoffs) / double(total_bytes));
+ progress(false, "Extracting, %.1f%% complete... \r", 100.0 * double(totaloutputoffs + outputoffs) / double(total_bytes));
+
+ int trk, frameofs;
+ if (tracknum > 0 && frame < trackinfo.splitframes)
+ {
+ // pull data from previous track, the reverse of how splitframes is used when making the GD-ROM CHDs
+ trk = tracknum - 1;
+ frameofs = toc.tracks[trk].frames - trackinfo.splitframes + frame;
+ }
+ else
+ {
+ trk = tracknum;
+ frameofs = frame - trackinfo.splitframes;
+ }
// read the data
- cdrom_read_data(cdrom, cdrom_get_track_start_phys(cdrom, tracknum) + frame, &buffer[bufferoffs], trackinfo.trktype, true);
+ cdrom->read_data(cdrom->get_track_start_phys(trk) + frameofs, &buffer[bufferoffs], toc.tracks[trk].trktype, true);
// for CDRWin and GDI audio tracks must be reversed
// in the case of GDI and CHD version < 5 we assuming source CHD image is GDROM so audio tracks is already reversed
- if (((mode == MODE_GDI && input_chd.version() > 4) || (mode == MODE_CUEBIN)) && (trackinfo.trktype == CD_TRACK_AUDIO))
- for (int swapindex = 0; swapindex < trackinfo.datasize; swapindex += 2)
+ if (((mode == MODE_GDI && input_chd.version() > 4) || (mode == MODE_CUEBIN)) && (toc.tracks[trk].trktype == cdrom_file::CD_TRACK_AUDIO))
+ for (int swapindex = 0; swapindex < toc.tracks[trk].datasize; swapindex += 2)
{
uint8_t swaptemp = buffer[bufferoffs + swapindex];
buffer[bufferoffs + swapindex] = buffer[bufferoffs + swapindex + 1];
buffer[bufferoffs + swapindex + 1] = swaptemp;
}
- bufferoffs += trackinfo.datasize;
+ bufferoffs += toc.tracks[trk].datasize;
discoffs++;
// read the subcode data
- if (trackinfo.subtype != CD_SUB_NONE && (mode == MODE_NORMAL))
+ if (toc.tracks[trk].subtype != cdrom_file::CD_SUB_NONE && (mode == MODE_NORMAL))
{
- cdrom_read_subcode(cdrom, cdrom_get_track_start_phys(cdrom, tracknum) + frame, &buffer[bufferoffs], true);
- bufferoffs += trackinfo.subsize;
+ cdrom->read_subcode(cdrom->get_track_start_phys(trk) + frameofs, &buffer[bufferoffs], true);
+ bufferoffs += toc.tracks[trk].subsize;
}
// write it out if we need to
if (bufferoffs == buffer.size() || frame == actualframes - 1)
{
output_bin_file->seek(outputoffs, SEEK_SET);
- uint32_t byteswritten = output_bin_file->write(&buffer[0], bufferoffs);
- if (byteswritten != bufferoffs)
- report_error(1, "Error writing frame %d to file (%s): %s\n", frame, output_file_str->second->c_str(), chd_file::error_string(CHDERR_WRITE_ERROR));
+ auto const [writerr, byteswritten] = write(*output_bin_file, &buffer[0], bufferoffs);
+ if (writerr)
+ report_error(1, "Error writing frame %d to file (%s): %s\n", frame, *output_file_str->second, "Write error");
outputoffs += bufferoffs;
bufferoffs = 0;
}
@@ -2537,26 +2999,29 @@ static void do_extract_cd(parameters_t &params)
// finish up
output_bin_file.reset();
output_toc_file.reset();
- printf("Extraction complete \n");
+ util::stream_format(std::cout, "Extraction complete \n");
}
catch (...)
{
// delete the output files
output_bin_file.reset();
output_toc_file.reset();
- osd_file::remove(*output_bin_file_str);
+ for (auto const &output_bin_filename : output_bin_filenames)
+ osd_file::remove(output_bin_filename);
osd_file::remove(*output_file_str->second);
throw;
}
}
+
+
//-------------------------------------------------
// do_extract_ld - extract an AVI file from a
// CHD image
//-------------------------------------------------
-static void do_extract_ld(parameters_t &params)
+static void do_extract_ld(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2565,8 +3030,8 @@ static void do_extract_ld(parameters_t &params)
// read core metadata
std::string metadata;
- chd_error err = input_chd.read_metadata(AV_METADATA_TAG, 0, metadata);
- if (err != CHDERR_NONE)
+ std::error_condition err = input_chd.read_metadata(AV_METADATA_TAG, 0, metadata);
+ if (err)
report_error(1, "Unable to find A/V metadata in the input CHD");
// parse the metadata
@@ -2594,15 +3059,13 @@ static void do_extract_ld(parameters_t &params)
report_error(1, "Frame size does not match hunk size for this CHD");
// parse out input start/end
- uint64_t input_start;
- uint64_t input_end;
- parse_input_start_end(params, input_chd.hunk_count() / interlace_factor, 0, 1, input_start, input_end);
+ auto [input_start, input_end] = parse_input_start_end(params, input_chd.hunk_count() / interlace_factor, 0, 1);
input_start *= interlace_factor;
input_end *= interlace_factor;
// build up the movie info
avi_file::movie_info info;
- info.video_format = FORMAT_YUY2;
+ info.video_format = avi_file::FORMAT_YUY2;
info.video_timescale = fps_times_1million / interlace_factor;
info.video_sampletime = 1000000;
info.video_width = width;
@@ -2618,15 +3081,15 @@ static void do_extract_ld(parameters_t &params)
// verify output file doesn't exist
auto output_file_str = params.find(OPTION_OUTPUT);
if (output_file_str != params.end())
- check_existing_output_file(params, output_file_str->second->c_str());
+ check_existing_output_file(params, *output_file_str->second);
// print some info
- printf("Output File: %s\n", output_file_str->second->c_str());
- printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
+ util::stream_format(std::cout, "Output File: %s\n", *output_file_str->second);
+ util::stream_format(std::cout, "Input CHD: %s\n", *params.find(OPTION_INPUT)->second);
if (input_start != 0 || input_end != input_chd.hunk_count())
{
- printf("Input start: %s\n", big_int_string(input_start).c_str());
- printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
+ util::stream_format(std::cout, "Input start: %s\n", big_int_string(input_start));
+ util::stream_format(std::cout, "Input length: %s\n", big_int_string(input_end - input_start));
}
// catch errors so we can close & delete the output file
@@ -2636,15 +3099,17 @@ static void do_extract_ld(parameters_t &params)
// process output file
avi_file::error avierr = avi_file::create(*output_file_str->second, info, output_file);
if (avierr != avi_file::error::NONE)
- report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
+ report_error(1, "Unable to open file (%s)", *output_file_str->second);
// create the codec configuration
- avhuff_decompress_config avconfig;
+ avhuff_decoder::config avconfig;
+ bitmap_yuy16 avvideo;
std::vector<int16_t> audio_data[16];
uint32_t actsamples;
+ avconfig.video = &avvideo;
avconfig.maxsamples = max_samples_per_frame;
avconfig.actsamples = &actsamples;
- for (int chnum = 0; chnum < ARRAY_LENGTH(audio_data); chnum++)
+ for (int chnum = 0; chnum < std::size(audio_data); chnum++)
{
audio_data[chnum].resize(std::max(1U,max_samples_per_frame));
avconfig.audio[chnum] = &audio_data[chnum][0];
@@ -2657,15 +3122,16 @@ static void do_extract_ld(parameters_t &params)
progress(framenum == input_start, "Extracting, %.1f%% complete... \r", 100.0 * double(framenum - input_start) / double(input_end - input_start));
// set up the fake bitmap for this frame
- avconfig.video.wrap(&fullbitmap.pix(framenum % interlace_factor), fullbitmap.width(), fullbitmap.height() / interlace_factor, fullbitmap.rowpixels() * interlace_factor);
+ avvideo.wrap(&fullbitmap.pix(framenum % interlace_factor), fullbitmap.width(), fullbitmap.height() / interlace_factor, fullbitmap.rowpixels() * interlace_factor);
input_chd.codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig);
// read the hunk into the buffers
- chd_error err = input_chd.read_hunk(framenum, nullptr);
- if (err != CHDERR_NONE)
+ std::error_condition err = input_chd.codec_process_hunk(framenum);
+ if (err)
{
- uint64_t filepos = static_cast<util::core_file &>(input_chd).tell();
- report_error(1, "Error reading hunk %d at offset %d from CHD file (%s): %s\n", framenum, filepos, params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err));
+ uint64_t filepos = ~uint64_t(0);
+ input_chd.file().tell(filepos);
+ report_error(1, "Error reading hunk %d at offset %d from CHD file (%s): %s\n", framenum, filepos, *params.find(OPTION_INPUT)->second, err.message());
}
// write audio
@@ -2673,7 +3139,7 @@ static void do_extract_ld(parameters_t &params)
{
avi_file::error avierr = output_file->append_sound_samples(chnum, avconfig.audio[chnum], actsamples, 0);
if (avierr != avi_file::error::NONE)
- report_error(1, "Error writing samples for hunk %d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_file::error_string(avierr));
+ report_error(1, "Error writing samples for hunk %d to file (%s): %s\n", framenum, *output_file_str->second, avi_file::error_string(avierr));
}
// write video
@@ -2681,13 +3147,13 @@ static void do_extract_ld(parameters_t &params)
{
avi_file::error avierr = output_file->append_video_frame(fullbitmap);
if (avierr != avi_file::error::NONE)
- report_error(1, "Error writing video for hunk %d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_file::error_string(avierr));
+ report_error(1, "Error writing video for hunk %d to file (%s): %s\n", framenum, *output_file_str->second, avi_file::error_string(avierr));
}
}
// close and return
output_file.reset();
- printf("Extraction complete \n");
+ util::stream_format(std::cout, "Extraction complete \n");
}
catch (...)
{
@@ -2704,7 +3170,7 @@ static void do_extract_ld(parameters_t &params)
// file
//-------------------------------------------------
-static void do_add_metadata(parameters_t &params)
+static void do_add_metadata(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2741,9 +3207,9 @@ static void do_add_metadata(parameters_t &params)
std::vector<uint8_t> file;
if (file_str != params.end())
{
- osd_file::error filerr = util::core_file::load(file_str->second->c_str(), file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Error reading metadata file (%s)", file_str->second->c_str());
+ std::error_condition const filerr = util::core_file::load(*file_str->second, file);
+ if (filerr)
+ report_error(1, "Error reading metadata file (%s): %s", *file_str->second, filerr.message());
}
// make sure we have one or the other
@@ -2758,24 +3224,24 @@ static void do_add_metadata(parameters_t &params)
flags &= ~CHD_MDFLAGS_CHECKSUM;
// print some info
- printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
- printf("Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
- printf("Index: %d\n", index);
+ util::stream_format(std::cout, "Input file: %s\n", *params.find(OPTION_INPUT)->second);
+ util::stream_format(std::cout, "Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
+ util::stream_format(std::cout, "Index: %d\n", index);
if (text_str != params.end())
- printf("Text: %s\n", text.c_str());
+ util::stream_format(std::cout, "Text: %s\n", text);
else
- printf("Data: %s (%d bytes)\n", file_str->second->c_str(), int(file.size()));
+ util::stream_format(std::cout, "Data: %s (%d bytes)\n", *file_str->second, file.size());
// write the metadata
- chd_error err;
+ std::error_condition err;
if (text_str != params.end())
err = input_chd.write_metadata(tag, index, text, flags);
else
err = input_chd.write_metadata(tag, index, file, flags);
- if (err != CHDERR_NONE)
- report_error(1, "Error adding metadata: %s", chd_file::error_string(err));
+ if (err)
+ report_error(1, "Error adding metadata: %s", err.message());
else
- printf("Metadata added\n");
+ util::stream_format(std::cout, "Metadata added\n");
}
@@ -2783,7 +3249,7 @@ static void do_add_metadata(parameters_t &params)
// do_del_metadata - remove metadata from a CHD
//-------------------------------------------------
-static void do_del_metadata(parameters_t &params)
+static void do_del_metadata(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2806,16 +3272,16 @@ static void do_del_metadata(parameters_t &params)
index = atoi(index_str->second->c_str());
// print some info
- printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
- printf("Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
- printf("Index: %d\n", index);
+ util::stream_format(std::cout, "Input file: %s\n", *params.find(OPTION_INPUT)->second);
+ util::stream_format(std::cout, "Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
+ util::stream_format(std::cout, "Index: %d\n", index);
// write the metadata
- chd_error err = input_chd.delete_metadata(tag, index);
- if (err != CHDERR_NONE)
- report_error(1, "Error removing metadata: %s", chd_file::error_string(err));
+ std::error_condition err = input_chd.delete_metadata(tag, index);
+ if (err)
+ report_error(1, "Error removing metadata: %s", err.message());
else
- printf("Metadata removed\n");
+ util::stream_format(std::cout, "Metadata removed\n");
}
@@ -2823,7 +3289,7 @@ static void do_del_metadata(parameters_t &params)
// do_dump_metadata - dump metadata from a CHD
//-------------------------------------------------
-static void do_dump_metadata(parameters_t &params)
+static void do_dump_metadata(parameters_map &params)
{
// parse out input files
chd_file input_parent_chd;
@@ -2833,7 +3299,7 @@ static void do_dump_metadata(parameters_t &params)
// verify output file doesn't exist
auto output_file_str = params.find(OPTION_OUTPUT);
if (output_file_str != params.end())
- check_existing_output_file(params, output_file_str->second->c_str());
+ check_existing_output_file(params, *output_file_str->second);
// process tag
chd_metadata_tag tag = CHD_MAKE_TAG('?','?','?','?');
@@ -2852,9 +3318,9 @@ static void do_dump_metadata(parameters_t &params)
// write the metadata
std::vector<uint8_t> buffer;
- chd_error err = input_chd.read_metadata(tag, index, buffer);
- if (err != CHDERR_NONE)
- report_error(1, "Error reading metadata: %s", chd_file::error_string(err));
+ std::error_condition err = input_chd.read_metadata(tag, index, buffer);
+ if (err)
+ report_error(1, "Error reading metadata: %s", err.message());
// catch errors so we can close & delete the output file
util::core_file::ptr output_file;
@@ -2863,23 +3329,28 @@ static void do_dump_metadata(parameters_t &params)
// create the file
if (output_file_str != params.end())
{
- osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
- if (filerr != osd_file::error::NONE)
- report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
+ std::error_condition filerr;
+
+ filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
+ if (filerr)
+ report_error(1, "Unable to open file (%s): %s", *output_file_str->second, filerr.message());
// output the metadata
- uint32_t count = output_file->write(&buffer[0], buffer.size());
- if (count != buffer.size())
- report_error(1, "Error writing file (%s)", output_file_str->second->c_str());
+ size_t count;
+ std::tie(filerr, count) = write(*output_file, &buffer[0], buffer.size());
+ if (!filerr)
+ filerr = output_file->flush();
+ if (filerr)
+ report_error(1, "Error writing file (%s)", *output_file_str->second);
output_file.reset();
// provide some feedback
- printf("File (%s) written, %s bytes\n", output_file_str->second->c_str(), big_int_string(buffer.size()).c_str());
+ util::stream_format(std::cout, "File (%s) written, %s bytes\n", *output_file_str->second, big_int_string(buffer.size()));
}
-
- // flush to stdout
else
{
+ // flush to stdout
+ // FIXME: check for errors
fwrite(&buffer[0], 1, buffer.size(), stdout);
fflush(stdout);
}
@@ -2895,18 +3366,18 @@ static void do_dump_metadata(parameters_t &params)
//-------------------------------------------------
-// do_dump_metadata - dump metadata from a CHD
+// do_list_templates - list hard drive templates
//-------------------------------------------------
-static void do_list_templates(parameters_t &params)
+static void do_list_templates(parameters_map &params)
{
- printf("\n");
- printf("ID Manufacturer Model Cylinders Heads Sectors Sector Size Total Size\n");
- printf("------------------------------------------------------------------------------------\n");
+ util::stream_format(std::cout, "\n");
+ util::stream_format(std::cout, "ID Manufacturer Model Cylinders Heads Sectors Sector Size Total Size\n");
+ util::stream_format(std::cout, "------------------------------------------------------------------------------------\n");
- for (int id = 0; id < ARRAY_LENGTH(s_hd_templates); id++)
+ for (int id = 0; id < std::size(s_hd_templates); id++)
{
- printf("%2d %-13s %-15s %9d %5d %7d %11d %7d MB\n",
+ util::stream_format(std::cout, "%2d %-13s %-15s %9d %5d %7d %11d %7d MB\n",
id,
s_hd_templates[id].manufacturer,
s_hd_templates[id].model,
@@ -2926,59 +3397,61 @@ static void do_list_templates(parameters_t &params)
int CLIB_DECL main(int argc, char *argv[])
{
+ const std::vector<std::string> args = osd_get_command_line(argc, argv);
+ chdman_osd_output osdoutput;
+
// print the header
extern const char build_version[];
- printf("chdman - MAME Compressed Hunks of Data (CHD) manager %s\n", build_version);
+ util::stream_format(std::cout, "chdman - MAME Compressed Hunks of Data (CHD) manager %s\n", build_version);
// handle help specially
- if (argc < 2)
- return print_help(argv[0]);
+ if (args.size() < 2)
+ return print_help(args[0]);
int argnum = 1;
- const char *command = argv[argnum++];
- bool help = (strcmp(command, COMMAND_HELP) == 0);
+ std::string command = args[argnum++];
+ bool help(command == COMMAND_HELP);
if (help)
{
- if (argc <= 2)
- return print_help(argv[0]);
- command = argv[argnum++];
+ if (args.size() <= 2)
+ return print_help(args[0]);
+ command = args[argnum++];
}
// iterate over commands to find our match
for (auto & s_command : s_commands)
- if (strcmp(command, s_command.name) == 0)
+ {
+ if (command == s_command.name)
{
const command_description &desc = s_command;
// print help if that was requested
if (help)
- return print_help(argv[0], desc);
+ return print_help(args[0], desc);
// otherwise, verify the parameters
- parameters_t parameters;
- while (argnum < argc)
+ parameters_map parameters;
+ while (argnum < args.size())
{
// should be an option name
- const char *arg = argv[argnum++];
- if (arg[0] != '-')
- return print_help(argv[0], desc, "Expected option, not parameter");
+ const std::string &arg = args[argnum++];
+ if (arg.empty() || (arg[0] != '-'))
+ return print_help(args[0], desc, "Expected option, not parameter");
// iterate over valid options
int valid;
- for (valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
+ for (valid = 0; (valid < std::size(desc.valid_options)) && desc.valid_options[valid]; valid++)
{
// reduce to the option name
const char *validname = desc.valid_options[valid];
- if (validname == nullptr)
- break;
if (*validname == REQUIRED[0])
validname++;
// find the matching option description
int optnum;
- for (optnum = 0; optnum < ARRAY_LENGTH(s_options); optnum++)
+ for (optnum = 0; optnum < std::size(s_options); optnum++)
if (strcmp(s_options[optnum].name, validname) == 0)
break;
- assert(optnum != ARRAY_LENGTH(s_options));
+ assert(optnum != std::size(s_options));
// do we match?
const option_description &odesc = s_options[optnum];
@@ -2989,31 +3462,31 @@ int CLIB_DECL main(int argc, char *argv[])
const char *param = "";
if (odesc.parameter)
{
- if (argnum >= argc || argv[argnum][0] == '-')
- return print_help(argv[0], desc, "Option is missing parameter");
- param = argv[argnum++];
+ if (argnum >= args.size() || (!args[argnum].empty() && args[argnum][0] == '-'))
+ return print_help(args[0], desc, "Option is missing parameter");
+ param = args[argnum++].c_str();
}
// add to the map
if (!parameters.insert(std::make_pair(odesc.name, new std::string(param))).second)
- return print_help(argv[0], desc, "Multiple parameters of the same type specified");
+ return print_help(args[0], desc, "Multiple parameters of the same type specified");
break;
}
}
// if not valid, error
- if (valid == ARRAY_LENGTH(desc.valid_options))
- return print_help(argv[0], desc, "Option not valid for this command");
+ if ((valid == std::size(desc.valid_options)) || !desc.valid_options[valid])
+ return print_help(args[0], desc, string_format("Option '%s' not valid for this command", arg).c_str());
}
// make sure we got all our required parameters
- for (int valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
+ for (int valid = 0; valid < std::size(desc.valid_options); valid++)
{
const char *validname = desc.valid_options[valid];
if (validname == nullptr)
break;
if (*validname == REQUIRED[0] && parameters.find(++validname) == parameters.end())
- return print_help(argv[0], desc, "Required parameters missing");
+ return print_help(args[0], desc, "Required parameters missing");
}
// all clear, run the command
@@ -3022,23 +3495,24 @@ int CLIB_DECL main(int argc, char *argv[])
(*s_command.handler)(parameters);
return 0;
}
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
- fprintf(stderr, "CHD error occurred (main): %s\n", chd_file::error_string(err));
+ util::stream_format(std::cerr, "CHD error occurred (main): %s\n", err.message());
return 1;
}
catch (fatal_error &err)
{
- fprintf(stderr, "Fatal error occurred: %d\n", err.error());
+ util::stream_format(std::cerr, "Fatal error occurred: %d\n", err.error());
return err.error();
}
catch (std::exception& ex)
{
- fprintf(stderr, "Unhandled exception: %s\n", ex.what());
+ util::stream_format(std::cerr, "Unhandled exception: %s\n", ex.what());
return 1;
}
}
+ }
// print generic help if nothing found
- return print_help(argv[0]);
+ return print_help(args[0]);
}