1
2 #include "chdtypes.h"
3
4 #include "chd.h"
5 #include "hashing.h"
6 #include "chdcdrom.h"
7 #include "coretmpl.h"
8 #include "bitstream.h"
9 #include "huffman.h"
10
11 // standard metadata formats
12 const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d";
13 const char *CDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d";
14 const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
15 const char *GDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
16 const char *AV_METADATA_FORMAT = "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d";
17
18 static const UINT32 METADATA_HEADER_SIZE = 16; // metadata header size
19
20 static const UINT8 V34_MAP_ENTRY_FLAG_TYPE_MASK = 0x0f; // what type of hunk
21 static const UINT8 V34_MAP_ENTRY_FLAG_NO_CRC = 0x10; // no CRC is present
22
23
24
25 // V3-V4 entry types
26 enum
27 {
28 V34_MAP_ENTRY_TYPE_INVALID = 0, // invalid type
29 V34_MAP_ENTRY_TYPE_COMPRESSED = 1, // standard compression
30 V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, // uncompressed data
31 V34_MAP_ENTRY_TYPE_MINI = 3, // mini: use offset as raw data
32 V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, // same as another hunk in this file
33 V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, // same as a hunk in the parent file
34 V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 // compressed with secondary algorithm (usually FLAC CDDA)
35 };
36
37 // V5 compression types
38 enum
39 {
40 // these types are live when running
41 COMPRESSION_TYPE_0 = 0, // codec #0
42 COMPRESSION_TYPE_1 = 1, // codec #1
43 COMPRESSION_TYPE_2 = 2, // codec #2
44 COMPRESSION_TYPE_3 = 3, // codec #3
45 COMPRESSION_NONE = 4, // no compression; implicit length = hunkbytes
46 COMPRESSION_SELF = 5, // same as another block in this chd
47 COMPRESSION_PARENT = 6, // same as a hunk's worth of units in the parent chd
48
49 // these additional pseudo-types are used for compressed encodings:
50 COMPRESSION_RLE_SMALL, // start of small RLE run (4-bit length)
51 COMPRESSION_RLE_LARGE, // start of large RLE run (8-bit length)
52 COMPRESSION_SELF_0, // same as the last COMPRESSION_SELF block
53 COMPRESSION_SELF_1, // same as the last COMPRESSION_SELF block + 1
54 COMPRESSION_PARENT_SELF, // same block in the parent
55 COMPRESSION_PARENT_0, // same as the last COMPRESSION_PARENT block
56 COMPRESSION_PARENT_1 // same as the last COMPRESSION_PARENT block + 1
57 };
58
59
60 //**************************************************************************
61 // TYPE DEFINITIONS
62 //**************************************************************************
63
64 // ======================> metadata_entry
65
66 // description of where a metadata entry lives within the file
67 struct chd_file::metadata_entry
68 {
69 UINT64 offset; // offset within the file of the header
70 UINT64 next; // offset within the file of the next header
71 UINT64 prev; // offset within the file of the previous header
72 UINT32 length; // length of the metadata
73 UINT32 metatag; // metadata tag
74 UINT8 flags; // flag bits
75 };
76
77
78 // ======================> metadata_hash
79
80 struct chd_file::metadata_hash
81 {
82 UINT8 tag[4]; // tag of the metadata in big-endian
83 sha1_t sha1; // hash data
84 };
85
86
87 //-------------------------------------------------
88 // be_read - extract a big-endian number from
89 // a byte buffer
90 //-------------------------------------------------
91
be_read(const UINT8 * base,int numbytes)92 inline UINT64 chd_file::be_read(const UINT8 *base, int numbytes)
93 {
94 UINT64 result = 0;
95 while (numbytes--)
96 result = (result << 8) | *base++;
97 return result;
98 }
99
100 //-------------------------------------------------
101 // be_write - write a big-endian number to a byte
102 // buffer
103 //-------------------------------------------------
104
be_write(UINT8 * base,UINT64 value,int numbytes)105 inline void chd_file::be_write(UINT8 *base, UINT64 value, int numbytes)
106 {
107 base += numbytes;
108 while (numbytes--)
109 {
110 *--base = value;
111 value >>= 8;
112 }
113 }
114
115 //-------------------------------------------------
116 // be_read_sha1 - fetch a sha1_t from a data
117 // stream in bigendian order
118 //-------------------------------------------------
119
be_read_sha1(const UINT8 * base)120 inline sha1_t chd_file::be_read_sha1(const UINT8 *base)
121 {
122 sha1_t result;
123 memcpy(&result.m_raw[0], base, sizeof(result.m_raw));
124 return result;
125 }
126
127 //-------------------------------------------------
128 // file_read - read from the file at the given
129 // offset; on failure throw an error
130 //-------------------------------------------------
131
file_read(UINT64 offset,void * dest,UINT32 length)132 inline void chd_file::file_read(UINT64 offset, void *dest, UINT32 length)
133 {
134 // no file = failure
135 if (m_file == NULL)
136 throw CHDERR_NOT_OPEN;
137
138 // seek and read
139 zfile_fseek(m_file, offset, SEEK_SET);
140 UINT32 count = zfile_fread(dest, 1, length, m_file);
141 if (count != length)
142 throw CHDERR_READ_ERROR;
143 }
144
145 //-------------------------------------------------
146 // hunk_info - return information about this
147 // hunk
148 //-------------------------------------------------
149
hunk_info(UINT32 hunknum,chd_codec_type & compressor,UINT32 & compbytes)150 chd_error chd_file::hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes)
151 {
152 // error if invalid
153 if (hunknum >= m_hunkcount)
154 return CHDERR_HUNK_OUT_OF_RANGE;
155
156 // get the map pointer
157 UINT8 *rawmap;
158 switch (m_version)
159 {
160 // v3/v4 map entries
161 case 3:
162 case 4:
163 rawmap = m_rawmap + 16 * hunknum;
164 switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
165 {
166 case V34_MAP_ENTRY_TYPE_COMPRESSED:
167 compressor = CHD_CODEC_ZLIB;
168 compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16);
169 break;
170
171 case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
172 compressor = CHD_CODEC_NONE;
173 compbytes = m_hunkbytes;
174 break;
175
176 case V34_MAP_ENTRY_TYPE_MINI:
177 compressor = CHD_CODEC_MINI;
178 compbytes = 0;
179 break;
180
181 case V34_MAP_ENTRY_TYPE_SELF_HUNK:
182 compressor = CHD_CODEC_SELF;
183 compbytes = 0;
184 break;
185
186 case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
187 compressor = CHD_CODEC_PARENT;
188 compbytes = 0;
189 break;
190 }
191 break;
192
193 // v5 map entries
194 case 5:
195 rawmap = m_rawmap + m_mapentrybytes * hunknum;
196
197 // uncompressed case
198 if (!compressed())
199 {
200 if (be_read(&rawmap[0], 4) == 0)
201 {
202 compressor = CHD_CODEC_PARENT;
203 compbytes = 0;
204 }
205 else
206 {
207 compressor = CHD_CODEC_NONE;
208 compbytes = m_hunkbytes;
209 }
210 break;
211 }
212
213 // compressed case
214 switch (rawmap[0])
215 {
216 case COMPRESSION_TYPE_0:
217 case COMPRESSION_TYPE_1:
218 case COMPRESSION_TYPE_2:
219 case COMPRESSION_TYPE_3:
220 compressor = m_compression[rawmap[0]];
221 compbytes = be_read(&rawmap[1], 3);
222 break;
223
224 case COMPRESSION_NONE:
225 compressor = CHD_CODEC_NONE;
226 compbytes = m_hunkbytes;
227 break;
228
229 case COMPRESSION_SELF:
230 compressor = CHD_CODEC_SELF;
231 compbytes = 0;
232 break;
233
234 case COMPRESSION_PARENT:
235 compressor = CHD_CODEC_PARENT;
236 compbytes = 0;
237 break;
238 }
239 break;
240 }
241 return CHDERR_NONE;
242 }
243
244 //-------------------------------------------------
245 // open - open an existing file for read or
246 // read/write
247 //-------------------------------------------------
248
open(struct zfile * file,bool writeable,chd_file * parent)249 chd_error chd_file::open(struct zfile *file, bool writeable, chd_file *parent)
250 {
251 // make sure we don't already have a file open
252 if (m_file != NULL)
253 return CHDERR_ALREADY_OPEN;
254
255 // open the file
256 m_file = file;
257 m_owns_file = false;
258 m_parent = parent;
259 return open_common(writeable);
260 }
261
262 //-------------------------------------------------
263 // close - close a CHD file for access
264 //-------------------------------------------------
265
close()266 void chd_file::close()
267 {
268 // reset file characteristics
269 if (m_owns_file && m_file != NULL)
270 zfile_fclose(m_file);
271 m_file = NULL;
272 m_owns_file = false;
273 m_allow_reads = false;
274 m_allow_writes = false;
275
276 // reset core parameters from the header
277 m_version = HEADER_VERSION;
278 m_logicalbytes = 0;
279 m_mapoffset = 0;
280 m_metaoffset = 0;
281 m_hunkbytes = 0;
282 m_hunkcount = 0;
283 m_unitbytes = 0;
284 m_unitcount = 0;
285 memset(m_compression, 0, sizeof(m_compression));
286 m_parent = NULL;
287 m_parent_missing = false;
288
289 // reset key offsets within the header
290 m_mapoffset_offset = 0;
291 m_metaoffset_offset = 0;
292 m_sha1_offset = 0;
293 m_rawsha1_offset = 0;
294 m_parentsha1_offset = 0;
295
296 // reset map information
297 m_mapentrybytes = 0;
298 m_rawmap.reset();
299
300 // reset compression management
301 for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++)
302 {
303 delete m_decompressor[decompnum];
304 m_decompressor[decompnum] = NULL;
305 }
306 m_compressed.reset();
307
308 // reset caching
309 m_cache.reset();
310 m_cachehunk = ~0;
311 }
312
313
314 //-------------------------------------------------
315 // read - read a single hunk from the CHD file
316 //-------------------------------------------------
317
read_hunk(UINT32 hunknum,void * buffer)318 chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer)
319 {
320 // wrap this for clean reporting
321 try
322 {
323 // punt if no file
324 if (m_file == NULL)
325 throw CHDERR_NOT_OPEN;
326
327 // return an error if out of range
328 if (hunknum >= m_hunkcount)
329 throw CHDERR_HUNK_OUT_OF_RANGE;
330
331 // get a pointer to the map entry
332 UINT64 blockoffs;
333 UINT32 blocklen;
334 UINT32 blockcrc;
335 UINT8 *rawmap;
336 UINT8 *dest = reinterpret_cast<UINT8 *>(buffer);
337 switch (m_version)
338 {
339 // v3/v4 map entries
340 case 3:
341 case 4:
342 rawmap = m_rawmap + 16 * hunknum;
343 blockoffs = be_read(&rawmap[0], 8);
344 blockcrc = be_read(&rawmap[8], 4);
345 switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
346 {
347 case V34_MAP_ENTRY_TYPE_COMPRESSED:
348 blocklen = be_read(&rawmap[12], 2) + (rawmap[14] << 16);
349 file_read(blockoffs, m_compressed, blocklen);
350 m_decompressor[0]->decompress(m_compressed, blocklen, dest, m_hunkbytes);
351 if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && dest != NULL && crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
352 throw CHDERR_DECOMPRESSION_ERROR;
353 return CHDERR_NONE;
354
355 case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
356 file_read(blockoffs, dest, m_hunkbytes);
357 if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
358 throw CHDERR_DECOMPRESSION_ERROR;
359 return CHDERR_NONE;
360
361 case V34_MAP_ENTRY_TYPE_MINI:
362 be_write(dest, blockoffs, 8);
363 for (UINT32 bytes = 8; bytes < m_hunkbytes; bytes++)
364 dest[bytes] = dest[bytes - 8];
365 if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
366 throw CHDERR_DECOMPRESSION_ERROR;
367 return CHDERR_NONE;
368
369 case V34_MAP_ENTRY_TYPE_SELF_HUNK:
370 return read_hunk(blockoffs, dest);
371
372 case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
373 if (m_parent_missing)
374 throw CHDERR_REQUIRES_PARENT;
375 return m_parent->read_hunk(blockoffs, dest);
376 }
377 break;
378
379 // v5 map entries
380 case 5:
381 rawmap = m_rawmap + m_mapentrybytes * hunknum;
382
383 // uncompressed case
384 if (!compressed())
385 {
386 blockoffs = UINT64(be_read(rawmap, 4)) * UINT64(m_hunkbytes);
387 if (blockoffs != 0)
388 file_read(blockoffs, dest, m_hunkbytes);
389 else if (m_parent_missing)
390 throw CHDERR_REQUIRES_PARENT;
391 else if (m_parent != NULL)
392 m_parent->read_hunk(hunknum, dest);
393 else
394 memset(dest, 0, m_hunkbytes);
395 return CHDERR_NONE;
396 }
397
398 // compressed case
399 blocklen = be_read(&rawmap[1], 3);
400 blockoffs = be_read(&rawmap[4], 6);
401 blockcrc = be_read(&rawmap[10], 2);
402 switch (rawmap[0])
403 {
404 case COMPRESSION_TYPE_0:
405 case COMPRESSION_TYPE_1:
406 case COMPRESSION_TYPE_2:
407 case COMPRESSION_TYPE_3:
408 file_read(blockoffs, m_compressed, blocklen);
409 m_decompressor[rawmap[0]]->decompress(m_compressed, blocklen, dest, m_hunkbytes);
410 if (!m_decompressor[rawmap[0]]->lossy() && dest != NULL && crc16_creator::simple(dest, m_hunkbytes) != blockcrc)
411 throw CHDERR_DECOMPRESSION_ERROR;
412 if (m_decompressor[rawmap[0]]->lossy() && crc16_creator::simple(m_compressed, blocklen) != blockcrc)
413 throw CHDERR_DECOMPRESSION_ERROR;
414 return CHDERR_NONE;
415
416 case COMPRESSION_NONE:
417 file_read(blockoffs, dest, m_hunkbytes);
418 if (crc16_creator::simple(dest, m_hunkbytes) != blockcrc)
419 throw CHDERR_DECOMPRESSION_ERROR;
420 return CHDERR_NONE;
421
422 case COMPRESSION_SELF:
423 return read_hunk(blockoffs, dest);
424
425 case COMPRESSION_PARENT:
426 if (m_parent_missing)
427 throw CHDERR_REQUIRES_PARENT;
428 return m_parent->read_bytes(UINT64(blockoffs) * UINT64(m_parent->unit_bytes()), dest, m_hunkbytes);
429 }
430 break;
431 }
432
433 // if we get here, something was wrong
434 throw CHDERR_READ_ERROR;
435 }
436
437 // just return errors
438 catch (chd_error &err)
439 {
440 return err;
441 }
442 }
443
444 //-------------------------------------------------
445 // read_bytes - read from the CHD at a byte level,
446 // using the cache to handle partial hunks
447 //-------------------------------------------------
448
read_bytes(UINT64 offset,void * buffer,UINT32 bytes)449 chd_error chd_file::read_bytes(UINT64 offset, void *buffer, UINT32 bytes)
450 {
451 // iterate over hunks
452 UINT32 first_hunk = offset / m_hunkbytes;
453 UINT32 last_hunk = (offset + bytes - 1) / m_hunkbytes;
454 UINT8 *dest = reinterpret_cast<UINT8 *>(buffer);
455 for (UINT32 curhunk = first_hunk; curhunk <= last_hunk; curhunk++)
456 {
457 // determine start/end boundaries
458 UINT32 startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0;
459 UINT32 endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1);
460
461 // if it's a full block, just read directly from disk unless it's the cached hunk
462 chd_error err = CHDERR_NONE;
463 if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
464 err = read_hunk(curhunk, dest);
465
466 // otherwise, read from the cache
467 else
468 {
469 if (curhunk != m_cachehunk)
470 {
471 err = read_hunk(curhunk, m_cache);
472 if (err != CHDERR_NONE)
473 return err;
474 m_cachehunk = curhunk;
475 }
476 memcpy(dest, &m_cache[startoffs], endoffs + 1 - startoffs);
477 }
478
479 // handle errors and advance
480 if (err != CHDERR_NONE)
481 return err;
482 dest += endoffs + 1 - startoffs;
483 }
484 return CHDERR_NONE;
485 }
486
487
488 //-------------------------------------------------
489 // read_metadata - read the indexed metadata
490 // of the given type
491 //-------------------------------------------------
492
read_metadata(chd_metadata_tag searchtag,UINT32 searchindex,astring & output)493 chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output)
494 {
495 // wrap this for clean reporting
496 try
497 {
498 // if we didn't find it, just return
499 metadata_entry metaentry;
500 if (!metadata_find(searchtag, searchindex, metaentry))
501 throw CHDERR_METADATA_NOT_FOUND;
502
503 // read the metadata
504 // TODO: how to properly allocate a dynamic char buffer?
505 char* metabuf = new char[metaentry.length+1];
506 memset(metabuf, 0x00, metaentry.length+1);
507 file_read(metaentry.offset + METADATA_HEADER_SIZE, metabuf, metaentry.length);
508 output.cpy(metabuf);
509 delete[] metabuf;
510 return CHDERR_NONE;
511 }
512
513 // just return errors
514 catch (chd_error &err)
515 {
516 return err;
517 }
518 }
519
read_metadata(chd_metadata_tag searchtag,UINT32 searchindex,dynamic_buffer & output)520 chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output)
521 {
522 // wrap this for clean reporting
523 try
524 {
525 // if we didn't find it, just return
526 metadata_entry metaentry;
527 if (!metadata_find(searchtag, searchindex, metaentry))
528 throw CHDERR_METADATA_NOT_FOUND;
529
530 // read the metadata
531 output.resize(metaentry.length);
532 file_read(metaentry.offset + METADATA_HEADER_SIZE, output, metaentry.length);
533 return CHDERR_NONE;
534 }
535
536 // just return errors
537 catch (chd_error &err)
538 {
539 return err;
540 }
541 }
542
read_metadata(chd_metadata_tag searchtag,UINT32 searchindex,void * output,UINT32 outputlen,UINT32 & resultlen)543 chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen)
544 {
545 // wrap this for clean reporting
546 try
547 {
548 // if we didn't find it, just return
549 metadata_entry metaentry;
550 if (!metadata_find(searchtag, searchindex, metaentry))
551 throw CHDERR_METADATA_NOT_FOUND;
552
553 // read the metadata
554 resultlen = metaentry.length;
555 file_read(metaentry.offset + METADATA_HEADER_SIZE, output, MIN(outputlen, resultlen));
556 return CHDERR_NONE;
557 }
558
559 // just return errors
560 catch (chd_error &err)
561 {
562 return err;
563 }
564 }
565
read_metadata(chd_metadata_tag searchtag,UINT32 searchindex,dynamic_buffer & output,chd_metadata_tag & resulttag,UINT8 & resultflags)566 chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags)
567 {
568 // wrap this for clean reporting
569 try
570 {
571 // if we didn't find it, just return
572 metadata_entry metaentry;
573 if (!metadata_find(searchtag, searchindex, metaentry))
574 throw CHDERR_METADATA_NOT_FOUND;
575
576 // read the metadata
577 output.resize(metaentry.length);
578 file_read(metaentry.offset + METADATA_HEADER_SIZE, output, metaentry.length);
579 resulttag = metaentry.metatag;
580 resultflags = metaentry.flags;
581 return CHDERR_NONE;
582 }
583
584 // just return errors
585 catch (chd_error &err)
586 {
587 return err;
588 }
589 }
590
591
592 //-------------------------------------------------
593 // guess_unitbytes - for older CHD formats, take
594 // a guess at the bytes/unit based on metadata
595 //-------------------------------------------------
596
guess_unitbytes()597 UINT32 chd_file::guess_unitbytes()
598 {
599 // look for hard disk metadata; if found, then the unit size == sector size
600 astring metadata;
601 int i0, i1, i2, i3;
602 if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4)
603 return i3;
604
605 // look for CD-ROM metadata; if found, then the unit size == CD frame size
606 if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
607 read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
608 read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE ||
609 read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
610 return CD_FRAME_SIZE;
611
612 // otherwise, just map 1:1 with the hunk size
613 return m_hunkbytes;
614 }
615
616
617 //-------------------------------------------------
618 // parse_v3_header - parse the header from a v3
619 // file and configure core parameters
620 //-------------------------------------------------
621
parse_v3_header(UINT8 * rawheader,sha1_t & parentsha1)622 void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1)
623 {
624 // verify header length
625 if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE)
626 throw CHDERR_INVALID_FILE;
627
628 // extract core info
629 m_logicalbytes = be_read(&rawheader[28], 8);
630 m_mapoffset = 120;
631 m_metaoffset = be_read(&rawheader[36], 8);
632 m_hunkbytes = be_read(&rawheader[76], 4);
633 m_hunkcount = be_read(&rawheader[24], 4);
634
635 // extract parent SHA-1
636 UINT32 flags = be_read(&rawheader[16], 4);
637 m_allow_writes = (flags & 2) == 0;
638
639 // determine compression
640 switch (be_read(&rawheader[20], 4))
641 {
642 case 0: m_compression[0] = CHD_CODEC_NONE; break;
643 case 1: m_compression[0] = CHD_CODEC_ZLIB; break;
644 case 2: m_compression[0] = CHD_CODEC_ZLIB; break;
645 case 3: m_compression[0] = CHD_CODEC_AVHUFF; break;
646 default: throw CHDERR_UNKNOWN_COMPRESSION;
647 }
648 m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE;
649
650 // describe the format
651 m_mapoffset_offset = 0;
652 m_metaoffset_offset = 36;
653 m_sha1_offset = 80;
654 m_rawsha1_offset = 0;
655 m_parentsha1_offset = 100;
656
657 // determine properties of map entries
658 m_mapentrybytes = 16;
659
660 // extract parent SHA-1
661 if (flags & 1)
662 parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]);
663
664 // guess at the units based on snooping the metadata
665 m_unitbytes = guess_unitbytes();
666 m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
667 }
668
669
670 //-------------------------------------------------
671 // parse_v4_header - parse the header from a v4
672 // file and configure core parameters
673 //-------------------------------------------------
674
parse_v4_header(UINT8 * rawheader,sha1_t & parentsha1)675 void chd_file::parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1)
676 {
677 // verify header length
678 if (be_read(&rawheader[8], 4) != V4_HEADER_SIZE)
679 throw CHDERR_INVALID_FILE;
680
681 // extract core info
682 m_logicalbytes = be_read(&rawheader[28], 8);
683 m_mapoffset = 108;
684 m_metaoffset = be_read(&rawheader[36], 8);
685 m_hunkbytes = be_read(&rawheader[44], 4);
686 m_hunkcount = be_read(&rawheader[24], 4);
687
688 // extract parent SHA-1
689 UINT32 flags = be_read(&rawheader[16], 4);
690 m_allow_writes = (flags & 2) == 0;
691
692 // determine compression
693 switch (be_read(&rawheader[20], 4))
694 {
695 case 0: m_compression[0] = CHD_CODEC_NONE; break;
696 case 1: m_compression[0] = CHD_CODEC_ZLIB; break;
697 case 2: m_compression[0] = CHD_CODEC_ZLIB; break;
698 case 3: m_compression[0] = CHD_CODEC_AVHUFF; break;
699 default: throw CHDERR_UNKNOWN_COMPRESSION;
700 }
701 m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE;
702
703 // describe the format
704 m_mapoffset_offset = 0;
705 m_metaoffset_offset = 36;
706 m_sha1_offset = 48;
707 m_rawsha1_offset = 88;
708 m_parentsha1_offset = 68;
709
710 // determine properties of map entries
711 m_mapentrybytes = 16;
712
713 // extract parent SHA-1
714 if (flags & 1)
715 parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]);
716
717 // guess at the units based on snooping the metadata
718 m_unitbytes = guess_unitbytes();
719 m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
720 }
721
722
723 //-------------------------------------------------
724 // parse_v5_header - read the header from a v5
725 // file and configure core parameters
726 //-------------------------------------------------
727
parse_v5_header(UINT8 * rawheader,sha1_t & parentsha1)728 void chd_file::parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1)
729 {
730 // verify header length
731 if (be_read(&rawheader[8], 4) != V5_HEADER_SIZE)
732 throw CHDERR_INVALID_FILE;
733
734 // extract core info
735 m_logicalbytes = be_read(&rawheader[32], 8);
736 m_mapoffset = be_read(&rawheader[40], 8);
737 m_metaoffset = be_read(&rawheader[48], 8);
738 m_hunkbytes = be_read(&rawheader[56], 4);
739 m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
740 m_unitbytes = be_read(&rawheader[60], 4);
741 m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
742
743 // determine compression
744 m_compression[0] = be_read(&rawheader[16], 4);
745 m_compression[1] = be_read(&rawheader[20], 4);
746 m_compression[2] = be_read(&rawheader[24], 4);
747 m_compression[3] = be_read(&rawheader[28], 4);
748
749 m_allow_writes = !compressed();
750
751 // describe the format
752 m_mapoffset_offset = 40;
753 m_metaoffset_offset = 48;
754 m_sha1_offset = 84;
755 m_rawsha1_offset = 64;
756 m_parentsha1_offset = 104;
757
758 // determine properties of map entries
759 m_mapentrybytes = compressed() ? 12 : 4;
760
761 // extract parent SHA-1
762 parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]);
763 }
764
765
766 //-------------------------------------------------
767 // open_common - common path when opening an
768 // existing CHD file for input
769 //-------------------------------------------------
770
open_common(bool writeable)771 chd_error chd_file::open_common(bool writeable)
772 {
773 // wrap in try for proper error handling
774 try
775 {
776 // reads are always permitted
777 m_allow_reads = true;
778
779 // read the raw header
780 UINT8 rawheader[MAX_HEADER_SIZE];
781 file_read(0, rawheader, sizeof(rawheader));
782
783 // verify the signature
784 if (memcmp(rawheader, "MComprHD", 8) != 0)
785 throw CHDERR_INVALID_FILE;
786
787 // only allow writes to the most recent version
788 m_version = be_read(&rawheader[12], 4);
789 if (writeable && m_version < HEADER_VERSION)
790 throw CHDERR_UNSUPPORTED_VERSION;
791
792 // read the header if we support it
793 sha1_t parentsha1 = sha1_t::null;
794 switch (m_version)
795 {
796 case 3: parse_v3_header(rawheader, parentsha1); break;
797 case 4: parse_v4_header(rawheader, parentsha1); break;
798 case 5: parse_v5_header(rawheader, parentsha1); break;
799 default: throw CHDERR_UNSUPPORTED_VERSION;
800 }
801
802 if (writeable && !m_allow_writes)
803 throw CHDERR_FILE_NOT_WRITEABLE;
804
805 // make sure we have a parent if we need one (and don't if we don't)
806 if (parentsha1 != sha1_t::null)
807 {
808 if (m_parent == NULL)
809 m_parent_missing = true;
810 else if (m_parent->sha1() != parentsha1)
811 throw CHDERR_INVALID_PARENT;
812 }
813 else if (m_parent != NULL)
814 throw CHDERR_INVALID_PARAMETER;
815
816 // finish opening the file
817 create_open_common();
818 return CHDERR_NONE;
819 }
820
821 // handle errors by closing ourself
822 catch (chd_error &err)
823 {
824 close();
825 return err;
826 }
827 }
828
829 //-------------------------------------------------
830 // create_open_common - common code for handling
831 // creation and opening of a file
832 //-------------------------------------------------
833
create_open_common()834 void chd_file::create_open_common()
835 {
836 // verify the compression types and initialize the codecs
837 for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++)
838 {
839 m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this);
840 if (m_decompressor[decompnum] == NULL && m_compression[decompnum] != 0)
841 throw CHDERR_UNKNOWN_COMPRESSION;
842 }
843
844 // read the map; v5+ compressed drives need to read and decompress their map
845 m_rawmap.resize(m_hunkcount * m_mapentrybytes);
846 if (m_version >= 5 && compressed())
847 decompress_v5_map();
848 else
849 file_read(m_mapoffset, m_rawmap, m_rawmap.count());
850
851 // allocate the temporary compressed buffer and a buffer for caching
852 m_compressed.resize(m_hunkbytes);
853 m_cache.resize(m_hunkbytes);
854 }
855
856
857 //-------------------------------------------------
858 // metadata_find - find a metadata entry
859 //-------------------------------------------------
860
metadata_find(chd_metadata_tag metatag,INT32 metaindex,metadata_entry & metaentry,bool resume)861 bool chd_file::metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume)
862 {
863 // start at the beginning unless we're resuming a previous search
864 if (!resume)
865 {
866 metaentry.offset = m_metaoffset;
867 metaentry.prev = 0;
868 }
869 else
870 {
871 metaentry.prev = metaentry.offset;
872 metaentry.offset = metaentry.next;
873 }
874
875 // loop until we run out of options
876 while (metaentry.offset != 0)
877 {
878 // read the raw header
879 UINT8 raw_meta_header[METADATA_HEADER_SIZE];
880 file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header));
881
882 // extract the data
883 metaentry.metatag = be_read(&raw_meta_header[0], 4);
884 metaentry.flags = raw_meta_header[4];
885 metaentry.length = be_read(&raw_meta_header[5], 3);
886 metaentry.next = be_read(&raw_meta_header[8], 8);
887
888 // if we got a match, proceed
889 if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag)
890 if (metaindex-- == 0)
891 return true;
892
893 // no match, fetch the next link
894 metaentry.prev = metaentry.offset;
895 metaentry.offset = metaentry.next;
896 }
897
898 // if we get here, we didn't find it
899 return false;
900 }
901
902
903 //-------------------------------------------------
904 // decompress_v5_map - decompress the v5 map
905 //-------------------------------------------------
906
decompress_v5_map()907 void chd_file::decompress_v5_map()
908 {
909 // if no offset, we haven't written it yet
910 if (m_mapoffset == 0)
911 {
912 memset(m_rawmap, 0xff, m_rawmap.count());
913 return;
914 }
915
916 // read the reader
917 UINT8 rawbuf[16];
918 file_read(m_mapoffset, rawbuf, sizeof(rawbuf));
919 UINT32 mapbytes = be_read(&rawbuf[0], 4);
920 UINT64 firstoffs = be_read(&rawbuf[4], 6);
921 UINT16 mapcrc = be_read(&rawbuf[10], 2);
922 UINT8 lengthbits = rawbuf[12];
923 UINT8 selfbits = rawbuf[13];
924 UINT8 parentbits = rawbuf[14];
925
926 // now read the map
927 dynamic_buffer compressed(mapbytes);
928 file_read(m_mapoffset + 16, compressed, mapbytes);
929 bitstream_in bitbuf(compressed, compressed.count());
930
931 // first decode the compression types
932 huffman_decoder<16, 8> decoder;
933 huffman_error err = decoder.import_tree_rle(bitbuf);
934 if (err != HUFFERR_NONE)
935 throw CHDERR_DECOMPRESSION_ERROR;
936 UINT8 lastcomp = 0;
937 int repcount = 0;
938 for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
939 {
940 UINT8 *rawmap = &m_rawmap[hunknum * 12];
941 if (repcount > 0)
942 rawmap[0] = lastcomp, repcount--;
943 else
944 {
945 UINT8 val = decoder.decode_one(bitbuf);
946 if (val == COMPRESSION_RLE_SMALL)
947 rawmap[0] = lastcomp, repcount = 2 + decoder.decode_one(bitbuf);
948 else if (val == COMPRESSION_RLE_LARGE)
949 rawmap[0] = lastcomp, repcount = 2 + 16 + (decoder.decode_one(bitbuf) << 4), repcount += decoder.decode_one(bitbuf);
950 else
951 rawmap[0] = lastcomp = val;
952 }
953 }
954
955 // then iterate through the hunks and extract the needed data
956 UINT64 curoffset = firstoffs;
957 UINT32 last_self = 0;
958 UINT64 last_parent = 0;
959 for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
960 {
961 UINT8 *rawmap = &m_rawmap[hunknum * 12];
962 UINT64 offset = curoffset;
963 UINT32 length = 0;
964 UINT16 crc = 0;
965 switch (rawmap[0])
966 {
967 // base types
968 case COMPRESSION_TYPE_0:
969 case COMPRESSION_TYPE_1:
970 case COMPRESSION_TYPE_2:
971 case COMPRESSION_TYPE_3:
972 curoffset += length = bitbuf.read(lengthbits);
973 crc = bitbuf.read(16);
974 break;
975
976 case COMPRESSION_NONE:
977 curoffset += length = m_hunkbytes;
978 crc = bitbuf.read(16);
979 break;
980
981 case COMPRESSION_SELF:
982 last_self = offset = bitbuf.read(selfbits);
983 break;
984
985 case COMPRESSION_PARENT:
986 offset = bitbuf.read(parentbits);
987 last_parent = offset;
988 break;
989
990 // pseudo-types; convert into base types
991 case COMPRESSION_SELF_1:
992 last_self++;
993 case COMPRESSION_SELF_0:
994 rawmap[0] = COMPRESSION_SELF;
995 offset = last_self;
996 break;
997
998 case COMPRESSION_PARENT_SELF:
999 rawmap[0] = COMPRESSION_PARENT;
1000 last_parent = offset = (UINT64(hunknum) * UINT64(m_hunkbytes)) / m_unitbytes;
1001 break;
1002
1003 case COMPRESSION_PARENT_1:
1004 last_parent += m_hunkbytes / m_unitbytes;
1005 case COMPRESSION_PARENT_0:
1006 rawmap[0] = COMPRESSION_PARENT;
1007 offset = last_parent;
1008 break;
1009 }
1010 be_write(&rawmap[1], length, 3);
1011 be_write(&rawmap[4], offset, 6);
1012 be_write(&rawmap[10], crc, 2);
1013 }
1014
1015 // verify the final CRC
1016 if (crc16_creator::simple(m_rawmap, m_hunkcount * 12) != mapcrc)
1017 throw CHDERR_DECOMPRESSION_ERROR;
1018 }
1019
1020 //-------------------------------------------------
1021 // sha1 - return our SHA1 value
1022 //-------------------------------------------------
1023
sha1()1024 sha1_t chd_file::sha1()
1025 {
1026 try
1027 {
1028 // read the big-endian version
1029 UINT8 rawbuf[sizeof(sha1_t)];
1030 file_read(m_sha1_offset, rawbuf, sizeof(rawbuf));
1031 return be_read_sha1(rawbuf);
1032 }
1033 catch (chd_error &)
1034 {
1035 // on failure, return NULL
1036 return sha1_t::null;
1037 }
1038 }
1039
1040
1041 //-------------------------------------------------
1042 // raw_sha1 - return our raw SHA1 value
1043 //-------------------------------------------------
1044
raw_sha1()1045 sha1_t chd_file::raw_sha1()
1046 {
1047 try
1048 {
1049 // determine offset within the file for data-only
1050 if (m_rawsha1_offset == 0)
1051 throw CHDERR_UNSUPPORTED_VERSION;
1052
1053 // read the big-endian version
1054 UINT8 rawbuf[sizeof(sha1_t)];
1055 file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf));
1056 return be_read_sha1(rawbuf);
1057 }
1058 catch (chd_error &)
1059 {
1060 // on failure, return NULL
1061 return sha1_t::null;
1062 }
1063 }
1064
1065
1066 //-------------------------------------------------
1067 // parent_sha1 - return our parent's SHA1 value
1068 //-------------------------------------------------
1069
parent_sha1()1070 sha1_t chd_file::parent_sha1()
1071 {
1072 try
1073 {
1074 // determine offset within the file
1075 if (m_parentsha1_offset == 0)
1076 throw CHDERR_UNSUPPORTED_VERSION;
1077
1078 // read the big-endian version
1079 UINT8 rawbuf[sizeof(sha1_t)];
1080 file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
1081 return be_read_sha1(rawbuf);
1082 }
1083 catch (chd_error &)
1084 {
1085 // on failure, return NULL
1086 return sha1_t::null;
1087 }
1088 }
1089
1090 //**************************************************************************
1091 // CHD FILE MANAGEMENT
1092 //**************************************************************************
1093
1094 //-------------------------------------------------
1095 // chd_file - constructor
1096 //-------------------------------------------------
1097
chd_file()1098 chd_file::chd_file()
1099 : m_file(NULL),
1100 m_owns_file(false)
1101 {
1102 // reset state
1103 memset(m_decompressor, 0, sizeof(m_decompressor));
1104 close();
1105 }
1106
1107
1108 //-------------------------------------------------
1109 // ~chd_file - destructor
1110 //-------------------------------------------------
1111
~chd_file()1112 chd_file::~chd_file()
1113 {
1114 // close any open files
1115 close();
1116 }
1117