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 &params);
121 static void do_verify(parameters_map &params);
122 static void do_create_raw(parameters_map &params);
123 static void do_create_hd(parameters_map &params);
124 static void do_create_cd(parameters_map &params);
125 static void do_create_ld(parameters_map &params);
126 static void do_copy(parameters_map &params);
127 static void do_extract_raw(parameters_map &params);
128 static void do_extract_cd(parameters_map &params);
129 static void do_extract_ld(parameters_map &params);
130 static void do_add_metadata(parameters_map &params);
131 static void do_del_metadata(parameters_map &params);
132 static void do_dump_metadata(parameters_map &params);
133 static void do_list_templates(parameters_map &params);
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 &sectors, 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, &sectors, &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 &params, 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 &params, 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 &params, 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 &params, 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 &params, 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 &params, 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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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, &sectors) != 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, &sectors, &sector_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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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 &params)
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