1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 CHD compression frontend
6
7 ****************************************************************************/
8 #include <stdio.h> // must be stdio.h and here otherwise issues with I64FMT in MINGW
9
10 // osd
11 #include "osdcore.h"
12
13 // lib/util
14 #include "avhuff.h"
15 #include "aviio.h"
16 #include "bitmap.h"
17 #include "chdcd.h"
18 #include "corefile.h"
19 #include "hashing.h"
20 #include "md5.h"
21 #include "vbiparse.h"
22
23 #include <cassert>
24 #include <cctype>
25 #include <cstdarg>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cstring>
29 #include <ctime>
30 #include <iostream>
31 #include <limits>
32 #include <memory>
33 #include <new>
34 #include <unordered_map>
35
36
37
38 //**************************************************************************
39 // CONSTANTS & DEFINES
40 //**************************************************************************
41 // MINGW has adopted the MSVC formatting for 64-bit ints as of GCC 4.4 and deprecated it as of GCC 9.3
42 #if defined(WIN32) && defined(__GNUC__) && ((__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3)))
43 #define I64FMT "I64"
44 #elif !defined(__APPLE__) && defined(__LP64__)
45 #define I64FMT "l"
46 #else
47 #define I64FMT "ll"
48 #endif
49
50 // default hard disk sector size
51 const uint32_t IDE_SECTOR_SIZE = 512;
52
53 // temporary input buffer size
54 const uint32_t TEMP_BUFFER_SIZE = 32 * 1024 * 1024;
55
56 // modes
57 const int MODE_NORMAL = 0;
58 const int MODE_CUEBIN = 1;
59 const int MODE_GDI = 2;
60
61 // command modifier
62 #define REQUIRED "~"
63
64 // command strings
65 #define COMMAND_HELP "help"
66 #define COMMAND_INFO "info"
67 #define COMMAND_VERIFY "verify"
68 #define COMMAND_CREATE_RAW "createraw"
69 #define COMMAND_CREATE_HD "createhd"
70 #define COMMAND_CREATE_CD "createcd"
71 #define COMMAND_CREATE_LD "createld"
72 #define COMMAND_EXTRACT_RAW "extractraw"
73 #define COMMAND_EXTRACT_HD "extracthd"
74 #define COMMAND_EXTRACT_CD "extractcd"
75 #define COMMAND_EXTRACT_LD "extractld"
76 #define COMMAND_COPY "copy"
77 #define COMMAND_ADD_METADATA "addmeta"
78 #define COMMAND_DEL_METADATA "delmeta"
79 #define COMMAND_DUMP_METADATA "dumpmeta"
80 #define COMMAND_LIST_TEMPLATES "listtemplates"
81
82 // option strings
83 #define OPTION_INPUT "input"
84 #define OPTION_OUTPUT "output"
85 #define OPTION_OUTPUT_BIN "outputbin"
86 #define OPTION_OUTPUT_FORCE "force"
87 #define OPTION_INPUT_START_BYTE "inputstartbyte"
88 #define OPTION_INPUT_START_HUNK "inputstarthunk"
89 #define OPTION_INPUT_START_FRAME "inputstartframe"
90 #define OPTION_INPUT_LENGTH_BYTES "inputbytes"
91 #define OPTION_INPUT_LENGTH_HUNKS "inputhunks"
92 #define OPTION_INPUT_LENGTH_FRAMES "inputframes"
93 #define OPTION_HUNK_SIZE "hunksize"
94 #define OPTION_UNIT_SIZE "unitsize"
95 #define OPTION_COMPRESSION "compression"
96 #define OPTION_INPUT_PARENT "inputparent"
97 #define OPTION_OUTPUT_PARENT "outputparent"
98 #define OPTION_IDENT "ident"
99 #define OPTION_CHS "chs"
100 #define OPTION_SECTOR_SIZE "sectorsize"
101 #define OPTION_TAG "tag"
102 #define OPTION_INDEX "index"
103 #define OPTION_VALUE_TEXT "valuetext"
104 #define OPTION_VALUE_FILE "valuefile"
105 #define OPTION_NO_CHECKSUM "nochecksum"
106 #define OPTION_VERBOSE "verbose"
107 #define OPTION_FIX "fix"
108 #define OPTION_NUMPROCESSORS "numprocessors"
109 #define OPTION_SIZE "size"
110 #define OPTION_TEMPLATE "template"
111
112
113 //**************************************************************************
114 // FUNCTION PROTOTYPES
115 //**************************************************************************
116
117 typedef std::unordered_map<std::string, std::string *> parameters_map;
118
119 template <typename Format, typename... Params> static void report_error(int error, Format &&fmt, Params &&...args);
120 static void do_info(parameters_map ¶ms);
121 static void do_verify(parameters_map ¶ms);
122 static void do_create_raw(parameters_map ¶ms);
123 static void do_create_hd(parameters_map ¶ms);
124 static void do_create_cd(parameters_map ¶ms);
125 static void do_create_ld(parameters_map ¶ms);
126 static void do_copy(parameters_map ¶ms);
127 static void do_extract_raw(parameters_map ¶ms);
128 static void do_extract_cd(parameters_map ¶ms);
129 static void do_extract_ld(parameters_map ¶ms);
130 static void do_add_metadata(parameters_map ¶ms);
131 static void do_del_metadata(parameters_map ¶ms);
132 static void do_dump_metadata(parameters_map ¶ms);
133 static void do_list_templates(parameters_map ¶ms);
134
135
136
137 //**************************************************************************
138 // TYPE DEFINITIONS
139 //**************************************************************************
140
141 // ======================> option_description
142
143 struct option_description
144 {
145 const char *name;
146 const char *shortname;
147 bool parameter;
148 const char *description;
149 };
150
151
152 // ======================> command_description
153
154 struct command_description
155 {
156 const char *name;
157 void (*handler)(parameters_map &);
158 const char *description;
159 const char *valid_options[16];
160 };
161
162
163 // ======================> avi_info
164
165 struct avi_info
166 {
167 uint32_t fps_times_1million;
168 uint32_t width;
169 uint32_t height;
170 bool interlaced;
171 uint32_t channels;
172 uint32_t rate;
173 uint32_t max_samples_per_frame;
174 uint32_t bytes_per_frame;
175 };
176
177 // ======================> hd_template
178
179 struct hd_template
180 {
181 const char *manufacturer;
182 const char *model;
183 uint32_t cylinders;
184 uint32_t heads;
185 uint32_t sectors;
186 uint32_t sector_size;
187 };
188
189 // ======================> metadata_index_info
190
191 struct metadata_index_info
192 {
193 chd_metadata_tag tag;
194 uint32_t index;
195 };
196
197
198 // ======================> fatal_error
199
200 class fatal_error : public std::exception
201 {
202 public:
fatal_error(int error)203 fatal_error(int error)
204 : m_error(error) { }
205
error() const206 int error() const { return m_error; }
207
208 private:
209 int m_error;
210 };
211
212
213 // ======================> chd_zero_compressor
214
215 class chd_zero_compressor : public chd_file_compressor
216 {
217 public:
218 // construction/destruction
chd_zero_compressor(std::uint64_t offset=0,std::uint64_t maxoffset=0)219 chd_zero_compressor(std::uint64_t offset = 0, std::uint64_t maxoffset = 0)
220 : m_offset(offset)
221 , m_maxoffset(maxoffset)
222 {
223 }
224
225 // read interface
read_data(void * dest,std::uint64_t offset,std::uint32_t length)226 virtual std::uint32_t read_data(void *dest, std::uint64_t offset, std::uint32_t length) override
227 {
228 offset += m_offset;
229 if (offset >= m_maxoffset)
230 return 0;
231 if (offset + length > m_maxoffset)
232 length = m_maxoffset - offset;
233 std::memset(dest, 0, length);
234 return length;
235 }
236
237 private:
238 // internal state
239 std::uint64_t m_offset;
240 std::uint64_t m_maxoffset;
241 };
242
243
244 // ======================> chd_rawfile_compressor
245
246 class chd_rawfile_compressor : public chd_file_compressor
247 {
248 public:
249 // 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 ())250 chd_rawfile_compressor(util::core_file &file, std::uint64_t offset = 0, std::uint64_t maxoffset = std::numeric_limits<std::uint64_t>::max())
251 : m_file(file)
252 , m_offset(offset)
253 , m_maxoffset((std::min)(maxoffset, file.size()))
254 {
255 }
256
257 // read interface
read_data(void * dest,std::uint64_t offset,std::uint32_t length)258 virtual std::uint32_t read_data(void *dest, std::uint64_t offset, std::uint32_t length) override
259 {
260 offset += m_offset;
261 if (offset >= m_maxoffset)
262 return 0;
263 if (offset + length > m_maxoffset)
264 length = m_maxoffset - offset;
265 m_file.seek(offset, SEEK_SET);
266 return m_file.read(dest, length);
267 }
268
269 private:
270 // internal state
271 util::core_file & m_file;
272 std::uint64_t m_offset;
273 std::uint64_t m_maxoffset;
274 };
275
276
277 // ======================> chd_chdfile_compressor
278
279 class chd_chdfile_compressor : public chd_file_compressor
280 {
281 public:
282 // construction/destruction
chd_chdfile_compressor(chd_file & file,uint64_t offset=0,uint64_t maxoffset=~0)283 chd_chdfile_compressor(chd_file &file, uint64_t offset = 0, uint64_t maxoffset = ~0)
284 : m_toc(nullptr),
285 m_file(file),
286 m_offset(offset),
287 m_maxoffset(std::min(maxoffset, file.logical_bytes())) { }
288
289 // read interface
read_data(void * dest,uint64_t offset,uint32_t length)290 virtual uint32_t read_data(void *dest, uint64_t offset, uint32_t length)
291 {
292 offset += m_offset;
293 if (offset >= m_maxoffset)
294 return 0;
295 if (offset + length > m_maxoffset)
296 length = m_maxoffset - offset;
297 chd_error err = m_file.read_bytes(offset, dest, length);
298 if (err != CHDERR_NONE)
299 throw err;
300
301 // if we have TOC - detect audio sectors and swap data
302 if (m_toc)
303 {
304 assert(offset % CD_FRAME_SIZE == 0);
305 assert(length % CD_FRAME_SIZE == 0);
306
307 int startlba = offset / CD_FRAME_SIZE;
308 int lenlba = length / CD_FRAME_SIZE;
309 uint8_t *_dest = reinterpret_cast<uint8_t *>(dest);
310
311 for (int chdlba = 0; chdlba < lenlba; chdlba++)
312 {
313 // find current frame's track number
314 int tracknum = m_toc->numtrks;
315 for (int track = 0; track < m_toc->numtrks; track++)
316 if ((chdlba + startlba) < m_toc->tracks[track + 1].chdframeofs)
317 {
318 tracknum = track;
319 break;
320 }
321 // is it audio ?
322 if (m_toc->tracks[tracknum].trktype != CD_TRACK_AUDIO)
323 continue;
324 // byteswap if yes
325 int dataoffset = chdlba * CD_FRAME_SIZE;
326 for (uint32_t swapindex = dataoffset; swapindex < (dataoffset + CD_MAX_SECTOR_DATA); swapindex += 2)
327 {
328 uint8_t temp = _dest[swapindex];
329 _dest[swapindex] = _dest[swapindex + 1];
330 _dest[swapindex + 1] = temp;
331 }
332 }
333 }
334
335 return length;
336 }
337
338 const cdrom_toc * m_toc;
339
340 private:
341 // internal state
342 chd_file & m_file;
343 uint64_t m_offset;
344 uint64_t m_maxoffset;
345 };
346
347
348 // ======================> chd_cd_compressor
349
350 class chd_cd_compressor : public chd_file_compressor
351 {
352 public:
353 // construction/destruction
chd_cd_compressor(cdrom_toc & toc,chdcd_track_input_info & info)354 chd_cd_compressor(cdrom_toc &toc, chdcd_track_input_info &info)
355 : m_file(),
356 m_toc(toc),
357 m_info(info) { }
358
~chd_cd_compressor()359 ~chd_cd_compressor()
360 {
361 }
362
363 // read interface
read_data(void * _dest,uint64_t offset,uint32_t length)364 virtual uint32_t read_data(void *_dest, uint64_t offset, uint32_t length)
365 {
366 // verify assumptions made below
367 assert(offset % CD_FRAME_SIZE == 0);
368 assert(length % CD_FRAME_SIZE == 0);
369
370 // initialize destination to 0 so that unused areas are filled
371 uint8_t *dest = reinterpret_cast<uint8_t *>(_dest);
372 memset(dest, 0, length);
373
374 // find out which track we're starting in
375 uint64_t startoffs = 0;
376 uint32_t length_remaining = length;
377 for (int tracknum = 0; tracknum < m_toc.numtrks; tracknum++)
378 {
379 const cdrom_track_info &trackinfo = m_toc.tracks[tracknum];
380 uint64_t endoffs = startoffs + (uint64_t)(trackinfo.frames + trackinfo.extraframes) * CD_FRAME_SIZE;
381 if (offset >= startoffs && offset < endoffs)
382 {
383 // if we don't already have this file open, open it now
384 if (!m_file || m_lastfile.compare(m_info.track[tracknum].fname)!=0)
385 {
386 m_file.reset();
387 m_lastfile = m_info.track[tracknum].fname;
388 osd_file::error filerr = util::core_file::open(m_lastfile, OPEN_FLAG_READ, m_file);
389 if (filerr != osd_file::error::NONE)
390 report_error(1, "Error opening input file (%s)'", m_lastfile.c_str());
391 }
392
393 // iterate over frames
394 uint64_t bytesperframe = trackinfo.datasize + trackinfo.subsize;
395 uint64_t src_track_start = m_info.track[tracknum].offset;
396 uint64_t src_track_end = src_track_start + bytesperframe * (uint64_t)trackinfo.frames;
397 uint64_t pad_track_start = src_track_end - ((uint64_t)m_toc.tracks[tracknum].padframes * bytesperframe);
398 while (length_remaining != 0 && offset < endoffs)
399 {
400 // determine start of current frame
401 uint64_t src_frame_start = src_track_start + ((offset - startoffs) / CD_FRAME_SIZE) * bytesperframe;
402 if (src_frame_start < src_track_end)
403 {
404 // read it in, or pad if we're into the padframes
405 if (src_frame_start >= pad_track_start)
406 {
407 memset(dest, 0, bytesperframe);
408 }
409 else
410 {
411 m_file->seek(src_frame_start, SEEK_SET);
412 uint32_t count = m_file->read(dest, bytesperframe);
413 if (count != bytesperframe)
414 report_error(1, "Error reading input file (%s)'", m_lastfile.c_str());
415 }
416
417 // swap if appropriate
418 if (m_info.track[tracknum].swap)
419 for (uint32_t swapindex = 0; swapindex < 2352; swapindex += 2)
420 {
421 uint8_t temp = dest[swapindex];
422 dest[swapindex] = dest[swapindex + 1];
423 dest[swapindex + 1] = temp;
424 }
425 }
426
427 // advance
428 offset += CD_FRAME_SIZE;
429 dest += CD_FRAME_SIZE;
430 length_remaining -= CD_FRAME_SIZE;
431 if (length_remaining == 0)
432 break;
433 }
434 }
435
436 // next track starts after the previous one
437 startoffs = endoffs;
438 }
439 return length - length_remaining;
440 }
441
442 private:
443 // internal state
444 std::string m_lastfile;
445 util::core_file::ptr m_file;
446 cdrom_toc & m_toc;
447 chdcd_track_input_info & m_info;
448 };
449
450
451 // ======================> chd_avi_compressor
452
453 class chd_avi_compressor : public chd_file_compressor
454 {
455 public:
456 // construction/destruction
chd_avi_compressor(avi_file & file,avi_info & info,uint32_t first_frame,uint32_t num_frames)457 chd_avi_compressor(avi_file &file, avi_info &info, uint32_t first_frame, uint32_t num_frames)
458 : m_file(file),
459 m_info(info),
460 m_bitmap(info.width, info.height * (info.interlaced ? 2 : 1)),
461 m_start_frame(first_frame),
462 m_frame_count(num_frames),
463 m_ldframedata(num_frames * VBI_PACKED_BYTES),
464 m_rawdata(info.bytes_per_frame) { }
465
466 // getters
ldframedata() const467 const std::vector<uint8_t> &ldframedata() const { return m_ldframedata; }
468
469 // read interface
read_data(void * _dest,uint64_t offset,uint32_t length)470 virtual uint32_t read_data(void *_dest, uint64_t offset, uint32_t length)
471 {
472 uint8_t *dest = reinterpret_cast<uint8_t *>(_dest);
473 uint8_t interlace_factor = m_info.interlaced ? 2 : 1;
474 uint32_t length_remaining = length;
475
476 // iterate over frames
477 int32_t start_frame = offset / m_info.bytes_per_frame;
478 int32_t end_frame = (offset + length - 1) / m_info.bytes_per_frame;
479 for (int32_t framenum = start_frame; framenum <= end_frame; framenum++)
480 if (framenum < m_frame_count)
481 {
482 // determine effective frame number and first/last samples
483 int32_t effframe = m_start_frame + framenum;
484 uint32_t first_sample = (uint64_t(m_info.rate) * uint64_t(effframe) * uint64_t(1000000) + m_info.fps_times_1million - 1) / uint64_t(m_info.fps_times_1million);
485 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;
486
487 // loop over channels and read the samples
488 int channels = unsigned((std::min<std::size_t>)(m_info.channels, ARRAY_LENGTH(m_audio)));
489 EQUIVALENT_ARRAY(m_audio, int16_t *) samplesptr;
490 for (int chnum = 0; chnum < channels; chnum++)
491 {
492 // read the sound samples
493 m_audio[chnum].resize(samples);
494 samplesptr[chnum] = &m_audio[chnum][0];
495 avi_file::error avierr = m_file.read_sound_samples(chnum, first_sample, samples, &m_audio[chnum][0]);
496 if (avierr != avi_file::error::NONE)
497 report_error(1, "Error reading audio samples %d-%d from channel %d: %s", first_sample, samples, chnum, avi_file::error_string(avierr));
498 }
499
500 // read the video data
501 avi_file::error avierr = m_file.read_video_frame(effframe / interlace_factor, m_bitmap);
502 if (avierr != avi_file::error::NONE)
503 report_error(1, "Error reading AVI frame %d: %s", effframe / interlace_factor, avi_file::error_string(avierr));
504 bitmap_yuy16 subbitmap(&m_bitmap.pix(effframe % interlace_factor), m_bitmap.width(), m_bitmap.height() / interlace_factor, m_bitmap.rowpixels() * interlace_factor);
505
506 // update metadata for this frame
507 if (m_info.height == 524/2 || m_info.height == 624/2)
508 {
509 vbi_metadata vbi;
510 vbi_parse_all(&subbitmap.pix(0), subbitmap.rowpixels(), subbitmap.width(), 8, &vbi);
511 vbi_metadata_pack(&m_ldframedata[framenum * VBI_PACKED_BYTES], framenum, &vbi);
512 }
513
514 // assemble the data into final form
515 avhuff_error averr = avhuff_encoder::assemble_data(m_rawdata, subbitmap, channels, samples, samplesptr);
516 if (averr != AVHERR_NONE)
517 report_error(1, "Error assembling data for frame %d", framenum);
518 if (m_rawdata.size() < m_info.bytes_per_frame)
519 {
520 int old_size = m_rawdata.size();
521 m_rawdata.resize(m_info.bytes_per_frame);
522 memset(&m_rawdata[old_size], 0, m_info.bytes_per_frame - old_size);
523 }
524
525 // copy to the destination
526 uint64_t start_offset = uint64_t(framenum) * uint64_t(m_info.bytes_per_frame);
527 uint64_t end_offset = start_offset + m_info.bytes_per_frame;
528 uint32_t bytes_to_copy = (std::min<uint64_t>)(length_remaining, end_offset - offset);
529 memcpy(dest, &m_rawdata[offset - start_offset], bytes_to_copy);
530
531 // advance
532 offset += bytes_to_copy;
533 dest += bytes_to_copy;
534 length_remaining -= bytes_to_copy;
535 }
536
537 return length;
538 }
539
540 private:
541 // internal state
542 avi_file & m_file;
543 avi_info & m_info;
544 bitmap_yuy16 m_bitmap;
545 uint32_t m_start_frame;
546 uint32_t m_frame_count;
547 std::vector<int16_t> m_audio[8];
548 std::vector<uint8_t> m_ldframedata;
549 std::vector<uint8_t> m_rawdata;
550 };
551
552
553
554 //**************************************************************************
555 // GLOBAL VARIABLES
556 //**************************************************************************
557
558 // timing
559 static clock_t lastprogress = 0;
560
561
562 // default compressors
563 static const chd_codec_type s_default_raw_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
564 static const chd_codec_type s_default_hd_compression[4] = { CHD_CODEC_LZMA, CHD_CODEC_ZLIB, CHD_CODEC_HUFFMAN, CHD_CODEC_FLAC };
565 static const chd_codec_type s_default_cd_compression[4] = { CHD_CODEC_CD_LZMA, CHD_CODEC_CD_ZLIB, CHD_CODEC_CD_FLAC };
566 static const chd_codec_type s_default_ld_compression[4] = { CHD_CODEC_AVHUFF };
567
568
569 // descriptions for each option
570 static const option_description s_options[] =
571 {
572 { OPTION_INPUT, "i", true, " <filename>: input file name" },
573 { OPTION_INPUT_PARENT, "ip", true, " <filename>: parent file name for input CHD" },
574 { OPTION_OUTPUT, "o", true, " <filename>: output file name" },
575 { OPTION_OUTPUT_BIN, "ob", true, " <filename>: output file name for binary data" },
576 { OPTION_OUTPUT_FORCE, "f", false, ": force overwriting an existing file" },
577 { OPTION_OUTPUT_PARENT, "op", true, " <filename>: parent file name for output CHD" },
578 { OPTION_INPUT_START_BYTE, "isb", true, " <offset>: starting byte offset within the input" },
579 { OPTION_INPUT_START_HUNK, "ish", true, " <offset>: starting hunk offset within the input" },
580 { OPTION_INPUT_START_FRAME, "isf", true, " <offset>: starting frame within the input" },
581 { OPTION_INPUT_LENGTH_BYTES, "ib", true, " <length>: effective length of input in bytes" },
582 { OPTION_INPUT_LENGTH_HUNKS, "ih", true, " <length>: effective length of input in hunks" },
583 { OPTION_INPUT_LENGTH_FRAMES, "if", true, " <length>: effective length of input in frames" },
584 { OPTION_HUNK_SIZE, "hs", true, " <bytes>: size of each hunk, in bytes" },
585 { OPTION_UNIT_SIZE, "us", true, " <bytes>: size of each unit, in bytes" },
586 { OPTION_COMPRESSION, "c", true, " <none|type1[,type2[,...]]>: which compression codecs to use (up to 4)" },
587 { OPTION_IDENT, "id", true, " <filename>: name of ident file to provide CHS information" },
588 { OPTION_CHS, "chs", true, " <cylinders,heads,sectors>: specifies CHS values directly" },
589 { OPTION_SECTOR_SIZE, "ss", true, " <bytes>: size of each hard disk sector" },
590 { OPTION_TAG, "t", true, " <tag>: 4-character tag for metadata" },
591 { OPTION_INDEX, "ix", true, " <index>: indexed instance of this metadata tag" },
592 { OPTION_VALUE_TEXT, "vt", true, " <text>: text for the metadata" },
593 { OPTION_VALUE_FILE, "vf", true, " <file>: file containing data to add" },
594 { OPTION_NUMPROCESSORS, "np", true, " <processors>: limit the number of processors to use during compression" },
595 { OPTION_NO_CHECKSUM, "nocs", false, ": do not include this metadata information in the overall SHA-1" },
596 { OPTION_FIX, "f", false, ": fix the SHA-1 if it is incorrect" },
597 { OPTION_VERBOSE, "v", false, ": output additional information" },
598 { OPTION_SIZE, "s", true, ": <bytes>: size of the output file" },
599 { OPTION_TEMPLATE, "tp", true, ": <id>: use hard disk template (see listtemplates)" },
600 };
601
602
603 // descriptions for each command
604 static const command_description s_commands[] =
605 {
606 { COMMAND_INFO, do_info, ": displays information about a CHD",
607 {
608 REQUIRED OPTION_INPUT,
609 OPTION_VERBOSE
610 }
611 },
612
613 { COMMAND_VERIFY, do_verify, ": verifies a CHD's integrity",
614 {
615 REQUIRED OPTION_INPUT,
616 OPTION_INPUT_PARENT
617 }
618 },
619
620 { COMMAND_CREATE_RAW, do_create_raw, ": create a raw CHD from the input file",
621 {
622 REQUIRED OPTION_OUTPUT,
623 OPTION_OUTPUT_PARENT,
624 OPTION_OUTPUT_FORCE,
625 REQUIRED OPTION_INPUT,
626 OPTION_INPUT_START_BYTE,
627 OPTION_INPUT_START_HUNK,
628 OPTION_INPUT_LENGTH_BYTES,
629 OPTION_INPUT_LENGTH_HUNKS,
630 REQUIRED OPTION_HUNK_SIZE,
631 REQUIRED OPTION_UNIT_SIZE,
632 OPTION_COMPRESSION,
633 OPTION_NUMPROCESSORS
634 }
635 },
636
637 { COMMAND_CREATE_HD, do_create_hd, ": create a hard disk CHD from the input file",
638 {
639 REQUIRED OPTION_OUTPUT,
640 OPTION_OUTPUT_PARENT,
641 OPTION_OUTPUT_FORCE,
642 OPTION_INPUT,
643 OPTION_INPUT_START_BYTE,
644 OPTION_INPUT_START_HUNK,
645 OPTION_INPUT_LENGTH_BYTES,
646 OPTION_INPUT_LENGTH_HUNKS,
647 OPTION_HUNK_SIZE,
648 OPTION_COMPRESSION,
649 OPTION_TEMPLATE,
650 OPTION_IDENT,
651 OPTION_CHS,
652 OPTION_SIZE,
653 OPTION_SECTOR_SIZE,
654 OPTION_NUMPROCESSORS
655 }
656 },
657
658 { COMMAND_CREATE_CD, do_create_cd, ": create a CD CHD from the input file",
659 {
660 REQUIRED OPTION_OUTPUT,
661 OPTION_OUTPUT_PARENT,
662 OPTION_OUTPUT_FORCE,
663 REQUIRED OPTION_INPUT,
664 OPTION_HUNK_SIZE,
665 OPTION_COMPRESSION,
666 OPTION_NUMPROCESSORS
667 }
668 },
669
670 { COMMAND_CREATE_LD, do_create_ld, ": create a laserdisc CHD from the input file",
671 {
672 REQUIRED OPTION_OUTPUT,
673 OPTION_OUTPUT_PARENT,
674 OPTION_OUTPUT_FORCE,
675 REQUIRED OPTION_INPUT,
676 OPTION_INPUT_START_FRAME,
677 OPTION_INPUT_LENGTH_FRAMES,
678 OPTION_HUNK_SIZE,
679 OPTION_COMPRESSION,
680 OPTION_NUMPROCESSORS
681 }
682 },
683
684 { COMMAND_EXTRACT_RAW, do_extract_raw, ": extract raw file from a CHD input file",
685 {
686 REQUIRED OPTION_OUTPUT,
687 OPTION_OUTPUT_FORCE,
688 REQUIRED OPTION_INPUT,
689 OPTION_INPUT_PARENT,
690 OPTION_INPUT_START_BYTE,
691 OPTION_INPUT_START_HUNK,
692 OPTION_INPUT_LENGTH_BYTES,
693 OPTION_INPUT_LENGTH_HUNKS
694 }
695 },
696
697 { COMMAND_EXTRACT_HD, do_extract_raw, ": extract raw hard disk file from a CHD input file",
698 {
699 REQUIRED OPTION_OUTPUT,
700 OPTION_OUTPUT_FORCE,
701 REQUIRED OPTION_INPUT,
702 OPTION_INPUT_PARENT,
703 OPTION_INPUT_START_BYTE,
704 OPTION_INPUT_START_HUNK,
705 OPTION_INPUT_LENGTH_BYTES,
706 OPTION_INPUT_LENGTH_HUNKS
707 }
708 },
709
710 { COMMAND_EXTRACT_CD, do_extract_cd, ": extract CD file from a CHD input file",
711 {
712 REQUIRED OPTION_OUTPUT,
713 OPTION_OUTPUT_BIN,
714 OPTION_OUTPUT_FORCE,
715 REQUIRED OPTION_INPUT,
716 OPTION_INPUT_PARENT,
717 }
718 },
719
720 { COMMAND_EXTRACT_LD, do_extract_ld, ": extract laserdisc AVI from a CHD input file",
721 {
722 REQUIRED OPTION_OUTPUT,
723 OPTION_OUTPUT_FORCE,
724 REQUIRED OPTION_INPUT,
725 OPTION_INPUT_PARENT,
726 OPTION_INPUT_START_FRAME,
727 OPTION_INPUT_LENGTH_FRAMES
728 }
729 },
730
731 { COMMAND_COPY, do_copy, ": copy data from one CHD to another of the same type",
732 {
733 REQUIRED OPTION_OUTPUT,
734 OPTION_OUTPUT_PARENT,
735 OPTION_OUTPUT_FORCE,
736 REQUIRED OPTION_INPUT,
737 OPTION_INPUT_PARENT,
738 OPTION_INPUT_START_BYTE,
739 OPTION_INPUT_START_HUNK,
740 OPTION_INPUT_LENGTH_BYTES,
741 OPTION_INPUT_LENGTH_HUNKS,
742 OPTION_HUNK_SIZE,
743 OPTION_COMPRESSION,
744 OPTION_NUMPROCESSORS
745 }
746 },
747
748 { COMMAND_ADD_METADATA, do_add_metadata, ": add metadata to the CHD",
749 {
750 REQUIRED OPTION_INPUT,
751 REQUIRED OPTION_TAG,
752 OPTION_INDEX,
753 OPTION_VALUE_TEXT,
754 OPTION_VALUE_FILE,
755 OPTION_NO_CHECKSUM
756 }
757 },
758
759 { COMMAND_DEL_METADATA, do_del_metadata, ": remove metadata from the CHD",
760 {
761 REQUIRED OPTION_INPUT,
762 REQUIRED OPTION_TAG,
763 OPTION_INDEX
764 }
765 },
766
767 { COMMAND_DUMP_METADATA, do_dump_metadata, ": dump metadata from the CHD to stdout or to a file",
768 {
769 REQUIRED OPTION_INPUT,
770 OPTION_OUTPUT,
771 OPTION_OUTPUT_FORCE,
772 REQUIRED OPTION_TAG,
773 OPTION_INDEX
774 }
775 },
776
777 { COMMAND_LIST_TEMPLATES, do_list_templates, ": list hard disk templates",
778 {
779 }
780 },
781 };
782
783
784 // hard disk templates
785 static const hd_template s_hd_templates[] =
786 {
787 { "Conner", "CFA170A", 332, 16, 63, 512 }, // 163 MB
788 { "Rodime", "R0201", 321, 2, 16, 512 }, // 5 MB
789 { "Rodime", "R0202", 321, 4, 16, 512 }, // 10 MB
790 { "Rodime", "R0203", 321, 6, 16, 512 }, // 15 MB
791 { "Rodime", "R0204", 321, 8, 16, 512 }, // 20 MB
792 };
793
794
795
796 //**************************************************************************
797 // IMPLEMENTATION
798 //**************************************************************************
799
800 //-------------------------------------------------
801 // report_error - report an error
802 //-------------------------------------------------
803
report_error(int error,Format && fmt,Params &&...args)804 template <typename Format, typename... Params> static void report_error(int error, Format &&fmt, Params &&...args)
805 {
806 // output to stderr
807 util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...);
808 std::cerr << std::endl;
809
810 // reset time for progress and return the error
811 lastprogress = 0;
812 throw fatal_error(error);
813 }
814
815
816 //-------------------------------------------------
817 // progress - generic progress callback
818 //-------------------------------------------------
819
progress(bool forceit,Format && fmt,Params &&...args)820 template <typename Format, typename... Params> static void progress(bool forceit, Format &&fmt, Params &&...args)
821 {
822 // skip if it hasn't been long enough
823 clock_t curtime = clock();
824 if (!forceit && lastprogress != 0 && curtime - lastprogress < CLOCKS_PER_SEC / 2)
825 return;
826 lastprogress = curtime;
827
828 // standard vfprintf stuff here
829 util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...);
830 std::cerr << std::flush;
831 }
832
833
834 //-------------------------------------------------
835 // print_help - print help for all the commands
836 //-------------------------------------------------
837
print_help(const std::string & argv0,const char * error=nullptr)838 static int print_help(const std::string &argv0, const char *error = nullptr)
839 {
840 // print the error message first
841 if (error != nullptr)
842 fprintf(stderr, "Error: %s\n\n", error);
843
844 // print a summary of each command
845 printf("Usage:\n");
846 for (auto & desc : s_commands)
847 {
848 printf(" %s %s%s\n", argv0.c_str(), desc.name, desc.description);
849 }
850 printf("\nFor help with any command, run:\n");
851 printf(" %s %s <command>\n", argv0.c_str(), COMMAND_HELP);
852 return 1;
853 }
854
855
856 //-------------------------------------------------
857 // print_help - print help for all a specific
858 // command
859 //-------------------------------------------------
860
print_help(const std::string & argv0,const command_description & desc,const char * error=nullptr)861 static int print_help(const std::string &argv0, const command_description &desc, const char *error = nullptr)
862 {
863 // print the error message first
864 if (error != nullptr)
865 fprintf(stderr, "Error: %s\n\n", error);
866
867 // print usage for this command
868 printf("Usage:\n");
869 printf(" %s %s [options], where valid options are:\n", argv0.c_str(), desc.name);
870 for (int valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
871 {
872 // determine whether we are required
873 const char *option = desc.valid_options[valid];
874 if (option == nullptr)
875 break;
876 bool required = (option[0] == REQUIRED[0]);
877 if (required)
878 option++;
879
880 // find the option
881 for (auto & s_option : s_options)
882 if (strcmp(option, s_option.name) == 0)
883 {
884 const option_description &odesc = s_option;
885 printf(" --%s", odesc.name);
886 if (odesc.shortname != nullptr)
887 printf(", -%s", odesc.shortname);
888 printf("%s%s\n", odesc.description, required ? " (required)" : "");
889 }
890 }
891 return 1;
892 }
893
894
895 //-------------------------------------------------
896 // big_int_string - create a 64-bit string
897 //-------------------------------------------------
898
big_int_string(uint64_t intvalue)899 std::string big_int_string(uint64_t intvalue)
900 {
901 // 0 is a special case
902 if (intvalue == 0)
903 return "0";
904
905 // loop until all chunks are done
906 std::string str;
907 bool first = true;
908 while (intvalue != 0)
909 {
910 int chunk = intvalue % 1000;
911 intvalue /= 1000;
912
913 std::string insert = string_format((intvalue != 0) ? "%03d" : "%d", chunk);
914
915 if (!first)
916 str.insert(0, ",");
917 first = false;
918 str.insert(0, insert);
919 }
920 return str;
921 }
922
923
924 //-------------------------------------------------
925 // msf_string_from_frames - output the given
926 // number of frames in M:S:F format
927 //-------------------------------------------------
928
msf_string_from_frames(uint32_t frames)929 std::string msf_string_from_frames(uint32_t frames)
930 {
931 return string_format("%02d:%02d:%02d", frames / (75 * 60), (frames / 75) % 60, frames % 75);
932 }
933
934
935 //-------------------------------------------------
936 // parse_number - parse a number string with an
937 // optional k/m/g suffix
938 //-------------------------------------------------
939
parse_number(const char * string)940 uint64_t parse_number(const char *string)
941 {
942 // 0-length string is 0
943 int length = strlen(string);
944 if (length == 0)
945 return 0;
946
947 // scan forward over digits
948 uint64_t result = 0;
949 while (isdigit(*string))
950 {
951 result = (result * 10) + (*string - '0');
952 string++;
953 }
954
955 // handle multipliers
956 if (*string == 'k' || *string == 'K')
957 result *= 1024;
958 if (*string == 'm' || *string == 'M')
959 result *= 1024 * 1024;
960 if (*string == 'g' || *string == 'G')
961 result *= 1024 * 1024 * 1024;
962
963 return result;
964 }
965
966
967 //-------------------------------------------------
968 // guess_chs - given a file and an offset,
969 // compute a best guess CHS value set
970 //-------------------------------------------------
971
guess_chs(std::string * filename,uint64_t filesize,int sectorsize,uint32_t & cylinders,uint32_t & heads,uint32_t & sectors,uint32_t & bps)972 static void guess_chs(std::string *filename, uint64_t filesize, int sectorsize, uint32_t &cylinders, uint32_t &heads, uint32_t §ors, uint32_t &bps)
973 {
974 // if this is a direct physical drive read, handle it specially
975 if (filename != nullptr && osd_get_physical_drive_geometry(filename->c_str(), &cylinders, &heads, §ors, &bps))
976 return;
977
978 // if we have no length to work with, we can't guess
979 if (filesize == 0)
980 report_error(1, "Can't guess CHS values because there is no input file");
981
982 // now find a valid value
983 for (uint32_t totalsectors = filesize / sectorsize; ; totalsectors++)
984 for (uint32_t cursectors = 63; cursectors > 1; cursectors--)
985 if (totalsectors % cursectors == 0)
986 {
987 uint32_t totalheads = totalsectors / cursectors;
988 for (uint32_t curheads = 16; curheads > 1; curheads--)
989 if (totalheads % curheads == 0)
990 {
991 cylinders = totalheads / curheads;
992 heads = curheads;
993 sectors = cursectors;
994 return;
995 }
996 }
997 }
998
999
1000 //-------------------------------------------------
1001 // parse_input_chd_parameters - parse the
1002 // standard set of input CHD parameters
1003 //-------------------------------------------------
1004
parse_input_chd_parameters(const parameters_map & params,chd_file & input_chd,chd_file & input_parent_chd,bool writeable=false)1005 static void parse_input_chd_parameters(const parameters_map ¶ms, chd_file &input_chd, chd_file &input_parent_chd, bool writeable = false)
1006 {
1007 // process input parent file
1008 auto input_chd_parent_str = params.find(OPTION_INPUT_PARENT);
1009 if (input_chd_parent_str != params.end())
1010 {
1011 chd_error err = input_parent_chd.open(input_chd_parent_str->second->c_str());
1012 if (err != CHDERR_NONE)
1013 report_error(1, "Error opening parent CHD file (%s): %s", input_chd_parent_str->second->c_str(), chd_file::error_string(err));
1014 }
1015
1016 // process input file
1017 auto input_chd_str = params.find(OPTION_INPUT);
1018 if (input_chd_str != params.end())
1019 {
1020 chd_error err = input_chd.open(input_chd_str->second->c_str(), writeable, input_parent_chd.opened() ? &input_parent_chd : nullptr);
1021 if (err != CHDERR_NONE)
1022 report_error(1, "Error opening CHD file (%s): %s", input_chd_str->second->c_str(), chd_file::error_string(err));
1023 }
1024 }
1025
1026
1027 //-------------------------------------------------
1028 // parse_input_start_end - parse input start/end
1029 // parameters in a standard way
1030 //-------------------------------------------------
1031
parse_input_start_end(const parameters_map & params,uint64_t logical_size,uint32_t hunkbytes,uint32_t framebytes,uint64_t & input_start,uint64_t & input_end)1032 static void parse_input_start_end(const parameters_map ¶ms, uint64_t logical_size, uint32_t hunkbytes, uint32_t framebytes, uint64_t &input_start, uint64_t &input_end)
1033 {
1034 // process start/end if we were provided an input CHD
1035 input_start = 0;
1036 input_end = logical_size;
1037
1038 // process input start
1039 auto input_start_byte_str = params.find(OPTION_INPUT_START_BYTE);
1040 auto input_start_hunk_str = params.find(OPTION_INPUT_START_HUNK);
1041 auto input_start_frame_str = params.find(OPTION_INPUT_START_FRAME);
1042 if (input_start_byte_str != params.end())
1043 input_start = parse_number(input_start_byte_str->second->c_str());
1044 if (input_start_hunk_str != params.end())
1045 input_start = parse_number(input_start_hunk_str->second->c_str()) * hunkbytes;
1046 if (input_start_frame_str != params.end())
1047 input_start = parse_number(input_start_frame_str->second->c_str()) * framebytes;
1048 if (input_start >= input_end)
1049 report_error(1, "Input start offset greater than input file size");
1050
1051 // process input length
1052 auto input_length_bytes_str = params.find(OPTION_INPUT_LENGTH_BYTES);
1053 auto input_length_hunks_str = params.find(OPTION_INPUT_LENGTH_HUNKS);
1054 auto input_length_frames_str = params.find(OPTION_INPUT_LENGTH_FRAMES);
1055 uint64_t input_length = input_end;
1056 if (input_length_bytes_str != params.end())
1057 input_length = parse_number(input_length_bytes_str->second->c_str());
1058 if (input_length_hunks_str != params.end())
1059 input_length = parse_number(input_length_hunks_str->second->c_str()) * hunkbytes;
1060 if (input_length_frames_str != params.end())
1061 input_length = parse_number(input_length_frames_str->second->c_str()) * framebytes;
1062 if (input_start + input_length < input_end)
1063 input_end = input_start + input_length;
1064 }
1065
1066
1067 //-------------------------------------------------
1068 // check_existing_output_file - see if an output
1069 // file already exists, and error if it does,
1070 // unless --force is specified
1071 //-------------------------------------------------
1072
check_existing_output_file(const parameters_map & params,const char * filename)1073 static void check_existing_output_file(const parameters_map ¶ms, const char *filename)
1074 {
1075 if (params.find(OPTION_OUTPUT_FORCE) == params.end())
1076 {
1077 util::core_file::ptr file;
1078 osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ, file);
1079 if (filerr == osd_file::error::NONE)
1080 {
1081 file.reset();
1082 report_error(1, "Error: file already exists (%s)\nUse --force (or -f) to force overwriting", filename);
1083 }
1084 }
1085 }
1086
1087
1088 //-------------------------------------------------
1089 // parse_output_chd_parameters - parse the
1090 // standard set of output CHD parameters
1091 //-------------------------------------------------
1092
parse_output_chd_parameters(const parameters_map & params,chd_file & output_parent_chd)1093 static std::string *parse_output_chd_parameters(const parameters_map ¶ms, chd_file &output_parent_chd)
1094 {
1095 // process output parent file
1096 auto output_chd_parent_str = params.find(OPTION_OUTPUT_PARENT);
1097 if (output_chd_parent_str != params.end())
1098 {
1099 chd_error err = output_parent_chd.open(output_chd_parent_str->second->c_str());
1100 if (err != CHDERR_NONE)
1101 report_error(1, "Error opening parent CHD file (%s): %s", output_chd_parent_str->second->c_str(), chd_file::error_string(err));
1102 }
1103
1104 // process output file
1105 auto output_chd_str = params.find(OPTION_OUTPUT);
1106 if (output_chd_str != params.end())
1107 check_existing_output_file(params, output_chd_str->second->c_str());
1108 return (output_chd_str != params.end()) ? output_chd_str->second : nullptr;
1109 }
1110
1111
1112 //-------------------------------------------------
1113 // parse_hunk_size - parse the hunk_size
1114 // parameter in a standard way
1115 //-------------------------------------------------
1116
parse_hunk_size(const parameters_map & params,uint32_t required_granularity,uint32_t & hunk_size)1117 static void parse_hunk_size(const parameters_map ¶ms, uint32_t required_granularity, uint32_t &hunk_size)
1118 {
1119 auto hunk_size_str = params.find(OPTION_HUNK_SIZE);
1120 if (hunk_size_str != params.end())
1121 {
1122 hunk_size = parse_number(hunk_size_str->second->c_str());
1123 if (hunk_size < 16 || hunk_size > 1024 * 1024)
1124 report_error(1, "Invalid hunk size");
1125 if (hunk_size % required_granularity != 0)
1126 report_error(1, "Hunk size is not an even multiple of %d", required_granularity);
1127 }
1128 }
1129
1130
1131 //-------------------------------------------------
1132 // parse_compression - parse a standard
1133 // compression parameter string
1134 //-------------------------------------------------
1135
parse_compression(const parameters_map & params,chd_codec_type compression[4])1136 static void parse_compression(const parameters_map ¶ms, chd_codec_type compression[4])
1137 {
1138 // see if anything was specified
1139 auto compression_str = params.find(OPTION_COMPRESSION);
1140 if (compression_str == params.end())
1141 return;
1142
1143 // special case: 'none'
1144 if (compression_str->second->compare("none")==0)
1145 {
1146 compression[0] = compression[1] = compression[2] = compression[3] = CHD_CODEC_NONE;
1147 return;
1148 }
1149
1150 // iterate through compressors
1151 int index = 0;
1152 for (int start = 0, end = compression_str->second->find_first_of(','); index < 4; start = end + 1, end = compression_str->second->find_first_of(',', end + 1))
1153 {
1154 std::string name(*compression_str->second, start, (end == -1) ? -1 : end - start);
1155 if (name.length() != 4)
1156 report_error(1, "Invalid compressor '%s' specified", name.c_str());
1157 chd_codec_type type = CHD_MAKE_TAG(name[0], name[1], name[2], name[3]);
1158 if (!chd_codec_list::codec_exists(type))
1159 report_error(1, "Invalid compressor '%s' specified", name.c_str());
1160 compression[index++] = type;
1161 if (end == -1)
1162 break;
1163 }
1164
1165 for(;index < 4; ++index)
1166 {
1167 compression[index] = CHD_CODEC_NONE;
1168 }
1169 }
1170
1171
1172 //-------------------------------------------------
1173 // parse_numprocessors - handle the numprocessors
1174 // command
1175 //-------------------------------------------------
1176
parse_numprocessors(const parameters_map & params)1177 static void parse_numprocessors(const parameters_map ¶ms)
1178 {
1179 auto numprocessors_str = params.find(OPTION_NUMPROCESSORS);
1180 if (numprocessors_str == params.end())
1181 return;
1182
1183 int count = atoi(numprocessors_str->second->c_str());
1184 if (count > 0)
1185 {
1186 extern int osd_num_processors;
1187 osd_num_processors = count;
1188 }
1189 }
1190
1191
1192 //-------------------------------------------------
1193 // compression_string - create a friendly string
1194 // describing a set of compressors
1195 //-------------------------------------------------
1196
compression_string(chd_codec_type compression[4])1197 static std::string compression_string(chd_codec_type compression[4])
1198 {
1199 // output compression types
1200 if (compression[0] == CHD_CODEC_NONE)
1201 return "none";
1202
1203 // iterate over types
1204 std::string str;
1205 for (int index = 0; index < 4; index++)
1206 {
1207 chd_codec_type type = compression[index];
1208 if (type == CHD_CODEC_NONE)
1209 break;
1210 if (index != 0)
1211 str.append(", ");
1212 str.push_back((type >> 24) & 0xff);
1213 str.push_back((type >> 16) & 0xff);
1214 str.push_back((type >> 8) & 0xff);
1215 str.push_back(type & 0xff);
1216 str.append(" (").append(chd_codec_list::codec_name(type)).append(")");
1217 }
1218 return str;
1219 }
1220
1221
1222 //-------------------------------------------------
1223 // compress_common - standard compression loop
1224 //-------------------------------------------------
1225
compress_common(chd_file_compressor & chd)1226 static void compress_common(chd_file_compressor &chd)
1227 {
1228 // begin compressing
1229 chd.compress_begin();
1230
1231 // loop until done
1232 double complete, ratio;
1233 chd_error err;
1234 while ((err = chd.compress_continue(complete, ratio)) == CHDERR_WALKING_PARENT || err == CHDERR_COMPRESSING)
1235 if (err == CHDERR_WALKING_PARENT)
1236 progress(false, "Examining parent, %.1f%% complete... \r", 100.0 * complete);
1237 else
1238 progress(false, "Compressing, %.1f%% complete... (ratio=%.1f%%) \r", 100.0 * complete, 100.0 * ratio);
1239
1240 // handle errors
1241 if (err != CHDERR_NONE)
1242 report_error(1, "Error during compression: %-40s", chd_file::error_string(err));
1243
1244 // final progress update
1245 progress(true, "Compression complete ... final ratio = %.1f%% \n", 100.0 * ratio);
1246 }
1247
1248
1249 //-------------------------------------------------
1250 // output_track_metadata - output track metadata
1251 // to a CUE file
1252 //-------------------------------------------------
1253
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)1254 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)
1255 {
1256 if (mode == MODE_GDI)
1257 {
1258 int mode = 0, size = 2048;
1259
1260 switch (info.trktype)
1261 {
1262 case CD_TRACK_MODE1:
1263 mode = 4;
1264 size = 2048;
1265 break;
1266
1267 case CD_TRACK_MODE1_RAW:
1268 mode = 4;
1269 size = 2352;
1270 break;
1271
1272 case CD_TRACK_MODE2:
1273 mode = 4;
1274 size = 2336;
1275 break;
1276
1277 case CD_TRACK_MODE2_FORM1:
1278 mode = 4;
1279 size = 2048;
1280 break;
1281
1282 case CD_TRACK_MODE2_FORM2:
1283 mode = 4;
1284 size = 2324;
1285 break;
1286
1287 case CD_TRACK_MODE2_FORM_MIX:
1288 mode = 4;
1289 size = 2336;
1290 break;
1291
1292 case CD_TRACK_MODE2_RAW:
1293 mode = 4;
1294 size = 2352;
1295 break;
1296
1297 case CD_TRACK_AUDIO:
1298 mode = 0;
1299 size = 2352;
1300 break;
1301 }
1302 bool needquote = strchr(filename, ' ') != nullptr;
1303 file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, mode, size, needquote?"\"":"", filename, needquote?"\"":"", discoffs);
1304 }
1305 else if (mode == MODE_CUEBIN)
1306 {
1307 // first track specifies the file
1308 if (tracknum == 0)
1309 file.printf("FILE \"%s\" BINARY\n", filename);
1310
1311 // determine submode
1312 std::string tempstr;
1313 switch (info.trktype)
1314 {
1315 case CD_TRACK_MODE1:
1316 case CD_TRACK_MODE1_RAW:
1317 tempstr = string_format("MODE1/%04d", info.datasize);
1318 break;
1319
1320 case CD_TRACK_MODE2:
1321 case CD_TRACK_MODE2_FORM1:
1322 case CD_TRACK_MODE2_FORM2:
1323 case CD_TRACK_MODE2_FORM_MIX:
1324 case CD_TRACK_MODE2_RAW:
1325 tempstr = string_format("MODE2/%04d", info.datasize);
1326 break;
1327
1328 case CD_TRACK_AUDIO:
1329 tempstr.assign("AUDIO");
1330 break;
1331 }
1332
1333 // output TRACK entry
1334 file.printf(" TRACK %02d %s\n", tracknum + 1, tempstr);
1335
1336 // output PREGAP tag if pregap sectors are not in the file
1337 if ((info.pregap > 0) && (info.pgdatasize == 0))
1338 {
1339 file.printf(" PREGAP %s\n", msf_string_from_frames(info.pregap));
1340 file.printf(" INDEX 01 %s\n", msf_string_from_frames(frameoffs));
1341 }
1342 else if ((info.pregap > 0) && (info.pgdatasize > 0))
1343 {
1344 file.printf(" INDEX 00 %s\n", msf_string_from_frames(frameoffs));
1345 file.printf(" INDEX 01 %s\n", msf_string_from_frames(frameoffs+info.pregap));
1346 }
1347
1348 // if no pregap at all, output index 01 only
1349 if (info.pregap == 0)
1350 {
1351 file.printf(" INDEX 01 %s\n", msf_string_from_frames(frameoffs));
1352 }
1353
1354 // output POSTGAP
1355 if (info.postgap > 0)
1356 file.printf(" POSTGAP %s\n", msf_string_from_frames(info.postgap));
1357 }
1358 // non-CUE mode
1359 else if (mode == MODE_NORMAL)
1360 {
1361 // header on the first track
1362 if (tracknum == 0)
1363 file.printf("CD_ROM\n\n\n");
1364 file.printf("// Track %d\n", tracknum + 1);
1365
1366 // write out the track type
1367 std::string modesubmode;
1368 if (info.subtype != CD_SUB_NONE)
1369 modesubmode = string_format("%s %s", cdrom_get_type_string(info.trktype), cdrom_get_subtype_string(info.subtype));
1370 else
1371 modesubmode = string_format("%s", cdrom_get_type_string(info.trktype));
1372 file.printf("TRACK %s\n", modesubmode);
1373
1374 // write out the attributes
1375 file.printf("NO COPY\n");
1376 if (info.trktype == CD_TRACK_AUDIO)
1377 {
1378 file.printf("NO PRE_EMPHASIS\n");
1379 file.printf("TWO_CHANNEL_AUDIO\n");
1380 }
1381
1382 // output pregap
1383 if (info.pregap > 0)
1384 file.printf("ZERO %s %s\n", modesubmode, msf_string_from_frames(info.pregap));
1385
1386 // all tracks but the first one have a file offset
1387 if (tracknum > 0)
1388 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));
1389 else
1390 file.printf("DATAFILE \"%s\" %s // length in bytes: %d\n", filename, msf_string_from_frames(info.frames), info.frames * (info.datasize + info.subsize));
1391
1392 // tracks with pregaps get a START marker too
1393 if (info.pregap > 0)
1394 file.printf("START %s\n", msf_string_from_frames(info.pregap));
1395
1396 file.printf("\n\n");
1397 }
1398 }
1399
1400
1401 //-------------------------------------------------
1402 // do_info - dump the header information from
1403 // a drive image
1404 //-------------------------------------------------
1405
do_info(parameters_map & params)1406 static void do_info(parameters_map ¶ms)
1407 {
1408 bool verbose = params.find(OPTION_VERBOSE) != params.end();
1409 // parse out input files
1410 chd_file input_parent_chd;
1411 chd_file input_chd;
1412 parse_input_chd_parameters(params, input_chd, input_parent_chd);
1413
1414 // print filename and version
1415 printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
1416 printf("File Version: %d\n", input_chd.version());
1417 if (input_chd.version() < 3)
1418 report_error(1, "Unsupported version (%d); use an older chdman to upgrade to version 3 or later", input_chd.version());
1419
1420 // output cmpression and size information
1421 chd_codec_type compression[4] = { input_chd.compression(0), input_chd.compression(1), input_chd.compression(2), input_chd.compression(3) };
1422 printf("Logical size: %s bytes\n", big_int_string(input_chd.logical_bytes()).c_str());
1423 printf("Hunk Size: %s bytes\n", big_int_string(input_chd.hunk_bytes()).c_str());
1424 printf("Total Hunks: %s\n", big_int_string(input_chd.hunk_count()).c_str());
1425 printf("Unit Size: %s bytes\n", big_int_string(input_chd.unit_bytes()).c_str());
1426 printf("Total Units: %s\n", big_int_string(input_chd.unit_count()).c_str());
1427 printf("Compression: %s\n", compression_string(compression).c_str());
1428 printf("CHD size: %s bytes\n", big_int_string(static_cast<util::core_file &>(input_chd).size()).c_str());
1429 if (compression[0] != CHD_CODEC_NONE)
1430 printf("Ratio: %.1f%%\n", 100.0 * double(static_cast<util::core_file &>(input_chd).size()) / double(input_chd.logical_bytes()));
1431
1432 // add SHA1 output
1433 util::sha1_t overall = input_chd.sha1();
1434 if (overall != util::sha1_t::null)
1435 {
1436 printf("SHA1: %s\n", overall.as_string().c_str());
1437 if (input_chd.version() >= 4)
1438 printf("Data SHA1: %s\n", input_chd.raw_sha1().as_string().c_str());
1439 }
1440 util::sha1_t parent = input_chd.parent_sha1();
1441 if (parent != util::sha1_t::null)
1442 printf("Parent SHA1: %s\n", parent.as_string().c_str());
1443
1444 // print out metadata
1445 std::vector<uint8_t> buffer;
1446 std::vector<metadata_index_info> info;
1447 for (int index = 0; ; index++)
1448 {
1449 // get the indexed metadata item; stop when we hit an error
1450 chd_metadata_tag metatag;
1451 uint8_t metaflags;
1452 chd_error err = input_chd.read_metadata(CHDMETATAG_WILDCARD, index, buffer, metatag, metaflags);
1453 if (err != CHDERR_NONE)
1454 break;
1455
1456 // determine our index
1457 uint32_t metaindex = ~0;
1458 for (auto & elem : info)
1459 if (elem.tag == metatag)
1460 {
1461 metaindex = ++elem.index;
1462 break;
1463 }
1464
1465 // if not found, add to our tracking
1466 if (metaindex == ~0)
1467 {
1468 metadata_index_info curinfo = { metatag, 0 };
1469 info.push_back(curinfo);
1470 metaindex = 0;
1471 }
1472
1473 // print either a string representation or a hex representation of the tag
1474 if (isprint((metatag >> 24) & 0xff) && isprint((metatag >> 16) & 0xff) && isprint((metatag >> 8) & 0xff) && isprint(metatag & 0xff))
1475 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()));
1476 else
1477 printf("Metadata: Tag=%08x Index=%d Length=%d bytes\n", metatag, metaindex, int(buffer.size()));
1478 printf(" ");
1479
1480 uint32_t count = buffer.size();
1481 // limit output to 60 characters of metadata if not verbose
1482 if (!verbose)
1483 count = std::min(60U, count);
1484 for (int chnum = 0; chnum < count; chnum++)
1485 printf("%c", isprint(uint8_t(buffer[chnum])) ? buffer[chnum] : '.');
1486 printf("\n");
1487 }
1488
1489 // print compression stats if verbose
1490 if (verbose)
1491 {
1492 uint32_t compression_types[10] = { 0 };
1493 for (uint32_t hunknum = 0; hunknum < input_chd.hunk_count(); hunknum++)
1494 {
1495 // get info on this hunk
1496 chd_codec_type codec;
1497 uint32_t compbytes;
1498 chd_error err = input_chd.hunk_info(hunknum, codec, compbytes);
1499 if (err != CHDERR_NONE)
1500 report_error(1, "Error getting info on hunk %d: %s", hunknum, chd_file::error_string(err));
1501
1502 // decode into our data
1503 if (codec > CHD_CODEC_MINI)
1504 for (int comptype = 0; comptype < 4; comptype++)
1505 if (codec == input_chd.compression(comptype))
1506 {
1507 codec = CHD_CODEC_MINI + 1 + comptype;
1508 break;
1509 }
1510 if (codec > ARRAY_LENGTH(compression_types))
1511 codec = ARRAY_LENGTH(compression_types) - 1;
1512
1513 // count stats
1514 compression_types[codec]++;
1515 }
1516
1517 // output the stats
1518 printf("\n");
1519 printf(" Hunks Percent Name\n");
1520 printf("---------- ------- ------------------------------------\n");
1521 for (int comptype = 0; comptype < ARRAY_LENGTH(compression_types); comptype++)
1522 if (compression_types[comptype] != 0)
1523 {
1524 // determine the name
1525 const char *name = "Unknown";
1526 switch (comptype)
1527 {
1528 case CHD_CODEC_NONE: name = "Uncompressed"; break;
1529 case CHD_CODEC_SELF: name = "Copy from self"; break;
1530 case CHD_CODEC_PARENT: name = "Copy from parent"; break;
1531 case CHD_CODEC_MINI: name = "Legacy 8-byte mini"; break;
1532 default:
1533 int index = comptype - 1 - CHD_CODEC_MINI;
1534 if (index < 4)
1535 name = chd_codec_list::codec_name(input_chd.compression(index));
1536 break;
1537 }
1538
1539 // output the stats
1540 printf("%10s %5.1f%% %-40s\n",
1541 big_int_string(compression_types[comptype]).c_str(),
1542 100.0 * double(compression_types[comptype]) / double(input_chd.hunk_count()),
1543 name);
1544 }
1545 }
1546 }
1547
1548
1549 //-------------------------------------------------
1550 // do_verify - validate the SHA1 on a CHD
1551 //-------------------------------------------------
1552
do_verify(parameters_map & params)1553 static void do_verify(parameters_map ¶ms)
1554 {
1555 // parse out input files
1556 chd_file input_parent_chd;
1557 chd_file input_chd;
1558 parse_input_chd_parameters(params, input_chd, input_parent_chd);
1559
1560 // only makes sense for compressed CHDs with valid SHA1's
1561 if (!input_chd.compressed())
1562 report_error(0, "No verification to be done; CHD is uncompressed");
1563 util::sha1_t raw_sha1 = (input_chd.version() <= 3) ? input_chd.sha1() : input_chd.raw_sha1();
1564 if (raw_sha1 == util::sha1_t::null)
1565 report_error(0, "No verification to be done; CHD has no checksum");
1566
1567 // create an array to read into
1568 std::vector<uint8_t> buffer((TEMP_BUFFER_SIZE / input_chd.hunk_bytes()) * input_chd.hunk_bytes());
1569
1570 // read all the data and build up an SHA-1
1571 util::sha1_creator rawsha1;
1572 for (uint64_t offset = 0; offset < input_chd.logical_bytes(); )
1573 {
1574 progress(false, "Verifying, %.1f%% complete... \r", 100.0 * double(offset) / double(input_chd.logical_bytes()));
1575
1576 // determine how much to read
1577 uint32_t bytes_to_read = (std::min<uint64_t>)(buffer.size(), input_chd.logical_bytes() - offset);
1578 chd_error err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
1579 if (err != CHDERR_NONE)
1580 report_error(1, "Error reading CHD file (%s): %s", params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err));
1581
1582 // add to the checksum
1583 rawsha1.append(&buffer[0], bytes_to_read);
1584 offset += bytes_to_read;
1585 }
1586 util::sha1_t computed_sha1 = rawsha1.finish();
1587
1588 // finish up
1589 if (raw_sha1 != computed_sha1)
1590 {
1591 fprintf(stderr, "Error: Raw SHA1 in header = %s\n", raw_sha1.as_string().c_str());
1592 fprintf(stderr, " actual SHA1 = %s\n", computed_sha1.as_string().c_str());
1593
1594 // fix it if requested; this also fixes the overall one so we don't need to do any more
1595 if (params.find(OPTION_FIX) != params.end())
1596 {
1597 input_chd.set_raw_sha1(computed_sha1);
1598 printf("SHA-1 updated to correct value in input CHD\n");
1599 }
1600 }
1601 else
1602 {
1603 printf("Raw SHA1 verification successful!\n");
1604
1605 // now include the metadata for >= v4
1606 if (input_chd.version() >= 4)
1607 {
1608 util::sha1_t computed_overall_sha1 = input_chd.compute_overall_sha1(computed_sha1);
1609 if (input_chd.sha1() == computed_overall_sha1)
1610 printf("Overall SHA1 verification successful!\n");
1611 else
1612 {
1613 fprintf(stderr, "Error: Overall SHA1 in header = %s\n", input_chd.sha1().as_string().c_str());
1614 fprintf(stderr, " actual SHA1 = %s\n", computed_overall_sha1.as_string().c_str());
1615
1616 // fix it if requested
1617 if (params.find(OPTION_FIX) != params.end())
1618 {
1619 input_chd.set_raw_sha1(computed_sha1);
1620 printf("SHA-1 updated to correct value in input CHD\n");
1621 }
1622 }
1623 }
1624 }
1625 }
1626
1627
1628 //-------------------------------------------------
1629 // do_create_raw - create a new compressed raw
1630 // image from a raw file
1631 //-------------------------------------------------
1632
do_create_raw(parameters_map & params)1633 static void do_create_raw(parameters_map ¶ms)
1634 {
1635 // process input file
1636 util::core_file::ptr input_file;
1637 auto input_file_str = params.find(OPTION_INPUT);
1638 if (input_file_str != params.end())
1639 {
1640 osd_file::error filerr = util::core_file::open(*input_file_str->second, OPEN_FLAG_READ, input_file);
1641 if (filerr != osd_file::error::NONE)
1642 report_error(1, "Unable to open file (%s)", input_file_str->second->c_str());
1643 }
1644
1645 // process output CHD
1646 chd_file output_parent;
1647 std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
1648
1649 // process hunk size
1650 uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : 0;
1651 parse_hunk_size(params, 1, hunk_size);
1652
1653 // process unit size
1654 uint32_t unit_size = output_parent.opened() ? output_parent.unit_bytes() : 0;
1655 auto unit_size_str = params.find(OPTION_UNIT_SIZE);
1656 if (unit_size_str != params.end())
1657 {
1658 unit_size = parse_number(unit_size_str->second->c_str());
1659 if (hunk_size % unit_size != 0)
1660 report_error(1, "Unit size is not an even divisor of the hunk size");
1661 }
1662
1663 // process input start/end (needs to know hunk_size)
1664 uint64_t input_start;
1665 uint64_t input_end;
1666 parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
1667
1668 // process compression
1669 chd_codec_type compression[4];
1670 memcpy(compression, s_default_raw_compression, sizeof(compression));
1671 parse_compression(params, compression);
1672
1673 // process numprocessors
1674 parse_numprocessors(params);
1675
1676 // print some info
1677 printf("Output CHD: %s\n", output_chd_str->c_str());
1678 if (output_parent.opened())
1679 printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
1680 printf("Input file: %s\n", input_file_str->second->c_str());
1681 if (input_start != 0 || input_end != input_file->size())
1682 {
1683 printf("Input start: %s\n", big_int_string(input_start).c_str());
1684 printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
1685 }
1686 printf("Compression: %s\n", compression_string(compression).c_str());
1687 printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
1688 printf("Logical size: %s\n", big_int_string(input_end - input_start).c_str());
1689
1690 // catch errors so we can close & delete the output file
1691 try
1692 {
1693 // create the new CHD
1694 std::unique_ptr<chd_file_compressor> chd(new chd_rawfile_compressor(*input_file, input_start, input_end));
1695 chd_error err;
1696 if (output_parent.opened())
1697 err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, compression, output_parent);
1698 else
1699 err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, unit_size, compression);
1700 if (err != CHDERR_NONE)
1701 report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
1702
1703 // if we have a parent, copy forward all the metadata
1704 if (output_parent.opened())
1705 chd->clone_all_metadata(output_parent);
1706
1707 // compress it generically
1708 compress_common(*chd);
1709 }
1710 catch (...)
1711 {
1712 // delete the output file
1713 auto output_chd_str = params.find(OPTION_OUTPUT);
1714 if (output_chd_str != params.end())
1715 osd_file::remove(*output_chd_str->second);
1716 throw;
1717 }
1718 }
1719
1720
1721 //-------------------------------------------------
1722 // do_create_hd - create a new compressed hard
1723 // disk image from a raw file
1724 //-------------------------------------------------
1725
do_create_hd(parameters_map & params)1726 static void do_create_hd(parameters_map ¶ms)
1727 {
1728 // process input file
1729 util::core_file::ptr input_file;
1730 auto input_file_str = params.find(OPTION_INPUT);
1731 if (input_file_str != params.end())
1732 {
1733 osd_file::error filerr = util::core_file::open(*input_file_str->second, OPEN_FLAG_READ, input_file);
1734 if (filerr != osd_file::error::NONE)
1735 report_error(1, "Unable to open file (%s)", input_file_str->second->c_str());
1736 }
1737
1738 // process output CHD
1739 chd_file output_parent;
1740 std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
1741
1742 // process sectorsize
1743 uint32_t sector_size = output_parent.opened() ? output_parent.unit_bytes() : IDE_SECTOR_SIZE;
1744 auto sectorsize_str = params.find(OPTION_SECTOR_SIZE);
1745 if (sectorsize_str != params.end())
1746 {
1747 if (output_parent.opened())
1748 report_error(1, "Sector size does not apply when creating a diff from the parent");
1749 sector_size = parse_number(sectorsize_str->second->c_str());
1750 }
1751
1752 // process hunk size (needs to know sector_size)
1753 uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : std::max((4096 / sector_size) * sector_size, sector_size);
1754 parse_hunk_size(params, sector_size, hunk_size);
1755
1756 // process input start/end (needs to know hunk_size)
1757 uint64_t filesize = 0;
1758 uint64_t input_start = 0;
1759 uint64_t input_end = 0;
1760 if (input_file)
1761 {
1762 parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
1763 filesize = input_end - input_start;
1764 }
1765 else
1766 {
1767 auto size_str = params.find(OPTION_SIZE);
1768 if (size_str != params.end())
1769 {
1770 if (sscanf(size_str->second->c_str(), "%" I64FMT"d", &filesize) != 1)
1771 report_error(1, "Invalid size string");
1772 }
1773 }
1774
1775 // process compression
1776 chd_codec_type compression[4];
1777 memcpy(compression, s_default_hd_compression, sizeof(compression));
1778 if (!input_file)
1779 compression[0] = compression[1] = compression[2] = compression[3] = CHD_CODEC_NONE;
1780 parse_compression(params, compression);
1781 if (!input_file && compression[0] != CHD_CODEC_NONE)
1782 report_error(1, "Blank hard disks must be uncompressed");
1783
1784 // process numprocessors
1785 parse_numprocessors(params);
1786
1787 // process chs
1788 uint32_t cylinders = 0;
1789 uint32_t heads = 0;
1790 uint32_t sectors = 0;
1791 auto chs_str = params.find(OPTION_CHS);
1792 if (chs_str != params.end())
1793 {
1794 if (output_parent.opened())
1795 report_error(1, "CHS does not apply when creating a diff from the parent");
1796 if (sscanf(chs_str->second->c_str(), "%d,%d,%d", &cylinders, &heads, §ors) != 3)
1797 report_error(1, "Invalid CHS string; must be of the form <cylinders>,<heads>,<sectors>");
1798 }
1799
1800 // process ident
1801 std::vector<uint8_t> identdata;
1802 if (output_parent.opened())
1803 output_parent.read_metadata(HARD_DISK_IDENT_METADATA_TAG, 0, identdata);
1804 auto ident_str = params.find(OPTION_IDENT);
1805 if (ident_str != params.end())
1806 {
1807 // load the file
1808 osd_file::error filerr = util::core_file::load(ident_str->second->c_str(), identdata);
1809 if (filerr != osd_file::error::NONE)
1810 report_error(1, "Error reading ident file (%s)", ident_str->second->c_str());
1811
1812 // must be at least 14 bytes; extract CHS data from there
1813 if (identdata.size() < 14)
1814 report_error(1, "Ident file '%s' is invalid (too short)", ident_str->second->c_str());
1815 cylinders = (identdata[3] << 8) | identdata[2];
1816 heads = (identdata[7] << 8) | identdata[6];
1817 sectors = (identdata[13] << 8) | identdata[12];
1818
1819 // ignore CHS for > 8GB drives
1820 if (cylinders * heads * sectors >= 16514064)
1821 cylinders = 0;
1822 }
1823
1824 // process template
1825 auto template_str = params.find(OPTION_TEMPLATE);
1826 if (template_str != params.end())
1827 {
1828 uint32_t id = parse_number(template_str->second->c_str());
1829
1830 if (id >= ARRAY_LENGTH(s_hd_templates))
1831 report_error(1, "Template '%d' is invalid\n", id);
1832
1833 cylinders = s_hd_templates[id].cylinders;
1834 heads = s_hd_templates[id].heads;
1835 sectors = s_hd_templates[id].sectors;
1836 sector_size = s_hd_templates[id].sector_size;
1837
1838 printf("Template: %s %s\n", s_hd_templates[id].manufacturer, s_hd_templates[id].model);
1839 }
1840
1841 // extract geometry from the parent if we have one
1842 if (output_parent.opened() && cylinders == 0)
1843 {
1844 std::string metadata;
1845 if (output_parent.read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) != CHDERR_NONE)
1846 report_error(1, "Unable to find hard disk metadata in parent CHD");
1847 if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &cylinders, &heads, §ors, §or_size) != 4)
1848 report_error(1, "Error parsing hard disk metadata in parent CHD");
1849 }
1850
1851 // validate the size
1852 if (filesize % sector_size != 0)
1853 report_error(1, "Data size is not divisible by sector size %d", sector_size);
1854
1855 // if no CHS values, try to guess them
1856 if (cylinders == 0)
1857 {
1858 if (!input_file && filesize == 0)
1859 report_error(1, "Blank hard drives must specify either a length or a set of CHS values");
1860 guess_chs((input_file_str != params.end()) ? input_file_str->second : nullptr, filesize, sector_size, cylinders, heads, sectors, sector_size);
1861 }
1862 uint32_t totalsectors = cylinders * heads * sectors;
1863
1864 // print some info
1865 printf("Output CHD: %s\n", output_chd_str->c_str());
1866 if (output_parent.opened())
1867 printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
1868 if (input_file)
1869 {
1870 printf("Input file: %s\n", input_file_str->second->c_str());
1871 if (input_start != 0 || input_end != input_file->size())
1872 {
1873 printf("Input start: %s\n", big_int_string(input_start).c_str());
1874 printf("Input length: %s\n", big_int_string(filesize).c_str());
1875 }
1876 }
1877 printf("Compression: %s\n", compression_string(compression).c_str());
1878 printf("Cylinders: %d\n", cylinders);
1879 printf("Heads: %d\n", heads);
1880 printf("Sectors: %d\n", sectors);
1881 printf("Bytes/sector: %d\n", sector_size);
1882 printf("Sectors/hunk: %d\n", hunk_size / sector_size);
1883 printf("Logical size: %s\n", big_int_string(uint64_t(totalsectors) * uint64_t(sector_size)).c_str());
1884
1885 // catch errors so we can close & delete the output file
1886 try
1887 {
1888 // create the new hard drive
1889 std::unique_ptr<chd_file_compressor> chd;
1890 if (input_file) chd.reset(new chd_rawfile_compressor(*input_file, input_start, input_end));
1891 else chd.reset(new chd_zero_compressor(input_start, input_end));
1892 chd_error err;
1893 if (output_parent.opened())
1894 err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(sector_size), hunk_size, compression, output_parent);
1895 else
1896 err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(sector_size), hunk_size, sector_size, compression);
1897 if (err != CHDERR_NONE)
1898 report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
1899
1900 // add the standard hard disk metadata
1901 std::string metadata = string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, sector_size);
1902 err = chd->write_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
1903 if (err != CHDERR_NONE)
1904 report_error(1, "Error adding hard disk metadata: %s", chd_file::error_string(err));
1905
1906 // write the ident if present
1907 if (!identdata.empty())
1908 {
1909 err = chd->write_metadata(HARD_DISK_IDENT_METADATA_TAG, 0, identdata);
1910 if (err != CHDERR_NONE)
1911 report_error(1, "Error adding hard disk metadata: %s", chd_file::error_string(err));
1912 }
1913
1914 // compress it generically
1915 if (input_file)
1916 compress_common(*chd);
1917 }
1918 catch (...)
1919 {
1920 // delete the output file
1921 auto output_chd_str = params.find(OPTION_OUTPUT);
1922 if (output_chd_str != params.end())
1923 osd_file::remove(*output_chd_str->second);
1924 throw;
1925 }
1926 }
1927
1928
1929 //-------------------------------------------------
1930 // do_create_cd - create a new compressed CD
1931 // image from a raw file
1932 //-------------------------------------------------
1933
do_create_cd(parameters_map & params)1934 static void do_create_cd(parameters_map ¶ms)
1935 {
1936 // process input file
1937 chdcd_track_input_info track_info;
1938 cdrom_toc toc = { 0 };
1939 auto input_file_str = params.find(OPTION_INPUT);
1940 if (input_file_str != params.end())
1941 {
1942 chd_error err = chdcd_parse_toc(input_file_str->second->c_str(), toc, track_info);
1943 if (err != CHDERR_NONE)
1944 report_error(1, "Error parsing input file (%s: %s)\n", input_file_str->second->c_str(), chd_file::error_string(err));
1945 }
1946
1947 // process output CHD
1948 chd_file output_parent;
1949 std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
1950
1951 // process hunk size
1952 uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : CD_FRAMES_PER_HUNK * CD_FRAME_SIZE;
1953 parse_hunk_size(params, CD_FRAME_SIZE, hunk_size);
1954
1955 // process compression
1956 chd_codec_type compression[4];
1957 memcpy(compression, s_default_cd_compression, sizeof(compression));
1958 parse_compression(params, compression);
1959
1960 // process numprocessors
1961 parse_numprocessors(params);
1962
1963 // pad each track to a 4-frame boundary. cdrom.c will deal with this on the read side
1964 uint32_t origtotalsectors = 0;
1965 uint32_t totalsectors = 0;
1966 for (int tracknum = 0; tracknum < toc.numtrks; tracknum++)
1967 {
1968 cdrom_track_info &trackinfo = toc.tracks[tracknum];
1969 int padded = (trackinfo.frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
1970 trackinfo.extraframes = padded * CD_TRACK_PADDING - trackinfo.frames;
1971 origtotalsectors += trackinfo.frames;
1972 totalsectors += trackinfo.frames + trackinfo.extraframes;
1973 }
1974
1975 // print some info
1976 printf("Output CHD: %s\n", output_chd_str->c_str());
1977 if (output_parent.opened())
1978 printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
1979 printf("Input file: %s\n", input_file_str->second->c_str());
1980 printf("Input tracks: %d\n", toc.numtrks);
1981 printf("Input length: %s\n", msf_string_from_frames(origtotalsectors).c_str());
1982 printf("Compression: %s\n", compression_string(compression).c_str());
1983 printf("Logical size: %s\n", big_int_string(uint64_t(totalsectors) * CD_FRAME_SIZE).c_str());
1984
1985 // catch errors so we can close & delete the output file
1986 chd_cd_compressor *chd = nullptr;
1987 try
1988 {
1989 // create the new CD
1990 chd = new chd_cd_compressor(toc, track_info);
1991 chd_error err;
1992 if (output_parent.opened())
1993 err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(CD_FRAME_SIZE), hunk_size, compression, output_parent);
1994 else
1995 err = chd->create(output_chd_str->c_str(), uint64_t(totalsectors) * uint64_t(CD_FRAME_SIZE), hunk_size, CD_FRAME_SIZE, compression);
1996 if (err != CHDERR_NONE)
1997 report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
1998
1999 // add the standard CD metadata; we do this even if we have a parent because it might be different
2000 err = cdrom_write_metadata(chd, &toc);
2001 if (err != CHDERR_NONE)
2002 report_error(1, "Error adding CD metadata: %s", chd_file::error_string(err));
2003
2004 // compress it generically
2005 compress_common(*chd);
2006 delete chd;
2007 }
2008 catch (...)
2009 {
2010 delete chd;
2011 // delete the output file
2012 auto output_chd_str = params.find(OPTION_OUTPUT);
2013 if (output_chd_str != params.end())
2014 osd_file::remove(*output_chd_str->second);
2015 throw;
2016 }
2017 }
2018
2019
2020 //-------------------------------------------------
2021 // do_create_ld - create a new A/V file from an
2022 // input AVI file and metadata
2023 //-------------------------------------------------
2024
do_create_ld(parameters_map & params)2025 static void do_create_ld(parameters_map ¶ms)
2026 {
2027 // process input file
2028 avi_file::ptr input_file;
2029 auto input_file_str = params.find(OPTION_INPUT);
2030 if (input_file_str != params.end())
2031 {
2032 avi_file::error avierr = avi_file::open(*input_file_str->second, input_file);
2033 if (avierr != avi_file::error::NONE)
2034 report_error(1, "Error opening AVI file (%s): %s\n", input_file_str->second->c_str(), avi_file::error_string(avierr));
2035 }
2036 const avi_file::movie_info &aviinfo = input_file->get_movie_info();
2037
2038 // process output CHD
2039 chd_file output_parent;
2040 std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
2041
2042 // process input start/end
2043 uint64_t input_start;
2044 uint64_t input_end;
2045 parse_input_start_end(params, aviinfo.video_numsamples, 0, 1, input_start, input_end);
2046
2047 // determine parameters of the incoming video stream
2048 avi_info info;
2049 info.fps_times_1million = uint64_t(aviinfo.video_timescale) * 1000000 / aviinfo.video_sampletime;
2050 info.width = aviinfo.video_width;
2051 info.height = aviinfo.video_height;
2052 info.interlaced = ((info.fps_times_1million / 1000000) <= 30) && (info.height % 2 == 0) && (info.height > 288);
2053 info.channels = aviinfo.audio_channels;
2054 info.rate = aviinfo.audio_samplerate;
2055
2056 // adjust for interlacing
2057 if (info.interlaced)
2058 {
2059 info.fps_times_1million *= 2;
2060 info.height /= 2;
2061 input_start *= 2;
2062 input_end *= 2;
2063 }
2064
2065 // determine the number of bytes per frame
2066 info.max_samples_per_frame = (uint64_t(info.rate) * 1000000 + info.fps_times_1million - 1) / info.fps_times_1million;
2067 info.bytes_per_frame = avhuff_encoder::raw_data_size(info.width, info.height, info.channels, info.max_samples_per_frame);
2068
2069 // process hunk size
2070 uint32_t hunk_size = output_parent.opened() ? output_parent.hunk_bytes() : info.bytes_per_frame;
2071 parse_hunk_size(params, info.bytes_per_frame, hunk_size);
2072
2073 // process compression
2074 chd_codec_type compression[4];
2075 memcpy(compression, s_default_ld_compression, sizeof(compression));
2076 parse_compression(params, compression);
2077 // disable support for uncompressed ones until the extraction code can handle it
2078 if (compression[0] == CHD_CODEC_NONE)
2079 report_error(1, "Uncompressed is not supported");
2080
2081 // process numprocessors
2082 parse_numprocessors(params);
2083
2084 // print some info
2085 printf("Output CHD: %s\n", output_chd_str->c_str());
2086 if (output_parent.opened())
2087 printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
2088 printf("Input file: %s\n", input_file_str->second->c_str());
2089 if (input_start != 0 && input_end != aviinfo.video_numsamples)
2090 printf("Input start: %s\n", big_int_string(input_start).c_str());
2091 printf("Input length: %s (%02d:%02d:%02d)\n", big_int_string(input_end - input_start).c_str(),
2092 uint32_t((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million / 60 / 60)),
2093 uint32_t(((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million / 60) % 60)),
2094 uint32_t(((uint64_t(input_end - input_start) * 1000000 / info.fps_times_1million) % 60)));
2095 printf("Frame rate: %d.%06d\n", info.fps_times_1million / 1000000, info.fps_times_1million % 1000000);
2096 printf("Frame size: %d x %d %s\n", info.width, info.height * (info.interlaced ? 2 : 1), info.interlaced ? "interlaced" : "non-interlaced");
2097 printf("Audio: %d channels at %d Hz\n", info.channels, info.rate);
2098 printf("Compression: %s\n", compression_string(compression).c_str());
2099 printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
2100 printf("Logical size: %s\n", big_int_string(uint64_t(input_end - input_start) * hunk_size).c_str());
2101
2102 // catch errors so we can close & delete the output file
2103 chd_avi_compressor *chd = nullptr;
2104 try
2105 {
2106 // create the new CHD
2107 chd = new chd_avi_compressor(*input_file, info, input_start, input_end);
2108 chd_error err;
2109 if (output_parent.opened())
2110 err = chd->create(output_chd_str->c_str(), uint64_t(input_end - input_start) * hunk_size, hunk_size, compression, output_parent);
2111 else
2112 err = chd->create(output_chd_str->c_str(), uint64_t(input_end - input_start) * hunk_size, hunk_size, info.bytes_per_frame, compression);
2113 if (err != CHDERR_NONE)
2114 report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
2115
2116 // write the core A/V metadata
2117 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);
2118 err = chd->write_metadata(AV_METADATA_TAG, 0, metadata);
2119 if (err != CHDERR_NONE)
2120 report_error(1, "Error adding AV metadata: %s\n", chd_file::error_string(err));
2121
2122 // create the compressor and then run it generically
2123 compress_common(*chd);
2124
2125 // write the final LD metadata
2126 if (info.height == 524/2 || info.height == 624/2)
2127 {
2128 err = chd->write_metadata(AV_LD_METADATA_TAG, 0, chd->ldframedata(), 0);
2129 if (err != CHDERR_NONE)
2130 report_error(1, "Error adding AVLD metadata: %s\n", chd_file::error_string(err));
2131 }
2132 delete chd;
2133 }
2134 catch (...)
2135 {
2136 delete chd;
2137 // delete the output file
2138 auto output_chd_str = params.find(OPTION_OUTPUT);
2139 if (output_chd_str != params.end())
2140 osd_file::remove(*output_chd_str->second);
2141 throw;
2142 }
2143 }
2144
2145
2146 //-------------------------------------------------
2147 // do_copy - create a new CHD with data from
2148 // another CHD
2149 //-------------------------------------------------
2150
do_copy(parameters_map & params)2151 static void do_copy(parameters_map ¶ms)
2152 {
2153 // parse out input files
2154 chd_file input_parent_chd;
2155 chd_file input_chd;
2156 parse_input_chd_parameters(params, input_chd, input_parent_chd);
2157
2158 // parse out input start/end
2159 uint64_t input_start;
2160 uint64_t input_end;
2161 parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes(), input_start, input_end);
2162
2163 // process output CHD
2164 chd_file output_parent;
2165 std::string *output_chd_str = parse_output_chd_parameters(params, output_parent);
2166
2167 // process hunk size
2168 uint32_t hunk_size = input_chd.hunk_bytes();
2169 parse_hunk_size(params, 1, hunk_size);
2170 if (hunk_size % input_chd.hunk_bytes() != 0 && input_chd.hunk_bytes() % hunk_size != 0)
2171 report_error(1, "Hunk size is not an even multiple or divisor of input hunk size");
2172
2173 // process compression; we default to our current preferences using metadata to pick the type
2174 chd_codec_type compression[4];
2175 {
2176 std::vector<uint8_t> metadata;
2177 if (input_chd.read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
2178 memcpy(compression, s_default_hd_compression, sizeof(compression));
2179 else if (input_chd.read_metadata(AV_METADATA_TAG, 0, metadata) == CHDERR_NONE)
2180 memcpy(compression, s_default_ld_compression, sizeof(compression));
2181 else if (input_chd.read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
2182 input_chd.read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
2183 input_chd.read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE ||
2184 input_chd.read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
2185 input_chd.read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
2186 memcpy(compression, s_default_cd_compression, sizeof(compression));
2187 else
2188 memcpy(compression, s_default_raw_compression, sizeof(compression));
2189 }
2190 parse_compression(params, compression);
2191
2192 // process numprocessors
2193 parse_numprocessors(params);
2194
2195 // print some info
2196 printf("Output CHD: %s\n", output_chd_str->c_str());
2197 if (output_parent.opened())
2198 printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
2199 printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
2200 if (input_start != 0 || input_end != input_chd.logical_bytes())
2201 {
2202 printf("Input start: %s\n", big_int_string(input_start).c_str());
2203 printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
2204 }
2205 printf("Compression: %s\n", compression_string(compression).c_str());
2206 printf("Hunk size: %s\n", big_int_string(hunk_size).c_str());
2207 printf("Logical size: %s\n", big_int_string(input_end - input_start).c_str());
2208
2209 // catch errors so we can close & delete the output file
2210 chd_chdfile_compressor *chd = nullptr;
2211 try
2212 {
2213 // create the new CHD
2214 chd = new chd_chdfile_compressor(input_chd, input_start, input_end);
2215 chd_error err;
2216 if (output_parent.opened())
2217 err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, compression, output_parent);
2218 else
2219 err = chd->create(output_chd_str->c_str(), input_end - input_start, hunk_size, input_chd.unit_bytes(), compression);
2220 if (err != CHDERR_NONE)
2221 report_error(1, "Error creating CHD file (%s): %s", output_chd_str->c_str(), chd_file::error_string(err));
2222
2223 // clone all the metadata, upgrading where appropriate
2224 std::vector<uint8_t> metadata;
2225 chd_metadata_tag metatag;
2226 uint8_t metaflags;
2227 uint32_t index = 0;
2228 bool redo_cd = false;
2229 bool cdda_swap = false;
2230 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))
2231 {
2232 // if this is an old CD-CHD tag, note that we want to re-do it
2233 if (metatag == CDROM_OLD_METADATA_TAG || metatag == CDROM_TRACK_METADATA_TAG)
2234 {
2235 redo_cd = true;
2236 continue;
2237 }
2238 // if this is old GD tag we want re-do it and swap CDDA
2239 if (metatag == GDROM_OLD_METADATA_TAG)
2240 {
2241 cdda_swap = redo_cd = true;
2242 continue;
2243 }
2244
2245 // otherwise, clone it
2246 err = chd->write_metadata(metatag, CHDMETAINDEX_APPEND, metadata, metaflags);
2247 if (err != CHDERR_NONE)
2248 report_error(1, "Error writing cloned metadata: %s", chd_file::error_string(err));
2249 }
2250
2251 // if we need to re-do the CD metadata, do it now
2252 if (redo_cd)
2253 {
2254 cdrom_file *cdrom = cdrom_open(&input_chd);
2255 if (cdrom == nullptr)
2256 report_error(1, "Error upgrading CD metadata");
2257 const cdrom_toc *toc = cdrom_get_toc(cdrom);
2258 err = cdrom_write_metadata(chd, toc);
2259 if (err != CHDERR_NONE)
2260 report_error(1, "Error writing upgraded CD metadata: %s", chd_file::error_string(err));
2261 if (cdda_swap)
2262 chd->m_toc = toc;
2263 }
2264
2265 // compress it generically
2266 compress_common(*chd);
2267 delete chd;
2268 }
2269 catch (...)
2270 {
2271 delete chd;
2272 // delete the output file
2273 auto output_chd_str = params.find(OPTION_OUTPUT);
2274 if (output_chd_str != params.end())
2275 osd_file::remove(*output_chd_str->second);
2276 throw;
2277 }
2278 }
2279
2280
2281 //-------------------------------------------------
2282 // do_extract_raw - extract a raw file from a
2283 // CHD image
2284 //-------------------------------------------------
2285
do_extract_raw(parameters_map & params)2286 static void do_extract_raw(parameters_map ¶ms)
2287 {
2288 // parse out input files
2289 chd_file input_parent_chd;
2290 chd_file input_chd;
2291 parse_input_chd_parameters(params, input_chd, input_parent_chd);
2292
2293 // parse out input start/end
2294 uint64_t input_start;
2295 uint64_t input_end;
2296 parse_input_start_end(params, input_chd.logical_bytes(), input_chd.hunk_bytes(), input_chd.hunk_bytes(), input_start, input_end);
2297
2298 // verify output file doesn't exist
2299 auto output_file_str = params.find(OPTION_OUTPUT);
2300 if (output_file_str != params.end())
2301 check_existing_output_file(params, output_file_str->second->c_str());
2302
2303 // print some info
2304 printf("Output File: %s\n", output_file_str->second->c_str());
2305 printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
2306 if (input_start != 0 || input_end != input_chd.logical_bytes())
2307 {
2308 printf("Input start: %s\n", big_int_string(input_start).c_str());
2309 printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
2310 }
2311
2312 // catch errors so we can close & delete the output file
2313 util::core_file::ptr output_file;
2314 try
2315 {
2316 // process output file
2317 osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
2318 if (filerr != osd_file::error::NONE)
2319 report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
2320
2321 // copy all data
2322 std::vector<uint8_t> buffer((TEMP_BUFFER_SIZE / input_chd.hunk_bytes()) * input_chd.hunk_bytes());
2323 for (uint64_t offset = input_start; offset < input_end; )
2324 {
2325 progress(false, "Extracting, %.1f%% complete... \r", 100.0 * double(offset - input_start) / double(input_end - input_start));
2326
2327 // determine how much to read
2328 uint32_t bytes_to_read = (std::min<uint64_t>)(buffer.size(), input_end - offset);
2329 chd_error err = input_chd.read_bytes(offset, &buffer[0], bytes_to_read);
2330 if (err != CHDERR_NONE)
2331 report_error(1, "Error reading CHD file (%s): %s", params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err));
2332
2333 // write to the output
2334 uint32_t count = output_file->write(&buffer[0], bytes_to_read);
2335 if (count != bytes_to_read)
2336 report_error(1, "Error writing to file; check disk space (%s)", output_file_str->second->c_str());
2337
2338 // advance
2339 offset += bytes_to_read;
2340 }
2341
2342 // finish up
2343 output_file.reset();
2344 printf("Extraction complete \n");
2345 }
2346 catch (...)
2347 {
2348 // delete the output file
2349 if (output_file != nullptr)
2350 {
2351 output_file.reset();
2352 osd_file::remove(*output_file_str->second);
2353 }
2354 throw;
2355 }
2356 }
2357
2358
2359 //-------------------------------------------------
2360 // do_extract_cd - extract a CD file from a
2361 // CHD image
2362 //-------------------------------------------------
2363
do_extract_cd(parameters_map & params)2364 static void do_extract_cd(parameters_map ¶ms)
2365 {
2366 // parse out input files
2367 chd_file input_parent_chd;
2368 chd_file input_chd;
2369 parse_input_chd_parameters(params, input_chd, input_parent_chd);
2370
2371 // further process input file
2372 cdrom_file *cdrom = cdrom_open(&input_chd);
2373 if (cdrom == nullptr)
2374 report_error(1, "Unable to recognize CHD file as a CD");
2375 const cdrom_toc *toc = cdrom_get_toc(cdrom);
2376
2377 // verify output file doesn't exist
2378 auto output_file_str = params.find(OPTION_OUTPUT);
2379 if (output_file_str != params.end())
2380 check_existing_output_file(params, output_file_str->second->c_str());
2381
2382 // verify output BIN file doesn't exist
2383 auto output_bin_file_fnd = params.find(OPTION_OUTPUT_BIN);
2384 std::string default_name(*output_file_str->second);
2385 int chop = default_name.find_last_of('.');
2386 if (chop != -1)
2387 default_name.erase(chop, default_name.size());
2388 char basename[128];
2389 strncpy(basename, default_name.c_str(), 127);
2390 default_name.append(".bin");
2391 std::string *output_bin_file_str;
2392 if (output_bin_file_fnd == params.end())
2393 output_bin_file_str = &default_name;
2394 else
2395 output_bin_file_str = output_bin_file_fnd->second;
2396
2397 check_existing_output_file(params, output_bin_file_str->c_str());
2398
2399 // print some info
2400 printf("Output TOC: %s\n", output_file_str->second->c_str());
2401 printf("Output Data: %s\n", output_bin_file_str->c_str());
2402 printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
2403
2404 // catch errors so we can close & delete the output file
2405 util::core_file::ptr output_bin_file;
2406 util::core_file::ptr output_toc_file;
2407 try
2408 {
2409 int mode = MODE_NORMAL;
2410
2411 if (output_file_str->second->find(".cue") != -1)
2412 {
2413 mode = MODE_CUEBIN;
2414 }
2415 else if (output_file_str->second->find(".gdi") != -1)
2416 {
2417 mode = MODE_GDI;
2418 }
2419
2420 // process output file
2421 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);
2422 if (filerr != osd_file::error::NONE)
2423 report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
2424
2425 // process output BIN file
2426 if (mode != MODE_GDI)
2427 {
2428 filerr = util::core_file::open(*output_bin_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file);
2429 if (filerr != osd_file::error::NONE)
2430 report_error(1, "Unable to open file (%s)", output_bin_file_str->c_str());
2431 }
2432
2433 // determine total frames
2434 uint64_t total_bytes = 0;
2435 for (int tracknum = 0; tracknum < toc->numtrks; tracknum++)
2436 total_bytes += toc->tracks[tracknum].frames * (toc->tracks[tracknum].datasize + toc->tracks[tracknum].subsize);
2437
2438 // GDI must start with the # of tracks
2439 if (mode == MODE_GDI)
2440 {
2441 output_toc_file->printf("%d\n", toc->numtrks);
2442 }
2443
2444 // iterate over tracks and copy all data
2445 uint64_t outputoffs = 0;
2446 uint32_t discoffs = 0;
2447 std::vector<uint8_t> buffer;
2448 for (int tracknum = 0; tracknum < toc->numtrks; tracknum++)
2449 {
2450 std::string trackbin_name(basename);
2451
2452 if (mode == MODE_GDI)
2453 {
2454 char temp[11];
2455 sprintf(temp, "%02d", tracknum+1);
2456 trackbin_name.append(temp);
2457 if (toc->tracks[tracknum].trktype == CD_TRACK_AUDIO)
2458 trackbin_name.append(".raw");
2459 else
2460 trackbin_name.append(".bin");
2461
2462 output_bin_file.reset();
2463
2464 filerr = util::core_file::open(trackbin_name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file);
2465 if (filerr != osd_file::error::NONE)
2466 report_error(1, "Unable to open file (%s)", trackbin_name.c_str());
2467
2468 outputoffs = 0;
2469 }
2470
2471 // output the metadata about the track to the TOC file
2472 const cdrom_track_info &trackinfo = toc->tracks[tracknum];
2473 if (mode == MODE_GDI)
2474 {
2475 output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, core_filename_extract_base(trackbin_name).c_str(), discoffs, outputoffs);
2476 }
2477 else
2478 {
2479 output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, core_filename_extract_base(*output_bin_file_str).c_str(), discoffs, outputoffs);
2480 }
2481
2482 // If this is bin/cue output and the CHD contains subdata, warn the user and don't include
2483 // the subdata size in the buffer calculation.
2484 uint32_t output_frame_size = trackinfo.datasize + ((trackinfo.subtype != CD_SUB_NONE) ? trackinfo.subsize : 0);
2485 if (trackinfo.subtype != CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI)))
2486 {
2487 printf("Warning: Track %d has subcode data. bin/cue and gdi formats cannot contain subcode data and it will be omitted.\n", tracknum+1);
2488 printf(" : This may affect usage of the output image. Use bin/toc output to keep all data.\n");
2489 output_frame_size = trackinfo.datasize;
2490 }
2491
2492 // resize the buffer for the track
2493 buffer.resize((TEMP_BUFFER_SIZE / output_frame_size) * output_frame_size);
2494
2495 // now read and output the actual data
2496 uint32_t bufferoffs = 0;
2497 uint32_t actualframes = trackinfo.frames - trackinfo.padframes;
2498 for (uint32_t frame = 0; frame < actualframes; frame++)
2499 {
2500 progress(false, "Extracting, %.1f%% complete... \r", 100.0 * double(outputoffs) / double(total_bytes));
2501
2502 // read the data
2503 cdrom_read_data(cdrom, cdrom_get_track_start_phys(cdrom, tracknum) + frame, &buffer[bufferoffs], trackinfo.trktype, true);
2504
2505 // for CDRWin and GDI audio tracks must be reversed
2506 // in the case of GDI and CHD version < 5 we assuming source CHD image is GDROM so audio tracks is already reversed
2507 if (((mode == MODE_GDI && input_chd.version() > 4) || (mode == MODE_CUEBIN)) && (trackinfo.trktype == CD_TRACK_AUDIO))
2508 for (int swapindex = 0; swapindex < trackinfo.datasize; swapindex += 2)
2509 {
2510 uint8_t swaptemp = buffer[bufferoffs + swapindex];
2511 buffer[bufferoffs + swapindex] = buffer[bufferoffs + swapindex + 1];
2512 buffer[bufferoffs + swapindex + 1] = swaptemp;
2513 }
2514 bufferoffs += trackinfo.datasize;
2515 discoffs++;
2516
2517 // read the subcode data
2518 if (trackinfo.subtype != CD_SUB_NONE && (mode == MODE_NORMAL))
2519 {
2520 cdrom_read_subcode(cdrom, cdrom_get_track_start_phys(cdrom, tracknum) + frame, &buffer[bufferoffs], true);
2521 bufferoffs += trackinfo.subsize;
2522 }
2523
2524 // write it out if we need to
2525 if (bufferoffs == buffer.size() || frame == actualframes - 1)
2526 {
2527 output_bin_file->seek(outputoffs, SEEK_SET);
2528 uint32_t byteswritten = output_bin_file->write(&buffer[0], bufferoffs);
2529 if (byteswritten != bufferoffs)
2530 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));
2531 outputoffs += bufferoffs;
2532 bufferoffs = 0;
2533 }
2534 }
2535
2536 discoffs += trackinfo.padframes;
2537 }
2538
2539 // finish up
2540 output_bin_file.reset();
2541 output_toc_file.reset();
2542 printf("Extraction complete \n");
2543 }
2544 catch (...)
2545 {
2546 // delete the output files
2547 output_bin_file.reset();
2548 output_toc_file.reset();
2549 osd_file::remove(*output_bin_file_str);
2550 osd_file::remove(*output_file_str->second);
2551 throw;
2552 }
2553 }
2554
2555
2556 //-------------------------------------------------
2557 // do_extract_ld - extract an AVI file from a
2558 // CHD image
2559 //-------------------------------------------------
2560
do_extract_ld(parameters_map & params)2561 static void do_extract_ld(parameters_map ¶ms)
2562 {
2563 // parse out input files
2564 chd_file input_parent_chd;
2565 chd_file input_chd;
2566 parse_input_chd_parameters(params, input_chd, input_parent_chd);
2567
2568 // read core metadata
2569 std::string metadata;
2570 chd_error err = input_chd.read_metadata(AV_METADATA_TAG, 0, metadata);
2571 if (err != CHDERR_NONE)
2572 report_error(1, "Unable to find A/V metadata in the input CHD");
2573
2574 // parse the metadata
2575 uint32_t fps_times_1million;
2576 uint32_t max_samples_per_frame;
2577 uint32_t frame_bytes;
2578 int width;
2579 int height;
2580 int interlaced;
2581 int channels;
2582 int rate;
2583 {
2584 int fps;
2585 int fpsfrac;
2586 if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7)
2587 report_error(1, "Improperly formatted A/V metadata found");
2588 fps_times_1million = fps * 1000000 + fpsfrac;
2589 }
2590 uint8_t interlace_factor = interlaced ? 2 : 1;
2591
2592 // determine key parameters and validate
2593 max_samples_per_frame = (uint64_t(rate) * 1000000 + fps_times_1million - 1) / fps_times_1million;
2594 frame_bytes = avhuff_encoder::raw_data_size(width, height, channels, max_samples_per_frame);
2595 if (frame_bytes != input_chd.hunk_bytes())
2596 report_error(1, "Frame size does not match hunk size for this CHD");
2597
2598 // parse out input start/end
2599 uint64_t input_start;
2600 uint64_t input_end;
2601 parse_input_start_end(params, input_chd.hunk_count() / interlace_factor, 0, 1, input_start, input_end);
2602 input_start *= interlace_factor;
2603 input_end *= interlace_factor;
2604
2605 // build up the movie info
2606 avi_file::movie_info info;
2607 info.video_format = FORMAT_YUY2;
2608 info.video_timescale = fps_times_1million / interlace_factor;
2609 info.video_sampletime = 1000000;
2610 info.video_width = width;
2611 info.video_height = height * interlace_factor;
2612 info.video_depth = 16;
2613 info.audio_format = 0;
2614 info.audio_timescale = rate;
2615 info.audio_sampletime = 1;
2616 info.audio_channels = channels;
2617 info.audio_samplebits = 16;
2618 info.audio_samplerate = rate;
2619
2620 // verify output file doesn't exist
2621 auto output_file_str = params.find(OPTION_OUTPUT);
2622 if (output_file_str != params.end())
2623 check_existing_output_file(params, output_file_str->second->c_str());
2624
2625 // print some info
2626 printf("Output File: %s\n", output_file_str->second->c_str());
2627 printf("Input CHD: %s\n", params.find(OPTION_INPUT)->second->c_str());
2628 if (input_start != 0 || input_end != input_chd.hunk_count())
2629 {
2630 printf("Input start: %s\n", big_int_string(input_start).c_str());
2631 printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
2632 }
2633
2634 // catch errors so we can close & delete the output file
2635 avi_file::ptr output_file;
2636 try
2637 {
2638 // process output file
2639 avi_file::error avierr = avi_file::create(*output_file_str->second, info, output_file);
2640 if (avierr != avi_file::error::NONE)
2641 report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
2642
2643 // create the codec configuration
2644 avhuff_decoder::config avconfig;
2645 bitmap_yuy16 avvideo;
2646 std::vector<int16_t> audio_data[16];
2647 uint32_t actsamples;
2648 avconfig.video = &avvideo;
2649 avconfig.maxsamples = max_samples_per_frame;
2650 avconfig.actsamples = &actsamples;
2651 for (int chnum = 0; chnum < ARRAY_LENGTH(audio_data); chnum++)
2652 {
2653 audio_data[chnum].resize(std::max(1U,max_samples_per_frame));
2654 avconfig.audio[chnum] = &audio_data[chnum][0];
2655 }
2656
2657 // iterate over frames
2658 bitmap_yuy16 fullbitmap(width, height * interlace_factor);
2659 for (uint64_t framenum = input_start; framenum < input_end; framenum++)
2660 {
2661 progress(framenum == input_start, "Extracting, %.1f%% complete... \r", 100.0 * double(framenum - input_start) / double(input_end - input_start));
2662
2663 // set up the fake bitmap for this frame
2664 avvideo.wrap(&fullbitmap.pix(framenum % interlace_factor), fullbitmap.width(), fullbitmap.height() / interlace_factor, fullbitmap.rowpixels() * interlace_factor);
2665 input_chd.codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig);
2666
2667 // read the hunk into the buffers
2668 chd_error err = input_chd.read_hunk(framenum, nullptr);
2669 if (err != CHDERR_NONE)
2670 {
2671 uint64_t filepos = static_cast<util::core_file &>(input_chd).tell();
2672 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));
2673 }
2674
2675 // write audio
2676 for (int chnum = 0; chnum < channels; chnum++)
2677 {
2678 avi_file::error avierr = output_file->append_sound_samples(chnum, avconfig.audio[chnum], actsamples, 0);
2679 if (avierr != avi_file::error::NONE)
2680 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));
2681 }
2682
2683 // write video
2684 if ((framenum + 1) % interlace_factor == 0)
2685 {
2686 avi_file::error avierr = output_file->append_video_frame(fullbitmap);
2687 if (avierr != avi_file::error::NONE)
2688 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));
2689 }
2690 }
2691
2692 // close and return
2693 output_file.reset();
2694 printf("Extraction complete \n");
2695 }
2696 catch (...)
2697 {
2698 // delete the output file
2699 output_file.reset();
2700 osd_file::remove(*output_file_str->second);
2701 throw;
2702 }
2703 }
2704
2705
2706 //-------------------------------------------------
2707 // do_add_metadata - add metadata to a CHD from a
2708 // file
2709 //-------------------------------------------------
2710
do_add_metadata(parameters_map & params)2711 static void do_add_metadata(parameters_map ¶ms)
2712 {
2713 // parse out input files
2714 chd_file input_parent_chd;
2715 chd_file input_chd;
2716 parse_input_chd_parameters(params, input_chd, input_parent_chd, true);
2717
2718 // process tag
2719 chd_metadata_tag tag = CHD_MAKE_TAG('?','?','?','?');
2720 auto tag_str = params.find(OPTION_TAG);
2721 if (tag_str != params.end())
2722 {
2723 tag_str->second->append(" ");
2724 tag = CHD_MAKE_TAG((*tag_str->second)[0], (*tag_str->second)[1], (*tag_str->second)[2], (*tag_str->second)[3]);
2725 }
2726
2727 // process index
2728 uint32_t index = 0;
2729 auto index_str = params.find(OPTION_INDEX);
2730 if (index_str != params.end())
2731 index = atoi(index_str->second->c_str());
2732
2733 // process text input
2734 auto text_str = params.find(OPTION_VALUE_TEXT);
2735 std::string text;
2736 if (text_str != params.end())
2737 {
2738 text = *text_str->second;
2739 if (text[0] == '"' && text[text.length() - 1] == '"')
2740 *text_str->second = text.substr(1, text.length() - 2);
2741 }
2742
2743 // process file input
2744 auto file_str = params.find(OPTION_VALUE_FILE);
2745 std::vector<uint8_t> file;
2746 if (file_str != params.end())
2747 {
2748 osd_file::error filerr = util::core_file::load(file_str->second->c_str(), file);
2749 if (filerr != osd_file::error::NONE)
2750 report_error(1, "Error reading metadata file (%s)", file_str->second->c_str());
2751 }
2752
2753 // make sure we have one or the other
2754 if (text_str == params.end() && file_str == params.end())
2755 report_error(1, "Error: missing either --valuetext/-vt or --valuefile/-vf parameters");
2756 if (text_str != params.end() && file_str != params.end())
2757 report_error(1, "Error: both --valuetext/-vt or --valuefile/-vf parameters specified; only one permitted");
2758
2759 // process no checksum
2760 uint8_t flags = CHD_MDFLAGS_CHECKSUM;
2761 if (params.find(OPTION_NO_CHECKSUM) != params.end())
2762 flags &= ~CHD_MDFLAGS_CHECKSUM;
2763
2764 // print some info
2765 printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
2766 printf("Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
2767 printf("Index: %d\n", index);
2768 if (text_str != params.end())
2769 printf("Text: %s\n", text.c_str());
2770 else
2771 printf("Data: %s (%d bytes)\n", file_str->second->c_str(), int(file.size()));
2772
2773 // write the metadata
2774 chd_error err;
2775 if (text_str != params.end())
2776 err = input_chd.write_metadata(tag, index, text, flags);
2777 else
2778 err = input_chd.write_metadata(tag, index, file, flags);
2779 if (err != CHDERR_NONE)
2780 report_error(1, "Error adding metadata: %s", chd_file::error_string(err));
2781 else
2782 printf("Metadata added\n");
2783 }
2784
2785
2786 //-------------------------------------------------
2787 // do_del_metadata - remove metadata from a CHD
2788 //-------------------------------------------------
2789
do_del_metadata(parameters_map & params)2790 static void do_del_metadata(parameters_map ¶ms)
2791 {
2792 // parse out input files
2793 chd_file input_parent_chd;
2794 chd_file input_chd;
2795 parse_input_chd_parameters(params, input_chd, input_parent_chd, true);
2796
2797 // process tag
2798 chd_metadata_tag tag = CHD_MAKE_TAG('?','?','?','?');
2799 auto tag_str = params.find(OPTION_TAG);
2800 if (tag_str != params.end())
2801 {
2802 tag_str->second->append(" ");
2803 tag = CHD_MAKE_TAG((*tag_str->second)[0], (*tag_str->second)[1], (*tag_str->second)[2], (*tag_str->second)[3]);
2804 }
2805
2806 // process index
2807 uint32_t index = 0;
2808 auto index_str = params.find(OPTION_INDEX);
2809 if (index_str != params.end())
2810 index = atoi(index_str->second->c_str());
2811
2812 // print some info
2813 printf("Input file: %s\n", params.find(OPTION_INPUT)->second->c_str());
2814 printf("Tag: %c%c%c%c\n", (tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
2815 printf("Index: %d\n", index);
2816
2817 // write the metadata
2818 chd_error err = input_chd.delete_metadata(tag, index);
2819 if (err != CHDERR_NONE)
2820 report_error(1, "Error removing metadata: %s", chd_file::error_string(err));
2821 else
2822 printf("Metadata removed\n");
2823 }
2824
2825
2826 //-------------------------------------------------
2827 // do_dump_metadata - dump metadata from a CHD
2828 //-------------------------------------------------
2829
do_dump_metadata(parameters_map & params)2830 static void do_dump_metadata(parameters_map ¶ms)
2831 {
2832 // parse out input files
2833 chd_file input_parent_chd;
2834 chd_file input_chd;
2835 parse_input_chd_parameters(params, input_chd, input_parent_chd);
2836
2837 // verify output file doesn't exist
2838 auto output_file_str = params.find(OPTION_OUTPUT);
2839 if (output_file_str != params.end())
2840 check_existing_output_file(params, output_file_str->second->c_str());
2841
2842 // process tag
2843 chd_metadata_tag tag = CHD_MAKE_TAG('?','?','?','?');
2844 auto tag_str = params.find(OPTION_TAG);
2845 if (tag_str != params.end())
2846 {
2847 tag_str->second->append(" ");
2848 tag = CHD_MAKE_TAG((*tag_str->second)[0], (*tag_str->second)[1], (*tag_str->second)[2], (*tag_str->second)[3]);
2849 }
2850
2851 // process index
2852 uint32_t index = 0;
2853 auto index_str = params.find(OPTION_INDEX);
2854 if (index_str != params.end())
2855 index = atoi(index_str->second->c_str());
2856
2857 // write the metadata
2858 std::vector<uint8_t> buffer;
2859 chd_error err = input_chd.read_metadata(tag, index, buffer);
2860 if (err != CHDERR_NONE)
2861 report_error(1, "Error reading metadata: %s", chd_file::error_string(err));
2862
2863 // catch errors so we can close & delete the output file
2864 util::core_file::ptr output_file;
2865 try
2866 {
2867 // create the file
2868 if (output_file_str != params.end())
2869 {
2870 osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
2871 if (filerr != osd_file::error::NONE)
2872 report_error(1, "Unable to open file (%s)", output_file_str->second->c_str());
2873
2874 // output the metadata
2875 uint32_t count = output_file->write(&buffer[0], buffer.size());
2876 if (count != buffer.size())
2877 report_error(1, "Error writing file (%s)", output_file_str->second->c_str());
2878 output_file.reset();
2879
2880 // provide some feedback
2881 printf("File (%s) written, %s bytes\n", output_file_str->second->c_str(), big_int_string(buffer.size()).c_str());
2882 }
2883
2884 // flush to stdout
2885 else
2886 {
2887 fwrite(&buffer[0], 1, buffer.size(), stdout);
2888 fflush(stdout);
2889 }
2890 }
2891 catch (...)
2892 {
2893 // delete the output file
2894 output_file.reset();
2895 osd_file::remove(*output_file_str->second);
2896 throw;
2897 }
2898 }
2899
2900
2901 //-------------------------------------------------
2902 // do_dump_metadata - dump metadata from a CHD
2903 //-------------------------------------------------
2904
do_list_templates(parameters_map & params)2905 static void do_list_templates(parameters_map ¶ms)
2906 {
2907 printf("\n");
2908 printf("ID Manufacturer Model Cylinders Heads Sectors Sector Size Total Size\n");
2909 printf("------------------------------------------------------------------------------------\n");
2910
2911 for (int id = 0; id < ARRAY_LENGTH(s_hd_templates); id++)
2912 {
2913 printf("%2d %-13s %-15s %9d %5d %7d %11d %7d MB\n",
2914 id,
2915 s_hd_templates[id].manufacturer,
2916 s_hd_templates[id].model,
2917 s_hd_templates[id].cylinders,
2918 s_hd_templates[id].heads,
2919 s_hd_templates[id].sectors,
2920 s_hd_templates[id].sector_size,
2921 (s_hd_templates[id].cylinders * s_hd_templates[id].heads * s_hd_templates[id].sectors * s_hd_templates[id].sector_size) / 1024 / 1024
2922 );
2923 }
2924 }
2925
2926
2927 //-------------------------------------------------
2928 // main - entry point
2929 //-------------------------------------------------
2930
main(int argc,char * argv[])2931 int CLIB_DECL main(int argc, char *argv[])
2932 {
2933 const std::vector<std::string> args = osd_get_command_line(argc, argv);
2934
2935 // print the header
2936 extern const char build_version[];
2937 printf("chdman - MAME Compressed Hunks of Data (CHD) manager %s\n", build_version);
2938
2939 // handle help specially
2940 if (args.size() < 2)
2941 return print_help(args[0]);
2942 int argnum = 1;
2943 std::string command = args[argnum++];
2944 bool help(command == COMMAND_HELP);
2945 if (help)
2946 {
2947 if (args.size() <= 2)
2948 return print_help(args[0]);
2949 command = args[argnum++];
2950 }
2951
2952 // iterate over commands to find our match
2953 for (auto & s_command : s_commands)
2954 if (command == s_command.name)
2955 {
2956 const command_description &desc = s_command;
2957
2958 // print help if that was requested
2959 if (help)
2960 return print_help(args[0], desc);
2961
2962 // otherwise, verify the parameters
2963 parameters_map parameters;
2964 while (argnum < args.size())
2965 {
2966 // should be an option name
2967 const std::string &arg = args[argnum++];
2968 if (arg.empty() || (arg[0] != '-'))
2969 return print_help(args[0], desc, "Expected option, not parameter");
2970
2971 // iterate over valid options
2972 int valid;
2973 for (valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
2974 {
2975 // reduce to the option name
2976 const char *validname = desc.valid_options[valid];
2977 if (validname == nullptr)
2978 break;
2979 if (*validname == REQUIRED[0])
2980 validname++;
2981
2982 // find the matching option description
2983 int optnum;
2984 for (optnum = 0; optnum < ARRAY_LENGTH(s_options); optnum++)
2985 if (strcmp(s_options[optnum].name, validname) == 0)
2986 break;
2987 assert(optnum != ARRAY_LENGTH(s_options));
2988
2989 // do we match?
2990 const option_description &odesc = s_options[optnum];
2991 if ((arg[1] == '-' && strcmp(odesc.name, &arg[2]) == 0) ||
2992 (arg[1] != '-' && odesc.shortname != nullptr && strcmp(odesc.shortname, &arg[1]) == 0))
2993 {
2994 // if we need a parameter, consume it
2995 const char *param = "";
2996 if (odesc.parameter)
2997 {
2998 if (argnum >= args.size() || (!args[argnum].empty() && args[argnum][0] == '-'))
2999 return print_help(args[0], desc, "Option is missing parameter");
3000 param = args[argnum++].c_str();
3001 }
3002
3003 // add to the map
3004 if (!parameters.insert(std::make_pair(odesc.name, new std::string(param))).second)
3005 return print_help(args[0], desc, "Multiple parameters of the same type specified");
3006 break;
3007 }
3008 }
3009
3010 // if not valid, error
3011 if (valid == ARRAY_LENGTH(desc.valid_options))
3012 return print_help(args[0], desc, "Option not valid for this command");
3013 }
3014
3015 // make sure we got all our required parameters
3016 for (int valid = 0; valid < ARRAY_LENGTH(desc.valid_options); valid++)
3017 {
3018 const char *validname = desc.valid_options[valid];
3019 if (validname == nullptr)
3020 break;
3021 if (*validname == REQUIRED[0] && parameters.find(++validname) == parameters.end())
3022 return print_help(args[0], desc, "Required parameters missing");
3023 }
3024
3025 // all clear, run the command
3026 try
3027 {
3028 (*s_command.handler)(parameters);
3029 return 0;
3030 }
3031 catch (chd_error &err)
3032 {
3033 fprintf(stderr, "CHD error occurred (main): %s\n", chd_file::error_string(err));
3034 return 1;
3035 }
3036 catch (fatal_error &err)
3037 {
3038 fprintf(stderr, "Fatal error occurred: %d\n", err.error());
3039 return err.error();
3040 }
3041 catch (std::exception& ex)
3042 {
3043 fprintf(stderr, "Unhandled exception: %s\n", ex.what());
3044 return 1;
3045 }
3046 }
3047
3048 // print generic help if nothing found
3049 return print_help(args[0]);
3050 }
3051