diff options
Diffstat (limited to 'deps/libchdr')
-rw-r--r-- | deps/libchdr/bitstream.c | 118 | ||||
-rw-r--r-- | deps/libchdr/bitstream.h | 42 | ||||
-rw-r--r-- | deps/libchdr/cdrom.c | 417 | ||||
-rw-r--r-- | deps/libchdr/cdrom.h | 107 | ||||
-rw-r--r-- | deps/libchdr/chd.c | 2517 | ||||
-rw-r--r-- | deps/libchdr/chd.h | 401 | ||||
-rw-r--r-- | deps/libchdr/coretypes.h | 40 | ||||
-rw-r--r-- | deps/libchdr/flac.c | 331 | ||||
-rw-r--r-- | deps/libchdr/flac.h | 50 | ||||
-rw-r--r-- | deps/libchdr/huffman.c | 528 | ||||
-rw-r--r-- | deps/libchdr/huffman.h | 87 |
11 files changed, 4638 insertions, 0 deletions
diff --git a/deps/libchdr/bitstream.c b/deps/libchdr/bitstream.c new file mode 100644 index 0000000..735b97f --- /dev/null +++ b/deps/libchdr/bitstream.c @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + bitstream.c + + Helper classes for reading/writing at the bit level. + +***************************************************************************/ + +#include "bitstream.h" +#include <stdlib.h> + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); } + +//------------------------------------------------- +// create_bitstream - constructor +//------------------------------------------------- + +struct bitstream* create_bitstream(const void *src, uint32_t srclength) +{ + struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream)); + bitstream->buffer = 0; + bitstream->bits = 0; + bitstream->read = (const uint8_t*)src; + bitstream->doffset = 0; + bitstream->dlength = srclength; + return bitstream; +} + + +//----------------------------------------------------- +// bitstream_peek - fetch the requested number of bits +// but don't advance the input pointer +//----------------------------------------------------- + +uint32_t bitstream_peek(struct bitstream* bitstream, int numbits) +{ + if (numbits == 0) + return 0; + + // fetch data if we need more + if (numbits > bitstream->bits) + { + while (bitstream->bits <= 24) + { + if (bitstream->doffset < bitstream->dlength) + bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits); + bitstream->doffset++; + bitstream->bits += 8; + } + } + + // return the data + return bitstream->buffer >> (32 - numbits); +} + + +//----------------------------------------------------- +// bitstream_remove - advance the input pointer by the +// specified number of bits +//----------------------------------------------------- + +void bitstream_remove(struct bitstream* bitstream, int numbits) +{ + bitstream->buffer <<= numbits; + bitstream->bits -= numbits; +} + + +//----------------------------------------------------- +// bitstream_read - fetch the requested number of bits +//----------------------------------------------------- + +uint32_t bitstream_read(struct bitstream* bitstream, int numbits) +{ + uint32_t result = bitstream_peek(bitstream, numbits); + bitstream_remove(bitstream, numbits); + return result; +} + + +//------------------------------------------------- +// read_offset - return the current read offset +//------------------------------------------------- + +uint32_t bitstream_read_offset(struct bitstream* bitstream) +{ + uint32_t result = bitstream->doffset; + int bits = bitstream->bits; + while (bits >= 8) + { + result--; + bits -= 8; + } + return result; +} + + +//------------------------------------------------- +// flush - flush to the nearest byte +//------------------------------------------------- + +uint32_t bitstream_flush(struct bitstream* bitstream) +{ + while (bitstream->bits >= 8) + { + bitstream->doffset--; + bitstream->bits -= 8; + } + bitstream->bits = bitstream->buffer = 0; + return bitstream->doffset; +} + diff --git a/deps/libchdr/bitstream.h b/deps/libchdr/bitstream.h new file mode 100644 index 0000000..9250d33 --- /dev/null +++ b/deps/libchdr/bitstream.h @@ -0,0 +1,42 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ bitstream.h
+
+ Helper classes for reading/writing at the bit level.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __BITSTREAM_H__
+#define __BITSTREAM_H__
+
+#include <stdint.h>
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// helper class for reading from a bit buffer
+struct bitstream
+{
+ uint32_t buffer; // current bit accumulator
+ int bits; // number of bits in the accumulator
+ const uint8_t * read; // read pointer
+ uint32_t doffset; // byte offset within the data
+ uint32_t dlength; // length of the data
+};
+
+struct bitstream* create_bitstream(const void *src, uint32_t srclength);
+int bitstream_overflow(struct bitstream* bitstream);
+uint32_t bitstream_read_offset(struct bitstream* bitstream);
+
+uint32_t bitstream_read(struct bitstream* bitstream, int numbits);
+uint32_t bitstream_peek(struct bitstream* bitstream, int numbits);
+void bitstream_remove(struct bitstream* bitstream, int numbits);
+uint32_t bitstream_flush(struct bitstream* bitstream);
+
+
+#endif
diff --git a/deps/libchdr/cdrom.c b/deps/libchdr/cdrom.c new file mode 100644 index 0000000..6c120cb --- /dev/null +++ b/deps/libchdr/cdrom.c @@ -0,0 +1,417 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ cdrom.c
+
+ Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
+
+****************************************************************************
+
+ IMPORTANT:
+ "physical" block addresses are the actual addresses on the emulated CD.
+ "chd" block addresses are the block addresses in the CHD file.
+ Because we pad each track to a 4-frame boundary, these addressing
+ schemes will differ after track 1!
+
+***************************************************************************/
+
+#include <assert.h>
+#include <string.h>
+
+#include "cdrom.h"
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+/** @brief The verbose. */
+#define VERBOSE (0)
+#if VERBOSE
+
+/**
+ * @def LOG(x) do
+ *
+ * @brief A macro that defines log.
+ *
+ * @param x The void to process.
+ */
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/**
+ * @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
+ *
+ * @brief Logerrors the given text.
+ *
+ * @param text The text.
+ *
+ * @return A CLIB_DECL.
+ */
+
+void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
+#else
+
+/**
+ * @def LOG(x);
+ *
+ * @brief A macro that defines log.
+ *
+ * @param x The void to process.
+ */
+
+#define LOG(x)
+#endif
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/** @brief offset within sector. */
+#define SYNC_OFFSET 0x000
+/** @brief 12 bytes. */
+#define SYNC_NUM_BYTES 12
+
+/** @brief offset within sector. */
+#define MODE_OFFSET 0x00f
+
+/** @brief offset within sector. */
+#define ECC_P_OFFSET 0x81c
+/** @brief 2 lots of 86. */
+#define ECC_P_NUM_BYTES 86
+/** @brief 24 bytes each. */
+#define ECC_P_COMP 24
+
+/** @brief The ECC q offset. */
+#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
+/** @brief 2 lots of 52. */
+#define ECC_Q_NUM_BYTES 52
+/** @brief 43 bytes each. */
+#define ECC_Q_COMP 43
+
+
+
+/**
+ * @brief -------------------------------------------------
+ * ECC lookup tables pre-calculated tables for ECC data calcs
+ * -------------------------------------------------.
+ */
+
+static const uint8_t ecclow[256] =
+{
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
+ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
+ 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
+ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
+ 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
+ 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+ 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
+ 0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
+ 0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
+ 0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
+ 0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
+ 0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
+ 0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
+ 0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
+ 0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
+};
+
+/** @brief The ecchigh[ 256]. */
+static const uint8_t ecchigh[256] =
+{
+ 0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
+ 0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
+ 0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
+ 0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
+ 0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
+ 0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
+ 0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
+ 0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
+ 0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
+ 0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
+ 0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
+ 0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
+ 0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
+ 0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
+ 0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
+ 0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
+};
+
+/**
+ * @brief -------------------------------------------------
+ * poffsets - each row represents the addresses used to calculate a byte of the ECC P
+ * data 86 (*2) ECC P bytes, 24 values represented by each
+ * -------------------------------------------------.
+ */
+
+static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
+{
+ { 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
+ { 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
+ { 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
+ { 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
+ { 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
+ { 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
+ { 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
+ { 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
+ { 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
+ { 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
+ { 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
+ { 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
+ { 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
+ { 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
+ { 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
+ { 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
+ { 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
+ { 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
+ { 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
+ { 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
+ { 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
+ { 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
+ { 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
+ { 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
+ { 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
+ { 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
+ { 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
+ { 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
+ { 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
+ { 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
+ { 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
+ { 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
+ { 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
+ { 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
+ { 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
+ { 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
+ { 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
+ { 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
+ { 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
+ { 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
+ { 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
+ { 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
+ { 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
+ { 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
+ { 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
+ { 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
+ { 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
+ { 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
+ { 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
+ { 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
+ { 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
+ { 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
+ { 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
+ { 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
+ { 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
+ { 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
+ { 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
+ { 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
+ { 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
+ { 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
+ { 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
+ { 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
+ { 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
+ { 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
+ { 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
+ { 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
+ { 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
+ { 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
+ { 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
+ { 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
+ { 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
+ { 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
+ { 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
+ { 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
+ { 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
+ { 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
+ { 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
+ { 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
+ { 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
+ { 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
+ { 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
+ { 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
+ { 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
+ { 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
+ { 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
+ { 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
+};
+
+/**
+ * @brief -------------------------------------------------
+ * qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
+ * data 52 (*2) ECC Q bytes, 43 values represented by each
+ * -------------------------------------------------.
+ */
+
+static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
+{
+ { 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
+ { 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
+ { 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
+ { 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
+ { 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
+ { 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
+ { 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
+ { 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
+ { 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
+ { 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
+ { 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
+ { 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
+ { 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
+ { 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
+ { 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
+ { 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
+ { 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
+ { 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
+ { 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
+ { 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
+ { 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
+ { 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
+ { 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
+ { 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
+ { 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
+ { 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
+ { 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
+ { 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
+ { 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
+ { 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
+ { 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
+ { 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
+ { 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
+ { 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
+ { 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
+ { 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
+ { 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
+ { 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
+ { 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
+ { 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
+ { 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
+ { 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
+ { 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
+ { 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
+ { 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
+ { 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
+ { 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
+ { 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
+ { 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
+ { 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
+ { 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
+ { 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
+};
+
+
+//-------------------------------------------------
+// ecc_source_byte - return data from the sector
+// at the given offset, masking anything
+// particular to a mode
+//-------------------------------------------------
+
+static uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
+{
+ // in mode 2 always treat these as 0 bytes
+ return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
+}
+
+/**
+ * @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
+ *
+ * @brief -------------------------------------------------
+ * ecc_compute_bytes - calculate an ECC value (P or Q)
+ * -------------------------------------------------.
+ *
+ * @param sector The sector.
+ * @param row The row.
+ * @param rowlen The rowlen.
+ * @param [in,out] val1 The first value.
+ * @param [in,out] val2 The second value.
+ */
+
+void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
+{
+ int component;
+ *val1 = *val2 = 0;
+ for (component = 0; component < rowlen; component++)
+ {
+ *val1 ^= ecc_source_byte(sector, row[component]);
+ *val2 ^= ecc_source_byte(sector, row[component]);
+ *val1 = ecclow[*val1];
+ }
+ *val1 = ecchigh[ecclow[*val1] ^ *val2];
+ *val2 ^= *val1;
+}
+
+/**
+ * @fn int ecc_verify(const uint8_t *sector)
+ *
+ * @brief -------------------------------------------------
+ * ecc_verify - verify the P and Q ECC codes in a sector
+ * -------------------------------------------------.
+ *
+ * @param sector The sector.
+ *
+ * @return true if it succeeds, false if it fails.
+ */
+
+int ecc_verify(const uint8_t *sector)
+{
+ int byte;
+
+ // first verify P bytes
+ for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
+ {
+ uint8_t val1, val2;
+ ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
+ if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
+ return 0;
+ }
+
+ // then verify Q bytes
+ for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
+ {
+ uint8_t val1, val2;
+ ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
+ if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * @fn void ecc_generate(uint8_t *sector)
+ *
+ * @brief -------------------------------------------------
+ * ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
+ * existing codes
+ * -------------------------------------------------.
+ *
+ * @param [in,out] sector If non-null, the sector.
+ */
+
+void ecc_generate(uint8_t *sector)
+{
+ int byte;
+ // first verify P bytes
+ for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
+ ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
+
+ // then verify Q bytes
+ for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
+ ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
+}
+
+/**
+ * @fn void ecc_clear(uint8_t *sector)
+ *
+ * @brief -------------------------------------------------
+ * ecc_clear - erase the ECC P and Q cods to 0 within a sector
+ * -------------------------------------------------.
+ *
+ * @param [in,out] sector If non-null, the sector.
+ */
+
+void ecc_clear(uint8_t *sector)
+{
+ memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
+ memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
+}
diff --git a/deps/libchdr/cdrom.h b/deps/libchdr/cdrom.h new file mode 100644 index 0000000..8a3341c --- /dev/null +++ b/deps/libchdr/cdrom.h @@ -0,0 +1,107 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ cdrom.h
+
+ Generic MAME cd-rom implementation
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CDROM_H__
+#define __CDROM_H__
+
+#include <stdint.h>
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+// tracks are padded to a multiple of this many frames
+extern const uint32_t CD_TRACK_PADDING;
+
+#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */
+#define CD_MAX_SECTOR_DATA (2352)
+#define CD_MAX_SUBCODE_DATA (96)
+
+#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
+#define CD_FRAMES_PER_HUNK (8)
+
+#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6))
+
+enum
+{
+ CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */
+ CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */
+ CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */
+ CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */
+ CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */
+ CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */
+ CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */
+ CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */
+
+ CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */
+};
+
+enum
+{
+ CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
+ CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
+ CD_SUB_NONE /* no subcode data stored */
+};
+
+#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+#define CD_FLAG_GDROMLE 0x00000002 // legacy GD-ROM, with little-endian CDDA data
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+// ECC utilities
+int ecc_verify(const uint8_t *sector);
+void ecc_generate(uint8_t *sector);
+void ecc_clear(uint8_t *sector);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+static inline uint32_t msf_to_lba(uint32_t msf)
+{
+ return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
+}
+
+static inline uint32_t lba_to_msf(uint32_t lba)
+{
+ uint8_t m, s, f;
+
+ m = lba / (60 * 75);
+ lba -= m * (60 * 75);
+ s = lba / 75;
+ f = lba % 75;
+
+ return ((m / 10) << 20) | ((m % 10) << 16) |
+ ((s / 10) << 12) | ((s % 10) << 8) |
+ ((f / 10) << 4) | ((f % 10) << 0);
+}
+
+// segacd needs it like this.. investigate
+// Angelo also says PCE tracks often start playing at the
+// wrong address.. related?
+static inline uint32_t lba_to_msf_alt(int lba)
+{
+ uint32_t ret = 0;
+
+ ret |= ((lba / (60 * 75))&0xff)<<16;
+ ret |= (((lba / 75) % 60)&0xff)<<8;
+ ret |= ((lba % 75)&0xff)<<0;
+
+ return ret;
+}
+
+#endif // __CDROM_H__
diff --git a/deps/libchdr/chd.c b/deps/libchdr/chd.c new file mode 100644 index 0000000..bf6d342 --- /dev/null +++ b/deps/libchdr/chd.c @@ -0,0 +1,2517 @@ +/*************************************************************************** + + chd.c + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ +#define DONT_SET_BYTE +typedef unsigned char Byte; + +#include "chd.h" +#include "cdrom.h" +#include "huffman.h" +#include "flac.h" + +#include "md5.h" +#include "sha1.h" +#include "LzmaEnc.h" +#include "LzmaDec.h" + +#include <string.h> +#include <zlib.h> + +#include <time.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> + +#include "../deps/crypto/md5.h" + +#define TRUE 1 +#define FALSE 0 + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define SHA1_DIGEST_SIZE 20 + +#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define PRINTF_MAX_HUNK (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAP_STACK_ENTRIES 512 /* max number of entries to use on the stack */ +#define MAP_ENTRY_SIZE 16 /* V3 and later */ +#define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */ +#define METADATA_HEADER_SIZE 16 /* metadata header size */ +#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */ + +#define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */ +#define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */ + +#define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ + +#define COOKIE_VALUE 0xbaadf00d +#define MAX_ZLIB_ALLOCS 64 + +#define END_OF_LIST_COOKIE "EndOfListCookie" + +#define NO_MATCH (~0) + +static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; + +// V3-V4 entry types +enum +{ + V34_MAP_ENTRY_TYPE_INVALID = 0, // invalid type + V34_MAP_ENTRY_TYPE_COMPRESSED = 1, // standard compression + V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, // uncompressed data + V34_MAP_ENTRY_TYPE_MINI = 3, // mini: use offset as raw data + V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, // same as another hunk in this file + V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, // same as a hunk in the parent file + V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 // compressed with secondary algorithm (usually FLAC CDDA) +}; + +// V5 compression types +enum +{ + ///< codec #0 + // these types are live when running + COMPRESSION_TYPE_0 = 0, + ///< codec #1 + COMPRESSION_TYPE_1 = 1, + ///< codec #2 + COMPRESSION_TYPE_2 = 2, + ///< codec #3 + COMPRESSION_TYPE_3 = 3, + ///< no compression; implicit length = hunkbytes + COMPRESSION_NONE = 4, + ///< same as another block in this chd + COMPRESSION_SELF = 5, + ///< same as a hunk's worth of units in the parent chd + COMPRESSION_PARENT = 6, + + ///< start of small RLE run (4-bit length) + // these additional pseudo-types are used for compressed encodings: + COMPRESSION_RLE_SMALL, + ///< start of large RLE run (8-bit length) + COMPRESSION_RLE_LARGE, + ///< same as the last COMPRESSION_SELF block + COMPRESSION_SELF_0, + ///< same as the last COMPRESSION_SELF block + 1 + COMPRESSION_SELF_1, + ///< same block in the parent + COMPRESSION_PARENT_SELF, + ///< same as the last COMPRESSION_PARENT block + COMPRESSION_PARENT_0, + ///< same as the last COMPRESSION_PARENT block + 1 + COMPRESSION_PARENT_1 +}; + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* interface to a codec */ +typedef struct _codec_interface codec_interface; +struct _codec_interface +{ + UINT32 compression; /* type of compression */ + const char *compname; /* name of the algorithm */ + UINT8 lossy; /* is this a lossy algorithm? */ + chd_error (*init)(void *codec, UINT32 hunkbytes); /* codec initialize */ + void (*free)(void *codec); /* codec free */ + chd_error (*decompress)(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); /* decompress data */ + chd_error (*config)(void *codec, int param, void *config); /* configure */ +}; + + +/* a single map entry */ +typedef struct _map_entry map_entry; +struct _map_entry +{ + UINT64 offset; /* offset within the file of the data */ + UINT32 crc; /* 32-bit CRC of the data */ + UINT32 length; /* length of the data */ + UINT8 flags; /* misc flags */ +}; + + +/* simple linked-list of hunks used for our CRC map */ +typedef struct _crcmap_entry crcmap_entry; +struct _crcmap_entry +{ + UINT32 hunknum; /* hunk number */ + crcmap_entry * next; /* next entry in list */ +}; + + +/* a single metadata entry */ +typedef struct _metadata_entry metadata_entry; +struct _metadata_entry +{ + UINT64 offset; /* offset within the file of the header */ + UINT64 next; /* offset within the file of the next header */ + UINT64 prev; /* offset within the file of the previous header */ + UINT32 length; /* length of the metadata */ + UINT32 metatag; /* metadata tag */ + UINT8 flags; /* flag bits */ +}; + +/* codec-private data for the ZLIB codec */ + +typedef struct _zlib_allocator zlib_allocator; +struct _zlib_allocator +{ + UINT32 * allocptr[MAX_ZLIB_ALLOCS]; +}; + +typedef struct _zlib_codec_data zlib_codec_data; +struct _zlib_codec_data +{ + z_stream inflater; + zlib_allocator allocator; +}; + +/* codec-private data for the LZMA codec */ +#define MAX_LZMA_ALLOCS 64 + +typedef struct _lzma_allocator lzma_allocator; +struct _lzma_allocator +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ + void (*FreeSz)(void *p, void *address, size_t size); /* address can be 0 */ + uint32_t* allocptr[MAX_LZMA_ALLOCS]; +}; + +typedef struct _lzma_codec_data lzma_codec_data; +struct _lzma_codec_data +{ + CLzmaDec decoder; + lzma_allocator allocator; +}; + +/* codec-private data for the CDZL codec */ +typedef struct _cdzl_codec_data cdzl_codec_data; +struct _cdzl_codec_data { + // internal state + zlib_codec_data base_decompressor; + zlib_codec_data subcode_decompressor; + uint8_t* buffer; +}; + +/* codec-private data for the CDLZ codec */ +typedef struct _cdlz_codec_data cdlz_codec_data; +struct _cdlz_codec_data { + // internal state + lzma_codec_data base_decompressor; + zlib_codec_data subcode_decompressor; + uint8_t* buffer; +}; + +/* codec-private data for the CDFL codec */ +typedef struct _cdfl_codec_data cdfl_codec_data; +struct _cdfl_codec_data { + // internal state + int swap_endian; + flac_decoder decoder; + z_stream inflater; + zlib_allocator allocator; + uint8_t* buffer; +}; + +/* internal representation of an open CHD file */ +struct _chd_file +{ + UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ + + core_file * file; /* handle to the open core file */ + UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ + chd_header header; /* header, extracted from file */ + + chd_file * parent; /* pointer to parent file, or NULL */ + + map_entry * map; /* array of map entries */ + + UINT8 * cache; /* hunk cache pointer */ + UINT32 cachehunk; /* index of currently cached hunk */ + + UINT8 * compare; /* hunk compare pointer */ + UINT32 comparehunk; /* index of current compare data */ + + UINT8 * compressed; /* pointer to buffer for compressed data */ + const codec_interface * codecintf[4]; /* interface to the codec */ + + zlib_codec_data zlib_codec_data; /* zlib codec data */ + cdzl_codec_data cdzl_codec_data; /* cdzl codec data */ + cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ + cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ + + crcmap_entry * crcmap; /* CRC map entries */ + crcmap_entry * crcfree; /* free list CRC entries */ + crcmap_entry ** crctable; /* table of CRC entries */ + + UINT32 maxhunk; /* maximum hunk accessed */ + + UINT8 compressing; /* are we compressing? */ + MD5_CTX compmd5; /* running MD5 during compression */ + SHA1_CTX compsha1; /* running SHA1 during compression */ + UINT32 comphunk; /* next hunk we will compress */ + + UINT8 verifying; /* are we verifying? */ + MD5_CTX vermd5; /* running MD5 during verification */ + SHA1_CTX versha1; /* running SHA1 during verification */ + UINT32 verhunk; /* next hunk we will verify */ + + UINT32 async_hunknum; /* hunk index for asynchronous operations */ + void * async_buffer; /* buffer pointer for asynchronous operations */ + + UINT8 * file_cache; /* cache of underlying file */ +}; + + +/* a single metadata hash entry */ +typedef struct _metadata_hash metadata_hash; +struct _metadata_hash +{ + UINT8 tag[4]; /* tag of the metadata in big-endian */ + UINT8 sha1[CHD_SHA1_BYTES]; /* hash */ +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 }; +static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* internal header operations */ +static chd_error header_validate(const chd_header *header); +static chd_error header_read(core_file *file, chd_header *header); + + +/* internal hunk read/write */ +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum); +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest); + +/* internal map access */ +static chd_error map_read(chd_file *chd); + +/* metadata management */ +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); + + +/* zlib compression codec */ +static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes); +static void zlib_codec_free(void *codec); +static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); +static void zlib_fast_free(voidpf opaque, voidpf address); + +/* lzma compression codec */ +static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes); +static void lzma_codec_free(void *codec); +static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdzl compression codec */ +static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); +static void cdzl_codec_free(void* codec); +static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdlz compression codec */ +static chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes); +static void cdlz_codec_free(void* codec); +static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdfl compression codec */ +static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); +static void cdfl_codec_free(void* codec); +static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +//************************************************************************** +// LZMA ALLOCATOR HELPER +//************************************************************************** + +void *lzma_fast_alloc(void *p, size_t size); +void lzma_fast_free(void *p, void *address); + +//------------------------------------------------- +// lzma_allocator_init +//------------------------------------------------- + +void lzma_allocator_init(void* p) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + // reset pointer list + memset(codec->allocptr, 0, sizeof(codec->allocptr)); + codec->Alloc = lzma_fast_alloc; + codec->Free = lzma_fast_free; +} + +//------------------------------------------------- +// lzma_allocator_free +//------------------------------------------------- + +void lzma_allocator_free(void* p ) +{ + int i; + lzma_allocator *codec = (lzma_allocator *)(p); + + // free our memory + for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++) + { + if (codec->allocptr[i] != NULL) + free(codec->allocptr[i]); + } +} + +//------------------------------------------------- +// lzma_fast_alloc - fast malloc for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void *lzma_fast_alloc(void *p, size_t size) +{ + int scan; + uint32_t *addr = NULL; + lzma_allocator *codec = (lzma_allocator *)(p); + + // compute the size, rounding to the nearest 1k + size = (size + 0x3ff) & ~0x3ff; + + // reuse a hunk if we can + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + uint32_t *ptr = codec->allocptr[scan]; + if (ptr != NULL && size == *ptr) + { + // set the low bit of the size so we don't match next time + *ptr |= 1; + return ptr + 1; + } + } + + // alloc a new one and put it into the list + addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t))); + if (addr==NULL) + return NULL; + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (codec->allocptr[scan] == NULL) + { + codec->allocptr[scan] = addr; + break; + } + } + + // set the low bit of the size so we don't match next time + *addr = size | 1; + return addr + 1; +} + + +//------------------------------------------------- +// lzma_fast_free - fast free for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void lzma_fast_free(void *p, void *address) +{ + int scan; + uint32_t *ptr; + lzma_allocator *codec; + if (address == NULL) + return; + + codec = (lzma_allocator *)(p); + + // find the hunk + ptr = (uint32_t *)(address) - 1; + for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (ptr == codec->allocptr[scan]) + { + // clear the low bit of the size to allow matches + *ptr &= ~1; + return; + } + } +} + +//************************************************************************** +// LZMA DECOMPRESSOR +//************************************************************************** + + +//------------------------------------------------- +// lzma_codec_init - constructor +//------------------------------------------------- + +chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) +{ + CLzmaEncHandle enc; + CLzmaEncProps encoder_props; + Byte decoder_props[LZMA_PROPS_SIZE]; + SizeT props_size; + lzma_allocator* alloc; + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + + // construct the decoder + LzmaDec_Construct(&lzma_codec->decoder); + + // FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK + // This code assumes that the current version of the encoder imposes the same requirements on the + // decoder as the encoder used to produce the file. This is not necessarily true. The format + // needs to be changed so the encoder properties are written to the file. + + // configure the properties like the compressor did + LzmaEncProps_Init(&encoder_props); + encoder_props.level = 9; + encoder_props.reduceSize = hunkbytes; + LzmaEncProps_Normalize(&encoder_props); + + // convert to decoder properties + alloc = &lzma_codec->allocator; + lzma_allocator_init(alloc); + enc = LzmaEnc_Create((ISzAlloc*)alloc); + if (!enc) + return CHDERR_DECOMPRESSION_ERROR; + if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + props_size = sizeof(decoder_props); + if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + + // do memory allocations + if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK) + return CHDERR_DECOMPRESSION_ERROR; + + // Okay + return CHDERR_NONE; +} + + +//------------------------------------------------- +// lzma_codec_free +//------------------------------------------------- + +void lzma_codec_free(void* codec) +{ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + + // free memory + LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator); +} + + +//------------------------------------------------- +// decompress - decompress data using the LZMA +// codec +//------------------------------------------------- + +chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + ELzmaStatus status; + SRes res; + SizeT consumedlen, decodedlen; + // initialize + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + LzmaDec_Init(&lzma_codec->decoder); + + // decode + consumedlen = complen; + decodedlen = destlen; + res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); + if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; +} + +// cdlz +chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes) +{ + cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; + + // allocate buffer + cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + + // make sure the CHD's hunk size is an even multiple of the frame size + lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA); + + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + return CHDERR_NONE; +} + +void cdlz_codec_free(void* codec) +{ + // TODO +} + +chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint32_t framenum; + cdlz_codec_data* cdlz = (cdlz_codec_data*)codec; + + // determine header bytes + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + // extract compressed length of base + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + // reset and decode + lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); + zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + + // reassemble the data + for (framenum = 0; framenum < frames; framenum++) + { + uint8_t *sector; + + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + + // reconstitute the ECC data and sync header + sector = &dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } + } + return CHDERR_NONE; +} + + +// cdzl + +chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes) +{ + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + // make sure the CHD's hunk size is an even multiple of the frame size + zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA); + + cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + return CHDERR_NONE; +} + +void cdzl_codec_free(void *codec) +{ + // TODO +} + +chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint32_t framenum; + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + // determine header bytes + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + // extract compressed length of base + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + // reset and decode + zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); + zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + + // reassemble the data + for (framenum = 0; framenum < frames; framenum++) + { + uint8_t *sector; + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + + // reconstitute the ECC data and sync header + sector = &dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } + } + return CHDERR_NONE; +} + +//************************************************************************** +// CD FLAC DECOMPRESSOR +//************************************************************************** + + + +//------------------------------------------------------ +// cdfl_codec_blocksize - return the optimal block size +//------------------------------------------------------ + +static uint32_t cdfl_codec_blocksize(uint32_t bytes) +{ + // determine FLAC block size, which must be 16-65535 + // clamp to 2k since that's supposed to be the sweet spot + uint32_t hunkbytes = bytes / 4; + while (hunkbytes > 2048) + hunkbytes /= 2; + return hunkbytes; +} + +chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes) +{ + int zerr; + uint16_t native_endian = 0; + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + + // make sure the CHD's hunk size is an even multiple of the frame size + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + // determine whether we want native or swapped samples + *(uint8_t *)(&native_endian) = 1; + cdfl->swap_endian = (native_endian & 1); + + // init the inflater + cdfl->inflater.next_in = (Bytef *)cdfl; // bogus, but that's ok + cdfl->inflater.avail_in = 0; + //cdfl->allocator.install(cdfl->inflater); + cdfl->inflater.zalloc = zlib_fast_alloc; + cdfl->inflater.zfree = zlib_fast_free; + cdfl->inflater.opaque = &cdfl->allocator; + zerr = inflateInit2(&cdfl->inflater, -MAX_WBITS); + + // convert errors + if (zerr == Z_MEM_ERROR) + return CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + return CHDERR_CODEC_ERROR; + + // init flac decoder + flac_decoder_init(&cdfl->decoder); + + return CHDERR_NONE; +} + +void cdfl_codec_free(void *codec) +{ + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + inflateEnd(&cdfl->inflater); +} + +chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + int zerr; + uint8_t *buffer; + uint32_t framenum, offset; + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + // reset and decode + uint32_t frames = destlen / CD_FRAME_SIZE; + if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) + return CHDERR_DECOMPRESSION_ERROR; + buffer = &cdfl->buffer[0]; + if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian)) + return CHDERR_DECOMPRESSION_ERROR; + + // inflate the subcode data + offset = flac_decoder_finish(&cdfl->decoder); + cdfl->inflater.next_in = (Bytef *)(src + offset); + cdfl->inflater.avail_in = complen - offset; + cdfl->inflater.total_in = 0; + cdfl->inflater.next_out = &cdfl->buffer[frames * CD_MAX_SECTOR_DATA]; + cdfl->inflater.avail_out = frames * CD_MAX_SUBCODE_DATA; + cdfl->inflater.total_out = 0; + zerr = inflateReset(&cdfl->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + // do it + zerr = inflate(&cdfl->inflater, Z_FINISH); + if (zerr != Z_STREAM_END) + return CHDERR_DECOMPRESSION_ERROR; + if (cdfl->inflater.total_out != frames * CD_MAX_SUBCODE_DATA) + return CHDERR_DECOMPRESSION_ERROR; + + // reassemble the data + for (framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + } + + return CHDERR_NONE; +} +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +// general codecs with CD frontend +#define CHD_CODEC_CD_ZLIB CHD_MAKE_TAG('c','d','z','l') +#define CHD_CODEC_CD_LZMA CHD_MAKE_TAG('c','d','l','z') +#define CHD_CODEC_CD_FLAC CHD_MAKE_TAG('c','d','f','l') + +static const codec_interface codec_interfaces[] = +{ + /* "none" or no compression */ + { + CHDCOMPRESSION_NONE, + "none", + FALSE, + NULL, + NULL, + NULL, + NULL + }, + + /* standard zlib compression */ + { + CHDCOMPRESSION_ZLIB, + "zlib", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_decompress, + NULL + }, + + /* zlib+ compression */ + { + CHDCOMPRESSION_ZLIB_PLUS, + "zlib+", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_decompress, + NULL + }, + + /* V5 CD zlib compression */ + { + CHD_CODEC_CD_ZLIB, + "cdzl (CD Deflate)", + FALSE, + cdzl_codec_init, + cdzl_codec_free, + cdzl_codec_decompress, + NULL + }, + + /* V5 CD lzma compression */ + { + CHD_CODEC_CD_LZMA, + "cdlz (CD LZMA)", + FALSE, + cdlz_codec_init, + cdlz_codec_free, + cdlz_codec_decompress, + NULL + }, + + /* V5 CD flac compression */ + { + CHD_CODEC_CD_FLAC, + "cdfl (CD FLAC)", + FALSE, + cdfl_codec_init, + cdfl_codec_free, + cdfl_codec_decompress, + NULL + }, +}; + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_bigendian_uint64 - fetch a UINT64 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT64 get_bigendian_uint64(const UINT8 *base) +{ + return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) | + ((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7]; +} + + +/*------------------------------------------------- + put_bigendian_uint64 - write a UINT64 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint64(UINT8 *base, UINT64 value) +{ + base[0] = value >> 56; + base[1] = value >> 48; + base[2] = value >> 40; + base[3] = value >> 32; + base[4] = value >> 24; + base[5] = value >> 16; + base[6] = value >> 8; + base[7] = value; +} + +/*------------------------------------------------- + get_bigendian_uint48 - fetch a UINT48 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT64 get_bigendian_uint48(const UINT8 *base) +{ + return ((UINT64)base[0] << 40) | ((UINT64)base[1] << 32) | + ((UINT64)base[2] << 24) | ((UINT64)base[3] << 16) | ((UINT64)base[4] << 8) | (UINT64)base[5]; +} + +/*------------------------------------------------- + put_bigendian_uint48 - write a UINT48 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint48(UINT8 *base, UINT64 value) +{ + value &= 0xffffffffffff; + base[0] = value >> 40; + base[1] = value >> 32; + base[2] = value >> 24; + base[3] = value >> 16; + base[4] = value >> 8; + base[5] = value; +} +/*------------------------------------------------- + get_bigendian_uint32 - fetch a UINT32 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT32 get_bigendian_uint32(const UINT8 *base) +{ + return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3]; +} + + +/*------------------------------------------------- + put_bigendian_uint32 - write a UINT32 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint24(UINT8 *base, UINT32 value) +{ + value &= 0xffffff; + base[0] = value >> 16; + base[1] = value >> 8; + base[2] = value; +} + + +/*------------------------------------------------- + put_bigendian_uint24 - write a UINT24 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint32(UINT8 *base, UINT32 value) +{ + value &= 0xffffff; + base[0] = value >> 16; + base[1] = value >> 8; + base[2] = value; +} + +/*------------------------------------------------- + get_bigendian_uint24 - fetch a UINT24 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT32 get_bigendian_uint24(const UINT8 *base) +{ + return (base[0] << 16) | (base[1] << 8) | base[2]; +} + +/*------------------------------------------------- + get_bigendian_uint16 - fetch a UINT16 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT16 get_bigendian_uint16(const UINT8 *base) +{ + return (base[0] << 8) | base[1]; +} + + +/*------------------------------------------------- + put_bigendian_uint16 - write a UINT16 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint16(UINT8 *base, UINT16 value) +{ + base[0] = value >> 8; + base[1] = value; +} + + +/*------------------------------------------------- + map_extract - extract a single map + entry from the datastream +-------------------------------------------------*/ + +static inline void map_extract(const UINT8 *base, map_entry *entry) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = get_bigendian_uint32(&base[8]); + entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16); + entry->flags = base[15]; +} + + +/*------------------------------------------------- + map_assemble - write a single map + entry to the datastream +-------------------------------------------------*/ + +static inline void map_assemble(UINT8 *base, map_entry *entry) +{ + put_bigendian_uint64(&base[0], entry->offset); + put_bigendian_uint32(&base[8], entry->crc); + put_bigendian_uint16(&base[12], entry->length); + base[14] = entry->length >> 16; + base[15] = entry->flags; +} + +/*------------------------------------------------- + map_size_v5 - calculate CHDv5 map size +-------------------------------------------------*/ +static inline int map_size_v5(chd_header* header) +{ + return header->hunkcount * header->mapentrybytes; +} + + +/*------------------------------------------------- + crc16 - calculate CRC16 (from hashing.cpp) +-------------------------------------------------*/ +uint16_t crc16(const void *data, uint32_t length) +{ + uint16_t crc = 0xffff; + + static const uint16_t s_table[256] = + { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; + + const uint8_t *src = (uint8_t*)data; + + // fetch the current value into a local and rip through the source data + while (length-- != 0) + crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++]; + return crc; +} + +/*------------------------------------------------- + decompress_v5_map - decompress the v5 map +-------------------------------------------------*/ + +static chd_error decompress_v5_map(chd_file* chd, chd_header* header) +{ + int hunknum; + uint8_t lastcomp = 0; + int repcount = 0; + uint32_t last_self = 0; + uint64_t last_parent = 0; + struct bitstream* bitbuf; + uint32_t mapbytes; + uint64_t firstoffs; + uint16_t mapcrc; + uint8_t lengthbits; + uint8_t selfbits; + uint8_t parentbits; + uint8_t *compressed; + uint8_t rawbuf[16]; + struct huffman_decoder* decoder; + enum huffman_error err; + uint64_t curoffset; + + if (header->mapoffset == 0) + { + //memset(header->rawmap, 0xff,map_size_v5(header)); + return CHDERR_READ_ERROR; + } + + // read the reader + core_fseek(chd->file, header->mapoffset, SEEK_SET); + core_fread(chd->file, rawbuf, sizeof(rawbuf)); + mapbytes = get_bigendian_uint32(&rawbuf[0]); + firstoffs = get_bigendian_uint48(&rawbuf[4]); + mapcrc = get_bigendian_uint16(&rawbuf[10]); + lengthbits = rawbuf[12]; + selfbits = rawbuf[13]; + parentbits = rawbuf[14]; + + // now read the map + compressed = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes); + core_fseek(chd->file, header->mapoffset + 16, SEEK_SET); + core_fread(chd->file, compressed, mapbytes); + bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes); + header->rawmap = (uint8_t*)malloc(sizeof(uint8_t) * map_size_v5(header)); + + // first decode the compression types + decoder = create_huffman_decoder(16, 8); + err = huffman_import_tree_rle(decoder, bitbuf); + if (err != HUFFERR_NONE) + return CHDERR_DECOMPRESSION_ERROR; + for (hunknum = 0; hunknum < header->hunkcount; hunknum++) + { + uint8_t *rawmap = header->rawmap + (hunknum * 12); + if (repcount > 0) + rawmap[0] = lastcomp, repcount--; + else + { + uint8_t val = huffman_decode_one(decoder, bitbuf); + if (val == COMPRESSION_RLE_SMALL) + rawmap[0] = lastcomp, repcount = 2 + huffman_decode_one(decoder, bitbuf); + else if (val == COMPRESSION_RLE_LARGE) + rawmap[0] = lastcomp, repcount = 2 + 16 + (huffman_decode_one(decoder, bitbuf) << 4), repcount += huffman_decode_one(decoder, bitbuf); + else + rawmap[0] = lastcomp = val; + } + } + + // then iterate through the hunks and extract the needed data + curoffset = firstoffs; + for (hunknum = 0; hunknum < header->hunkcount; hunknum++) + { + uint8_t *rawmap = header->rawmap + (hunknum * 12); + uint64_t offset = curoffset; + uint32_t length = 0; + uint16_t crc = 0; + switch (rawmap[0]) + { + // base types + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + curoffset += length = bitstream_read(bitbuf, lengthbits); + crc = bitstream_read(bitbuf, 16); + break; + + case COMPRESSION_NONE: + curoffset += length = header->hunkbytes; + crc = bitstream_read(bitbuf, 16); + break; + + case COMPRESSION_SELF: + last_self = offset = bitstream_read(bitbuf, selfbits); + break; + + case COMPRESSION_PARENT: + offset = bitstream_read(bitbuf, parentbits); + last_parent = offset; + break; + + // pseudo-types; convert into base types + case COMPRESSION_SELF_1: + last_self++; + case COMPRESSION_SELF_0: + rawmap[0] = COMPRESSION_SELF; + offset = last_self; + break; + + case COMPRESSION_PARENT_SELF: + rawmap[0] = COMPRESSION_PARENT; + last_parent = offset = ( ((uint64_t)hunknum) * ((uint64_t)header->hunkbytes) ) / header->unitbytes; + break; + + case COMPRESSION_PARENT_1: + last_parent += header->hunkbytes / header->unitbytes; + case COMPRESSION_PARENT_0: + rawmap[0] = COMPRESSION_PARENT; + offset = last_parent; + break; + } + // UINT24 length + put_bigendian_uint24(&rawmap[1], length); + + // UINT48 offset + put_bigendian_uint48(&rawmap[4], offset); + + // crc16 + put_bigendian_uint16(&rawmap[10], crc); + } + + // verify the final CRC + if (crc16(&header->rawmap[0], header->hunkcount * 12) != mapcrc) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + +/*------------------------------------------------- + map_extract_old - extract a single map + entry in old format from the datastream +-------------------------------------------------*/ + +static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = 0; + entry->length = entry->offset >> 44; + entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED); +#ifdef __MWERKS__ + entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL; +#else + entry->offset = (entry->offset << 20) >> 20; +#endif +} + + +/*************************************************************************** + CHD FILE MANAGEMENT +***************************************************************************/ + + +/*------------------------------------------------- + chd_open_file - open a CHD file for access +-------------------------------------------------*/ + +chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) +{ + chd_file *newchd = NULL; + chd_error err; + int intfnum; + + /* verify parameters */ + if (file == NULL) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* punt if invalid parent */ + if (parent != NULL && parent->cookie != COOKIE_VALUE) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* allocate memory for the final result */ + newchd = (chd_file *)malloc(sizeof(chd_file)); + if (newchd == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + memset(newchd, 0, sizeof(chd_file)); + newchd->cookie = COOKIE_VALUE; + newchd->parent = parent; + newchd->file = file; + + /* now attempt to read the header */ + err = header_read(newchd->file, &newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* validate the header */ + err = header_validate(&newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* make sure we don't open a read-only file writeable */ + if (mode == CHD_OPEN_READWRITE && !(newchd->header.flags & CHDFLAGS_IS_WRITEABLE)) + EARLY_EXIT(err = CHDERR_FILE_NOT_WRITEABLE); + + /* also, never open an older version writeable */ + if (mode == CHD_OPEN_READWRITE && newchd->header.version < CHD_HEADER_VERSION) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); + + /* if we need a parent, make sure we have one */ + if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT)) + EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + + /* make sure we have a valid parent */ + if (parent != NULL) + { + /* check MD5 if it isn't empty */ + if (memcmp(nullmd5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0 && + memcmp(nullmd5, newchd->parent->header.md5, sizeof(newchd->parent->header.md5)) != 0 && + memcmp(newchd->parent->header.md5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + + /* check SHA1 if it isn't empty */ + if (memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0 && + memcmp(nullsha1, newchd->parent->header.sha1, sizeof(newchd->parent->header.sha1)) != 0 && + memcmp(newchd->parent->header.sha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + } + + /* now read the hunk map */ + if (newchd->header.version < 5) + { + err = map_read(newchd); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + } + else + { + err = decompress_v5_map(newchd, &(newchd->header)); + } + + /* allocate and init the hunk cache */ + newchd->cache = (UINT8 *)malloc(newchd->header.hunkbytes); + newchd->compare = (UINT8 *)malloc(newchd->header.hunkbytes); + if (newchd->cache == NULL || newchd->compare == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + newchd->cachehunk = ~0; + newchd->comparehunk = ~0; + + /* allocate the temporary compressed buffer */ + newchd->compressed = (UINT8 *)malloc(newchd->header.hunkbytes); + if (newchd->compressed == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + + /* find the codec interface */ + if (newchd->header.version < 5) + { + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == newchd->header.compression[0]) + { + newchd->codecintf[0] = &codec_interfaces[intfnum]; + break; + } + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + + /* initialize the codec */ + if (newchd->codecintf[0]->init != NULL) + err = (*newchd->codecintf[0]->init)(&newchd->zlib_codec_data, newchd->header.hunkbytes); + } + else + { + int decompnum; + + // verify the compression types and initialize the codecs + for (decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++) + { + int i; + for (i = 0 ; i < ARRAY_LENGTH(codec_interfaces) ; i++) + { + if (codec_interfaces[i].compression == newchd->header.compression[decompnum]) + { + newchd->codecintf[decompnum] = &codec_interfaces[i]; + if (newchd->codecintf[decompnum] == NULL && newchd->header.compression[decompnum] != 0) + err = CHDERR_UNSUPPORTED_FORMAT; + + /* initialize the codec */ + if (newchd->codecintf[decompnum]->init != NULL) + { + void* codec = NULL; + switch (newchd->header.compression[decompnum]) + { + case CHD_CODEC_CD_ZLIB: + codec = &newchd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_LZMA: + codec = &newchd->cdlz_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &newchd->cdfl_codec_data; + break; + } + if (codec != NULL) + { + err = (*newchd->codecintf[decompnum]->init)(codec, newchd->header.hunkbytes); + } + } + } + } + } + } + + // HACK + //if (err != CHDERR_NONE) + // EARLY_EXIT(err); + + /* all done */ + *chd = newchd; + return CHDERR_NONE; + +cleanup: + if (newchd != NULL) + chd_close(newchd); + return err; +} + +/*------------------------------------------------- + chd_open - open a CHD file by + filename +-------------------------------------------------*/ + +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd) +{ + chd_error err; + core_file *file = NULL; + UINT32 openflags; + + /* choose the proper mode */ + switch(mode) + { + case CHD_OPEN_READ: + break; + + default: + err = CHDERR_INVALID_PARAMETER; + goto cleanup; + } + + /* open the file */ + file = core_fopen(filename); + if (file == 0) + { + err = CHDERR_FILE_NOT_FOUND; + goto cleanup; + } + + /* now open the CHD */ + err = chd_open_file(file, mode, parent, chd); + if (err != CHDERR_NONE) + goto cleanup; + + /* we now own this file */ + (*chd)->owns_file = TRUE; + +cleanup: + if ((err != CHDERR_NONE) && (file != NULL)) + core_fclose(file); + return err; +} + +chd_error chd_precache(chd_file *chd) +{ + ssize_t size, count; + + if (chd->file_cache == NULL) + { + core_fseek(chd->file, 0, SEEK_END); + size = core_ftell(chd->file); + if (size <= 0) + return CHDERR_INVALID_DATA; + chd->file_cache = malloc(size); + if (chd->file_cache == NULL) + return CHDERR_OUT_OF_MEMORY; + core_fseek(chd->file, 0, SEEK_SET); + count = core_fread(chd->file, chd->file_cache, size); + if (count != size) + { + free(chd->file_cache); + chd->file_cache = NULL; + return CHDERR_READ_ERROR; + } + } + + return CHDERR_NONE; +} + +/*------------------------------------------------- + chd_close - close a CHD file for access +-------------------------------------------------*/ + +void chd_close(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return; + + /* deinit the codec */ + if (chd->header.version < 5) + { + if (chd->codecintf[0] != NULL && chd->codecintf[0]->free != NULL) + (*chd->codecintf[0]->free)(&chd->zlib_codec_data); + } + else + { + int i; + // Free the codecs + for (i = 0 ; i < 4 ; i++) + { + void* codec = NULL; + switch (chd->codecintf[i]->compression) + { + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + + case CHD_CODEC_CD_ZLIB: + codec = &chd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &chd->cdfl_codec_data; + break; + } + if (codec) + { + (*chd->codecintf[i]->free)(codec); + } + } + + // Free the raw map + if (chd->header.rawmap != NULL) + free(chd->header.rawmap); + } + + /* free the compressed data buffer */ + if (chd->compressed != NULL) + free(chd->compressed); + + /* free the hunk cache and compare data */ + if (chd->compare != NULL) + free(chd->compare); + if (chd->cache != NULL) + free(chd->cache); + + /* free the hunk map */ + if (chd->map != NULL) + free(chd->map); + + /* free the CRC table */ + if (chd->crctable != NULL) + free(chd->crctable); + + /* free the CRC map */ + if (chd->crcmap != NULL) + free(chd->crcmap); + + /* close the file */ + if (chd->owns_file && chd->file != NULL) + core_fclose(chd->file); + + if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); + + if (chd->file_cache) + free(chd->file_cache); + + /* free our memory */ + free(chd); +} + + +/*------------------------------------------------- + chd_core_file - return the associated + core_file +-------------------------------------------------*/ + +core_file *chd_core_file(chd_file *chd) +{ + return chd->file; +} + + +/*------------------------------------------------- + chd_error_string - return an error string for + the given CHD error +-------------------------------------------------*/ + +const char *chd_error_string(chd_error err) +{ + switch (err) + { + case CHDERR_NONE: return "no error"; + case CHDERR_NO_INTERFACE: return "no drive interface"; + case CHDERR_OUT_OF_MEMORY: return "out of memory"; + case CHDERR_INVALID_FILE: return "invalid file"; + case CHDERR_INVALID_PARAMETER: return "invalid parameter"; + case CHDERR_INVALID_DATA: return "invalid data"; + case CHDERR_FILE_NOT_FOUND: return "file not found"; + case CHDERR_REQUIRES_PARENT: return "requires parent"; + case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable"; + case CHDERR_READ_ERROR: return "read error"; + case CHDERR_WRITE_ERROR: return "write error"; + case CHDERR_CODEC_ERROR: return "codec error"; + case CHDERR_INVALID_PARENT: return "invalid parent"; + case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range"; + case CHDERR_DECOMPRESSION_ERROR: return "decompression error"; + case CHDERR_COMPRESSION_ERROR: return "compression error"; + case CHDERR_CANT_CREATE_FILE: return "can't create file"; + case CHDERR_CANT_VERIFY: return "can't verify file"; + case CHDERR_NOT_SUPPORTED: return "operation not supported"; + case CHDERR_METADATA_NOT_FOUND: return "can't find metadata"; + case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size"; + case CHDERR_UNSUPPORTED_VERSION: return "unsupported CHD version"; + case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify"; + case CHDERR_INVALID_METADATA: return "invalid metadata"; + case CHDERR_INVALID_STATE: return "invalid state"; + case CHDERR_OPERATION_PENDING: return "operation pending"; + case CHDERR_NO_ASYNC_OPERATION: return "no async operation in progress"; + case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format"; + default: return "undocumented error"; + } +} + + + +/*************************************************************************** + CHD HEADER MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_header - return a pointer to the + extracted header data +-------------------------------------------------*/ + +const chd_header *chd_get_header(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return NULL; + + return &chd->header; +} + + + +/*************************************************************************** + CORE DATA READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + chd_read - read a single hunk from the CHD + file +-------------------------------------------------*/ + +chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* perform the read */ + return hunk_read_into_memory(chd, hunknum, (UINT8 *)buffer); +} + + + + + +/*************************************************************************** + METADATA MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_metadata - get the indexed metadata + of the given type +-------------------------------------------------*/ + +chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags) +{ + metadata_entry metaentry; + chd_error err; + UINT32 count; + + /* if we didn't find it, just return */ + err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); + if (err != CHDERR_NONE) + { + /* unless we're an old version and they are requesting hard disk metadata */ + if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0) + { + char faux_metadata[256]; + UINT32 faux_length; + + /* fill in the faux metadata */ + sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, chd->header.hunkbytes / chd->header.obsolete_hunksize); + faux_length = (UINT32)strlen(faux_metadata) + 1; + + /* copy the metadata itself */ + memcpy(output, faux_metadata, MIN(outputlen, faux_length)); + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = faux_length; + if (resulttag != NULL) + *resulttag = HARD_DISK_METADATA_TAG; + return CHDERR_NONE; + } + return err; + } + + /* read the metadata */ + outputlen = MIN(outputlen, metaentry.length); + core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); + count = core_fread(chd->file, output, outputlen); + if (count != outputlen) + return CHDERR_READ_ERROR; + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = metaentry.length; + if (resulttag != NULL) + *resulttag = metaentry.metatag; + if (resultflags != NULL) + *resultflags = metaentry.flags; + return CHDERR_NONE; +} + + + +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + chd_codec_config - set internal codec + parameters +-------------------------------------------------*/ + +chd_error chd_codec_config(chd_file *chd, int param, void *config) +{ + + return CHDERR_INVALID_PARAMETER; +} + + +/*------------------------------------------------- + chd_get_codec_name - get the name of a + particular codec +-------------------------------------------------*/ + +const char *chd_get_codec_name(UINT32 codec) +{ + return "Unknown"; +} + + +/*************************************************************************** + INTERNAL HEADER OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + header_validate - check the validity of a + CHD header +-------------------------------------------------*/ + +static chd_error header_validate(const chd_header *header) +{ + int intfnum; + + /* require a valid version */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* require a valid length */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || + (header->version == 4 && header->length != CHD_V4_HEADER_SIZE) || + (header->version == 5 && header->length != CHD_V5_HEADER_SIZE)) + return CHDERR_INVALID_PARAMETER; + + /* Do not validate v5 header */ + if (header->version <= 4) + { + /* require valid flags */ + if (header->flags & CHDFLAGS_UNDEFINED) + return CHDERR_INVALID_PARAMETER; + + /* require a supported compression mechanism */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == header->compression[0]) + break; + + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunksize */ + if (header->hunkbytes == 0 || header->hunkbytes >= 65536 * 256) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunk count */ + if (header->totalhunks == 0) + return CHDERR_INVALID_PARAMETER; + + /* require a valid MD5 and/or SHA1 if we're using a parent */ + if ((header->flags & CHDFLAGS_HAS_PARENT) && memcmp(header->parentmd5, nullmd5, sizeof(nullmd5)) == 0 && memcmp(header->parentsha1, nullsha1, sizeof(nullsha1)) == 0) + return CHDERR_INVALID_PARAMETER; + + /* if we're V3 or later, the obsolete fields must be 0 */ + if (header->version >= 3 && + (header->obsolete_cylinders != 0 || header->obsolete_sectors != 0 || + header->obsolete_heads != 0 || header->obsolete_hunksize != 0)) + return CHDERR_INVALID_PARAMETER; + + /* if we're pre-V3, the obsolete fields must NOT be 0 */ + if (header->version < 3 && + (header->obsolete_cylinders == 0 || header->obsolete_sectors == 0 || + header->obsolete_heads == 0 || header->obsolete_hunksize == 0)) + return CHDERR_INVALID_PARAMETER; + } + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + header_read - read a CHD header into the + internal data structure +-------------------------------------------------*/ + +static chd_error header_read(core_file *file, chd_header *header) +{ + UINT8 rawheader[CHD_MAX_HEADER_SIZE]; + UINT32 count; + + /* punt if NULL */ + if (header == NULL) + return CHDERR_INVALID_PARAMETER; + + /* punt if invalid file */ + if (file == NULL) + return CHDERR_INVALID_FILE; + + /* seek and read */ + core_fseek(file, 0, SEEK_SET); + count = core_fread(file, rawheader, sizeof(rawheader)); + if (count != sizeof(rawheader)) + return CHDERR_READ_ERROR; + + /* verify the tag */ + if (strncmp((char *)rawheader, "MComprHD", 8) != 0) + return CHDERR_INVALID_DATA; + + /* extract the direct data */ + memset(header, 0, sizeof(*header)); + header->length = get_bigendian_uint32(&rawheader[8]); + header->version = get_bigendian_uint32(&rawheader[12]); + + /* make sure it's a version we understand */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* make sure the length is expected */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || + (header->version == 4 && header->length != CHD_V4_HEADER_SIZE) || + (header->version == 5 && header->length != CHD_V5_HEADER_SIZE)) + + return CHDERR_INVALID_DATA; + + /* extract the common data */ + header->flags = get_bigendian_uint32(&rawheader[16]); + header->compression[0] = get_bigendian_uint32(&rawheader[20]); + + /* extract the V1/V2-specific data */ + if (header->version < 3) + { + int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]); + header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]); + header->totalhunks = get_bigendian_uint32(&rawheader[28]); + header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]); + header->obsolete_heads = get_bigendian_uint32(&rawheader[36]); + header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]); + memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); + memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); + header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen; + header->hunkbytes = seclen * header->obsolete_hunksize; + header->metaoffset = 0; + } + + /* extract the V3-specific data */ + else if (header->version == 3) + { + header->totalhunks = get_bigendian_uint32(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64(&rawheader[28]); + header->metaoffset = get_bigendian_uint64(&rawheader[36]); + memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); + memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); + header->hunkbytes = get_bigendian_uint32(&rawheader[76]); + memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES); + } + + /* extract the V4-specific data */ + else if (header->version == 4) + { + header->totalhunks = get_bigendian_uint32(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64(&rawheader[28]); + header->metaoffset = get_bigendian_uint64(&rawheader[36]); + header->hunkbytes = get_bigendian_uint32(&rawheader[44]); + memcpy(header->sha1, &rawheader[48], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[68], CHD_SHA1_BYTES); + memcpy(header->rawsha1, &rawheader[88], CHD_SHA1_BYTES); + } + + /* extract the V5-specific data */ + else if (header->version == 5) + { + /* TODO */ + header->compression[0] = get_bigendian_uint32(&rawheader[16]); + header->compression[1] = get_bigendian_uint32(&rawheader[20]); + header->compression[2] = get_bigendian_uint32(&rawheader[24]); + header->compression[3] = get_bigendian_uint32(&rawheader[28]); + header->logicalbytes = get_bigendian_uint64(&rawheader[32]); + header->mapoffset = get_bigendian_uint64(&rawheader[40]); + header->metaoffset = get_bigendian_uint64(&rawheader[48]); + header->hunkbytes = get_bigendian_uint32(&rawheader[56]); + header->hunkcount = (header->logicalbytes + header->hunkbytes - 1) / header->hunkbytes; + header->unitbytes = get_bigendian_uint32(&rawheader[60]); + header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; + memcpy(header->sha1, &rawheader[84], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[104], CHD_SHA1_BYTES); + memcpy(header->rawsha1, &rawheader[64], CHD_SHA1_BYTES); + + // determine properties of map entries + header->mapentrybytes = 12; //TODO compressed() ? 12 : 4; + + // hack + header->totalhunks = header->hunkcount; + } + + /* Unknown version */ + else + { + /* TODO */ + } + + /* guess it worked */ + return CHDERR_NONE; +} + + +/*************************************************************************** + INTERNAL HUNK READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + hunk_read_into_cache - read a hunk into + the CHD's hunk cache +-------------------------------------------------*/ + +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) +{ + chd_error err; + + /* track the max */ + if (hunknum > chd->maxhunk) + chd->maxhunk = hunknum; + + /* if we're already in the cache, we're done */ + if (chd->cachehunk == hunknum) + return CHDERR_NONE; + chd->cachehunk = ~0; + + /* otherwise, read the data */ + err = hunk_read_into_memory(chd, hunknum, chd->cache); + if (err != CHDERR_NONE) + return err; + + /* mark the hunk successfully cached in */ + chd->cachehunk = hunknum; + return CHDERR_NONE; +} + + +static UINT8* read_compressed(chd_file *chd, UINT64 offset, size_t size) +{ + ssize_t bytes; + if (chd->file_cache != NULL) + { + return chd->file_cache + offset; + } + else + { + core_fseek(chd->file, offset, SEEK_SET); + bytes = core_fread(chd->file, chd->compressed, size); + if (bytes != size) + return NULL; + return chd->compressed; + } +} + +static chd_error read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest) +{ + ssize_t bytes; + if (chd->file_cache != NULL) + { + memcpy(dest, chd->file_cache + offset, size); + } + else + { + core_fseek(chd->file, offset, SEEK_SET); + bytes = core_fread(chd->file, dest, size); + if (bytes != size) + return CHDERR_READ_ERROR; + } + return CHDERR_NONE; +} + +/*------------------------------------------------- + hunk_read_into_memory - read a hunk into + memory at the given location +-------------------------------------------------*/ + +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest) +{ + chd_error err; + + // punt if no file + if (chd->file == NULL) + return CHDERR_INVALID_FILE; + + /* return an error if out of range */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + if (chd->header.version < 5) + { + map_entry *entry = &chd->map[hunknum]; + UINT32 bytes; + + /* switch off the entry type */ + switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) + { + /* compressed data */ + case V34_MAP_ENTRY_TYPE_COMPRESSED: + { + void* codec; + UINT8 *bytes = read_compressed(chd, entry->offset, entry->length); + if (bytes == NULL) + return CHDERR_READ_ERROR; + + /* now decompress using the codec */ + err = CHDERR_NONE; + codec = &chd->zlib_codec_data; + if (chd->codecintf[0]->decompress != NULL) + err = (*chd->codecintf[0]->decompress)(codec, bytes, entry->length, dest, chd->header.hunkbytes); + if (err != CHDERR_NONE) + return err; + } + break; + + /* uncompressed data */ + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + err = read_uncompressed(chd, entry->offset, chd->header.hunkbytes, dest); + if (err != CHDERR_NONE) + return err; + break; + + /* mini-compressed data */ + case V34_MAP_ENTRY_TYPE_MINI: + put_bigendian_uint64(&dest[0], entry->offset); + for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + break; + + /* self-referenced data */ + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + if (chd->cachehunk == entry->offset && dest == chd->cache) + break; + return hunk_read_into_memory(chd, entry->offset, dest); + + /* parent-referenced data */ + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + err = hunk_read_into_memory(chd->parent, entry->offset, dest); + if (err != CHDERR_NONE) + return err; + break; + } + return CHDERR_NONE; + } + else + { + + // get a pointer to the map entry + uint64_t blockoffs; + uint32_t blocklen; + uint16_t blockcrc; + void* codec = NULL; + uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum]; + UINT8 *bytes; + + // uncompressed case + /* TODO + if (!compressed()) + { + blockoffs = uint64_t(be_read(rawmap, 4)) * uint64_t(m_hunkbytes); + if (blockoffs != 0) + file_read(blockoffs, dest, m_hunkbytes); + else if (m_parent_missing) + throw CHDERR_REQUIRES_PARENT; + else if (m_parent != nullptr) + m_parent->read_hunk(hunknum, dest); + else + memset(dest, 0, m_hunkbytes); + return CHDERR_NONE; + }*/ + + // compressed case + blocklen = get_bigendian_uint24(&rawmap[1]); + blockoffs = get_bigendian_uint48(&rawmap[4]); + blockcrc = get_bigendian_uint16(&rawmap[10]); + switch (rawmap[0]) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + bytes = read_compressed(chd, blockoffs, blocklen); + if (bytes == NULL) + return CHDERR_READ_ERROR; + switch (chd->codecintf[rawmap[0]]->compression) + { + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + + case CHD_CODEC_CD_ZLIB: + codec = &chd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &chd->cdfl_codec_data; + break; + } + if (codec==NULL) + return CHDERR_DECOMPRESSION_ERROR; + chd->codecintf[rawmap[0]]->decompress(codec, bytes, blocklen, dest, chd->header.hunkbytes); + if (dest != NULL && crc16(dest, chd->header.hunkbytes) != blockcrc) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_NONE: + err = read_uncompressed(chd, blockoffs, blocklen, dest); + if (err != CHDERR_NONE) + return err; + if (crc16(dest, chd->header.hunkbytes) != blockcrc) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_SELF: + return hunk_read_into_memory(chd, blockoffs, dest); + + case COMPRESSION_PARENT: + // TODO + //if (m_parent_missing) + // return CHDERR_REQUIRES_PARENT; + //return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); + return CHDERR_DECOMPRESSION_ERROR; + } + return CHDERR_NONE; + } + + // We should not reach this code + return CHDERR_DECOMPRESSION_ERROR; +} + + +/*************************************************************************** + INTERNAL MAP ACCESS +***************************************************************************/ + +/*------------------------------------------------- + map_read - read the initial sector map +-------------------------------------------------*/ + +static chd_error map_read(chd_file *chd) +{ + UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; + UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; + UINT64 fileoffset, maxoffset = 0; + UINT8 cookie[MAP_ENTRY_SIZE]; + UINT32 count; + chd_error err; + int i; + + /* first allocate memory */ + chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks); + if (!chd->map) + return CHDERR_OUT_OF_MEMORY; + + /* read the map entries in in chunks and extract to the map list */ + fileoffset = chd->header.length; + for (i = 0; i < chd->header.totalhunks; i += MAP_STACK_ENTRIES) + { + /* compute how many entries this time */ + int entries = chd->header.totalhunks - i, j; + if (entries > MAP_STACK_ENTRIES) + entries = MAP_STACK_ENTRIES; + + /* read that many */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, raw_map_entries, entries * entrysize); + if (count != entries * entrysize) + { + err = CHDERR_READ_ERROR; + goto cleanup; + } + fileoffset += entries * entrysize; + + /* process that many */ + if (entrysize == MAP_ENTRY_SIZE) + { + for (j = 0; j < entries; j++) + map_extract(&raw_map_entries[j * MAP_ENTRY_SIZE], &chd->map[i + j]); + } + else + { + for (j = 0; j < entries; j++) + map_extract_old(&raw_map_entries[j * OLD_MAP_ENTRY_SIZE], &chd->map[i + j], chd->header.hunkbytes); + } + + /* track the maximum offset */ + for (j = 0; j < entries; j++) + if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_COMPRESSED || + (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_UNCOMPRESSED) + maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length); + } + + /* verify the cookie */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, &cookie, entrysize); + if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + + /* verify the length */ + if (maxoffset > core_fsize(chd->file)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + return CHDERR_NONE; + +cleanup: + if (chd->map) + free(chd->map); + chd->map = NULL; + return err; +} + + + + +/*************************************************************************** + INTERNAL METADATA ACCESS +***************************************************************************/ + +/*------------------------------------------------- + metadata_find_entry - find a metadata entry +-------------------------------------------------*/ + +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry) +{ + /* start at the beginning */ + metaentry->offset = chd->header.metaoffset; + metaentry->prev = 0; + + /* loop until we run out of options */ + while (metaentry->offset != 0) + { + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + UINT32 count; + + /* read the raw header */ + core_fseek(chd->file, metaentry->offset, SEEK_SET); + count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + break; + + /* extract the data */ + metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]); + metaentry->length = get_bigendian_uint32(&raw_meta_header[4]); + metaentry->next = get_bigendian_uint64(&raw_meta_header[8]); + + /* flags are encoded in the high byte of length */ + metaentry->flags = metaentry->length >> 24; + metaentry->length &= 0x00ffffff; + + /* if we got a match, proceed */ + if (metatag == CHDMETATAG_WILDCARD || metaentry->metatag == metatag) + if (metaindex-- == 0) + return CHDERR_NONE; + + /* no match, fetch the next link */ + metaentry->prev = metaentry->offset; + metaentry->offset = metaentry->next; + } + + /* if we get here, we didn't find it */ + return CHDERR_METADATA_NOT_FOUND; +} + + + +/*************************************************************************** + ZLIB COMPRESSION CODEC +***************************************************************************/ + +/*------------------------------------------------- + zlib_codec_init - initialize the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) +{ + zlib_codec_data *data = (zlib_codec_data*)codec; + chd_error err; + int zerr; + + /* clear the buffers */ + memset(data, 0, sizeof(zlib_codec_data)); + + /* init the inflater first */ + data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ + data->inflater.avail_in = 0; + data->inflater.zalloc = zlib_fast_alloc; + data->inflater.zfree = zlib_fast_free; + data->inflater.opaque = &data->allocator; + zerr = inflateInit2(&data->inflater, -MAX_WBITS); + + /* convert errors */ + if (zerr == Z_MEM_ERROR) + err = CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + err = CHDERR_CODEC_ERROR; + else + err = CHDERR_NONE; + + /* handle an error */ + if (err != CHDERR_NONE) + free(data); + + return err; +} + + +/*------------------------------------------------- + zlib_codec_free - free data for the ZLIB + codec +-------------------------------------------------*/ + +static void zlib_codec_free(void *codec) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + + /* deinit the streams */ + if (data != NULL) + { + int i; + zlib_allocator alloc; + + inflateEnd(&data->inflater); + + /* free our fast memory */ + alloc = data->allocator; + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (alloc.allocptr[i]) + free(alloc.allocptr[i]); + } +} + + +/*------------------------------------------------- + zlib_codec_decompress - decomrpess data using + the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + int zerr; + + /* reset the decompressor */ + data->inflater.next_in = (Bytef *)src; + data->inflater.avail_in = complen; + data->inflater.total_in = 0; + data->inflater.next_out = (Bytef *)dest; + data->inflater.avail_out = destlen; + data->inflater.total_out = 0; + zerr = inflateReset(&data->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* do it */ + zerr = inflate(&data->inflater, Z_FINISH); + if (data->inflater.total_out != destlen) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + zlib_fast_alloc - fast malloc for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr; + int i; + + /* compute the size, rounding to the nearest 1k */ + size = (size * items + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + { + ptr = alloc->allocptr[i]; + if (ptr && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one */ + ptr = (UINT32 *)malloc(size + sizeof(UINT32)); + if (!ptr) + return NULL; + + /* put it into the list */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (!alloc->allocptr[i]) + { + alloc->allocptr[i] = ptr; + break; + } + + /* set the low bit of the size so we don't match next time */ + *ptr = size | 1; + return ptr + 1; +} + + +/*------------------------------------------------- + zlib_fast_free - fast free for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static void zlib_fast_free(voidpf opaque, voidpf address) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr = (UINT32 *)address - 1; + int i; + + /* find the hunk */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (ptr == alloc->allocptr[i]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } +} diff --git a/deps/libchdr/chd.h b/deps/libchdr/chd.h new file mode 100644 index 0000000..37aeab4 --- /dev/null +++ b/deps/libchdr/chd.h @@ -0,0 +1,401 @@ +/*************************************************************************** + + chd.h + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#pragma once + +#ifndef __CHD_H__ +#define __CHD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "coretypes.h" + + +/*************************************************************************** + + Compressed Hunks of Data header format. All numbers are stored in + Motorola (big-endian) byte ordering. The header is 76 (V1) or 80 (V2) + bytes long. + + V1 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 hunksize; // 512-byte sectors per hunk + [ 28] UINT32 totalhunks; // total # of hunks represented + [ 32] UINT32 cylinders; // number of cylinders on hard disk + [ 36] UINT32 heads; // number of heads on hard disk + [ 40] UINT32 sectors; // number of sectors on hard disk + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] (V1 header length) + + V2 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 hunksize; // seclen-byte sectors per hunk + [ 28] UINT32 totalhunks; // total # of hunks represented + [ 32] UINT32 cylinders; // number of cylinders on hard disk + [ 36] UINT32 heads; // number of heads on hard disk + [ 40] UINT32 sectors; // number of sectors on hard disk + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] UINT32 seclen; // number of bytes per sector + [ 80] (V2 header length) + + V3 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 totalhunks; // total # of hunks represented + [ 28] UINT64 logicalbytes; // logical size of the data (in bytes) + [ 36] UINT64 metaoffset; // offset to the first blob of metadata + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] UINT32 hunkbytes; // number of bytes per hunk + [ 80] UINT8 sha1[20]; // SHA1 checksum of raw data + [100] UINT8 parentsha1[20];// SHA1 checksum of parent file + [120] (V3 header length) + + V4 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 totalhunks; // total # of hunks represented + [ 28] UINT64 logicalbytes; // logical size of the data (in bytes) + [ 36] UINT64 metaoffset; // offset to the first blob of metadata + [ 44] UINT32 hunkbytes; // number of bytes per hunk + [ 48] UINT8 sha1[20]; // combined raw+meta SHA1 + [ 68] UINT8 parentsha1[20];// combined raw+meta SHA1 of parent + [ 88] UINT8 rawsha1[20]; // raw data SHA1 + [108] (V4 header length) + + Flags: + 0x00000001 - set if this drive has a parent + 0x00000002 - set if this drive allows writes + + ========================================================================= + + V5 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] uint32_t length; // length of header (including tag and length fields) + [ 12] uint32_t version; // drive format version + [ 16] uint32_t compressors[4];// which custom compressors are used? + [ 32] uint64_t logicalbytes; // logical size of the data (in bytes) + [ 40] uint64_t mapoffset; // offset to the map + [ 48] uint64_t metaoffset; // offset to the first blob of metadata + [ 56] uint32_t hunkbytes; // number of bytes per hunk (512k maximum) + [ 60] uint32_t unitbytes; // number of bytes per unit within each hunk + [ 64] uint8_t rawsha1[20]; // raw data SHA1 + [ 84] uint8_t sha1[20]; // combined raw+meta SHA1 + [104] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent + [124] (V5 header length) + + If parentsha1 != 0, we have a parent (no need for flags) + If compressors[0] == 0, we are uncompressed (including maps) + + V5 uncompressed map format: + + [ 0] uint32_t offset; // starting offset / hunk size + + V5 compressed map format header: + + [ 0] uint32_t length; // length of compressed map + [ 4] UINT48 datastart; // offset of first block + [ 10] uint16_t crc; // crc-16 of the map + [ 12] uint8_t lengthbits; // bits used to encode complength + [ 13] uint8_t hunkbits; // bits used to encode self-refs + [ 14] uint8_t parentunitbits; // bits used to encode parent unit refs + [ 15] uint8_t reserved; // future use + [ 16] (compressed header length) + + Each compressed map entry, once expanded, looks like: + + [ 0] uint8_t compression; // compression type + [ 1] UINT24 complength; // compressed length + [ 4] UINT48 offset; // offset + [ 10] uint16_t crc; // crc-16 of the data + +***************************************************************************/ + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* header information */ +#define CHD_HEADER_VERSION 5 +#define CHD_V1_HEADER_SIZE 76 +#define CHD_V2_HEADER_SIZE 80 +#define CHD_V3_HEADER_SIZE 120 +#define CHD_V4_HEADER_SIZE 108 +#define CHD_V5_HEADER_SIZE 124 + +#define CHD_MAX_HEADER_SIZE CHD_V5_HEADER_SIZE + +/* checksumming information */ +#define CHD_MD5_BYTES 16 +#define CHD_SHA1_BYTES 20 + +/* CHD global flags */ +#define CHDFLAGS_HAS_PARENT 0x00000001 +#define CHDFLAGS_IS_WRITEABLE 0x00000002 +#define CHDFLAGS_UNDEFINED 0xfffffffc + +/* compression types */ +#define CHDCOMPRESSION_NONE 0 +#define CHDCOMPRESSION_ZLIB 1 +#define CHDCOMPRESSION_ZLIB_PLUS 2 +#define CHDCOMPRESSION_AV 3 + +/* A/V codec configuration parameters */ +#define AV_CODEC_COMPRESS_CONFIG 1 +#define AV_CODEC_DECOMPRESS_CONFIG 2 + +/* metadata parameters */ +#define CHDMETATAG_WILDCARD 0 +#define CHD_METAINDEX_APPEND ((UINT32)-1) + +/* metadata flags */ +#define CHD_MDFLAGS_CHECKSUM 0x01 /* indicates data is checksummed */ + +/* standard hard disk metadata */ +#define HARD_DISK_METADATA_TAG 0x47444444 /* 'GDDD' */ +#define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d" + +/* hard disk identify information */ +#define HARD_DISK_IDENT_METADATA_TAG 0x49444e54 /* 'IDNT' */ + +/* hard disk key information */ +#define HARD_DISK_KEY_METADATA_TAG 0x4b455920 /* 'KEY ' */ + +/* pcmcia CIS information */ +#define PCMCIA_CIS_METADATA_TAG 0x43495320 /* 'CIS ' */ + +/* standard CD-ROM metadata */ +#define CDROM_OLD_METADATA_TAG 0x43484344 /* 'CHCD' */ +#define CDROM_TRACK_METADATA_TAG 0x43485452 /* 'CHTR' */ +#define CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" +#define CDROM_TRACK_METADATA2_TAG 0x43485432 /* 'CHT2' */ +#define CDROM_TRACK_METADATA2_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" +#define GDROM_TRACK_METADATA_TAG 0x43484744 /* 'CHTD' */ +#define GDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" + +/* standard A/V metadata */ +#define AV_METADATA_TAG 0x41564156 /* 'AVAV' */ +#define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d" + +/* A/V laserdisc frame metadata */ +#define AV_LD_METADATA_TAG 0x41564C44 /* 'AVLD' */ + +/* CHD open values */ +#define CHD_OPEN_READ 1 +#define CHD_OPEN_READWRITE 2 + +/* error types */ +enum _chd_error +{ + CHDERR_NONE, + CHDERR_NO_INTERFACE, + CHDERR_OUT_OF_MEMORY, + CHDERR_INVALID_FILE, + CHDERR_INVALID_PARAMETER, + CHDERR_INVALID_DATA, + CHDERR_FILE_NOT_FOUND, + CHDERR_REQUIRES_PARENT, + CHDERR_FILE_NOT_WRITEABLE, + CHDERR_READ_ERROR, + CHDERR_WRITE_ERROR, + CHDERR_CODEC_ERROR, + CHDERR_INVALID_PARENT, + CHDERR_HUNK_OUT_OF_RANGE, + CHDERR_DECOMPRESSION_ERROR, + CHDERR_COMPRESSION_ERROR, + CHDERR_CANT_CREATE_FILE, + CHDERR_CANT_VERIFY, + CHDERR_NOT_SUPPORTED, + CHDERR_METADATA_NOT_FOUND, + CHDERR_INVALID_METADATA_SIZE, + CHDERR_UNSUPPORTED_VERSION, + CHDERR_VERIFY_INCOMPLETE, + CHDERR_INVALID_METADATA, + CHDERR_INVALID_STATE, + CHDERR_OPERATION_PENDING, + CHDERR_NO_ASYNC_OPERATION, + CHDERR_UNSUPPORTED_FORMAT +}; +typedef enum _chd_error chd_error; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* opaque types */ +typedef struct _chd_file chd_file; + + +/* extract header structure (NOT the on-disk header structure) */ +typedef struct _chd_header chd_header; +struct _chd_header +{ + UINT32 length; /* length of header data */ + UINT32 version; /* drive format version */ + UINT32 flags; /* flags field */ + UINT32 compression[4]; /* compression type */ + UINT32 hunkbytes; /* number of bytes per hunk */ + UINT32 totalhunks; /* total # of hunks represented */ + UINT64 logicalbytes; /* logical size of the data */ + UINT64 metaoffset; /* offset in file of first metadata */ + UINT64 mapoffset; /* TOOD V5 */ + UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */ + UINT8 parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */ + UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */ + UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */ + UINT8 parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */ + UINT32 unitbytes; /* TODO V5 */ + UINT64 unitcount; /* TODO V5 */ + UINT32 hunkcount; /* TODO V5 */ + + // map information + UINT32 mapentrybytes; // length of each entry in a map (V5) + UINT8* rawmap; // raw map data + + UINT32 obsolete_cylinders; /* obsolete field -- do not use! */ + UINT32 obsolete_sectors; /* obsolete field -- do not use! */ + UINT32 obsolete_heads; /* obsolete field -- do not use! */ + UINT32 obsolete_hunksize; /* obsolete field -- do not use! */ +}; + + +/* structure for returning information about a verification pass */ +typedef struct _chd_verify_result chd_verify_result; +struct _chd_verify_result +{ + UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */ + UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */ + UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */ + UINT8 metasha1[CHD_SHA1_BYTES]; /* SHA1 checksum of metadata */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- CHD file management ----- */ + +/* create a new CHD file fitting the given description */ +// chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); + +/* same as chd_create(), but accepts an already-opened core_file object */ +// chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); + +/* open an existing CHD file */ +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd); + +/* precache underlying file */ +chd_error chd_precache(chd_file *chd); + +/* close a CHD file */ +void chd_close(chd_file *chd); + +/* return the associated core_file */ +core_file *chd_core_file(chd_file *chd); + +/* return an error string for the given CHD error */ +const char *chd_error_string(chd_error err); + + + +/* ----- CHD header management ----- */ + +/* return a pointer to the extracted CHD header data */ +const chd_header *chd_get_header(chd_file *chd); + + + + +/* ----- core data read/write ----- */ + +/* read one hunk from the CHD file */ +chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer); + + + +/* ----- metadata management ----- */ + +/* get indexed metadata of a particular sort */ +chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags); + + + + +/* ----- codec interfaces ----- */ + +/* set internal codec parameters */ +chd_error chd_codec_config(chd_file *chd, int param, void *config); + +/* return a string description of a codec */ +const char *chd_get_codec_name(UINT32 codec); + +#ifdef __cplusplus +} +#endif + +#endif /* __CHD_H__ */ diff --git a/deps/libchdr/coretypes.h b/deps/libchdr/coretypes.h new file mode 100644 index 0000000..6264bea --- /dev/null +++ b/deps/libchdr/coretypes.h @@ -0,0 +1,40 @@ +#ifndef __CORETYPES_H__ +#define __CORETYPES_H__ + +#include <stdint.h> +#include <stdio.h> + +#ifdef __LIBRETRO__ +#include <streams/file_stream_transforms.h> +#endif + +#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0])) + +typedef uint64_t UINT64; +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; + +typedef int64_t INT64; +typedef int32_t INT32; +typedef int16_t INT16; +typedef int8_t INT8; + +#define core_file FILE +#define core_fopen(file) fopen(file, "rb") +#define core_fseek fseek +#define core_fread(fc, buff, len) fread(buff, 1, len, fc) +#define core_fclose fclose +#define core_ftell ftell + +static size_t core_fsize(core_file* f) +{ + size_t rv; + size_t p = ftell(f); + fseek(f, 0, SEEK_END); + rv = ftell(f); + fseek(f, p, SEEK_SET); + return rv; +} + +#endif diff --git a/deps/libchdr/flac.c b/deps/libchdr/flac.c new file mode 100644 index 0000000..29844b6 --- /dev/null +++ b/deps/libchdr/flac.c @@ -0,0 +1,331 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ flac.c
+
+ FLAC compression wrappers
+
+***************************************************************************/
+
+#include <assert.h>
+#include <string.h>
+#include "flac.h"
+
+//**************************************************************************
+// FLAC DECODER
+//**************************************************************************
+
+static FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes);
+static void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+// getters (valid after reset)
+static uint32_t sample_rate(flac_decoder *decoder) { return decoder->sample_rate; }
+static uint8_t channels(flac_decoder *decoder) { return decoder->channels; }
+static uint8_t bits_per_sample(flac_decoder *decoder) { return decoder->bits_per_sample; }
+static uint32_t total_samples(flac_decoder *decoder) { return FLAC__stream_decoder_get_total_samples(decoder->decoder); }
+static FLAC__StreamDecoderState state(flac_decoder *decoder) { return FLAC__stream_decoder_get_state(decoder->decoder); }
+static const char *state_string(flac_decoder *decoder) { return FLAC__stream_decoder_get_resolved_state_string(decoder->decoder); }
+
+//-------------------------------------------------
+// flac_decoder - constructor
+//-------------------------------------------------
+
+void flac_decoder_init(flac_decoder *decoder)
+{
+ decoder->decoder = FLAC__stream_decoder_new();
+ decoder->sample_rate = 0;
+ decoder->channels = 0;
+ decoder->bits_per_sample = 0;
+ decoder->compressed_offset = 0;
+ decoder->compressed_start = NULL;
+ decoder->compressed_length = 0;
+ decoder->compressed2_start = NULL;
+ decoder->compressed2_length = 0;
+ decoder->uncompressed_offset = 0;
+ decoder->uncompressed_length = 0;
+ decoder->uncompressed_swap = 0;
+}
+
+//-------------------------------------------------
+// flac_decoder - destructor
+//-------------------------------------------------
+
+void flac_decoder_free(flac_decoder* decoder)
+{
+ if ((decoder != NULL) && (decoder->decoder != NULL))
+ FLAC__stream_decoder_delete(decoder->decoder);
+}
+
+
+//-------------------------------------------------
+// reset - reset state with the original
+// parameters
+//-------------------------------------------------
+
+static int flac_decoder_internal_reset(flac_decoder* decoder)
+{
+ decoder->compressed_offset = 0;
+ if (FLAC__stream_decoder_init_stream(decoder->decoder,
+ &flac_decoder_read_callback_static,
+ NULL,
+ &flac_decoder_tell_callback_static,
+ NULL,
+ NULL,
+ &flac_decoder_write_callback_static,
+ &flac_decoder_metadata_callback_static,
+ &flac_decoder_error_callback_static, decoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ return 0;
+ return FLAC__stream_decoder_process_until_end_of_metadata(decoder->decoder);
+}
+
+
+
+//-------------------------------------------------
+// reset - reset state with new memory parameters
+// and a custom-generated header
+//-------------------------------------------------
+
+int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length)
+{
+ // modify the template header with our parameters
+ static const uint8_t s_header_template[0x2a] =
+ {
+ 0x66, 0x4C, 0x61, 0x43, // +00: 'fLaC' stream header
+ 0x80, // +04: metadata block type 0 (STREAMINFO),
+ // flagged as last block
+ 0x00, 0x00, 0x22, // +05: metadata block length = 0x22
+ 0x00, 0x00, // +08: minimum block size
+ 0x00, 0x00, // +0A: maximum block size
+ 0x00, 0x00, 0x00, // +0C: minimum frame size (0 == unknown)
+ 0x00, 0x00, 0x00, // +0F: maximum frame size (0 == unknown)
+ 0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, // +12: sample rate (0x0ac44 == 44100),
+ // numchannels (2), sample bits (16),
+ // samples in stream (0 == unknown)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // +1A: MD5 signature (0 == none)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
+ // +2A: start of stream data
+ };
+ memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template));
+ decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8;
+ decoder->custom_header[0x09] = decoder->custom_header[0x0b] = block_size & 0xff;
+ decoder->custom_header[0x12] = sample_rate >> 12;
+ decoder->custom_header[0x13] = sample_rate >> 4;
+ decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
+
+ // configure the header ahead of the provided buffer
+ decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header);
+ decoder->compressed_length = sizeof(decoder->custom_header);
+ decoder->compressed2_start = (const FLAC__byte *)(buffer);
+ decoder->compressed2_length = length;
+ return flac_decoder_internal_reset(decoder);
+}
+
+
+//-------------------------------------------------
+// decode_interleaved - decode to an interleaved
+// sound stream
+//-------------------------------------------------
+
+int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian)
+{
+ // configure the uncompressed buffer
+ memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start));
+ decoder->uncompressed_start[0] = samples;
+ decoder->uncompressed_offset = 0;
+ decoder->uncompressed_length = num_samples;
+ decoder->uncompressed_swap = swap_endian;
+
+ // loop until we get everything we want
+ while (decoder->uncompressed_offset < decoder->uncompressed_length)
+ if (!FLAC__stream_decoder_process_single(decoder->decoder))
+ return 0;
+ return 1;
+}
+
+
+/*
+//-------------------------------------------------
+// decode - decode to an multiple independent
+// data streams
+//-------------------------------------------------
+
+bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian)
+{
+ // make sure we don't have too many channels
+ int chans = channels();
+ if (chans > ARRAY_LENGTH(m_uncompressed_start))
+ return false;
+
+ // configure the uncompressed buffer
+ memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
+ for (int curchan = 0; curchan < chans; curchan++)
+ m_uncompressed_start[curchan] = samples[curchan];
+ m_uncompressed_offset = 0;
+ m_uncompressed_length = num_samples;
+ m_uncompressed_swap = swap_endian;
+
+ // loop until we get everything we want
+ while (m_uncompressed_offset < m_uncompressed_length)
+ if (!FLAC__stream_decoder_process_single(m_decoder))
+ return false;
+ return true;
+}
+*/
+
+//-------------------------------------------------
+// finish - finish up the decode
+//-------------------------------------------------
+
+uint32_t flac_decoder_finish(flac_decoder* decoder)
+{
+ // get the final decoding position and move forward
+ FLAC__uint64 position = 0;
+ FLAC__stream_decoder_get_decode_position(decoder->decoder, &position);
+ FLAC__stream_decoder_finish(decoder->decoder);
+
+ // adjust position if we provided the header
+ if (position == 0)
+ return 0;
+ if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header))
+ position -= decoder->compressed_length;
+ return position;
+}
+
+
+//-------------------------------------------------
+// read_callback - handle reads from the input
+// stream
+//-------------------------------------------------
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ return flac_decoder_read_callback(client_data, buffer, bytes);
+}
+
+FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes)
+{
+ flac_decoder* decoder = (flac_decoder*)client_data;
+
+ uint32_t expected = *bytes;
+
+ // copy from primary buffer first
+ uint32_t outputpos = 0;
+ if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length)
+ {
+ uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed_length - decoder->compressed_offset);
+ memcpy(&buffer[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy);
+ outputpos += bytes_to_copy;
+ decoder->compressed_offset += bytes_to_copy;
+ }
+
+ // once we're out of that, copy from the secondary buffer
+ if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length)
+ {
+ uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length));
+ memcpy(&buffer[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy);
+ outputpos += bytes_to_copy;
+ decoder->compressed_offset += bytes_to_copy;
+ }
+ *bytes = outputpos;
+
+ // return based on whether we ran out of data
+ return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+
+//-------------------------------------------------
+// metadata_callback - handle STREAMINFO metadata
+//-------------------------------------------------
+
+void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ flac_decoder *fldecoder;
+ // ignore all but STREAMINFO metadata
+ if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO)
+ return;
+
+ // parse out the data we care about
+ fldecoder = (flac_decoder *)(client_data);
+ fldecoder->sample_rate = metadata->data.stream_info.sample_rate;
+ fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample;
+ fldecoder->channels = metadata->data.stream_info.channels;
+}
+
+
+//-------------------------------------------------
+// tell_callback - handle requests to find out
+// where in the input stream we are
+//-------------------------------------------------
+
+FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ *absolute_byte_offset = ((flac_decoder *)client_data)->compressed_offset;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+
+//-------------------------------------------------
+// write_callback - handle writes to the output
+// stream
+//-------------------------------------------------
+
+FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+ return flac_decoder_write_callback(client_data, frame, buffer);
+}
+
+FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+ int shift, blocksize;
+ flac_decoder * decoder = (flac_decoder *)client_data;
+
+ assert(frame->header.channels == channels(decoder));
+
+ // interleaved case
+ shift = decoder->uncompressed_swap ? 8 : 0;
+ blocksize = frame->header.blocksize;
+
+ if (decoder->uncompressed_start[1] == NULL)
+ {
+ int sampnum, chan;
+ int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels;
+ for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
+ for (chan = 0; chan < frame->header.channels; chan++)
+ *dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift));
+ }
+
+ // non-interleaved case
+ else
+ {
+ int sampnum, chan;
+ for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
+ for (chan = 0; chan < frame->header.channels; chan++)
+ if (decoder->uncompressed_start[chan] != NULL)
+ decoder->uncompressed_start[chan][decoder->uncompressed_offset] = (int16_t) ( (((uint16_t)(buffer[chan][sampnum])) << shift) | ( ((uint16_t)(buffer[chan][sampnum])) >> shift) );
+ }
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+/**
+ * @fn void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+ *
+ * @brief -------------------------------------------------
+ * error_callback - handle errors (ignore them)
+ * -------------------------------------------------.
+ *
+ * @param decoder The decoder.
+ * @param status The status.
+ * @param [in,out] client_data If non-null, information describing the client.
+ */
+
+void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+}
diff --git a/deps/libchdr/flac.h b/deps/libchdr/flac.h new file mode 100644 index 0000000..23e91a9 --- /dev/null +++ b/deps/libchdr/flac.h @@ -0,0 +1,50 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ flac.h
+
+ FLAC compression wrappers
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __FLAC_H__
+#define __FLAC_H__
+
+#include <stdint.h>
+#include "FLAC/all.h"
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef struct _flac_decoder flac_decoder;
+struct _flac_decoder {
+ // output state
+ FLAC__StreamDecoder* decoder; // actual encoder
+ uint32_t sample_rate; // decoded sample rate
+ uint8_t channels; // decoded number of channels
+ uint8_t bits_per_sample; // decoded bits per sample
+ uint32_t compressed_offset; // current offset in compressed data
+ const FLAC__byte * compressed_start; // start of compressed data
+ uint32_t compressed_length; // length of compressed data
+ const FLAC__byte * compressed2_start; // start of compressed data
+ uint32_t compressed2_length; // length of compressed data
+ int16_t * uncompressed_start[8]; // pointer to start of uncompressed data (up to 8 streams)
+ uint32_t uncompressed_offset; // current position in uncompressed data
+ uint32_t uncompressed_length; // length of uncompressed data
+ int uncompressed_swap; // swap uncompressed sample data
+ uint8_t custom_header[0x2a]; // custom header
+};
+
+// ======================> flac_decoder
+
+void flac_decoder_init(flac_decoder* decoder);
+void flac_decoder_free(flac_decoder* decoder);
+int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length);
+int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian);
+uint32_t flac_decoder_finish(flac_decoder* decoder);
+
+#endif // __FLAC_H__
diff --git a/deps/libchdr/huffman.c b/deps/libchdr/huffman.c new file mode 100644 index 0000000..d67ec19 --- /dev/null +++ b/deps/libchdr/huffman.c @@ -0,0 +1,528 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ huffman.c
+
+ Static Huffman compression and decompression helpers.
+
+****************************************************************************
+
+ Maximum codelength is officially (alphabetsize - 1). This would be 255 bits
+ (since we use 1 byte values). However, it is also dependent upon the number
+ of samples used, as follows:
+
+ 2 bits -> 3..4 samples
+ 3 bits -> 5..7 samples
+ 4 bits -> 8..12 samples
+ 5 bits -> 13..20 samples
+ 6 bits -> 21..33 samples
+ 7 bits -> 34..54 samples
+ 8 bits -> 55..88 samples
+ 9 bits -> 89..143 samples
+ 10 bits -> 144..232 samples
+ 11 bits -> 233..376 samples
+ 12 bits -> 377..609 samples
+ 13 bits -> 610..986 samples
+ 14 bits -> 987..1596 samples
+ 15 bits -> 1597..2583 samples
+ 16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits
+ 17 bits -> 4181..6764 samples
+ 18 bits -> 6765..10945 samples
+ 19 bits -> 10946..17710 samples
+ 20 bits -> 17711..28656 samples
+ 21 bits -> 28657..46367 samples
+ 22 bits -> 46368..75024 samples
+ 23 bits -> 75025..121392 samples
+ 24 bits -> 121393..196417 samples
+ 25 bits -> 196418..317810 samples
+ 26 bits -> 317811..514228 samples
+ 27 bits -> 514229..832039 samples
+ 28 bits -> 832040..1346268 samples
+ 29 bits -> 1346269..2178308 samples
+ 30 bits -> 2178309..3524577 samples
+ 31 bits -> 3524578..5702886 samples
+ 32 bits -> 5702887..9227464 samples
+
+ Looking at it differently, here is where powers of 2 fall into these buckets:
+
+ 256 samples -> 11 bits max
+ 512 samples -> 12 bits max
+ 1k samples -> 14 bits max
+ 2k samples -> 15 bits max
+ 4k samples -> 16 bits max
+ 8k samples -> 18 bits max
+ 16k samples -> 19 bits max
+ 32k samples -> 21 bits max
+ 64k samples -> 22 bits max
+ 128k samples -> 24 bits max
+ 256k samples -> 25 bits max
+ 512k samples -> 27 bits max
+ 1M samples -> 28 bits max
+ 2M samples -> 29 bits max
+ 4M samples -> 31 bits max
+ 8M samples -> 32 bits max
+
+****************************************************************************
+
+ Delta-RLE encoding works as follows:
+
+ Starting value is assumed to be 0. All data is encoded as a delta
+ from the previous value, such that final[i] = final[i - 1] + delta.
+ Long runs of 0s are RLE-encoded as follows:
+
+ 0x100 = repeat count of 8
+ 0x101 = repeat count of 9
+ 0x102 = repeat count of 10
+ 0x103 = repeat count of 11
+ 0x104 = repeat count of 12
+ 0x105 = repeat count of 13
+ 0x106 = repeat count of 14
+ 0x107 = repeat count of 15
+ 0x108 = repeat count of 16
+ 0x109 = repeat count of 32
+ 0x10a = repeat count of 64
+ 0x10b = repeat count of 128
+ 0x10c = repeat count of 256
+ 0x10d = repeat count of 512
+ 0x10e = repeat count of 1024
+ 0x10f = repeat count of 2048
+
+ Note that repeat counts are reset at the end of a row, so if a 0 run
+ extends to the end of a row, a large repeat count may be used.
+
+ The reason for starting the run counts at 8 is that 0 is expected to
+ be the most common symbol, and is typically encoded in 1 or 2 bits.
+
+***************************************************************************/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "huffman.h"
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// huffman_context_base - create an encoding/
+// decoding context
+//-------------------------------------------------
+
+struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits)
+{
+ struct huffman_decoder* decoder;
+
+ /* limit to 24 bits */
+ if (maxbits > 24)
+ return NULL;
+
+ decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
+ decoder->numcodes = numcodes;
+ decoder->maxbits = maxbits;
+ decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits));
+ decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes);
+ decoder->datahisto = NULL;
+ decoder->prevdata = 0;
+ decoder->rleremaining = 0;
+ return decoder;
+}
+
+//-------------------------------------------------
+// decode_one - decode a single code from the
+// huffman stream
+//-------------------------------------------------
+
+uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf)
+{
+ /* peek ahead to get maxbits worth of data */
+ uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits);
+
+ /* look it up, then remove the actual number of bits for this code */
+ lookup_value lookup = decoder->lookup[bits];
+ bitstream_remove(bitbuf, lookup & 0x1f);
+
+ /* return the value */
+ return lookup >> 5;
+}
+
+//-------------------------------------------------
+// import_tree_rle - import an RLE-encoded
+// huffman tree from a source data stream
+//-------------------------------------------------
+
+enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf)
+{
+ enum huffman_error error;
+ int curnode;
+ // bits per entry depends on the maxbits
+ int numbits;
+ if (decoder->maxbits >= 16)
+ numbits = 5;
+ else if (decoder->maxbits >= 8)
+ numbits = 4;
+ else
+ numbits = 3;
+
+ // loop until we read all the nodes
+ for (curnode = 0; curnode < decoder->numcodes; )
+ {
+ // a non-one value is just raw
+ int nodebits = bitstream_read(bitbuf, numbits);
+ if (nodebits != 1)
+ decoder->huffnode[curnode++].numbits = nodebits;
+
+ // a one value is an escape code
+ else
+ {
+ // a double 1 is just a single 1
+ nodebits = bitstream_read(bitbuf, numbits);
+ if (nodebits == 1)
+ decoder->huffnode[curnode++].numbits = nodebits;
+
+ // otherwise, we need one for value for the repeat count
+ else
+ {
+ int repcount = bitstream_read(bitbuf, numbits) + 3;
+ while (repcount--)
+ decoder->huffnode[curnode++].numbits = nodebits;
+ }
+ }
+ }
+
+ // make sure we ended up with the right number
+ if (curnode != decoder->numcodes)
+ return HUFFERR_INVALID_DATA;
+
+ // assign canonical codes for all nodes based on their code lengths
+ error = huffman_assign_canonical_codes(decoder);
+ if (error != HUFFERR_NONE)
+ return error;
+
+ // build the lookup table
+ huffman_build_lookup_table(decoder);
+
+ // determine final input length and report errors
+ return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
+}
+
+
+//-------------------------------------------------
+// import_tree_huffman - import a huffman-encoded
+// huffman tree from a source data stream
+//-------------------------------------------------
+
+enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf)
+{
+ int index;
+ int start;
+ int count = 0;
+ uint8_t rlefullbits = 0;
+ int last = 0;
+ int curcode;
+ enum huffman_error error;
+ uint32_t temp;
+ // start by parsing the lengths for the small tree
+ struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
+
+ smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
+ start = bitstream_read(bitbuf, 3) + 1;
+
+ for (index = 1; index < 24; index++)
+ {
+ if (index < start || count == 7)
+ smallhuff->huffnode[index].numbits = 0;
+ else
+ {
+ count = bitstream_read(bitbuf, 3);
+ smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count;
+ }
+ }
+
+ // then regenerate the tree
+ error = huffman_assign_canonical_codes(smallhuff);
+ if (error != HUFFERR_NONE)
+ return error;
+ huffman_build_lookup_table(smallhuff);
+
+ // determine the maximum length of an RLE count
+ temp = decoder->numcodes - 9;
+ while (temp != 0)
+ temp >>= 1, rlefullbits++;
+
+ // now process the rest of the data
+ for (curcode = 0; curcode < decoder->numcodes; )
+ {
+ int value = huffman_decode_one(smallhuff, bitbuf);
+ if (value != 0)
+ decoder->huffnode[curcode++].numbits = last = value - 1;
+ else
+ {
+ int count = bitstream_read(bitbuf, 3) + 2;
+ if (count == 7+2)
+ count += bitstream_read(bitbuf, rlefullbits);
+ for ( ; count != 0 && curcode < decoder->numcodes; count--)
+ decoder->huffnode[curcode++].numbits = last;
+ }
+ }
+
+ // make sure we ended up with the right number
+ if (curcode != decoder->numcodes)
+ return HUFFERR_INVALID_DATA;
+
+ // assign canonical codes for all nodes based on their code lengths
+ error = huffman_assign_canonical_codes(decoder);
+ if (error != HUFFERR_NONE)
+ return error;
+
+ // build the lookup table
+ huffman_build_lookup_table(decoder);
+
+ // determine final input length and report errors
+ return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
+}
+
+
+//-------------------------------------------------
+// compute_tree_from_histo - common backend for
+// computing a tree based on the data histogram
+//-------------------------------------------------
+
+enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder)
+{
+ int i;
+ uint32_t upperweight;
+ uint32_t lowerweight = 0;
+ // compute the number of data items in the histogram
+ uint32_t sdatacount = 0;
+ for (i = 0; i < decoder->numcodes; i++)
+ sdatacount += decoder->datahisto[i];
+
+ // binary search to achieve the optimum encoding
+ upperweight = sdatacount * 2;
+ while (1)
+ {
+ // build a tree using the current weight
+ uint32_t curweight = (upperweight + lowerweight) / 2;
+ int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight);
+
+ // apply binary search here
+ if (curmaxbits <= decoder->maxbits)
+ {
+ lowerweight = curweight;
+
+ // early out if it worked with the raw weights, or if we're done searching
+ if (curweight == sdatacount || (upperweight - lowerweight) <= 1)
+ break;
+ }
+ else
+ upperweight = curweight;
+ }
+
+ // assign canonical codes for all nodes based on their code lengths
+ return huffman_assign_canonical_codes(decoder);
+}
+
+
+
+//**************************************************************************
+// INTERNAL FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+// tree_node_compare - compare two tree nodes
+// by weight
+//-------------------------------------------------
+
+static int huffman_tree_node_compare(const void *item1, const void *item2)
+{
+ const struct node_t *node1 = *(const struct node_t **)item1;
+ const struct node_t *node2 = *(const struct node_t **)item2;
+ if (node2->weight != node1->weight)
+ return node2->weight - node1->weight;
+ if (node2->bits - node1->bits == 0)
+ fprintf(stderr, "identical node sort keys, should not happen!\n");
+ return (int)node1->bits - (int)node2->bits;
+}
+
+
+//-------------------------------------------------
+// build_tree - build a huffman tree based on the
+// data distribution
+//-------------------------------------------------
+
+int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight)
+{
+ int curcode;
+ int nextalloc;
+ int maxbits = 0;
+ // make a list of all non-zero nodes
+ struct node_t** list = (struct node_t**)malloc(sizeof(struct node_t*) * decoder->numcodes * 2);
+ int listitems = 0;
+ memset(decoder->huffnode, 0, decoder->numcodes * sizeof(decoder->huffnode[0]));
+ for (curcode = 0; curcode < decoder->numcodes; curcode++)
+ if (decoder->datahisto[curcode] != 0)
+ {
+ list[listitems++] = &decoder->huffnode[curcode];
+ decoder->huffnode[curcode].count = decoder->datahisto[curcode];
+ decoder->huffnode[curcode].bits = curcode;
+
+ // scale the weight by the current effective length, ensuring we don't go to 0
+ decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
+ if (decoder->huffnode[curcode].weight == 0)
+ decoder->huffnode[curcode].weight = 1;
+ }
+/*
+ fprintf(stderr, "Pre-sort:\n");
+ for (int i = 0; i < listitems; i++) {
+ fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
+ }
+*/
+ // sort the list by weight, largest weight first
+ qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare);
+/*
+ fprintf(stderr, "Post-sort:\n");
+ for (int i = 0; i < listitems; i++) {
+ fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
+ }
+ fprintf(stderr, "===================\n");
+*/
+ // now build the tree
+ nextalloc = decoder->numcodes;
+
+ while (listitems > 1)
+ {
+ int curitem;
+ // remove lowest two items
+ struct node_t* node1 = &(*list[--listitems]);
+ struct node_t* node0 = &(*list[--listitems]);
+
+ // create new node
+ struct node_t* newnode = &decoder->huffnode[nextalloc++];
+ newnode->parent = NULL;
+ node0->parent = node1->parent = newnode;
+ newnode->weight = node0->weight + node1->weight;
+
+ // insert into list at appropriate location
+ for (curitem = 0; curitem < listitems; curitem++)
+ if (newnode->weight > list[curitem]->weight)
+ {
+ memmove(&list[curitem+1], &list[curitem], (listitems - curitem) * sizeof(list[0]));
+ break;
+ }
+ list[curitem] = newnode;
+ listitems++;
+ }
+
+ // compute the number of bits in each code, and fill in another histogram
+ for (curcode = 0; curcode < decoder->numcodes; curcode++)
+ {
+ struct node_t* node = &decoder->huffnode[curcode];
+ node->numbits = 0;
+ node->bits = 0;
+
+ // if we have a non-zero weight, compute the number of bits
+ if (node->weight > 0)
+ {
+ struct node_t *curnode;
+ // determine the number of bits for this node
+ for (curnode = node; curnode->parent != NULL; curnode = curnode->parent)
+ node->numbits++;
+ if (node->numbits == 0)
+ node->numbits = 1;
+
+ // keep track of the max
+ maxbits = MAX(maxbits, ((int)node->numbits));
+ }
+ }
+ return maxbits;
+}
+
+
+//-------------------------------------------------
+// assign_canonical_codes - assign canonical codes
+// to all the nodes based on the number of bits
+// in each
+//-------------------------------------------------
+
+enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder)
+{
+ int curcode, codelen;
+ uint32_t curstart = 0;
+
+ // build up a histogram of bit lengths
+ uint32_t bithisto[33] = { 0 };
+ for (curcode = 0; curcode < decoder->numcodes; curcode++)
+ {
+ struct node_t* node = &decoder->huffnode[curcode];
+ if (node->numbits > decoder->maxbits)
+ return HUFFERR_INTERNAL_INCONSISTENCY;
+ if (node->numbits <= 32)
+ bithisto[node->numbits]++;
+ }
+
+ // for each code length, determine the starting code number
+ for (codelen = 32; codelen > 0; codelen--)
+ {
+ uint32_t nextstart = (curstart + bithisto[codelen]) >> 1;
+ if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen]))
+ return HUFFERR_INTERNAL_INCONSISTENCY;
+ bithisto[codelen] = curstart;
+ curstart = nextstart;
+ }
+
+ // now assign canonical codes
+ for (curcode = 0; curcode < decoder->numcodes; curcode++)
+ {
+ struct node_t* node = &decoder->huffnode[curcode];
+ if (node->numbits > 0)
+ node->bits = bithisto[node->numbits]++;
+ }
+ return HUFFERR_NONE;
+}
+
+
+//-------------------------------------------------
+// build_lookup_table - build a lookup table for
+// fast decoding
+//-------------------------------------------------
+
+void huffman_build_lookup_table(struct huffman_decoder* decoder)
+{
+ int curcode;
+ // iterate over all codes
+ for (curcode = 0; curcode < decoder->numcodes; curcode++)
+ {
+ // process all nodes which have non-zero bits
+ struct node_t* node = &decoder->huffnode[curcode];
+ if (node->numbits > 0)
+ {
+ int shift;
+ lookup_value *dest;
+ lookup_value *destend;
+
+ // set up the entry
+ lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
+
+ // fill all matching entries
+ shift = decoder->maxbits - node->numbits;
+ dest = &decoder->lookup[node->bits << shift];
+ destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
+
+ while (dest <= destend)
+ *dest++ = value;
+ }
+ }
+}
diff --git a/deps/libchdr/huffman.h b/deps/libchdr/huffman.h new file mode 100644 index 0000000..71de399 --- /dev/null +++ b/deps/libchdr/huffman.h @@ -0,0 +1,87 @@ +// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ huffman.h
+
+ Static Huffman compression and decompression helpers.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __HUFFMAN_H__
+#define __HUFFMAN_H__
+
+#include "bitstream.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+enum huffman_error
+{
+ HUFFERR_NONE = 0,
+ HUFFERR_TOO_MANY_BITS,
+ HUFFERR_INVALID_DATA,
+ HUFFERR_INPUT_BUFFER_TOO_SMALL,
+ HUFFERR_OUTPUT_BUFFER_TOO_SMALL,
+ HUFFERR_INTERNAL_INCONSISTENCY,
+ HUFFERR_TOO_MANY_CONTEXTS
+};
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef uint16_t lookup_value;
+
+// a node in the huffman tree
+struct node_t
+{
+ struct node_t* parent; // pointer to parent node
+ uint32_t count; // number of hits on this node
+ uint32_t weight; // assigned weight of this node
+ uint32_t bits; // bits used to encode the node
+ uint8_t numbits; // number of bits needed for this node
+};
+
+// ======================> huffman_context_base
+
+// context class for decoding
+struct huffman_decoder
+{
+ // internal state
+ uint32_t numcodes; // number of total codes being processed
+ uint8_t maxbits; // maximum bits per code
+ uint8_t prevdata; // value of the previous data (for delta-RLE encoding)
+ int rleremaining; // number of RLE bytes remaining (for delta-RLE encoding)
+ lookup_value * lookup; // pointer to the lookup table
+ struct node_t * huffnode; // array of nodes
+ uint32_t * datahisto; // histogram of data values
+
+ // array versions of the info we need
+ //node_t* huffnode_array; //[_NumCodes];
+ //lookup_value* lookup_array; //[1 << _MaxBits];
+};
+
+// ======================> huffman_decoder
+
+struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits);
+
+// single item operations
+uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf);
+
+enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf);
+enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf);
+
+int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight);
+enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder);
+enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder);
+
+void huffman_build_lookup_table(struct huffman_decoder* decoder);
+
+#endif
|