From 8c59a0da6e06ee51ed9d72418c1e85a52d59793d Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 12 Jan 2013 17:26:26 +0200 Subject: [PATCH] chd support added --- archivers/chd/astring.cpp | 513 +++++++++++ archivers/chd/astring.h | 189 ++++ archivers/chd/bitstream.h | 265 ++++++ archivers/chd/chd.cpp | 1116 +++++++++++++++++++++++ archivers/chd/chd.h | 447 +++++++++ archivers/chd/chdcd.h | 39 + archivers/chd/chdcdrom.cpp | 1200 ++++++++++++++++++++++++ archivers/chd/chdcdrom.h | 211 +++++ archivers/chd/chdcodec.cpp | 1752 ++++++++++++++++++++++++++++++++++++ archivers/chd/chdcodec.h | 222 +++++ archivers/chd/chdtypes.h | 48 + archivers/chd/corefile.h | 0 archivers/chd/coretmpl.h | 109 +++ archivers/chd/flac.cpp | 648 +++++++++++++ archivers/chd/flac.h | 178 ++++ archivers/chd/hashing.cpp | 308 +++++++ archivers/chd/hashing.h | 247 +++++ archivers/chd/huffman.cpp | 782 ++++++++++++++++ archivers/chd/huffman.h | 252 ++++++ archivers/chd/md5.cpp | 238 +++++ archivers/chd/md5.h | 41 + archivers/chd/osdcore.h | 0 archivers/chd/sha1.cpp | 389 ++++++++ archivers/chd/sha1.h | 61 ++ 24 files changed, 9255 insertions(+) create mode 100644 archivers/chd/astring.cpp create mode 100644 archivers/chd/astring.h create mode 100644 archivers/chd/bitstream.h create mode 100644 archivers/chd/chd.cpp create mode 100644 archivers/chd/chd.h create mode 100644 archivers/chd/chdcd.h create mode 100644 archivers/chd/chdcdrom.cpp create mode 100644 archivers/chd/chdcdrom.h create mode 100644 archivers/chd/chdcodec.cpp create mode 100644 archivers/chd/chdcodec.h create mode 100644 archivers/chd/chdtypes.h create mode 100644 archivers/chd/corefile.h create mode 100644 archivers/chd/coretmpl.h create mode 100644 archivers/chd/flac.cpp create mode 100644 archivers/chd/flac.h create mode 100644 archivers/chd/hashing.cpp create mode 100644 archivers/chd/hashing.h create mode 100644 archivers/chd/huffman.cpp create mode 100644 archivers/chd/huffman.h create mode 100644 archivers/chd/md5.cpp create mode 100644 archivers/chd/md5.h create mode 100644 archivers/chd/osdcore.h create mode 100644 archivers/chd/sha1.cpp create mode 100644 archivers/chd/sha1.h diff --git a/archivers/chd/astring.cpp b/archivers/chd/astring.cpp new file mode 100644 index 00000000..e74d8122 --- /dev/null +++ b/archivers/chd/astring.cpp @@ -0,0 +1,513 @@ +/*************************************************************************** + + astring.c + + Allocated string manipulation functions. + +**************************************************************************** + + 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. + +****************************************************************************/ + +#include "chdtypes.h" + +#include "astring.h" + +#include +#include +#include +#include +#include + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const astring dummy_astring; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// ensure_room - ensure we have room for a +// given string, or else reallocate our buffer +//------------------------------------------------- + +bool astring::ensure_room(int length) +{ + // always fail to expand the dummy + if (this == &dummy_astring) + return false; + + // if we have the room, do nothing + if (m_alloclen >= length + 1) + return true; + + // allocate a new buffer with some slop + int alloclen = length + 256; + char *newbuf = new char[alloclen]; + + // swap in the new buffer and free the old one + char *oldbuf = (m_text == m_smallbuf) ? NULL : m_text; + m_text = strcpy(newbuf, m_text); + m_len = strlen(m_text); + m_alloclen = alloclen; + delete[] oldbuf; + + return true; +} + + +//------------------------------------------------- +// safe_string_base - return a "safe" string +// base for a given start index +//------------------------------------------------- + +inline char *astring::safe_string_base(int start) const +{ + int max = len(); + return (start >= 0 && start < max) ? m_text + start : m_text + max; +} + + +//------------------------------------------------- +// normalize_substr - normalize substr parameters +//------------------------------------------------- + +inline void astring::normalize_substr(int &start, int &count, int length) const +{ + // limit start + if (start < 0) + start = 0; + else if (start > length) + start = length; + + // update count + if (count == -1 || start + count > length) + count = length - start; +} + + + +//************************************************************************** +// ASTRING ALLOCATION +//************************************************************************** + +//------------------------------------------------- +// init - constructor helper +//------------------------------------------------- + +astring &astring::init() +{ + // initialize ourselves to point to the internal buffer + m_text = m_smallbuf; + m_alloclen = ARRAY_LENGTH(m_smallbuf); + m_smallbuf[0] = 0; + m_len = 0; + return *this; +} + + +//------------------------------------------------- +// ~astring - destructor +//------------------------------------------------- + +astring::~astring() +{ + if (m_text != m_smallbuf) + delete[] m_text; +} + + + +/*************************************************************************** + INLINE ASTRING CHANGES +***************************************************************************/ + +//------------------------------------------------- +// cpy - copy a character array into an astring +//------------------------------------------------- + +astring &astring::cpy(const char *src, int count) +{ + // make room; if we fail or if we are the dummy, do nothing + if (!ensure_room(count)) + return *this; + + // copy the raw data and NULL-terminate + if (count > 0 && m_text != src) + memcpy(m_text, src, count); + m_text[count] = 0; + m_len = count; + return *this; +} + + +//------------------------------------------------- +// cpysubstr - copy a substring of one string to +// another +//------------------------------------------------- + +astring &astring::cpysubstr(const astring &src, int start, int count) +{ + normalize_substr(start, count, src.len()); + return cpy(src.m_text + start, count); +} + + +//------------------------------------------------- +// ins - insert a character array into an astring +//------------------------------------------------- + +astring &astring::ins(int insbefore, const char *src, int count) +{ + // make room; if we fail or if we are the dummy, do nothing + int dstlength = len(); + if (!ensure_room(dstlength + count)) + return *this; + + // adjust insbefore to be logical + if (insbefore < 0 || insbefore > dstlength) + insbefore = dstlength; + + // copy the data an NULL-terminate + if (insbefore < dstlength) + memmove(m_text + insbefore + count, m_text + insbefore, dstlength - insbefore); + memcpy(m_text + insbefore, src, count); + m_text[dstlength + count] = 0; + m_len = dstlength + count; + return *this; +} + + +//------------------------------------------------- +// inssubstr - insert a substring of one string +// into another +//------------------------------------------------- + +astring &astring::inssubstr(int insbefore, const astring &src, int start, int count) +{ + normalize_substr(start, count, src.len()); + return ins(insbefore, src.m_text + start, count); +} + + +//------------------------------------------------- +// substr - extract a substring of ourself, +// removing everything else +//------------------------------------------------- + +astring &astring::substr(int start, int count) +{ + // ignore attempts to do this on the dummy + if (this == &dummy_astring) + return *this; + + // normalize parameters + normalize_substr(start, count, len()); + + // move the data and NULL-terminate + if (count > 0 && start > 0) + memmove(m_text, m_text + start, count); + m_text[count] = 0; + m_len = count; + return *this; +} + + +//------------------------------------------------- +// del - delete a substring of ourself, keeping +// everything else +//------------------------------------------------- + +astring &astring::del(int start, int count) +{ + // ignore attempts to do this on the dummy + if (this == &dummy_astring) + return *this; + + // normalize parameters + int strlength = len(); + normalize_substr(start, count, strlength); + + // move the data and NULL-terminate + if (count > 0) + memmove(m_text + start, m_text + start + count, strlength - (start + count)); + m_text[strlength - count] = 0; + m_len = strlength - count; + return *this; +} + + +//------------------------------------------------- +// vprintf - vprintf text into an astring +//-------------------------------------------------*/ + +int astring::vprintf(const char *format, va_list args) +{ + // sprintf into the temporary buffer + char tempbuf[4096]; + int result = vsprintf(tempbuf, format, args); + + // set the result + cpy(tempbuf); + return result; +} + + +//------------------------------------------------- +// catprintf - formatted vprintf to the end of +// an astring +//------------------------------------------------- + +int astring::catvprintf(const char *format, va_list args) +{ + // sprintf into the temporary buffer + char tempbuf[4096]; + int result = vsprintf(tempbuf, format, args); + + // append the result + cat(tempbuf); + return result; +} + + + +/*************************************************************************** + ASTRING QUERIES +***************************************************************************/ + +//------------------------------------------------- +// cmp - compare a character array to an astring +//------------------------------------------------- + +int astring::cmp(const char *str2, int count) const +{ + // loop while equal until we hit the end of strings + int index; + for (index = 0; index < count; index++) + if (m_text[index] == 0 || m_text[index] != str2[index]) + break; + + // determine the final result + if (index < count) + return m_text[index] - str2[index]; + if (m_text[index] == 0) + return 0; + return 1; +} + + +//------------------------------------------------- +// cmpsubstr - compare a substring to an astring +//------------------------------------------------- + +int astring::cmpsubstr(const astring &str2, int start, int count) const +{ + normalize_substr(start, count, str2.len()); + return cmp(str2.m_text + start, count); +} + + +//------------------------------------------------- +// icmp - compare a character array to an astring +//------------------------------------------------- + +int astring::icmp(const char *str2, int count) const +{ + // loop while equal until we hit the end of strings + int index; + for (index = 0; index < count; index++) + if (m_text[index] == 0 || tolower(m_text[index]) != tolower(str2[index])) + break; + + // determine the final result + if (index < count) + return tolower(m_text[index]) - tolower(str2[index]); + if (m_text[index] == 0) + return 0; + return 1; +} + + +//------------------------------------------------- +// icmpsubstr - compare a substring to an astring +//------------------------------------------------- + +int astring::icmpsubstr(const astring &str2, int start, int count) const +{ + normalize_substr(start, count, str2.len()); + return icmp(str2.m_text + start, count); +} + + +//------------------------------------------------- +// chr - return the index of a character in an +// astring +//------------------------------------------------- + +int astring::chr(int start, int ch) const +{ + char *result = strchr(safe_string_base(start), ch); + return (result != NULL) ? (result - m_text) : -1; +} + + +//------------------------------------------------- +// rchr - return the index of a character in an +// astring, searching from the end +//------------------------------------------------- + +int astring::rchr(int start, int ch) const +{ + char *result = strrchr(safe_string_base(start), ch); + return (result != NULL) ? (result - m_text) : -1; +} + + +//------------------------------------------------- +// find - find a C string in an astring +//-------------------------------------------------*/ + +int astring::find(int start, const char *search) const +{ + char *result = strstr(safe_string_base(start), search); + return (result != NULL) ? (result - m_text) : -1; +} + + +//------------------------------------------------- +// replacec - search in an astring for a C string, +// replacing all instances with another C string +// and returning the number of matches +//------------------------------------------------- + +int astring::replace(int start, const char *search, const char *replace) +{ + int searchlen = strlen(search); + int replacelen = strlen(replace); + int matches = 0; + + for (int curindex = find(start, search); curindex != -1; curindex = find(curindex + replacelen, search)) + { + matches++; + del(curindex, searchlen).ins(curindex, replace); + } + return matches; +} + + + +//************************************************************************** +// ASTRING UTILITIES +//************************************************************************** + +//------------------------------------------------- +// delchr - delete all instances of 'ch' +//------------------------------------------------- + +astring &astring::delchr(int ch) +{ + // simple deletion + char *dst = m_text; + for (char *src = m_text; *src != 0; src++) + if (*src != ch) + *dst++ = *src; + *dst = 0; + m_len = strlen(m_text); + return *this; +} + + +//------------------------------------------------- +// replacechr - replace all instances of 'ch' +// with 'newch' +//------------------------------------------------- + +astring &astring::replacechr(int ch, int newch) +{ + // simple replacement + for (char *text = m_text; *text != 0; text++) + if (*text == ch) + *text = newch; + return *this; +} + + +//------------------------------------------------- +// makeupper - convert string to all upper-case +//------------------------------------------------- + +astring &astring::makeupper() +{ + // just makeupper() on all characters + for (char *text = m_text; *text != 0; text++) + *text = toupper((UINT8)*text); + return *this; +} + + +//------------------------------------------------- +// makelower - convert string to all lower-case +//------------------------------------------------- + +astring &astring::makelower() +{ + // just tolower() on all characters + for (char *text = m_text; *text != 0; text++) + *text = tolower((UINT8)*text); + return *this; +} + + +//------------------------------------------------- +// trimspace - remove all space characters from +// beginning/end +//------------------------------------------------- + +astring &astring::trimspace() +{ + // first remove stuff from the end + for (char *ptr = m_text + len() - 1; ptr >= m_text && (!(*ptr & 0x80) && isspace(UINT8(*ptr))); ptr--) + *ptr = 0; + + // then count how much to remove from the beginning + char *ptr; + for (ptr = m_text; *ptr != 0 && (!(*ptr & 0x80) && isspace(UINT8(*ptr))); ptr++) ; + if (ptr > m_text) + substr(ptr - m_text); + m_len = strlen(m_text); + return *this; +} diff --git a/archivers/chd/astring.h b/archivers/chd/astring.h new file mode 100644 index 00000000..ca528d67 --- /dev/null +++ b/archivers/chd/astring.h @@ -0,0 +1,189 @@ +/*************************************************************************** + + astring.h + + Allocated string manipulation functions. + +**************************************************************************** + + 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 __ASTRING_H__ +#define __ASTRING_H__ + +#include +#include +#include + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// basic allocated string class +class astring +{ +public: + // simple construction/destruction + astring() { init(); } + ~astring(); + + // construction with copy + astring(const char *string) { init().cpy(string); } + astring(const char *string, int length) { init().cpy(string, length); } + astring(const char *str1, const char *str2) { init().cpy(str1).cat(str2); } + astring(const char *str1, const char *str2, const char *str3) { init().cpy(str1).cat(str2).cat(str3); } + astring(const char *str1, const char *str2, const char *str3, const char *str4) { init().cpy(str1).cat(str2).cat(str3).cat(str4); } + astring(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) { init().cpy(str1).cat(str2).cat(str3).cat(str4).cat(str5); } + astring(const astring &string) { init().cpy(string); } + astring(const astring &string, int start, int count = -1) { init().cpysubstr(string, start, count); } + + // assignment operators + astring &operator=(const char *string) { return cpy(string); } + astring &operator=(const astring &string) { return cpy(string); } + + // concatenation operators + astring& operator+=(const astring &string) { return cat(string); } + friend astring operator+(const astring &lhs, const astring &rhs) { return astring(lhs) += rhs; } + friend astring operator+(const astring &lhs, const char *rhs) { return astring(lhs) += rhs; } + friend astring operator+(const char *lhs, const astring &rhs) { return astring(lhs) += rhs; } + + // comparison operators + bool operator==(const char *string) const { return (cmp(string) == 0); } + bool operator==(const astring &string) const { return (cmp(string) == 0); } + bool operator!=(const char *string) const { return (cmp(string) != 0); } + bool operator!=(const astring &string) const { return (cmp(string) != 0); } + bool operator<(const char *string) const { return (cmp(string) < 0); } + bool operator<(const astring &string) const { return (cmp(string) < 0); } + bool operator<=(const char *string) const { return (cmp(string) <= 0); } + bool operator<=(const astring &string) const { return (cmp(string) <= 0); } + bool operator>(const char *string) const { return (cmp(string) > 0); } + bool operator>(const astring &string) const { return (cmp(string) > 0); } + bool operator>=(const char *string) const { return (cmp(string) >= 0); } + bool operator>=(const astring &string) const { return (cmp(string) >= 0); } + + // character access operators + char operator[](int index) const { return (index < len()) ? m_text[index] : 0; } + + // implicit boolean conversion operators + operator bool() { return m_text[0] != 0; } + operator bool() const { return m_text[0] != 0; } + + // C string conversion operators and helpers + operator const char *() const { return m_text; } + const char *cstr() const { return m_text; } + + // buffer management + astring &reset() { return cpy(""); } + astring &expand(int length) { ensure_room(length); return *this; } + + // length query + int len() const { return m_len; } + + // copy helpers + astring &cpy(const char *src, int count); + astring &cpysubstr(const astring &src, int start, int count = -1); + astring &cpy(const astring &src) { return cpy(src.cstr(), src.len()); } + astring &cpy(const char *src) { return cpy(src, strlen(src)); } + + // insertion helpers + astring &ins(int insbefore, const char *src, int count); + astring &inssubstr(int insbefore, const astring &src, int start, int count = -1); + astring &ins(int insbefore, const astring &src) { return ins(insbefore, src.cstr(), src.len()); } + astring &ins(int insbefore, const char *src) { return ins(insbefore, src, strlen(src)); } + + // concatenation helpers (== insert at end) + astring &cat(const char *src, int count) { return ins(-1, src, count); } + astring &catsubstr(const astring &src, int start, int count = -1) { return inssubstr(-1, src, start, count); } + astring &cat(const astring &src) { return ins(-1, src.cstr(), src.len()); } + astring &cat(const char *src) { return ins(-1, src, strlen(src)); } + astring &cat(char ch) { return ins(-1, &ch, 1); } + + // substring helpers + astring &substr(int start, int count = -1); + astring &del(int start, int count = -1); + + // formatted string helpers + int vprintf(const char *format, va_list args); + int catvprintf(const char *format, va_list args); + int printf(const char *format, ...) { va_list ap; va_start(ap, format); int result = this->vprintf(format, ap); va_end(ap); return result; } + int catprintf(const char *format, ...) { va_list ap; va_start(ap, format); int result = catvprintf(format, ap); va_end(ap); return result; } + astring &format(const char *format, ...) { va_list ap; va_start(ap, format); this->vprintf(format, ap); va_end(ap); return *this; } + astring &catformat(const char *format, ...) { va_list ap; va_start(ap, format); catvprintf(format, ap); va_end(ap); return *this; } + + // comparison helpers + int cmp(const char *str2, int count) const; + int cmpsubstr(const astring &str2, int start, int count = -1) const; + int cmp(const astring &str2) const { return cmp(str2.cstr(), str2.len()); } + int cmp(const char *str2) const { return cmp(str2, strlen(str2)); } + + // case-insensitive comparison helpers + int icmp(const char *str2, int count) const; + int icmpsubstr(const astring &str2, int start, int count = -1) const; + int icmp(const astring &str2) const { return icmp(str2.cstr(), str2.len()); } + int icmp(const char *str2) const { return icmp(str2, strlen(str2)); } + + // character searching helpers + int chr(int start, int ch) const; + int rchr(int start, int ch) const; + + // string searching/replacing helpers + int find(int start, const char *search) const; + int find(const char *search) const { return find(0, search); } + int replace(int start, const char *search, const char *replace); + int replace(const char *search, const char *_replace) { return replace(0, search, _replace); } + + // misc utilities + astring &delchr(int ch); + astring &replacechr(int ch, int newch); + astring &makeupper(); + astring &makelower(); + astring &trimspace(); + +private: + // internal helpers + astring &init(); + char *safe_string_base(int start) const; + bool ensure_room(int length); + void normalize_substr(int &start, int &count, int length) const; + + // internal state + char * m_text; + int m_alloclen; + char m_smallbuf[64]; + int m_len; +}; + + +#endif /* __ASTRING_H__ */ diff --git a/archivers/chd/bitstream.h b/archivers/chd/bitstream.h new file mode 100644 index 00000000..1c185919 --- /dev/null +++ b/archivers/chd/bitstream.h @@ -0,0 +1,265 @@ +/*************************************************************************** + + bitstream.h + + Helper classes for reading/writing at the bit level. + +**************************************************************************** + + 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 __BITSTREAM_H__ +#define __BITSTREAM_H__ + +#include "osdcore.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// helper class for reading from a bit buffer +class bitstream_in +{ +public: + // construction/destruction + bitstream_in(const void *src, UINT32 srclength); + + // getters + bool overflow() const { return ((m_doffset - m_bits / 8) > m_dlength); } + UINT32 read_offset() const; + + // operations + UINT32 read(int numbits); + UINT32 peek(int numbits); + void remove(int numbits); + UINT32 flush(); + +private: + // internal state + UINT32 m_buffer; // current bit accumulator + int m_bits; // number of bits in the accumulator + const UINT8 * m_read; // read pointer + UINT32 m_doffset; // byte offset within the data + UINT32 m_dlength; // length of the data +}; + + +// helper class for writing to a bit buffer +class bitstream_out +{ +public: + // construction/destruction + bitstream_out(void *dest, UINT32 destlength); + + // getters + bool overflow() const { return (m_doffset > m_dlength); } + + // operations + void write(UINT32 newbits, int numbits); + UINT32 flush(); + +private: + // internal state + UINT32 m_buffer; // current bit accumulator + int m_bits; // number of bits in the accumulator + UINT8 * m_write; // write pointer + UINT32 m_doffset; // byte offset within the data + UINT32 m_dlength; // length of the data +}; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// bitstream_in - constructor +//------------------------------------------------- + +inline bitstream_in::bitstream_in(const void *src, UINT32 srclength) + : m_buffer(0), + m_bits(0), + m_read(reinterpret_cast(src)), + m_doffset(0), + m_dlength(srclength) +{ +} + + +//------------------------------------------------- +// peek - fetch the requested number of bits +// but don't advance the input pointer +//------------------------------------------------- + +inline UINT32 bitstream_in::peek(int numbits) +{ + // fetch data if we need more + if (numbits > m_bits) + { + while (m_bits <= 24) + { + if (m_doffset < m_dlength) + m_buffer |= m_read[m_doffset] << (24 - m_bits); + m_doffset++; + m_bits += 8; + } + } + + // return the data + return m_buffer >> (32 - numbits); +} + + +//------------------------------------------------- +// remove - advance the input pointer by the +// specified number of bits +//------------------------------------------------- + +inline void bitstream_in::remove(int numbits) +{ + m_buffer <<= numbits; + m_bits -= numbits; +} + + +//------------------------------------------------- +// read - fetch the requested number of bits +//------------------------------------------------- + +inline UINT32 bitstream_in::read(int numbits) +{ + UINT32 result = peek(numbits); + remove(numbits); + return result; +} + + +//------------------------------------------------- +// read_offset - return the current read offset +//------------------------------------------------- + +inline UINT32 bitstream_in::read_offset() const +{ + UINT32 result = m_doffset; + int bits = m_bits; + while (bits >= 8) + { + result--; + bits -= 8; + } + return result; +} + + +//------------------------------------------------- +// flush - flush to the nearest byte +//------------------------------------------------- + +inline UINT32 bitstream_in::flush() +{ + while (m_bits >= 8) + { + m_doffset--; + m_bits -= 8; + } + m_bits = m_buffer = 0; + return m_doffset; +} + + +//------------------------------------------------- +// bitstream_out - constructor +//------------------------------------------------- + +inline bitstream_out::bitstream_out(void *dest, UINT32 destlength) + : m_buffer(0), + m_bits(0), + m_write(reinterpret_cast(dest)), + m_doffset(0), + m_dlength(destlength) +{ +} + + + +//------------------------------------------------- +// write - write the given number of bits to the +// data stream +//------------------------------------------------- + +inline void bitstream_out::write(UINT32 newbits, int numbits) +{ + // flush the buffer if we're going to overflow it + if (m_bits + numbits > 32) + while (m_bits >= 8) + { + if (m_doffset < m_dlength) + m_write[m_doffset] = m_buffer >> 24; + m_doffset++; + m_buffer <<= 8; + m_bits -= 8; + } + + // shift the bits to the top + newbits <<= 32 - numbits; + + // now shift it down to account for the number of bits we already have and OR them in + m_buffer |= newbits >> m_bits; + m_bits += numbits; +} + + +//------------------------------------------------- +// flush - output remaining bits and return the +// final output size in bytes +//------------------------------------------------- + +inline UINT32 bitstream_out::flush() +{ + while (m_bits > 0) + { + if (m_doffset < m_dlength) + m_write[m_doffset] = m_buffer >> 24; + m_doffset++; + m_buffer <<= 8; + m_bits -= 8; + } + m_bits = m_buffer = 0; + return m_doffset; +} + + +#endif diff --git a/archivers/chd/chd.cpp b/archivers/chd/chd.cpp new file mode 100644 index 00000000..14d7d5e1 --- /dev/null +++ b/archivers/chd/chd.cpp @@ -0,0 +1,1116 @@ + +#include "chdtypes.h" + +#include "chd.h" +#include "hashing.h" +#include "chdcdrom.h" +#include "coretmpl.h" +#include "bitstream.h" +#include "huffman.h" + +// standard metadata formats +const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d"; +const char *CDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d"; +const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"; +const char *GDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"; +const char *AV_METADATA_FORMAT = "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d"; + +static const UINT32 METADATA_HEADER_SIZE = 16; // metadata header size + +static const UINT8 V34_MAP_ENTRY_FLAG_TYPE_MASK = 0x0f; // what type of hunk +static const UINT8 V34_MAP_ENTRY_FLAG_NO_CRC = 0x10; // no CRC is present + + + +// 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 +{ + // these types are live when running + COMPRESSION_TYPE_0 = 0, // codec #0 + COMPRESSION_TYPE_1 = 1, // codec #1 + COMPRESSION_TYPE_2 = 2, // codec #2 + COMPRESSION_TYPE_3 = 3, // codec #3 + COMPRESSION_NONE = 4, // no compression; implicit length = hunkbytes + COMPRESSION_SELF = 5, // same as another block in this chd + COMPRESSION_PARENT = 6, // same as a hunk's worth of units in the parent chd + + // these additional pseudo-types are used for compressed encodings: + COMPRESSION_RLE_SMALL, // start of small RLE run (4-bit length) + COMPRESSION_RLE_LARGE, // start of large RLE run (8-bit length) + COMPRESSION_SELF_0, // same as the last COMPRESSION_SELF block + COMPRESSION_SELF_1, // same as the last COMPRESSION_SELF block + 1 + COMPRESSION_PARENT_SELF, // same block in the parent + COMPRESSION_PARENT_0, // same as the last COMPRESSION_PARENT block + COMPRESSION_PARENT_1 // same as the last COMPRESSION_PARENT block + 1 +}; + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> metadata_entry + +// description of where a metadata entry lives within the file +struct chd_file::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 +}; + + +// ======================> metadata_hash + +struct chd_file::metadata_hash +{ + UINT8 tag[4]; // tag of the metadata in big-endian + sha1_t sha1; // hash data +}; + + +//------------------------------------------------- +// be_read - extract a big-endian number from +// a byte buffer +//------------------------------------------------- + +inline UINT64 chd_file::be_read(const UINT8 *base, int numbytes) +{ + UINT64 result = 0; + while (numbytes--) + result = (result << 8) | *base++; + return result; +} + +//------------------------------------------------- +// be_write - write a big-endian number to a byte +// buffer +//------------------------------------------------- + +inline void chd_file::be_write(UINT8 *base, UINT64 value, int numbytes) +{ + base += numbytes; + while (numbytes--) + { + *--base = value; + value >>= 8; + } +} + +//------------------------------------------------- +// be_read_sha1 - fetch a sha1_t from a data +// stream in bigendian order +//------------------------------------------------- + +inline sha1_t chd_file::be_read_sha1(const UINT8 *base) +{ + sha1_t result; + memcpy(&result.m_raw[0], base, sizeof(result.m_raw)); + return result; +} + +//------------------------------------------------- +// file_read - read from the file at the given +// offset; on failure throw an error +//------------------------------------------------- + +inline void chd_file::file_read(UINT64 offset, void *dest, UINT32 length) +{ + // no file = failure + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // seek and read + zfile_fseek(m_file, offset, SEEK_SET); + UINT32 count = zfile_fread(dest, 1, length, m_file); + if (count != length) + throw CHDERR_READ_ERROR; +} + +//------------------------------------------------- +// hunk_info - return information about this +// hunk +//------------------------------------------------- + +chd_error chd_file::hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes) +{ + // error if invalid + if (hunknum >= m_hunkcount) + return CHDERR_HUNK_OUT_OF_RANGE; + + // get the map pointer + UINT8 *rawmap; + switch (m_version) + { + // v3/v4 map entries + case 3: + case 4: + rawmap = m_rawmap + 16 * hunknum; + switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) + { + case V34_MAP_ENTRY_TYPE_COMPRESSED: + compressor = CHD_CODEC_ZLIB; + compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16); + break; + + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + compressor = CHD_CODEC_NONE; + compbytes = m_hunkbytes; + break; + + case V34_MAP_ENTRY_TYPE_MINI: + compressor = CHD_CODEC_MINI; + compbytes = 0; + break; + + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + compressor = CHD_CODEC_SELF; + compbytes = 0; + break; + + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + compressor = CHD_CODEC_PARENT; + compbytes = 0; + break; + } + break; + + // v5 map entries + case 5: + rawmap = m_rawmap + m_mapentrybytes * hunknum; + + // uncompressed case + if (!compressed()) + { + if (be_read(&rawmap[0], 4) == 0) + { + compressor = CHD_CODEC_PARENT; + compbytes = 0; + } + else + { + compressor = CHD_CODEC_NONE; + compbytes = m_hunkbytes; + } + break; + } + + // compressed case + switch (rawmap[0]) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + compressor = m_compression[rawmap[0]]; + compbytes = be_read(&rawmap[1], 3); + break; + + case COMPRESSION_NONE: + compressor = CHD_CODEC_NONE; + compbytes = m_hunkbytes; + break; + + case COMPRESSION_SELF: + compressor = CHD_CODEC_SELF; + compbytes = 0; + break; + + case COMPRESSION_PARENT: + compressor = CHD_CODEC_PARENT; + compbytes = 0; + break; + } + break; + } + return CHDERR_NONE; +} + +//------------------------------------------------- +// open - open an existing file for read or +// read/write +//------------------------------------------------- + +chd_error chd_file::open(struct zfile *file, bool writeable, chd_file *parent) +{ + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; + + // open the file + m_file = file; + m_owns_file = false; + m_parent = parent; + return open_common(writeable); +} + +//------------------------------------------------- +// close - close a CHD file for access +//------------------------------------------------- + +void chd_file::close() +{ + // reset file characteristics + if (m_owns_file && m_file != NULL) + zfile_fclose(m_file); + m_file = NULL; + m_owns_file = false; + m_allow_reads = false; + m_allow_writes = false; + + // reset core parameters from the header + m_version = HEADER_VERSION; + m_logicalbytes = 0; + m_mapoffset = 0; + m_metaoffset = 0; + m_hunkbytes = 0; + m_hunkcount = 0; + m_unitbytes = 0; + m_unitcount = 0; + memset(m_compression, 0, sizeof(m_compression)); + m_parent = NULL; + m_parent_missing = false; + + // reset key offsets within the header + m_mapoffset_offset = 0; + m_metaoffset_offset = 0; + m_sha1_offset = 0; + m_rawsha1_offset = 0; + m_parentsha1_offset = 0; + + // reset map information + m_mapentrybytes = 0; + m_rawmap.reset(); + + // reset compression management + for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++) + { + delete m_decompressor[decompnum]; + m_decompressor[decompnum] = NULL; + } + m_compressed.reset(); + + // reset caching + m_cache.reset(); + m_cachehunk = ~0; +} + + +//------------------------------------------------- +// read - read a single hunk from the CHD file +//------------------------------------------------- + +chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer) +{ + // wrap this for clean reporting + try + { + // punt if no file + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // return an error if out of range + if (hunknum >= m_hunkcount) + throw CHDERR_HUNK_OUT_OF_RANGE; + + // get a pointer to the map entry + UINT64 blockoffs; + UINT32 blocklen; + UINT32 blockcrc; + UINT8 *rawmap; + UINT8 *dest = reinterpret_cast(buffer); + switch (m_version) + { + // v3/v4 map entries + case 3: + case 4: + rawmap = m_rawmap + 16 * hunknum; + blockoffs = be_read(&rawmap[0], 8); + blockcrc = be_read(&rawmap[8], 4); + switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) + { + case V34_MAP_ENTRY_TYPE_COMPRESSED: + blocklen = be_read(&rawmap[12], 2) + (rawmap[14] << 16); + file_read(blockoffs, m_compressed, blocklen); + m_decompressor[0]->decompress(m_compressed, blocklen, dest, m_hunkbytes); + if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && dest != NULL && crc32_creator::simple(dest, m_hunkbytes) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + file_read(blockoffs, dest, m_hunkbytes); + if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && crc32_creator::simple(dest, m_hunkbytes) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case V34_MAP_ENTRY_TYPE_MINI: + be_write(dest, blockoffs, 8); + for (UINT32 bytes = 8; bytes < m_hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && crc32_creator::simple(dest, m_hunkbytes) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + return read_hunk(blockoffs, dest); + + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + if (m_parent_missing) + throw CHDERR_REQUIRES_PARENT; + return m_parent->read_hunk(blockoffs, dest); + } + break; + + // v5 map entries + case 5: + rawmap = m_rawmap + m_mapentrybytes * hunknum; + + // uncompressed case + if (!compressed()) + { + blockoffs = UINT64(be_read(rawmap, 4)) * UINT64(m_hunkbytes); + if (blockoffs != 0) + file_read(blockoffs, dest, m_hunkbytes); + else if (m_parent_missing) + throw CHDERR_REQUIRES_PARENT; + else if (m_parent != NULL) + m_parent->read_hunk(hunknum, dest); + else + memset(dest, 0, m_hunkbytes); + return CHDERR_NONE; + } + + // compressed case + blocklen = be_read(&rawmap[1], 3); + blockoffs = be_read(&rawmap[4], 6); + blockcrc = be_read(&rawmap[10], 2); + switch (rawmap[0]) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + file_read(blockoffs, m_compressed, blocklen); + m_decompressor[rawmap[0]]->decompress(m_compressed, blocklen, dest, m_hunkbytes); + if (!m_decompressor[rawmap[0]]->lossy() && dest != NULL && crc16_creator::simple(dest, m_hunkbytes) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + if (m_decompressor[rawmap[0]]->lossy() && crc16_creator::simple(m_compressed, blocklen) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_NONE: + file_read(blockoffs, dest, m_hunkbytes); + if (crc16_creator::simple(dest, m_hunkbytes) != blockcrc) + throw CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_SELF: + return read_hunk(blockoffs, dest); + + case COMPRESSION_PARENT: + if (m_parent_missing) + throw CHDERR_REQUIRES_PARENT; + return m_parent->read_bytes(UINT64(blockoffs) * UINT64(m_parent->unit_bytes()), dest, m_hunkbytes); + } + break; + } + + // if we get here, something was wrong + throw CHDERR_READ_ERROR; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + +//------------------------------------------------- +// read_bytes - read from the CHD at a byte level, +// using the cache to handle partial hunks +//------------------------------------------------- + +chd_error chd_file::read_bytes(UINT64 offset, void *buffer, UINT32 bytes) +{ + // iterate over hunks + UINT32 first_hunk = offset / m_hunkbytes; + UINT32 last_hunk = (offset + bytes - 1) / m_hunkbytes; + UINT8 *dest = reinterpret_cast(buffer); + for (UINT32 curhunk = first_hunk; curhunk <= last_hunk; curhunk++) + { + // determine start/end boundaries + UINT32 startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + UINT32 endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + + // if it's a full block, just read directly from disk unless it's the cached hunk + chd_error err = CHDERR_NONE; + if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) + err = read_hunk(curhunk, dest); + + // otherwise, read from the cache + else + { + if (curhunk != m_cachehunk) + { + err = read_hunk(curhunk, m_cache); + if (err != CHDERR_NONE) + return err; + m_cachehunk = curhunk; + } + memcpy(dest, &m_cache[startoffs], endoffs + 1 - startoffs); + } + + // handle errors and advance + if (err != CHDERR_NONE) + return err; + dest += endoffs + 1 - startoffs; + } + return CHDERR_NONE; +} + + +//------------------------------------------------- +// read_metadata - read the indexed metadata +// of the given type +//------------------------------------------------- + +chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output) +{ + // wrap this for clean reporting + try + { + // if we didn't find it, just return + metadata_entry metaentry; + if (!metadata_find(searchtag, searchindex, metaentry)) + throw CHDERR_METADATA_NOT_FOUND; + + // read the metadata + // TODO: how to properly allocate a dynamic char buffer? + char* metabuf = new char[metaentry.length+1]; + memset(metabuf, 0x00, metaentry.length+1); + file_read(metaentry.offset + METADATA_HEADER_SIZE, metabuf, metaentry.length); + output.cpy(metabuf); + delete[] metabuf; + return CHDERR_NONE; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + +chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output) +{ + // wrap this for clean reporting + try + { + // if we didn't find it, just return + metadata_entry metaentry; + if (!metadata_find(searchtag, searchindex, metaentry)) + throw CHDERR_METADATA_NOT_FOUND; + + // read the metadata + output.resize(metaentry.length); + file_read(metaentry.offset + METADATA_HEADER_SIZE, output, metaentry.length); + return CHDERR_NONE; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + +chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen) +{ + // wrap this for clean reporting + try + { + // if we didn't find it, just return + metadata_entry metaentry; + if (!metadata_find(searchtag, searchindex, metaentry)) + throw CHDERR_METADATA_NOT_FOUND; + + // read the metadata + resultlen = metaentry.length; + file_read(metaentry.offset + METADATA_HEADER_SIZE, output, MIN(outputlen, resultlen)); + return CHDERR_NONE; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + +chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags) +{ + // wrap this for clean reporting + try + { + // if we didn't find it, just return + metadata_entry metaentry; + if (!metadata_find(searchtag, searchindex, metaentry)) + throw CHDERR_METADATA_NOT_FOUND; + + // read the metadata + output.resize(metaentry.length); + file_read(metaentry.offset + METADATA_HEADER_SIZE, output, metaentry.length); + resulttag = metaentry.metatag; + resultflags = metaentry.flags; + return CHDERR_NONE; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + + +//------------------------------------------------- +// guess_unitbytes - for older CHD formats, take +// a guess at the bytes/unit based on metadata +//------------------------------------------------- + +UINT32 chd_file::guess_unitbytes() +{ + // look for hard disk metadata; if found, then the unit size == sector size + astring metadata; + int i0, i1, i2, i3; + if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) + return i3; + + // look for CD-ROM metadata; if found, then the unit size == CD frame size + if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE) + return CD_FRAME_SIZE; + + // otherwise, just map 1:1 with the hunk size + return m_hunkbytes; +} + + +//------------------------------------------------- +// parse_v3_header - parse the header from a v3 +// file and configure core parameters +//------------------------------------------------- + +void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1) +{ + // verify header length + if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE) + throw CHDERR_INVALID_FILE; + + // extract core info + m_logicalbytes = be_read(&rawheader[28], 8); + m_mapoffset = 120; + m_metaoffset = be_read(&rawheader[36], 8); + m_hunkbytes = be_read(&rawheader[76], 4); + m_hunkcount = be_read(&rawheader[24], 4); + + // extract parent SHA-1 + UINT32 flags = be_read(&rawheader[16], 4); + m_allow_writes = (flags & 2) == 0; + + // determine compression + switch (be_read(&rawheader[20], 4)) + { + case 0: m_compression[0] = CHD_CODEC_NONE; break; + case 1: m_compression[0] = CHD_CODEC_ZLIB; break; + case 2: m_compression[0] = CHD_CODEC_ZLIB; break; + case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; + default: throw CHDERR_UNKNOWN_COMPRESSION; + } + m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; + + // describe the format + m_mapoffset_offset = 0; + m_metaoffset_offset = 36; + m_sha1_offset = 80; + m_rawsha1_offset = 0; + m_parentsha1_offset = 100; + + // determine properties of map entries + m_mapentrybytes = 16; + + // extract parent SHA-1 + if (flags & 1) + parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]); + + // guess at the units based on snooping the metadata + m_unitbytes = guess_unitbytes(); + m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes; +} + + +//------------------------------------------------- +// parse_v4_header - parse the header from a v4 +// file and configure core parameters +//------------------------------------------------- + +void chd_file::parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1) +{ + // verify header length + if (be_read(&rawheader[8], 4) != V4_HEADER_SIZE) + throw CHDERR_INVALID_FILE; + + // extract core info + m_logicalbytes = be_read(&rawheader[28], 8); + m_mapoffset = 108; + m_metaoffset = be_read(&rawheader[36], 8); + m_hunkbytes = be_read(&rawheader[44], 4); + m_hunkcount = be_read(&rawheader[24], 4); + + // extract parent SHA-1 + UINT32 flags = be_read(&rawheader[16], 4); + m_allow_writes = (flags & 2) == 0; + + // determine compression + switch (be_read(&rawheader[20], 4)) + { + case 0: m_compression[0] = CHD_CODEC_NONE; break; + case 1: m_compression[0] = CHD_CODEC_ZLIB; break; + case 2: m_compression[0] = CHD_CODEC_ZLIB; break; + case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; + default: throw CHDERR_UNKNOWN_COMPRESSION; + } + m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; + + // describe the format + m_mapoffset_offset = 0; + m_metaoffset_offset = 36; + m_sha1_offset = 48; + m_rawsha1_offset = 88; + m_parentsha1_offset = 68; + + // determine properties of map entries + m_mapentrybytes = 16; + + // extract parent SHA-1 + if (flags & 1) + parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]); + + // guess at the units based on snooping the metadata + m_unitbytes = guess_unitbytes(); + m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes; +} + + +//------------------------------------------------- +// parse_v5_header - read the header from a v5 +// file and configure core parameters +//------------------------------------------------- + +void chd_file::parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1) +{ + // verify header length + if (be_read(&rawheader[8], 4) != V5_HEADER_SIZE) + throw CHDERR_INVALID_FILE; + + // extract core info + m_logicalbytes = be_read(&rawheader[32], 8); + m_mapoffset = be_read(&rawheader[40], 8); + m_metaoffset = be_read(&rawheader[48], 8); + m_hunkbytes = be_read(&rawheader[56], 4); + m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes; + m_unitbytes = be_read(&rawheader[60], 4); + m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes; + + // determine compression + m_compression[0] = be_read(&rawheader[16], 4); + m_compression[1] = be_read(&rawheader[20], 4); + m_compression[2] = be_read(&rawheader[24], 4); + m_compression[3] = be_read(&rawheader[28], 4); + + m_allow_writes = !compressed(); + + // describe the format + m_mapoffset_offset = 40; + m_metaoffset_offset = 48; + m_sha1_offset = 84; + m_rawsha1_offset = 64; + m_parentsha1_offset = 104; + + // determine properties of map entries + m_mapentrybytes = compressed() ? 12 : 4; + + // extract parent SHA-1 + parentsha1 = be_read_sha1(&rawheader[m_parentsha1_offset]); +} + + +//------------------------------------------------- +// open_common - common path when opening an +// existing CHD file for input +//------------------------------------------------- + +chd_error chd_file::open_common(bool writeable) +{ + // wrap in try for proper error handling + try + { + // reads are always permitted + m_allow_reads = true; + + // read the raw header + UINT8 rawheader[MAX_HEADER_SIZE]; + file_read(0, rawheader, sizeof(rawheader)); + + // verify the signature + if (memcmp(rawheader, "MComprHD", 8) != 0) + throw CHDERR_INVALID_FILE; + + // only allow writes to the most recent version + m_version = be_read(&rawheader[12], 4); + if (writeable && m_version < HEADER_VERSION) + throw CHDERR_UNSUPPORTED_VERSION; + + // read the header if we support it + sha1_t parentsha1 = sha1_t::null; + switch (m_version) + { + case 3: parse_v3_header(rawheader, parentsha1); break; + case 4: parse_v4_header(rawheader, parentsha1); break; + case 5: parse_v5_header(rawheader, parentsha1); break; + default: throw CHDERR_UNSUPPORTED_VERSION; + } + + if (writeable && !m_allow_writes) + throw CHDERR_FILE_NOT_WRITEABLE; + + // make sure we have a parent if we need one (and don't if we don't) + if (parentsha1 != sha1_t::null) + { + if (m_parent == NULL) + m_parent_missing = true; + else if (m_parent->sha1() != parentsha1) + throw CHDERR_INVALID_PARENT; + } + else if (m_parent != NULL) + throw CHDERR_INVALID_PARAMETER; + + // finish opening the file + create_open_common(); + return CHDERR_NONE; + } + + // handle errors by closing ourself + catch (chd_error &err) + { + close(); + return err; + } +} + +//------------------------------------------------- +// create_open_common - common code for handling +// creation and opening of a file +//------------------------------------------------- + +void chd_file::create_open_common() +{ + // verify the compression types and initialize the codecs + for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++) + { + m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this); + if (m_decompressor[decompnum] == NULL && m_compression[decompnum] != 0) + throw CHDERR_UNKNOWN_COMPRESSION; + } + + // read the map; v5+ compressed drives need to read and decompress their map + m_rawmap.resize(m_hunkcount * m_mapentrybytes); + if (m_version >= 5 && compressed()) + decompress_v5_map(); + else + file_read(m_mapoffset, m_rawmap, m_rawmap.count()); + + // allocate the temporary compressed buffer and a buffer for caching + m_compressed.resize(m_hunkbytes); + m_cache.resize(m_hunkbytes); +} + + +//------------------------------------------------- +// metadata_find - find a metadata entry +//------------------------------------------------- + +bool chd_file::metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume) +{ + // start at the beginning unless we're resuming a previous search + if (!resume) + { + metaentry.offset = m_metaoffset; + metaentry.prev = 0; + } + else + { + metaentry.prev = metaentry.offset; + metaentry.offset = metaentry.next; + } + + // loop until we run out of options + while (metaentry.offset != 0) + { + // read the raw header + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); + + // extract the data + metaentry.metatag = be_read(&raw_meta_header[0], 4); + metaentry.flags = raw_meta_header[4]; + metaentry.length = be_read(&raw_meta_header[5], 3); + metaentry.next = be_read(&raw_meta_header[8], 8); + + // if we got a match, proceed + if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) + if (metaindex-- == 0) + return true; + + // no match, fetch the next link + metaentry.prev = metaentry.offset; + metaentry.offset = metaentry.next; + } + + // if we get here, we didn't find it + return false; +} + + +//------------------------------------------------- +// decompress_v5_map - decompress the v5 map +//------------------------------------------------- + +void chd_file::decompress_v5_map() +{ + // if no offset, we haven't written it yet + if (m_mapoffset == 0) + { + memset(m_rawmap, 0xff, m_rawmap.count()); + return; + } + + // read the reader + UINT8 rawbuf[16]; + file_read(m_mapoffset, rawbuf, sizeof(rawbuf)); + UINT32 mapbytes = be_read(&rawbuf[0], 4); + UINT64 firstoffs = be_read(&rawbuf[4], 6); + UINT16 mapcrc = be_read(&rawbuf[10], 2); + UINT8 lengthbits = rawbuf[12]; + UINT8 selfbits = rawbuf[13]; + UINT8 parentbits = rawbuf[14]; + + // now read the map + dynamic_buffer compressed(mapbytes); + file_read(m_mapoffset + 16, compressed, mapbytes); + bitstream_in bitbuf(compressed, compressed.count()); + + // first decode the compression types + huffman_decoder<16, 8> decoder; + huffman_error err = decoder.import_tree_rle(bitbuf); + if (err != HUFFERR_NONE) + throw CHDERR_DECOMPRESSION_ERROR; + UINT8 lastcomp = 0; + int repcount = 0; + for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + { + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + if (repcount > 0) + rawmap[0] = lastcomp, repcount--; + else + { + UINT8 val = decoder.decode_one(bitbuf); + if (val == COMPRESSION_RLE_SMALL) + rawmap[0] = lastcomp, repcount = 2 + decoder.decode_one(bitbuf); + else if (val == COMPRESSION_RLE_LARGE) + rawmap[0] = lastcomp, repcount = 2 + 16 + (decoder.decode_one(bitbuf) << 4), repcount += decoder.decode_one(bitbuf); + else + rawmap[0] = lastcomp = val; + } + } + + // then iterate through the hunks and extract the needed data + UINT64 curoffset = firstoffs; + UINT32 last_self = 0; + UINT64 last_parent = 0; + for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + { + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + UINT64 offset = curoffset; + UINT32 length = 0; + UINT16 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 = bitbuf.read(lengthbits); + crc = bitbuf.read(16); + break; + + case COMPRESSION_NONE: + curoffset += length = m_hunkbytes; + crc = bitbuf.read(16); + break; + + case COMPRESSION_SELF: + last_self = offset = bitbuf.read(selfbits); + break; + + case COMPRESSION_PARENT: + offset = bitbuf.read(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(hunknum) * UINT64(m_hunkbytes)) / m_unitbytes; + break; + + case COMPRESSION_PARENT_1: + last_parent += m_hunkbytes / m_unitbytes; + case COMPRESSION_PARENT_0: + rawmap[0] = COMPRESSION_PARENT; + offset = last_parent; + break; + } + be_write(&rawmap[1], length, 3); + be_write(&rawmap[4], offset, 6); + be_write(&rawmap[10], crc, 2); + } + + // verify the final CRC + if (crc16_creator::simple(m_rawmap, m_hunkcount * 12) != mapcrc) + throw CHDERR_DECOMPRESSION_ERROR; +} + +//------------------------------------------------- +// sha1 - return our SHA1 value +//------------------------------------------------- + +sha1_t chd_file::sha1() +{ + try + { + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } +} + + +//------------------------------------------------- +// raw_sha1 - return our raw SHA1 value +//------------------------------------------------- + +sha1_t chd_file::raw_sha1() +{ + try + { + // determine offset within the file for data-only + if (m_rawsha1_offset == 0) + throw CHDERR_UNSUPPORTED_VERSION; + + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } +} + + +//------------------------------------------------- +// parent_sha1 - return our parent's SHA1 value +//------------------------------------------------- + +sha1_t chd_file::parent_sha1() +{ + try + { + // determine offset within the file + if (m_parentsha1_offset == 0) + throw CHDERR_UNSUPPORTED_VERSION; + + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } +} + +//************************************************************************** +// CHD FILE MANAGEMENT +//************************************************************************** + +//------------------------------------------------- +// chd_file - constructor +//------------------------------------------------- + +chd_file::chd_file() + : m_file(NULL), + m_owns_file(false) +{ + // reset state + memset(m_decompressor, 0, sizeof(m_decompressor)); + close(); +} + + +//------------------------------------------------- +// ~chd_file - destructor +//------------------------------------------------- + +chd_file::~chd_file() +{ + // close any open files + close(); +} diff --git a/archivers/chd/chd.h b/archivers/chd/chd.h new file mode 100644 index 00000000..c548a34b --- /dev/null +++ b/archivers/chd/chd.h @@ -0,0 +1,447 @@ +/*************************************************************************** + + 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__ + +#include "osdcore.h" +#include "coretmpl.h" +#include "astring.h" +#include "corefile.h" +#include "hashing.h" +#include "chdcodec.h" + + +/*************************************************************************** + + Compressed Hunks of Data header format. All numbers are stored in + Motorola (big-endian) byte ordering. + + ========================================================================= + + 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) + + Flags: + 0x00000001 - set if this drive has a parent + 0x00000002 - set if this drive allows writes + + Compression types: + CHDCOMPRESSION_NONE = 0 + CHDCOMPRESSION_ZLIB = 1 + + V1 map format: + + [ 0] UINT64 offset : 44; // starting offset within the file + [ 0] UINT64 length : 20; // length of data; if == hunksize, data is uncompressed + + ========================================================================= + + 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) + + Flags and map format are same as V1 + + ========================================================================= + + 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) + + Flags are the same as V1 + + Compression types: + CHDCOMPRESSION_NONE = 0 + CHDCOMPRESSION_ZLIB = 1 + CHDCOMPRESSION_ZLIB_PLUS = 2 + + V3 map format: + + [ 0] UINT64 offset; // starting offset within the file + [ 8] UINT32 crc32; // 32-bit CRC of the uncompressed data + [ 12] UINT16 length_lo; // lower 16 bits of length + [ 14] UINT8 length_hi; // upper 8 bits of length + [ 15] UINT8 flags; // flags, indicating compression info + + ========================================================================= + + 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 are the same as V1 + + Compression types: + CHDCOMPRESSION_NONE = 0 + CHDCOMPRESSION_ZLIB = 1 + CHDCOMPRESSION_ZLIB_PLUS = 2 + CHDCOMPRESSION_AV = 3 + + Map format is the same as V3 + + ========================================================================= + + V5 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 compressors[4];// which custom compressors are used? + [ 32] UINT64 logicalbytes; // logical size of the data (in bytes) + [ 40] UINT64 mapoffset; // offset to the map + [ 48] UINT64 metaoffset; // offset to the first blob of metadata + [ 56] UINT32 hunkbytes; // number of bytes per hunk (512k maximum) + [ 60] UINT32 unitbytes; // number of bytes per unit within each hunk + [ 64] UINT8 rawsha1[20]; // raw data SHA1 + [ 84] UINT8 sha1[20]; // combined raw+meta SHA1 + [104] UINT8 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 offset; // starting offset / hunk size + + V5 compressed map format header: + + [ 0] UINT32 length; // length of compressed map + [ 4] UINT48 datastart; // offset of first block + [ 10] UINT16 crc; // crc-16 of the map + [ 12] UINT8 lengthbits; // bits used to encode complength + [ 13] UINT8 hunkbits; // bits used to encode self-refs + [ 14] UINT8 parentunitbits; // bits used to encode parent unit refs + [ 15] UINT8 reserved; // future use + [ 16] (compressed header length) + + Each compressed map entry, once expanded, looks like: + + [ 0] UINT8 compression; // compression type + [ 1] UINT24 complength; // compressed length + [ 4] UINT48 offset; // offset + [ 10] UINT16 crc; // crc-16 of the data + +***************************************************************************/ + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// pseudo-codecs returned by hunk_info +const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk +const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk +const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat + +// core types +typedef UINT32 chd_metadata_tag; + +// metadata parameters +const chd_metadata_tag CHDMETATAG_WILDCARD = 0; +const UINT32 CHDMETAINDEX_APPEND = ~0; + +// metadata flags +const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed + +// standard hard disk metadata +const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'); +extern const char *HARD_DISK_METADATA_FORMAT; + +// hard disk identify information +const chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T'); + +// hard disk key information +const chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' '); + +// pcmcia CIS information +const chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' '); + +// standard CD-ROM metadata +const chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D'); +const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'); +extern const char *CDROM_TRACK_METADATA_FORMAT; +const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'); +extern const char *CDROM_TRACK_METADATA2_FORMAT; +const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); +extern const char *GDROM_TRACK_METADATA_FORMAT; + +// standard A/V metadata +const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V'); +extern const char *AV_METADATA_FORMAT; + +// A/V laserdisc frame metadata +const chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D'); + +// error types +enum chd_error +{ + CHDERR_NONE, + CHDERR_NO_INTERFACE, + CHDERR_OUT_OF_MEMORY, + CHDERR_NOT_OPEN, + CHDERR_ALREADY_OPEN, + 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_UNSUPPORTED_FORMAT, + CHDERR_UNKNOWN_COMPRESSION, + CHDERR_WALKING_PARENT, + CHDERR_COMPRESSING +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class chd_codec; + + +// ======================> chd_file + +// core file class +class chd_file +{ + friend class chd_file_compressor; + friend class chd_verifier; + + // constants + static const UINT32 HEADER_VERSION = 5; + static const UINT32 V3_HEADER_SIZE = 120; + static const UINT32 V4_HEADER_SIZE = 108; + static const UINT32 V5_HEADER_SIZE = 124; + static const UINT32 MAX_HEADER_SIZE = V5_HEADER_SIZE; + +public: + // construction/destruction + chd_file(); + virtual ~chd_file(); + + // getters + bool opened() const { return (m_file != NULL); } + UINT32 version() const { return m_version; } + UINT64 logical_bytes() const { return m_logicalbytes; } + UINT32 hunk_bytes() const { return m_hunkbytes; } + UINT32 hunk_count() const { return m_hunkcount; } + UINT32 unit_bytes() const { return m_unitbytes; } + UINT64 unit_count() const { return m_unitcount; } + bool compressed() const { return (m_compression[0] != CHD_CODEC_NONE); } + chd_codec_type compression(int index) const { return m_compression[index]; } + chd_file *parent() const { return m_parent; } + sha1_t sha1(); + sha1_t raw_sha1(); + sha1_t parent_sha1(); + chd_error hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes); + + // file open + chd_error open(const char *filename, bool writeable = false, chd_file *parent = NULL); + chd_error open(struct zfile *file, bool writeable = false, chd_file *parent = NULL); + + // file close + void close(); + + // read/write + chd_error read_hunk(UINT32 hunknum, void *buffer); + chd_error read_units(UINT64 unitnum, void *buffer, UINT32 count = 1); + chd_error read_bytes(UINT64 offset, void *buffer, UINT32 bytes); + + // metadata management + chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output); + chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output); + chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen); + chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags); + + // hashing helper + sha1_t compute_overall_sha1(sha1_t rawsha1); + + // codec interfaces + chd_error codec_configure(chd_codec_type codec, int param, void *config); + + // static helpers + static const char *error_string(chd_error err); + +private: + struct metadata_entry; + struct metadata_hash; + + // inline helpers + UINT64 be_read(const UINT8 *base, int numbytes); + void be_write(UINT8 *base, UINT64 value, int numbytes); + sha1_t be_read_sha1(const UINT8 *base); + void be_write_sha1(UINT8 *base, sha1_t value); + void file_read(UINT64 offset, void *dest, UINT32 length); + void file_write(UINT64 offset, const void *source, UINT32 length); + UINT64 file_append(const void *source, UINT32 length, UINT32 alignment = 0); + UINT8 bits_for_value(UINT64 value); + + // internal helpers + UINT32 guess_unitbytes(); + void parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1); + void parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1); + void parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1); + chd_error compress_v5_map(); + void decompress_v5_map(); + chd_error create_common(); + chd_error open_common(bool writeable); + void create_open_common(); + void verify_proper_compression_append(UINT32 hunknum); + void hunk_write_compressed(UINT32 hunknum, INT8 compression, const UINT8 *compressed, UINT32 complength, crc16_t crc16); + void hunk_copy_from_self(UINT32 hunknum, UINT32 otherhunk); + void hunk_copy_from_parent(UINT32 hunknum, UINT64 parentunit); + bool metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume = false); + void metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset); + void metadata_update_hash(); +// static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2); + + // file characteristics + struct zfile * m_file; // handle to the open core file + bool m_owns_file; // flag indicating if this file should be closed on chd_close() + bool m_allow_reads; // permit reads from this CHD? + bool m_allow_writes; // permit writes to this CHD? + + // core parameters from the header + UINT32 m_version; // version of the header + UINT64 m_logicalbytes; // logical size of the raw CHD data in bytes + UINT64 m_mapoffset; // offset of map + UINT64 m_metaoffset; // offset to first metadata bit + UINT32 m_hunkbytes; // size of each raw hunk in bytes + UINT32 m_hunkcount; // number of hunks represented + UINT32 m_unitbytes; // size of each unit in bytes + UINT64 m_unitcount; // number of units represented + chd_codec_type m_compression[4]; // array of compression types used + chd_file * m_parent; // pointer to parent file, or NULL if none + bool m_parent_missing; // are we missing our parent? + + // key offsets within the header + UINT64 m_mapoffset_offset; // offset of map offset field + UINT64 m_metaoffset_offset;// offset of metaoffset field + UINT64 m_sha1_offset; // offset of SHA1 field + UINT64 m_rawsha1_offset; // offset of raw SHA1 field + UINT64 m_parentsha1_offset;// offset of paren SHA1 field + + // map information + UINT32 m_mapentrybytes; // length of each entry in a map + dynamic_buffer m_rawmap; // raw map data + + // compression management + chd_decompressor * m_decompressor[4]; // array of decompression codecs + dynamic_buffer m_compressed; // temporary buffer for compressed data + + // caching + dynamic_buffer m_cache; // single-hunk cache for partial reads/writes + UINT32 m_cachehunk; // which hunk is in the cache? +}; + +#endif // __CHD_H__ \ No newline at end of file diff --git a/archivers/chd/chdcd.h b/archivers/chd/chdcd.h new file mode 100644 index 00000000..ac8b1831 --- /dev/null +++ b/archivers/chd/chdcd.h @@ -0,0 +1,39 @@ +/*************************************************************************** + + CDRDAO TOC parser for CHD compression frontend + + Copyright Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __CHDCD_H__ +#define __CHDCD_H__ + +#include "chdcdrom.h" + +struct chdcd_track_input_entry +{ + chdcd_track_input_entry() { reset(); } + void reset() { fname.reset(); offset = idx0offs = idx1offs = 0; swap = false; } + + astring fname; // filename for each track + UINT32 offset; // offset in the data file for each track + bool swap; // data needs to be byte swapped + UINT32 idx0offs; + UINT32 idx1offs; +}; + +struct chdcd_track_input_info +{ + void reset() { for (int i = 0; i < CD_MAX_TRACKS; i++) track[i].reset(); } + + chdcd_track_input_entry track[CD_MAX_TRACKS]; +}; + + +chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo); + +#endif /* __CHDCD_H__ */ diff --git a/archivers/chd/chdcdrom.cpp b/archivers/chd/chdcdrom.cpp new file mode 100644 index 00000000..a6549ea8 --- /dev/null +++ b/archivers/chd/chdcdrom.cpp @@ -0,0 +1,1200 @@ +#include "chdtypes.h" + +/*************************************************************************** + + cdrom.c + + Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this + +**************************************************************************** + + 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. + +**************************************************************************** + + 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 boundry, these addressing + schemes will differ after track 1! + +***************************************************************************/ + +#include "chdcdrom.h" + +#include +#include "chdcd.h" + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE (0) +#if VERBOSE +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) +void CLIB_DECL logerror(const char *text,...); +#else +#define LOG(x) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +const int SYNC_OFFSET = 0x000; // offset within sector +const int SYNC_NUM_BYTES = 12; // 12 bytes + +const int MODE_OFFSET = 0x00f; // offset within sector + +const int ECC_P_OFFSET = 0x81c; // offset within sector +const int ECC_P_NUM_BYTES = 86; // 2 lots of 86 +const int ECC_P_COMP = 24; // 24 bytes each + +const int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES; +const int ECC_Q_NUM_BYTES = 52; // 2 lots of 52 +const int ECC_Q_COMP = 43; // 43 bytes each + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct cdrom_file +{ + chd_file * chd; /* CHD file */ + cdrom_toc cdtoc; /* TOC for the CD */ + chdcd_track_input_info track_info; /* track info */ + struct zfile * fhandle[CD_MAX_TRACKS];/* file handle */ +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + physical_to_chd_lba - find the CHD LBA + and the track number +-------------------------------------------------*/ + +INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 &tracknum) +{ + UINT32 chdlba; + int track; + + /* loop until our current LBA is less than the start LBA of the next track */ + for (track = 0; track < file->cdtoc.numtrks; track++) + if (physlba < file->cdtoc.tracks[track + 1].physframeofs) + { + chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs; + tracknum = track; + return chdlba; + } + + return physlba; +} + + + +/*************************************************************************** + BASE FUNCTIONALITY +***************************************************************************/ + +#if 0 +cdrom_file *cdrom_open(const char *inputfile) +{ + int i; + cdrom_file *file; + UINT32 physofs; + + /* allocate memory for the CD-ROM file */ + file = new cdrom_file(); + if (file == NULL) + return NULL; + + /* setup the CDROM module and get the disc info */ + chd_error err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err)); + delete file; + return NULL; + } + + /* fill in the data */ + file->chd = NULL; + + LOG(("CD has %d tracks\n", file->cdtoc.numtrks)); + + for (i = 0; i < file->cdtoc.numtrks; i++) + { + file_error filerr = core_fopen(file->track_info.track[i].fname, OPEN_FLAG_READ, &file->fhandle[i]); + if (filerr != FILERR_NONE) + { + fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.cstr()); + cdrom_close(file); + return NULL; + } + } + /* calculate the starting frame for each track, keeping in mind that CHDMAN + pads tracks out with extra frames to fit 4-frame size boundries + */ + physofs = 0; + for (i = 0; i < file->cdtoc.numtrks; i++) + { + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = 0; + + physofs += file->cdtoc.tracks[i].frames; + + LOG(("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d physofs %d chdofs %d\n", i+1, + file->cdtoc.tracks[i].trktype, + file->cdtoc.tracks[i].subtype, + file->cdtoc.tracks[i].datasize, + file->cdtoc.tracks[i].subsize, + file->cdtoc.tracks[i].frames, + file->cdtoc.tracks[i].extraframes, + file->cdtoc.tracks[i].physframeofs, + file->cdtoc.tracks[i].chdframeofs)); + } + + /* fill out dummy entries for the last track to help our search */ + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = 0; + + return file; +} +#endif + +/*------------------------------------------------- + cdrom_open - "open" a CD-ROM file from an + already-opened CHD file +-------------------------------------------------*/ + +cdrom_file *cdrom_open(chd_file *chd) +{ + int i; + cdrom_file *file; + UINT32 physofs, chdofs; + chd_error err; + + /* punt if no CHD */ + if (!chd) + return NULL; + + /* validate the CHD information */ + if (chd->hunk_bytes() % CD_FRAME_SIZE != 0) + return NULL; + if (chd->unit_bytes() != CD_FRAME_SIZE) + return NULL; + + /* allocate memory for the CD-ROM file */ + file = new cdrom_file(); + if (file == NULL) + return NULL; + + /* fill in the data */ + file->chd = chd; + + /* read the CD-ROM metadata */ + err = cdrom_parse_metadata(chd, &file->cdtoc); + if (err != CHDERR_NONE) + { + delete file; + return NULL; + } + + LOG(("CD has %d tracks\n", file->cdtoc.numtrks)); + + /* calculate the starting frame for each track, keeping in mind that CHDMAN + pads tracks out with extra frames to fit 4-frame size boundries + */ + physofs = chdofs = 0; + for (i = 0; i < file->cdtoc.numtrks; i++) + { + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = chdofs; + + physofs += file->cdtoc.tracks[i].frames; + chdofs += file->cdtoc.tracks[i].frames; + chdofs += file->cdtoc.tracks[i].extraframes; + + LOG(("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d physofs %d chdofs %d\n", i+1, + file->cdtoc.tracks[i].trktype, + file->cdtoc.tracks[i].subtype, + file->cdtoc.tracks[i].datasize, + file->cdtoc.tracks[i].subsize, + file->cdtoc.tracks[i].frames, + file->cdtoc.tracks[i].extraframes, + file->cdtoc.tracks[i].physframeofs, + file->cdtoc.tracks[i].chdframeofs)); + } + + /* fill out dummy entries for the last track to help our search */ + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = chdofs; + + return file; +} + + +/*------------------------------------------------- + cdrom_close - "close" a CD-ROM file +-------------------------------------------------*/ + +void cdrom_close(cdrom_file *file) +{ + if (file == NULL) + return; + + if (file->chd == NULL) + { + for (int i = 0; i < file->cdtoc.numtrks; i++) + { + zfile_fclose(file->fhandle[i]); + } + } + + delete file; +} + + + +/*************************************************************************** + CORE READ ACCESS +***************************************************************************/ + +chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length) +{ + // if a CHD, just read + if (file->chd != NULL) + return file->chd->read_bytes(UINT64(chdsector) * UINT64(CD_FRAME_SIZE) + startoffs, dest, length); +#if 0 + // else read from the appropriate file + core_file *srcfile = file->fhandle[tracknum]; + + UINT64 sourcefileoffset = file->track_info.track[tracknum].offset; + int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize; + + sourcefileoffset += chdsector * bytespersector + startoffs; + + core_fseek(srcfile, sourcefileoffset, SEEK_SET); + core_fread(srcfile, dest, length); + + if (file->track_info.track[tracknum].swap) + { + UINT8 *buffer = (UINT8 *)dest - startoffs; + for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 ) + { + int swaptemp = buffer[ swapindex ]; + buffer[ swapindex ] = buffer[ swapindex + 1 ]; + buffer[ swapindex + 1 ] = swaptemp; + } + } +#endif + return CHDERR_NONE; +} + + +/*------------------------------------------------- + cdrom_read_data - read one or more sectors + from a CD-ROM +-------------------------------------------------*/ + +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype) +{ + if (file == NULL) + return 0; + + // compute CHD sector and tracknumber + UINT32 tracknum = 0; + UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum); + + /* copy out the requested sector */ + UINT32 tracktype = file->cdtoc.tracks[tracknum].trktype; + if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE)) + { + return (read_partial_sector(file, buffer, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize) == CHDERR_NONE); + } + else + { + /* return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector */ + if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW)) + { + return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2048) == CHDERR_NONE); + } + + /* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */ + if ((datatype == CD_TRACK_MODE1_RAW) && (tracktype == CD_TRACK_MODE1)) + { + UINT8 *bufptr = (UINT8 *)buffer; + UINT32 msf = lba_to_msf(lbasector); + + static const UINT8 syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; + memcpy(bufptr, syncbytes, 12); + bufptr[12] = msf>>16; + bufptr[13] = msf>>8; + bufptr[14] = msf&0xff; + bufptr[15] = 1; // mode 1 + LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n")); + return (read_partial_sector(file, bufptr+16, chdsector, tracknum, 0, 2048) == CHDERR_NONE); + } + + /* return 2048 bytes of mode 1 data from a mode2 form1 or raw sector */ + if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW))) + { + return (read_partial_sector(file, buffer, chdsector, tracknum, 24, 2048) == CHDERR_NONE); + } + + /* return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) */ + if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW))) + { + return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2336) == CHDERR_NONE); + } + + LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype)); + return 0; + } +} + + +/*------------------------------------------------- + cdrom_read_subcode - read subcode data for + a sector +-------------------------------------------------*/ + +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer) +{ + if (file == NULL) + return ~0; + + // compute CHD sector and tracknumber + UINT32 tracknum = 0; + UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum); + if (file->cdtoc.tracks[tracknum].subsize == 0) + return 1; + + // read the data + chd_error err = read_partial_sector(file, buffer, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize); + return (err == CHDERR_NONE); +} + + + +/*************************************************************************** + HANDY UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_get_track - get the track number + for a physical frame number +-------------------------------------------------*/ + +UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame) +{ + UINT32 track = 0; + + if (file == NULL) + return ~0; + + /* convert to a CHD sector offset and get track information */ + physical_to_chd_lba(file, frame, track); + return track; +} + + +/*------------------------------------------------- + cdrom_get_track_start - get the frame number + that a track starts at +-------------------------------------------------*/ + +UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track) +{ + if (file == NULL) + return ~0; + + /* handle lead-out specially */ + if (track == 0xaa) + track = file->cdtoc.numtrks; + + return file->cdtoc.tracks[track].physframeofs; +} + + + +/*************************************************************************** + TOC UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_get_last_track - returns the last track + number +-------------------------------------------------*/ + +int cdrom_get_last_track(cdrom_file *file) +{ + if (file == NULL) + return -1; + + return file->cdtoc.numtrks; +} + + +/*------------------------------------------------- + cdrom_get_adr_control - get the ADR | CONTROL + for a track +-------------------------------------------------*/ + +int cdrom_get_adr_control(cdrom_file *file, int track) +{ + if (file == NULL) + return -1; + + if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO) + { + return 0x10; // audio track, subchannel is position + } + + return 0x14; // data track, subchannel is position +} + + +/*------------------------------------------------- + cdrom_get_track_type - return the track type +-------------------------------------------------*/ + +int cdrom_get_track_type(cdrom_file *file, int track) +{ + if (file == NULL) + return -1; + + return file->cdtoc.tracks[track].trktype; +} + + +/*------------------------------------------------- + cdrom_get_toc - return the TOC data for a + CD-ROM +-------------------------------------------------*/ + +const cdrom_toc *cdrom_get_toc(cdrom_file *file) +{ + if (file == NULL) + return NULL; + + return &file->cdtoc; +} + + + +/*************************************************************************** + EXTRA UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_get_info_from_type_string + take a string and convert it into track type + and track data size +-------------------------------------------------*/ + +static void cdrom_get_info_from_type_string(const char *typestring, UINT32 *trktype, UINT32 *datasize) +{ + if (!strcmp(typestring, "MODE1")) + { + *trktype = CD_TRACK_MODE1; + *datasize = 2048; + } + else if (!strcmp(typestring, "MODE1/2048")) + { + *trktype = CD_TRACK_MODE1; + *datasize = 2048; + } + else if (!strcmp(typestring, "MODE1_RAW")) + { + *trktype = CD_TRACK_MODE1_RAW; + *datasize = 2352; + } + else if (!strcmp(typestring, "MODE1/2352")) + { + *trktype = CD_TRACK_MODE1_RAW; + *datasize = 2352; + } + else if (!strcmp(typestring, "MODE2")) + { + *trktype = CD_TRACK_MODE2; + *datasize = 2336; + } + else if (!strcmp(typestring, "MODE2/2336")) + { + *trktype = CD_TRACK_MODE2; + *datasize = 2336; + } + else if (!strcmp(typestring, "MODE2_FORM1")) + { + *trktype = CD_TRACK_MODE2_FORM1; + *datasize = 2048; + } + else if (!strcmp(typestring, "MODE2/2048")) + { + *trktype = CD_TRACK_MODE2_FORM1; + *datasize = 2048; + } + else if (!strcmp(typestring, "MODE2_FORM2")) + { + *trktype = CD_TRACK_MODE2_FORM2; + *datasize = 2324; + } + else if (!strcmp(typestring, "MODE2/2324")) + { + *trktype = CD_TRACK_MODE2_FORM2; + *datasize = 2324; + } + else if (!strcmp(typestring, "MODE2_FORM_MIX")) + { + *trktype = CD_TRACK_MODE2_FORM_MIX; + *datasize = 2336; + } + else if (!strcmp(typestring, "MODE2/2336")) + { + *trktype = CD_TRACK_MODE2_FORM_MIX; + *datasize = 2336; + } + else if (!strcmp(typestring, "MODE2_RAW")) + { + *trktype = CD_TRACK_MODE2_RAW; + *datasize = 2352; + } + else if (!strcmp(typestring, "MODE2/2352")) + { + *trktype = CD_TRACK_MODE2_RAW; + *datasize = 2352; + } + else if (!strcmp(typestring, "AUDIO")) + { + *trktype = CD_TRACK_AUDIO; + *datasize = 2352; + } +} + +/*------------------------------------------------- + cdrom_convert_type_string_to_track_info - + take a string and convert it into track type + and track data size +-------------------------------------------------*/ + +void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info) +{ + cdrom_get_info_from_type_string(typestring, &info->trktype, &info->datasize); +} + +/*------------------------------------------------- + cdrom_convert_type_string_to_pregap_info - + take a string and convert it into pregap type + and pregap data size +-------------------------------------------------*/ + +void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info) +{ + cdrom_get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize); +} + +/*------------------------------------------------- + cdrom_convert_subtype_string_to_track_info - + take a string and convert it into track subtype + and track subcode data size +-------------------------------------------------*/ + +void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info) +{ + if (!strcmp(typestring, "RW")) + { + info->subtype = CD_SUB_NORMAL; + info->subsize = 96; + } + else if (!strcmp(typestring, "RW_RAW")) + { + info->subtype = CD_SUB_RAW; + info->subsize = 96; + } +} + +/*------------------------------------------------- + cdrom_convert_subtype_string_to_pregap_info - + take a string and convert it into track subtype + and track subcode data size +-------------------------------------------------*/ + +void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info) +{ + if (!strcmp(typestring, "RW")) + { + info->pgsub = CD_SUB_NORMAL; + info->pgsubsize = 96; + } + else if (!strcmp(typestring, "RW_RAW")) + { + info->pgsub = CD_SUB_RAW; + info->pgsubsize = 96; + } +} + +/*------------------------------------------------- + cdrom_get_type_string - get the string + associated with the given type +-------------------------------------------------*/ + +const char *cdrom_get_type_string(UINT32 trktype) +{ + switch (trktype) + { + case CD_TRACK_MODE1: return "MODE1"; + case CD_TRACK_MODE1_RAW: return "MODE1_RAW"; + case CD_TRACK_MODE2: return "MODE2"; + case CD_TRACK_MODE2_FORM1: return "MODE2_FORM1"; + case CD_TRACK_MODE2_FORM2: return "MODE2_FORM2"; + case CD_TRACK_MODE2_FORM_MIX: return "MODE2_FORM_MIX"; + case CD_TRACK_MODE2_RAW: return "MODE2_RAW"; + case CD_TRACK_AUDIO: return "AUDIO"; + default: return "UNKNOWN"; + } +} + + +/*------------------------------------------------- + cdrom_get_subtype_string - get the string + associated with the given subcode type +-------------------------------------------------*/ + +const char *cdrom_get_subtype_string(UINT32 subtype) +{ + switch (subtype) + { + case CD_SUB_NORMAL: return "RW"; + case CD_SUB_RAW: return "RW_RAW"; + default: return "NONE"; + } +} + + + +/*************************************************************************** + INTERNAL UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_parse_metadata - parse metadata into the + TOC structure +-------------------------------------------------*/ + +chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) +{ + astring metadata; + chd_error err; + int i; + + /* start with no tracks */ + for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++) + { + int tracknum = -1, frames = 0, pregap, postgap, padframes; + char type[16], subtype[16], pgtype[16], pgsub[16]; + cdrom_track_info *track; + + pregap = postgap = padframes = 0; + + /* fetch the metadata for this track */ + err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); + if (err == CHDERR_NONE) + { + /* parse the metadata */ + type[0] = subtype[0] = 0; + pgtype[0] = pgsub[0] = 0; + if (sscanf(metadata, CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4) + return CHDERR_INVALID_DATA; + if (tracknum == 0 || tracknum > CD_MAX_TRACKS) + return CHDERR_INVALID_DATA; + track = &toc->tracks[tracknum - 1]; + } + else + { + err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata); + if (err == CHDERR_NONE) + { + /* parse the metadata */ + type[0] = subtype[0] = 0; + pregap = postgap = 0; + if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8) + return CHDERR_INVALID_DATA; + if (tracknum == 0 || tracknum > CD_MAX_TRACKS) + return CHDERR_INVALID_DATA; + track = &toc->tracks[tracknum - 1]; + } + else + { + err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); + + if (err == CHDERR_NONE) + { + /* parse the metadata */ + type[0] = subtype[0] = 0; + pregap = postgap = 0; + if (sscanf(metadata, GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9) + return CHDERR_INVALID_DATA; + if (tracknum == 0 || tracknum > CD_MAX_TRACKS) + return CHDERR_INVALID_DATA; + track = &toc->tracks[tracknum - 1]; + } + else + { + break; + } + } + } + + /* extract the track type and determine the data size */ + track->trktype = CD_TRACK_MODE1; + track->datasize = 0; + cdrom_convert_type_string_to_track_info(type, track); + if (track->datasize == 0) + return CHDERR_INVALID_DATA; + + /* extract the subtype and determine the subcode data size */ + track->subtype = CD_SUB_NONE; + track->subsize = 0; + cdrom_convert_subtype_string_to_track_info(subtype, track); + + /* set the frames and extra frames data */ + track->frames = frames; + track->padframes = padframes; + int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING; + track->extraframes = padded * CD_TRACK_PADDING - frames; + + /* set the pregap info */ + track->pregap = pregap; + track->pgtype = CD_TRACK_MODE1; + track->pgsub = CD_SUB_NONE; + track->pgdatasize = 0; + track->pgsubsize = 0; + cdrom_convert_type_string_to_pregap_info(pgtype, track); + cdrom_convert_subtype_string_to_pregap_info(pgsub, track); + + /* set the postgap info */ + track->postgap = postgap; + } + + /* if we got any tracks this way, we're done */ + if (toc->numtrks > 0) + return CHDERR_NONE; + + //printf("toc->numtrks = %d?!\n", toc->numtrks); + + /* look for old-style metadata */ + dynamic_buffer oldmetadata; + err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata); + if (err != CHDERR_NONE) + return err; + + /* reconstruct the TOC from it */ + UINT32 *mrp = reinterpret_cast(&oldmetadata[0]); + toc->numtrks = *mrp++; + + for (i = 0; i < CD_MAX_TRACKS; i++) + { + toc->tracks[i].trktype = *mrp++; + toc->tracks[i].subtype = *mrp++; + toc->tracks[i].datasize = *mrp++; + toc->tracks[i].subsize = *mrp++; + toc->tracks[i].frames = *mrp++; + toc->tracks[i].extraframes = *mrp++; + toc->tracks[i].pregap = 0; + toc->tracks[i].postgap = 0; + toc->tracks[i].pgtype = 0; + toc->tracks[i].pgsub = 0; + toc->tracks[i].pgdatasize = 0; + toc->tracks[i].pgsubsize = 0; + } + + /* TODO: I don't know why sometimes the data is one endian and sometimes another */ + if (toc->numtrks > CD_MAX_TRACKS) + { + toc->numtrks = FLIPENDIAN_INT32(toc->numtrks); + for (i = 0; i < CD_MAX_TRACKS; i++) + { + toc->tracks[i].trktype = FLIPENDIAN_INT32(toc->tracks[i].trktype); + toc->tracks[i].subtype = FLIPENDIAN_INT32(toc->tracks[i].subtype); + toc->tracks[i].datasize = FLIPENDIAN_INT32(toc->tracks[i].datasize); + toc->tracks[i].subsize = FLIPENDIAN_INT32(toc->tracks[i].subsize); + toc->tracks[i].frames = FLIPENDIAN_INT32(toc->tracks[i].frames); + toc->tracks[i].padframes = FLIPENDIAN_INT32(toc->tracks[i].padframes); + toc->tracks[i].extraframes = FLIPENDIAN_INT32(toc->tracks[i].extraframes); + } + } + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + cdrom_write_metadata - write metadata +-------------------------------------------------*/ +#if 0 +chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) +{ + chd_error err; + int i; + + /* write the metadata */ + for (i = 0; i < toc->numtrks; i++) + { + astring metadata; + if (!(toc->flags & CD_FLAG_GDROM)) + { + metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), + cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap, + cdrom_get_type_string(toc->tracks[i].pgtype), cdrom_get_subtype_string(toc->tracks[i].pgsub), + toc->tracks[i].postgap); + + err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata); + } + else + { + metadata.format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), + cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes, + toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype), + cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap); + + err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata); + } + if (err != CHDERR_NONE) + return err; + } + return CHDERR_NONE; +} +#endif + +//------------------------------------------------- +// ECC lookup tables +// pre-calculated tables for ECC data calcs +//------------------------------------------------- + +static const UINT8 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 +}; + +static const UINT8 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 +}; + + +//------------------------------------------------- +// 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 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 } +}; + + +//------------------------------------------------- +// 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 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 +//------------------------------------------------- + +inline UINT8 ecc_source_byte(const UINT8 *sector, UINT32 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]; +} + + +//------------------------------------------------- +// ecc_compute_bytes - calculate an ECC value +// (P or Q) +//------------------------------------------------- + +void ecc_compute_bytes(const UINT8 *sector, const UINT16 *row, int rowlen, UINT8 &val1, UINT8 &val2) +{ + val1 = val2 = 0; + for (int 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; +} + + +//------------------------------------------------- +// ecc_verify - verify the P and Q ECC codes in +// a sector +//------------------------------------------------- + +bool ecc_verify(const UINT8 *sector) +{ + // first verify P bytes + for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++) + { + UINT8 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 false; + } + + // then verify Q bytes + for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + { + UINT8 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 false; + } + return true; +} + + +//------------------------------------------------- +// ecc_generate - generate the P and Q ECC codes +// for a sector, overwriting any existing codes +//------------------------------------------------- + +void ecc_generate(UINT8 *sector) +{ + // first verify P bytes + for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++) + ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, sector[ECC_P_OFFSET + byte], sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]); + + // then verify Q bytes + for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, sector[ECC_Q_OFFSET + byte], sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]); +} + + +//------------------------------------------------- +// ecc_clear - erase the ECC P and Q cods to 0 +// within a sector +//------------------------------------------------- + +void ecc_clear(UINT8 *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/archivers/chd/chdcdrom.h b/archivers/chd/chdcdrom.h new file mode 100644 index 00000000..dab39178 --- /dev/null +++ b/archivers/chd/chdcdrom.h @@ -0,0 +1,211 @@ +/*************************************************************************** + + cdrom.h + + Generic MAME cd-rom implementation + +**************************************************************************** + + 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 __CDROM_H__ +#define __CDROM_H__ + +#include "osdcore.h" +#include "chd.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// tracks are padded to a multiple of this many frames +const UINT32 CD_TRACK_PADDING = 4; + +#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 + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct cdrom_file; + +struct cdrom_track_info +{ + /* fields used by CHDMAN and in MAME */ + UINT32 trktype; /* track type */ + UINT32 subtype; /* subcode data type */ + UINT32 datasize; /* size of data in each sector of this track */ + UINT32 subsize; /* size of subchannel data in each sector of this track */ + UINT32 frames; /* number of frames in this track */ + UINT32 extraframes; /* number of "spillage" frames in this track */ + UINT32 pregap; /* number of pregap frames */ + UINT32 postgap; /* number of postgap frames */ + UINT32 pgtype; /* type of sectors in pregap */ + UINT32 pgsub; /* type of subchannel data in pregap */ + UINT32 pgdatasize; /* size of data in each sector of the pregap */ + UINT32 pgsubsize; /* size of subchannel data in each sector of the pregap */ + + /* fields used in CHDMAN only */ + UINT32 padframes; /* number of frames of padding to add to the end of the track; needed for GDI */ + + /* fields used in MAME only */ + UINT32 physframeofs; /* frame number on the real CD this track starts at */ + UINT32 chdframeofs; /* frame number this track starts at on the CHD */ +}; + + +struct cdrom_toc +{ + UINT32 numtrks; /* number of tracks */ + UINT32 flags; /* see FLAG_ above */ + cdrom_track_info tracks[CD_MAX_TRACKS]; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* base functionality */ +cdrom_file *cdrom_open(chd_file *chd); +void cdrom_close(cdrom_file *file); + +cdrom_file *cdrom_open(const char *inputfile); + +/* core read access */ +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype); +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer); +chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length); + +/* handy utilities */ +UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame); +UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track); + +/* TOC utilities */ +int cdrom_get_last_track(cdrom_file *file); +int cdrom_get_adr_control(cdrom_file *file, int track); +int cdrom_get_track_type(cdrom_file *file, int track); +const cdrom_toc *cdrom_get_toc(cdrom_file *file); + +/* extra utilities */ +void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info); +void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info); +void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info); +void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info); +const char *cdrom_get_type_string(UINT32 trktype); +const char *cdrom_get_subtype_string(UINT32 subtype); +chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc); +chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc); + +// ECC utilities +bool ecc_verify(const UINT8 *sector); +void ecc_generate(UINT8 *sector); +void ecc_clear(UINT8 *sector); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE UINT32 msf_to_lba(UINT32 msf) +{ + return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0); +} + +INLINE UINT32 lba_to_msf(UINT32 lba) +{ + UINT8 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? +INLINE UINT32 lba_to_msf_alt(int lba) +{ + UINT32 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/archivers/chd/chdcodec.cpp b/archivers/chd/chdcodec.cpp new file mode 100644 index 00000000..f1234462 --- /dev/null +++ b/archivers/chd/chdcodec.cpp @@ -0,0 +1,1752 @@ +#include "chdtypes.h" + +/*************************************************************************** + + chdcodec.c + + Codecs used by the CHD 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. + +***************************************************************************/ + +#include "chd.h" +#include "hashing.h" +#include "flac.h" +#include "chdcdrom.h" +#include "huffman.h" +#include +#include "7z/LzmaEnc.h" +#include "7z/LzmaDec.h" +#include + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +static const UINT8 s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> chd_zlib_allocator + +// allocation helper clas for zlib +class chd_zlib_allocator +{ +public: + // construction/destruction + chd_zlib_allocator(); + ~chd_zlib_allocator(); + + // installation + void install(z_stream &stream); + +private: + // internal helpers + static voidpf fast_alloc(voidpf opaque, uInt items, uInt size); + static void fast_free(voidpf opaque, voidpf address); + + static const int MAX_ZLIB_ALLOCS = 64; + UINT32 * m_allocptr[MAX_ZLIB_ALLOCS]; +}; + + +// ======================> chd_zlib_compressor + +// ZLIB compressor +class chd_zlib_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_zlib_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_zlib_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + z_stream m_deflater; + chd_zlib_allocator m_allocator; +}; + + +// ======================> chd_zlib_decompressor + +// ZLIB decompressor +class chd_zlib_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_zlib_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_zlib_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + z_stream m_inflater; + chd_zlib_allocator m_allocator; +}; + + +// ======================> chd_lzma_allocator + +// allocation helper clas for zlib +class chd_lzma_allocator : public ISzAlloc +{ +public: + // construction/destruction + chd_lzma_allocator(); + ~chd_lzma_allocator(); + +private: + // internal helpers + static void *fast_alloc(void *p, size_t size); + static void fast_free(void *p, void *address); + + static const int MAX_LZMA_ALLOCS = 64; + UINT32 * m_allocptr[MAX_LZMA_ALLOCS]; +}; + + +// ======================> chd_lzma_compressor + +// LZMA compressor +class chd_lzma_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_lzma_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_lzma_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + + // helpers + static void configure_properties(CLzmaEncProps &props, UINT32 hunkbytes); + +private: + // internal state + CLzmaEncProps m_props; + chd_lzma_allocator m_allocator; +}; + + +// ======================> chd_lzma_decompressor + +// LZMA decompressor +class chd_lzma_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_lzma_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_lzma_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + CLzmaProps m_props; + CLzmaDec m_decoder; + chd_lzma_allocator m_allocator; +}; + + +// ======================> chd_huffman_compressor + +// Huffman compressor +class chd_huffman_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_huffman_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + huffman_8bit_encoder m_encoder; +}; + + +// ======================> chd_huffman_decompressor + +// Huffman decompressor +class chd_huffman_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_huffman_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + huffman_8bit_decoder m_decoder; +}; + + +// ======================> chd_flac_compressor + +// FLAC compressor +class chd_flac_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + + // static helpers + static UINT32 blocksize(UINT32 bytes); + +private: + // internal state + bool m_big_endian; + flac_encoder m_encoder; +}; + + +// ======================> chd_flac_decompressor + +// FLAC decompressor +class chd_flac_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + bool m_big_endian; + flac_decoder m_decoder; +}; + + +// ======================> chd_cd_flac_compressor + +// CD/FLAC compressor +class chd_cd_flac_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_cd_flac_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + + // static helpers + static UINT32 blocksize(UINT32 bytes); + +private: + // internal state + bool m_swap_endian; + flac_encoder m_encoder; + z_stream m_deflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; +}; + + +// ======================> chd_cd_flac_decompressor + +// FLAC decompressor +class chd_cd_flac_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + ~chd_cd_flac_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + bool m_swap_endian; + flac_decoder m_decoder; + z_stream m_inflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; +}; + + +// ======================> chd_cd_compressor + +template +class chd_cd_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_cd_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy), + m_base_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), + m_subcode_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), + m_buffer(hunkbytes + (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA) + { + // make sure the CHD's hunk size is an even multiple of the frame size + if (hunkbytes % CD_FRAME_SIZE != 0) + throw CHDERR_CODEC_ERROR; + } + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) + { + // determine header bytes + UINT32 frames = srclen / CD_FRAME_SIZE; + UINT32 complen_bytes = (srclen < 65536) ? 2 : 3; + UINT32 ecc_bytes = (frames + 7) / 8; + UINT32 header_bytes = ecc_bytes + complen_bytes; + + // clear out destination header + memset(dest, 0, header_bytes); + + // copy audio data followed by subcode data + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&m_buffer[framenum * CD_MAX_SECTOR_DATA], &src[framenum * CD_FRAME_SIZE], CD_MAX_SECTOR_DATA); + memcpy(&m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], &src[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], CD_MAX_SUBCODE_DATA); + + // clear out ECC data if we can + UINT8 *sector = &m_buffer[framenum * CD_MAX_SECTOR_DATA]; + if (memcmp(sector, s_cd_sync_header, sizeof(s_cd_sync_header)) == 0 && ecc_verify(sector)) + { + dest[framenum / 8] |= 1 << (framenum % 8); + memset(sector, 0, sizeof(s_cd_sync_header)); + ecc_clear(sector); + } + } + + // encode the base portion + UINT32 complen = m_base_compressor.compress(&m_buffer[0], frames * CD_MAX_SECTOR_DATA, &dest[header_bytes]); + if (complen >= srclen) + throw CHDERR_COMPRESSION_ERROR; + + // write compressed length + dest[ecc_bytes + 0] = complen >> ((complen_bytes - 1) * 8); + dest[ecc_bytes + 1] = complen >> ((complen_bytes - 2) * 8); + if (complen_bytes > 2) + dest[ecc_bytes + 2] = complen >> ((complen_bytes - 3) * 8); + + // encode the subcode + return header_bytes + complen + m_subcode_compressor.compress(&m_buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA, &dest[header_bytes + complen]); + } + +private: + // internal state + _BaseCompressor m_base_compressor; + _SubcodeCompressor m_subcode_compressor; + dynamic_buffer m_buffer; +}; + + +// ======================> chd_cd_decompressor + +template +class chd_cd_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_cd_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy), + m_base_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), + m_subcode_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), + m_buffer(hunkbytes) + { + // make sure the CHD's hunk size is an even multiple of the frame size + if (hunkbytes % CD_FRAME_SIZE != 0) + throw CHDERR_CODEC_ERROR; + } + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) + { + // determine header bytes + UINT32 frames = destlen / CD_FRAME_SIZE; + UINT32 complen_bytes = (destlen < 65536) ? 2 : 3; + UINT32 ecc_bytes = (frames + 7) / 8; + UINT32 header_bytes = ecc_bytes + complen_bytes; + + // extract compressed length of base + UINT32 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 + m_base_decompressor.decompress(&src[header_bytes], complen_base, &m_buffer[0], frames * CD_MAX_SECTOR_DATA); + m_subcode_decompressor.decompress(&src[header_bytes + complen_base], complen - complen_base - header_bytes, &m_buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + + // reassemble the data + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &m_buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + + // reconstitute the ECC data and sync header + UINT8 *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); + } + } + } + +private: + // internal state + _BaseDecompressor m_base_decompressor; + _SubcodeDecompressor m_subcode_decompressor; + dynamic_buffer m_buffer; +}; + + +#if 0 +// ======================> chd_avhuff_compressor + +// A/V compressor +class chd_avhuff_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_avhuff_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal helpers + void postinit(); + + // internal state + avhuff_encoder m_encoder; + bool m_postinit; +}; + + +// ======================> chd_avhuff_decompressor + +// A/V decompressor +class chd_avhuff_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_avhuff_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + virtual void configure(int param, void *config); + +private: + // internal state + avhuff_decoder m_decoder; +}; +#endif + + +//************************************************************************** +// CODEC LIST +//************************************************************************** + +// static list of available known codecs +const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = +{ + // general codecs + { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + + // general codecs with CD frontend + { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, + { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, + { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + + // A/V codecs +// { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, +}; + + + +//************************************************************************** +// CHD CODEC +//************************************************************************** + +//------------------------------------------------- +// chd_codec - constructor +//------------------------------------------------- + +chd_codec::chd_codec(chd_file &chd, UINT32 hunkbytes, bool lossy) + : m_chd(chd), + m_hunkbytes(hunkbytes), + m_lossy(lossy) +{ +} + + +//------------------------------------------------- +// ~chd_codec - destructor +//------------------------------------------------- + +chd_codec::~chd_codec() +{ +} + + +//------------------------------------------------- +// configure - configuration +//------------------------------------------------- + +void chd_codec::configure(int param, void *config) +{ + // if not overridden, it is always a failure + throw CHDERR_INVALID_PARAMETER; +} + + + +//************************************************************************** +// CHD COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_compressor - constructor +//------------------------------------------------- + +chd_compressor::chd_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_codec(chd, hunkbytes, lossy) +{ +} + + + +//************************************************************************** +// CHD DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_decompressor - constructor +//------------------------------------------------- + +chd_decompressor::chd_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_codec(chd, hunkbytes, lossy) +{ +} + + + +//************************************************************************** +// CHD CODEC LIST +//************************************************************************** + +//------------------------------------------------- +// new_compressor - create a new compressor +// instance of the given type +//------------------------------------------------- + +chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &chd) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : (*entry->m_construct_compressor)(chd, chd.hunk_bytes(), entry->m_lossy); +} + + +//------------------------------------------------- +// new_compressor - create a new decompressor +// instance of the given type +//------------------------------------------------- + +chd_decompressor *chd_codec_list::new_decompressor(chd_codec_type type, chd_file &chd) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : (*entry->m_construct_decompressor)(chd, chd.hunk_bytes(), entry->m_lossy); +} + + +//------------------------------------------------- +// codec_name - return the name of the given +// codec +//------------------------------------------------- + +const char *chd_codec_list::codec_name(chd_codec_type type) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : entry->m_name; +} + + +//------------------------------------------------- +// find_in_list - create a new compressor +// instance of the given type +//------------------------------------------------- + +const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type type) +{ + // find in the list and construct the class + for (int listnum = 0; listnum < ARRAY_LENGTH(s_codec_list); listnum++) + if (s_codec_list[listnum].m_type == type) + return &s_codec_list[listnum]; + return NULL; +} + + + +//************************************************************************** +// CODEC INSTANCE +//************************************************************************** + +//------------------------------------------------- +// chd_compressor_group - constructor +//------------------------------------------------- + +chd_compressor_group::chd_compressor_group(chd_file &chd, UINT32 compressor_list[4]) + : m_hunkbytes(chd.hunk_bytes()), + m_compress_test(m_hunkbytes) +#if CHDCODEC_VERIFY_COMPRESSION + ,m_decompressed(m_hunkbytes) +#endif +{ + // verify the compression types and initialize the codecs + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + { + m_compressor[codecnum] = NULL; + if (compressor_list[codecnum] != CHD_CODEC_NONE) + { + m_compressor[codecnum] = chd_codec_list::new_compressor(compressor_list[codecnum], chd); + if (m_compressor[codecnum] == NULL) + throw CHDERR_UNKNOWN_COMPRESSION; +#if CHDCODEC_VERIFY_COMPRESSION + m_decompressor[codecnum] = chd_codec_list::new_decompressor(compressor_list[codecnum], chd); + if (m_decompressor[codecnum] == NULL) + throw CHDERR_UNKNOWN_COMPRESSION; +#endif + } + } +} + + +//------------------------------------------------- +// ~chd_compressor_group - destructor +//------------------------------------------------- + +chd_compressor_group::~chd_compressor_group() +{ + // delete the codecs and the test buffer + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + delete m_compressor[codecnum]; +} + + +//------------------------------------------------- +// find_best_compressor - iterate over all codecs +// to determine which one produces the best +// compression for this hunk +//------------------------------------------------- + +INT8 chd_compressor_group::find_best_compressor(const UINT8 *src, UINT8 *compressed, UINT32 &complen) +{ + // determine best compression technique + complen = m_hunkbytes; + INT8 compression = -1; + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + if (m_compressor[codecnum] != NULL) + { + // attempt to compress, swallowing errors + try + { + // if this is the best one, copy the data into the permanent buffer + UINT32 compbytes = m_compressor[codecnum]->compress(src, m_hunkbytes, m_compress_test); +#if CHDCODEC_VERIFY_COMPRESSION + try + { + memset(m_decompressed, 0, m_hunkbytes); + m_decompressor[codecnum]->decompress(m_compress_test, compbytes, m_decompressed, m_hunkbytes); + } + catch (...) + { + } + + if (memcmp(src, m_decompressed, m_hunkbytes) != 0) + { + compbytes = m_compressor[codecnum]->compress(src, m_hunkbytes, m_compress_test); + try + { + m_decompressor[codecnum]->decompress(m_compress_test, compbytes, m_decompressed, m_hunkbytes); + } + catch (...) + { + memset(m_decompressed, 0, m_hunkbytes); + } + } +printf(" codec%d=%d bytes \n", codecnum, compbytes); +#endif + if (compbytes < complen) + { + compression = codecnum; + complen = compbytes; + memcpy(compressed, m_compress_test, compbytes); + } + } + catch (...) { } + } + + // if the best is none, copy it over + if (compression == -1) + memcpy(compressed, src, m_hunkbytes); + return compression; +} + + + +//************************************************************************** +// ZLIB ALLOCATOR HELPER +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_allocator - constructor +//------------------------------------------------- + +chd_zlib_allocator::chd_zlib_allocator() +{ + // reset pointer list + memset(m_allocptr, 0, sizeof(m_allocptr)); +} + + +//------------------------------------------------- +// ~chd_zlib_allocator - constructor +//------------------------------------------------- + +chd_zlib_allocator::~chd_zlib_allocator() +{ + // free our memory + for (int memindex = 0; memindex < ARRAY_LENGTH(m_allocptr); memindex++) + delete[] m_allocptr[memindex]; +} + + +//------------------------------------------------- +// install - configure the allocators for a +// stream +//------------------------------------------------- + +void chd_zlib_allocator::install(z_stream &stream) +{ + stream.zalloc = &chd_zlib_allocator::fast_alloc; + stream.zfree = &chd_zlib_allocator::fast_free; + stream.opaque = this; +} + + +//------------------------------------------------- +// zlib_fast_alloc - fast malloc for ZLIB, which +// allocates and frees memory frequently +//------------------------------------------------- + +voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size) +{ +#if 0 + return calloc(items, size); +#else + chd_zlib_allocator *codec = reinterpret_cast(opaque); + + // compute the size, rounding to the nearest 1k + size = (size * items + 0x3ff) & ~0x3ff; + + // reuse a hunk if we can + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + { + UINT32 *ptr = codec->m_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 + UINT32 *ptr = reinterpret_cast(new UINT8[size + sizeof(UINT32)]); + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + if (codec->m_allocptr[scan] == NULL) + { + codec->m_allocptr[scan] = ptr; + break; + } + + // set the low bit of the size so we don't match next time + *ptr = size | 1; + return ptr + 1; +#endif +} + + +//------------------------------------------------- +// zlib_fast_free - fast free for ZLIB, which +// allocates and frees memory frequently +//------------------------------------------------- + +void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address) +{ +#if 0 + free (address); +#else + chd_zlib_allocator *codec = reinterpret_cast(opaque); + + // find the hunk + UINT32 *ptr = reinterpret_cast(address) - 1; + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + if (ptr == codec->m_allocptr[scan]) + { + // clear the low bit of the size to allow matches + *ptr &= ~1; + return; + } +#endif +} + + + +//************************************************************************** +// ZLIB COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_compressor - constructor +//------------------------------------------------- + +chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy) +{ + // initialize the deflater + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.avail_in = 0; + m_allocator.install(m_deflater); + int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_zlib_compressor - destructor +//------------------------------------------------- + +chd_zlib_compressor::~chd_zlib_compressor() +{ + deflateEnd(&m_deflater); +} + + +//------------------------------------------------- +// compress - compress data using the ZLIB codec +//------------------------------------------------- + +UINT32 chd_zlib_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // reset the decompressor + m_deflater.next_in = const_cast(src); + m_deflater.avail_in = srclen; + m_deflater.total_in = 0; + m_deflater.next_out = dest; + m_deflater.avail_out = srclen; + m_deflater.total_out = 0; + int zerr = deflateReset(&m_deflater); + if (zerr != Z_OK) + throw CHDERR_COMPRESSION_ERROR; + + // do it + zerr = deflate(&m_deflater, Z_FINISH); + + // if we ended up with more data than we started with, return an error + if (zerr != Z_STREAM_END || m_deflater.total_out >= srclen) + throw CHDERR_COMPRESSION_ERROR; + + // otherwise, return the length + return m_deflater.total_out; +} + + + +//************************************************************************** +// ZLIB DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_decompressor - constructor +//------------------------------------------------- + +chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) +{ + // init the inflater + memset (&m_inflater, 0, sizeof (z_stream)); + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.avail_in = 0; + m_allocator.install(m_inflater); + int zerr = inflateInit2(&m_inflater, -MAX_WBITS); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_zlib_decompressor - destructor +//------------------------------------------------- + +chd_zlib_decompressor::~chd_zlib_decompressor() +{ + inflateEnd(&m_inflater); +} + + +//------------------------------------------------- +// decompress - decompress data using the ZLIB +// codec +//------------------------------------------------- + +void chd_zlib_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // reset the decompressor + m_inflater.next_in = const_cast(src); + m_inflater.avail_in = complen; + m_inflater.total_in = 0; + m_inflater.next_out = dest; + m_inflater.avail_out = destlen; + m_inflater.total_out = 0; + int zerr = inflateReset(&m_inflater); + if (zerr != Z_OK) + throw CHDERR_DECOMPRESSION_ERROR; + + // do it + zerr = inflate(&m_inflater, Z_FINISH); + if (zerr != Z_STREAM_END) + throw CHDERR_DECOMPRESSION_ERROR; + if (m_inflater.total_out != destlen) + throw CHDERR_DECOMPRESSION_ERROR; +} + + + +//************************************************************************** +// LZMA ALLOCATOR HELPER +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_allocator - constructor +//------------------------------------------------- + +chd_lzma_allocator::chd_lzma_allocator() +{ + // reset pointer list + memset(m_allocptr, 0, sizeof(m_allocptr)); + + // set our pointers + Alloc = &chd_lzma_allocator::fast_alloc; + Free = &chd_lzma_allocator::fast_free; +} + + +//------------------------------------------------- +// ~chd_lzma_allocator - constructor +//------------------------------------------------- + +chd_lzma_allocator::~chd_lzma_allocator() +{ + // free our memory + for (int memindex = 0; memindex < ARRAY_LENGTH(m_allocptr); memindex++) + delete[] m_allocptr[memindex]; +} + + +//------------------------------------------------- +// lzma_fast_alloc - fast malloc for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void *chd_lzma_allocator::fast_alloc(void *p, size_t size) +{ + chd_lzma_allocator *codec = reinterpret_cast(p); + + // compute the size, rounding to the nearest 1k + size = (size + 0x3ff) & ~0x3ff; + + // reuse a hunk if we can + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + UINT32 *ptr = codec->m_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 + UINT32 *ptr = reinterpret_cast(new UINT8[size + sizeof(UINT32)]); + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + if (codec->m_allocptr[scan] == NULL) + { + codec->m_allocptr[scan] = ptr; + break; + } + + // set the low bit of the size so we don't match next time + *ptr = size | 1; + return ptr + 1; +} + + +//------------------------------------------------- +// lzma_fast_free - fast free for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void chd_lzma_allocator::fast_free(void *p, void *address) +{ + if (address == NULL) + return; + + chd_lzma_allocator *codec = reinterpret_cast(p); + + // find the hunk + UINT32 *ptr = reinterpret_cast(address) - 1; + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + if (ptr == codec->m_allocptr[scan]) + { + // clear the low bit of the size to allow matches + *ptr &= ~1; + return; + } +} + + + +//************************************************************************** +// LZMA COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_compressor - constructor +//------------------------------------------------- + +chd_lzma_compressor::chd_lzma_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy) +{ + // initialize the properties + configure_properties(m_props, hunkbytes); +} + + +//------------------------------------------------- +// ~chd_lzma_compressor - destructor +//------------------------------------------------- + +chd_lzma_compressor::~chd_lzma_compressor() +{ +} + + +//------------------------------------------------- +// compress - compress data using the LZMA codec +//------------------------------------------------- + +UINT32 chd_lzma_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // allocate the encoder + CLzmaEncHandle encoder = LzmaEnc_Create(&m_allocator); + if (encoder == NULL) + throw CHDERR_COMPRESSION_ERROR; + + try + { + // configure the encoder + SRes res = LzmaEnc_SetProps(encoder, &m_props); + if (res != SZ_OK) + throw CHDERR_COMPRESSION_ERROR; + + // run it + SizeT complen = srclen; + res = LzmaEnc_MemEncode(encoder, dest, &complen, src, srclen, 0, NULL, &m_allocator, &m_allocator); + if (res != SZ_OK) + throw CHDERR_COMPRESSION_ERROR; + + // clean up + LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator); + return complen; + } + catch (...) + { + // destroy before re-throwing + LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator); + throw; + } +} + + +//------------------------------------------------- +// configure_properties - configure the LZMA +// codec +//------------------------------------------------- + +void chd_lzma_compressor::configure_properties(CLzmaEncProps &props, UINT32 hunkbytes) +{ + LzmaEncProps_Init(&props); + props.level = 9; + props.reduceSize = hunkbytes; + LzmaEncProps_Normalize(&props); +} + + + +//************************************************************************** +// LZMA DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_decompressor - constructor +//------------------------------------------------- + +chd_lzma_decompressor::chd_lzma_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) +{ + // construct the decoder + LzmaDec_Construct(&m_decoder); + + // configure the properties like the compressor did + CLzmaEncProps encoder_props; + chd_lzma_compressor::configure_properties(encoder_props, hunkbytes); + + // convert to decoder properties + CLzmaProps decoder_props; + decoder_props.lc = encoder_props.lc; + decoder_props.lp = encoder_props.lp; + decoder_props.pb = encoder_props.pb; + decoder_props.dicSize = encoder_props.dictSize; + + // do memory allocations + SRes res = LzmaDec_Allocate_MAME(&m_decoder, &decoder_props, &m_allocator); + if (res != SZ_OK) + throw CHDERR_DECOMPRESSION_ERROR; +} + + +//------------------------------------------------- +// ~chd_lzma_decompressor - destructor +//------------------------------------------------- + +chd_lzma_decompressor::~chd_lzma_decompressor() +{ + // free memory + LzmaDec_Free(&m_decoder, &m_allocator); +} + + +//------------------------------------------------- +// decompress - decompress data using the LZMA +// codec +//------------------------------------------------- + +void chd_lzma_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // initialize + LzmaDec_Init(&m_decoder); + + // decode + SizeT consumedlen = complen; + SizeT decodedlen = destlen; + ELzmaStatus status; + SRes res = LzmaDec_DecodeToBuf(&m_decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); + if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) + throw CHDERR_DECOMPRESSION_ERROR; +} + + + +//************************************************************************** +// HUFFMAN COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_huffman_compressor - constructor +//------------------------------------------------- + +chd_huffman_compressor::chd_huffman_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy) +{ +} + + +//------------------------------------------------- +// compress - compress data using the Huffman +// codec +//------------------------------------------------- + +UINT32 chd_huffman_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ +#if 0 + UINT32 complen; + if (m_encoder.encode(src, srclen, dest, srclen, complen) != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; + return complen; +#else + return 0; +#endif +} + + + +//************************************************************************** +// HUFFMAN DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_huffman_decompressor - constructor +//------------------------------------------------- + +chd_huffman_decompressor::chd_huffman_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) +{ +} + + +//------------------------------------------------- +// decompress - decompress data using the Huffman +// codec +//------------------------------------------------- + +void chd_huffman_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + if (m_decoder.decode(src, complen, dest, destlen) != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; +} + + + +//************************************************************************** +// FLAC COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_flac_compressor - constructor +//------------------------------------------------- + +chd_flac_compressor::chd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy) +{ +#if 0 + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast(&native_endian) = 1; + m_big_endian = (native_endian == 0x100); + + // configure the encoder + m_encoder.set_sample_rate(44100); + m_encoder.set_num_channels(2); + m_encoder.set_block_size(blocksize(hunkbytes)); + m_encoder.set_strip_metadata(true); +#endif +} + + +//------------------------------------------------- +// compress - compress data using the FLAC codec +//------------------------------------------------- + +UINT32 chd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ +#if 0 + // reset and encode big-endian + m_encoder.reset(dest + 1, hunkbytes() - 1); + if (!m_encoder.encode_interleaved(reinterpret_cast(src), srclen / 4, !m_big_endian)) + throw CHDERR_COMPRESSION_ERROR; + UINT32 complen_be = m_encoder.finish(); + + // reset and encode little-endian + m_encoder.reset(dest + 1, hunkbytes() - 1); + if (!m_encoder.encode_interleaved(reinterpret_cast(src), srclen / 4, m_big_endian)) + throw CHDERR_COMPRESSION_ERROR; + UINT32 complen_le = m_encoder.finish(); + + // pick the best one and add a byte + UINT32 complen = MIN(complen_le, complen_be); + if (complen + 1 >= hunkbytes()) + throw CHDERR_COMPRESSION_ERROR; + + // if big-endian was better, re-do it + dest[0] = 'L'; + if (complen != complen_le) + { + dest[0] = 'B'; + m_encoder.reset(dest + 1, hunkbytes() - 1); + if (!m_encoder.encode_interleaved(reinterpret_cast(src), srclen / 4, !m_big_endian)) + throw CHDERR_COMPRESSION_ERROR; + m_encoder.finish(); + } + return complen + 1; +#else + return 0; +#endif +} + + +//------------------------------------------------- +// blocksize - return the optimal block size +//------------------------------------------------- + +UINT32 chd_flac_compressor::blocksize(UINT32 bytes) +{ + // determine FLAC block size, which must be 16-65535 + // clamp to 2k since that's supposed to be the sweet spot + UINT32 hunkbytes = bytes / 4; + while (hunkbytes > 2048) + hunkbytes /= 2; + return hunkbytes; +} + + + +//************************************************************************** +// FLAC DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_flac_decompressor - constructor +//------------------------------------------------- + +chd_flac_decompressor::chd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) +{ + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast(&native_endian) = 1; + m_big_endian = (native_endian == 0x100); +} + + +//------------------------------------------------- +// decompress - decompress data using the FLAC +// codec +//------------------------------------------------- + +void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // determine the endianness + bool swap_endian; + if (src[0] == 'L') + swap_endian = m_big_endian; + else if (src[0] == 'B') + swap_endian = !m_big_endian; + else + throw CHDERR_DECOMPRESSION_ERROR; + + // reset and decode + if (!m_decoder.reset(44100, 2, chd_flac_compressor::blocksize(destlen), src + 1, complen - 1)) + throw CHDERR_DECOMPRESSION_ERROR; + if (!m_decoder.decode_interleaved(reinterpret_cast(dest), destlen / 4, swap_endian)) + throw CHDERR_DECOMPRESSION_ERROR; + + // finish up + m_decoder.finish(); +} + + + +//************************************************************************** +// CD FLAC COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_cd_flac_compressor - constructor +//------------------------------------------------- + +chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy), + m_buffer(hunkbytes) +{ +#if 0 + // make sure the CHD's hunk size is an even multiple of the frame size + if (hunkbytes % CD_FRAME_SIZE != 0) + throw CHDERR_CODEC_ERROR; + + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast(&native_endian) = 1; + m_swap_endian = (native_endian == 1); + + // configure the encoder + m_encoder.set_sample_rate(44100); + m_encoder.set_num_channels(2); + m_encoder.set_block_size(blocksize((hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA)); + m_encoder.set_strip_metadata(true); + + // initialize the deflater + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.avail_in = 0; + m_allocator.install(m_deflater); + int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +#endif +} + + +//------------------------------------------------- +// ~chd_cd_flac_compressor - destructor +//------------------------------------------------- + +chd_cd_flac_compressor::~chd_cd_flac_compressor() +{ + deflateEnd(&m_deflater); +} + + +//------------------------------------------------- +// compress - compress data using the FLAC codec, +// and use zlib on the subcode data +//------------------------------------------------- + +UINT32 chd_cd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ +#if 0 + // copy audio data followed by subcode data + UINT32 frames = hunkbytes() / CD_FRAME_SIZE; + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&m_buffer[framenum * CD_MAX_SECTOR_DATA], &src[framenum * CD_FRAME_SIZE], CD_MAX_SECTOR_DATA); + memcpy(&m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], &src[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], CD_MAX_SUBCODE_DATA); + } + + // reset and encode the audio portion + m_encoder.reset(dest, hunkbytes()); + UINT8 *buffer = m_buffer; + if (!m_encoder.encode_interleaved(reinterpret_cast(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) + throw CHDERR_COMPRESSION_ERROR; + + // finish up + UINT32 complen = m_encoder.finish(); + + // deflate the subcode data + m_deflater.next_in = const_cast(&m_buffer[frames * CD_MAX_SECTOR_DATA]); + m_deflater.avail_in = frames * CD_MAX_SUBCODE_DATA; + m_deflater.total_in = 0; + m_deflater.next_out = &dest[complen]; + m_deflater.avail_out = hunkbytes() - complen; + m_deflater.total_out = 0; + int zerr = deflateReset(&m_deflater); + if (zerr != Z_OK) + throw CHDERR_COMPRESSION_ERROR; + + // do it + zerr = deflate(&m_deflater, Z_FINISH); + + // if we ended up with more data than we started with, return an error + complen += m_deflater.total_out; + if (zerr != Z_STREAM_END || complen >= srclen) + throw CHDERR_COMPRESSION_ERROR; + return complen; +#else + return 0; +#endif +} + + +//------------------------------------------------- +// blocksize - return the optimal block size +//------------------------------------------------- + +UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes) +{ + // for CDs it seems that CD_MAX_SECTOR_DATA is the right target + UINT32 blocksize = bytes / 4; + while (blocksize > CD_MAX_SECTOR_DATA) + blocksize /= 2; + return blocksize; +} + + + +//************************************************************************** +// CD FLAC DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_cd_flac_decompressor - constructor +//------------------------------------------------- + +chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy), + m_buffer(hunkbytes) +{ + // make sure the CHD's hunk size is an even multiple of the frame size + if (hunkbytes % CD_FRAME_SIZE != 0) + throw CHDERR_CODEC_ERROR; + + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast(&native_endian) = 1; + m_swap_endian = (native_endian == 1); + + // init the inflater + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.avail_in = 0; + m_allocator.install(m_inflater); + int zerr = inflateInit2(&m_inflater, -MAX_WBITS); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_cd_flac_decompressor - destructor +//------------------------------------------------- + +chd_cd_flac_decompressor::~chd_cd_flac_decompressor() +{ + inflateEnd(&m_inflater); +} + + +//------------------------------------------------- +// decompress - decompress data using the FLAC +// codec +//------------------------------------------------- + +void chd_cd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // reset and decode + UINT32 frames = destlen / CD_FRAME_SIZE; + if (!m_decoder.reset(44100, 2, chd_cd_flac_compressor::blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) + throw CHDERR_DECOMPRESSION_ERROR; + UINT8 *buffer = m_buffer; + if (!m_decoder.decode_interleaved(reinterpret_cast(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) + throw CHDERR_DECOMPRESSION_ERROR; + + // inflate the subcode data + UINT32 offset = m_decoder.finish(); + m_inflater.next_in = const_cast(src + offset); + m_inflater.avail_in = complen - offset; + m_inflater.total_in = 0; + m_inflater.next_out = &m_buffer[frames * CD_MAX_SECTOR_DATA]; + m_inflater.avail_out = frames * CD_MAX_SUBCODE_DATA; + m_inflater.total_out = 0; + int zerr = inflateReset(&m_inflater); + if (zerr != Z_OK) + throw CHDERR_DECOMPRESSION_ERROR; + + // do it + zerr = inflate(&m_inflater, Z_FINISH); + if (zerr != Z_STREAM_END) + throw CHDERR_DECOMPRESSION_ERROR; + if (m_inflater.total_out != frames * CD_MAX_SUBCODE_DATA) + throw CHDERR_DECOMPRESSION_ERROR; + + // reassemble the data + for (UINT32 framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &m_buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + } +} + + +#if 0 +//************************************************************************** +// AVHUFF COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_avhuff_compressor - constructor +//------------------------------------------------- + +chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_compressor(chd, hunkbytes, lossy), + m_postinit(false) +{ + try + { + // attempt to do a post-init now + postinit(); + } + catch (chd_error &) + { + // if we're creating a new CHD, it won't work but that's ok + } +} + + +//------------------------------------------------- +// compress - compress data using the A/V codec +//------------------------------------------------- + +UINT32 chd_avhuff_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // if we haven't yet set up the avhuff code, do it now + if (!m_postinit) + postinit(); + + // make sure short frames are padded with 0 + if (src != NULL) + { + int size = avhuff_encoder::raw_data_size(src); + while (size < srclen) + if (src[size++] != 0) + throw CHDERR_INVALID_DATA; + } + + // encode the audio and video + UINT32 complen; + avhuff_error averr = m_encoder.encode_data(src, dest, complen); + if (averr != AVHERR_NONE || complen > srclen) + throw CHDERR_COMPRESSION_ERROR; + return complen; +} + + +//------------------------------------------------- +// postinit - actual initialization of avhuff +// happens here, on the first attempt to compress +// or decompress data +//------------------------------------------------- + +void chd_avhuff_compressor::postinit() +{ + // get the metadata + astring metadata; + chd_error err = chd().read_metadata(AV_METADATA_TAG, 0, metadata); + if (err != CHDERR_NONE) + throw err; + + // extract the info + int fps, fpsfrac, width, height, interlaced, channels, rate; + if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + throw CHDERR_INVALID_METADATA; + + // compute the bytes per frame + UINT32 fps_times_1million = fps * 1000000 + fpsfrac; + UINT32 max_samples_per_frame = (UINT64(rate) * 1000000 + fps_times_1million - 1) / fps_times_1million; + UINT32 bytes_per_frame = 12 + channels * max_samples_per_frame * 2 + width * height * 2; + if (bytes_per_frame > hunkbytes()) + throw CHDERR_INVALID_METADATA; + + // done with post-init + m_postinit = true; +} + + + +//************************************************************************** +// AVHUFF DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_avhuff_decompressor - constructor +//------------------------------------------------- + +chd_avhuff_decompressor::chd_avhuff_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) + : chd_decompressor(chd, hunkbytes, lossy) +{ +} + + +//------------------------------------------------- +// decompress - decompress data using the A/V +// codec +//------------------------------------------------- + +void chd_avhuff_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + + // decode the audio and video + avhuff_error averr = m_decoder.decode_data(src, complen, dest); + if (averr != AVHERR_NONE) + throw CHDERR_DECOMPRESSION_ERROR; + + // pad short frames with 0 + if (dest != NULL) + { + int size = avhuff_encoder::raw_data_size(dest); + if (size < destlen) + memset(dest + size, 0, destlen - size); + } +} + +//------------------------------------------------- +// config - codec-specific configuration for the +// A/V codec +//------------------------------------------------- + +void chd_avhuff_decompressor::configure(int param, void *config) +{ + // if we're getting the decompression configuration, apply it now + if (param == AVHUFF_CODEC_DECOMPRESS_CONFIG) + m_decoder.configure(*reinterpret_cast(config)); + + // anything else is invalid + else + throw CHDERR_INVALID_PARAMETER; +} +#endif \ No newline at end of file diff --git a/archivers/chd/chdcodec.h b/archivers/chd/chdcodec.h new file mode 100644 index 00000000..a8d6f538 --- /dev/null +++ b/archivers/chd/chdcodec.h @@ -0,0 +1,222 @@ +/*************************************************************************** + + chdcodec.h + + Codecs used by the CHD 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 __CHDCODEC_H__ +#define __CHDCODEC_H__ + +#include "osdcore.h" + + +#define CHDCODEC_VERIFY_COMPRESSION 0 + + +//************************************************************************** +// MACROS +//************************************************************************** + +#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// forward references +class chd_file; + +// base types +typedef UINT32 chd_codec_type; + + +// ======================> chd_codec + +// common base class for all compressors and decompressors +class chd_codec +{ +protected: + // can't create these directly + chd_codec(chd_file &file, UINT32 hunkbytes, bool lossy); + +public: + // allow public deletion + virtual ~chd_codec(); + + // accessors + chd_file &chd() const { return m_chd; } + UINT32 hunkbytes() const { return m_hunkbytes; } + bool lossy() const { return m_lossy; } + + // implementation + virtual void configure(int param, void *config); + +private: + // internal state + chd_file & m_chd; + UINT32 m_hunkbytes; + bool m_lossy; +}; + + +// ======================> chd_compressor + +// base class for all compressors +class chd_compressor : public chd_codec +{ +protected: + // can't create these directly + chd_compressor(chd_file &file, UINT32 hunkbytes, bool lossy); + +public: + // implementation + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) = 0; +}; + + +// ======================> chd_decompressor + +// base class for all decompressors +class chd_decompressor : public chd_codec +{ +protected: + // can't create these directly + chd_decompressor(chd_file &file, UINT32 hunkbytes, bool lossy); + +public: + // implementation + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) = 0; +}; + + +// ======================> chd_codec_list + +// wrapper to get at the list of codecs +class chd_codec_list +{ +public: + // create compressors or decompressors + static chd_compressor *new_compressor(chd_codec_type type, chd_file &file); + static chd_decompressor *new_decompressor(chd_codec_type type, chd_file &file); + + // utilities + static bool codec_exists(chd_codec_type type) { return (find_in_list(type) != NULL); } + static const char *codec_name(chd_codec_type type); + +private: + // an entry in the list + struct codec_entry + { + chd_codec_type m_type; + bool m_lossy; + const char * m_name; + chd_compressor * (*m_construct_compressor)(chd_file &, UINT32, bool); + chd_decompressor * (*m_construct_decompressor)(chd_file &, UINT32, bool); + }; + + // internal helper functions + static const codec_entry *find_in_list(chd_codec_type type); + + template + static chd_compressor *construct_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) { return new _CompressorClass(chd, hunkbytes, lossy); } + + template + static chd_decompressor *construct_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) { return new _DecompressorClass(chd, hunkbytes, lossy); } + + // the static list + static const codec_entry s_codec_list[]; +}; + + +// ======================> chd_compressor_group + +// helper class that wraps several compressors +class chd_compressor_group +{ +public: + // construction/destruction + chd_compressor_group(chd_file &file, chd_codec_type compressor_list[4]); + ~chd_compressor_group(); + + // find the best compressor + INT8 find_best_compressor(const UINT8 *src, UINT8 *compressed, UINT32 &complen); + +private: + // internal state + UINT32 m_hunkbytes; // number of bytes in a hunk + chd_compressor * m_compressor[4]; // array of active codecs + dynamic_buffer m_compress_test; // test buffer for compression +#if CHDCODEC_VERIFY_COMPRESSION + chd_decompressor * m_decompressor[4]; // array of active codecs + dynamic_buffer m_decompressed; // verification buffer +#endif +}; + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// currently-defined codecs +const chd_codec_type CHD_CODEC_NONE = 0; + +// general codecs +const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b'); +const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); +const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); +const chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c'); + +// general codecs with CD frontend +const chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'); +const chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z'); +const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); + +// A/V codecs +const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); + +// A/V codec configuration parameters +enum +{ + AVHUFF_CODEC_DECOMPRESS_CONFIG = 1 +}; + + +#endif // __CHDCODEC_H__ diff --git a/archivers/chd/chdtypes.h b/archivers/chd/chdtypes.h new file mode 100644 index 00000000..cad86c91 --- /dev/null +++ b/archivers/chd/chdtypes.h @@ -0,0 +1,48 @@ + + +#ifndef UAE_CHD +#define UAE_CHD + +#ifndef USE_ZFILE +#include "sysconfig.h" +#include "sysdeps.h" +#include "zfile.h" +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#define DECL_NORETURN __declspec(noreturn) +#else +#define DECL_NORETURN +#endif + +#define INLINE __inline + +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) + +#define CLIB_DECL __cdecl + +#define FLAC__NO_DLL + +/* Macros for normalizing data into big or little endian formats */ +#define FLIPENDIAN_INT16(x) (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff) +#define FLIPENDIAN_INT32(x) ((((UINT32) (x)) << 24) | (((UINT32) (x)) >> 24) | \ + (( ((UINT32) (x)) & 0x0000ff00) << 8) | (( ((UINT32) (x)) & 0x00ff0000) >> 8)) +#define FLIPENDIAN_INT64(x) \ + ( \ + (((((UINT64) (x)) >> 56) & ((UINT64) 0xFF)) << 0) | \ + (((((UINT64) (x)) >> 48) & ((UINT64) 0xFF)) << 8) | \ + (((((UINT64) (x)) >> 40) & ((UINT64) 0xFF)) << 16) | \ + (((((UINT64) (x)) >> 32) & ((UINT64) 0xFF)) << 24) | \ + (((((UINT64) (x)) >> 24) & ((UINT64) 0xFF)) << 32) | \ + (((((UINT64) (x)) >> 16) & ((UINT64) 0xFF)) << 40) | \ + (((((UINT64) (x)) >> 8) & ((UINT64) 0xFF)) << 48) | \ + (((((UINT64) (x)) >> 0) & ((UINT64) 0xFF)) << 56) \ + ) +#endif diff --git a/archivers/chd/corefile.h b/archivers/chd/corefile.h new file mode 100644 index 00000000..e69de29b diff --git a/archivers/chd/coretmpl.h b/archivers/chd/coretmpl.h new file mode 100644 index 00000000..30d181ed --- /dev/null +++ b/archivers/chd/coretmpl.h @@ -0,0 +1,109 @@ +/*************************************************************************** + + coretmpl.h + + Core templates for basic non-string types. + +**************************************************************************** + + 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 __CORETMPL_H__ +#define __CORETMPL_H__ + +#include +#include "osdcore.h" + + +// ======================> dynamic_array + +// an array that is dynamically sized and can optionally auto-expand +template +class dynamic_array +{ +private: + // we don't support deep copying + dynamic_array(const dynamic_array &); + dynamic_array &operator=(const dynamic_array &); + +public: + // construction/destruction + dynamic_array(int initial = 0) + : m_array(NULL), + m_count(0), + m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; } + virtual ~dynamic_array() { reset(); } + + // operators + operator _ElementType *() { return &m_array[0]; } + operator const _ElementType *() const { return &m_array[0]; } + _ElementType &operator[](int index) { assert(index < m_count); return m_array[index]; } + const _ElementType &operator[](int index) const { assert(index < m_count); return m_array[index]; } + + // simple getters + int count() const { return m_count; } + + // helpers + void append(const _ElementType &element) { if (m_count == m_allocated) expand_internal((m_allocated == 0) ? 16 : (m_allocated << 1), true); m_array[m_count++] = element; } + void reset() { delete[] m_array; m_array = NULL; m_count = m_allocated = 0; } + void resize(int count, bool keepdata = false) { if (count > m_allocated) expand_internal(count, keepdata); m_count = count; } + +private: + // internal helpers + void expand_internal(int count, bool keepdata = true) + { + // allocate a new array, copy the old one, and proceed + m_allocated = count; + _ElementType *newarray = new _ElementType[m_allocated]; + if (keepdata) + for (int index = 0; index < m_count; index++) + newarray[index] = m_array[index]; + delete[] m_array; + m_array = newarray; + } + + // internal state + _ElementType * m_array; // allocated array + int m_count; // number of objects accessed in the list + int m_allocated; // amount of space allocated for the array +}; + + +// ======================> dynamic_buffer + +typedef dynamic_array dynamic_buffer; + + + +#endif diff --git a/archivers/chd/flac.cpp b/archivers/chd/flac.cpp new file mode 100644 index 00000000..955a2848 --- /dev/null +++ b/archivers/chd/flac.cpp @@ -0,0 +1,648 @@ +#include "chdtypes.h" + +/*************************************************************************** + + flac.c + + FLAC compression wrappers + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "flac.h" +#include +#include + + +//************************************************************************** +// FLAC ENCODER +//************************************************************************** + +//------------------------------------------------- +// flac_encoder - constructors +//------------------------------------------------- + +flac_encoder::flac_encoder() +{ + init_common(); +} + + +flac_encoder::flac_encoder(void *buffer, UINT32 buflength) +{ + init_common(); + reset(buffer, buflength); +} + + +flac_encoder::flac_encoder(struct zfile *file) +{ + init_common(); + reset(file); +} + + +//------------------------------------------------- +// ~flac_encoder - destructor +//------------------------------------------------- + +flac_encoder::~flac_encoder() +{ + // delete the encoder + FLAC__stream_encoder_delete(m_encoder); +} + + +//------------------------------------------------- +// reset - reset state with the original +// parameters +//------------------------------------------------- + +bool flac_encoder::reset() +{ + // configure the output + m_compressed_offset = 0; + m_ignore_bytes = m_strip_metadata ? 4 : 0; + m_found_audio = !m_strip_metadata; + + // configure the encoder in a standard way + // note we do this on each reset; if we don't, results are NOT consistent! + FLAC__stream_encoder_set_verify(m_encoder, false); +// FLAC__stream_encoder_set_do_md5(m_encoder, false); + FLAC__stream_encoder_set_compression_level(m_encoder, 8); + FLAC__stream_encoder_set_channels(m_encoder, m_channels); + FLAC__stream_encoder_set_bits_per_sample(m_encoder, 16); + FLAC__stream_encoder_set_sample_rate(m_encoder, m_sample_rate); + FLAC__stream_encoder_set_total_samples_estimate(m_encoder, 0); + FLAC__stream_encoder_set_streamable_subset(m_encoder, false); + FLAC__stream_encoder_set_blocksize(m_encoder, m_block_size); + + // re-start processing + return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, NULL, NULL, NULL, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK); +} + + +//------------------------------------------------- +// reset - reset state with new memory parameters +//------------------------------------------------- + +bool flac_encoder::reset(void *buffer, UINT32 buflength) +{ + // configure the output + m_compressed_start = reinterpret_cast(buffer); + m_compressed_length = buflength; + m_file = NULL; + return reset(); +} + + +//------------------------------------------------- +// reset - reset state with new file parameters +//------------------------------------------------- + +bool flac_encoder::reset(struct zfile *file) +{ + // configure the output + m_compressed_start = NULL; + m_compressed_length = 0; + m_file = file; + return reset(); +} + + +//------------------------------------------------- +// encode_interleaved - encode a buffer with +// interleaved samples +//------------------------------------------------- + +bool flac_encoder::encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian) +{ + int shift = swap_endian ? 8 : 0; + + // loop over source samples + int num_channels = FLAC__stream_encoder_get_channels(m_encoder); + UINT32 srcindex = 0; + while (samples_per_channel != 0) + { + // process in batches of 2k samples + FLAC__int32 converted_buffer[2048]; + FLAC__int32 *dest = converted_buffer; + UINT32 cur_samples = MIN(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel); + + // convert a buffers' worth + for (UINT32 sampnum = 0; sampnum < cur_samples; sampnum++) + for (int channel = 0; channel < num_channels; channel++, srcindex++) + *dest++ = INT16((UINT16(samples[srcindex]) << shift) | (UINT16(samples[srcindex]) >> shift)); + + // process this batch + if (!FLAC__stream_encoder_process_interleaved(m_encoder, converted_buffer, cur_samples)) + return false; + samples_per_channel -= cur_samples; + } + return true; +} + + +//------------------------------------------------- +// encode - encode a buffer with individual +// sample streams +//------------------------------------------------- + +bool flac_encoder::encode(INT16 *const *samples, UINT32 samples_per_channel, bool swap_endian) +{ + int shift = swap_endian ? 8 : 0; + + // loop over source samples + int num_channels = FLAC__stream_encoder_get_channels(m_encoder); + UINT32 srcindex = 0; + while (samples_per_channel != 0) + { + // process in batches of 2k samples + FLAC__int32 converted_buffer[2048]; + FLAC__int32 *dest = converted_buffer; + UINT32 cur_samples = MIN(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel); + + // convert a buffers' worth + for (UINT32 sampnum = 0; sampnum < cur_samples; sampnum++, srcindex++) + for (int channel = 0; channel < num_channels; channel++) + *dest++ = INT16((UINT16(samples[channel][srcindex]) << shift) | (UINT16(samples[channel][srcindex]) >> shift)); + + // process this batch + if (!FLAC__stream_encoder_process_interleaved(m_encoder, converted_buffer, cur_samples)) + return false; + samples_per_channel -= cur_samples; + } + return true; +} + + +//------------------------------------------------- +// finish - complete encoding and flush the +// stream +//------------------------------------------------- + +UINT32 flac_encoder::finish() +{ + // process the data and return the amount written + FLAC__stream_encoder_finish(m_encoder); + return (m_file != NULL) ? zfile_ftell(m_file) : m_compressed_offset; +} + + +//------------------------------------------------- +// init_common - common initialization +//------------------------------------------------- + +void flac_encoder::init_common() +{ + // allocate the encoder + m_encoder = FLAC__stream_encoder_new(); + if (m_encoder == NULL) + throw std::bad_alloc(); + + // initialize default state + m_file = NULL; + m_compressed_offset = 0; + m_compressed_start = NULL; + m_compressed_length = 0; + m_sample_rate = 44100; + m_channels = 2; + m_block_size = 0; + m_strip_metadata = false; + m_ignore_bytes = 0; + m_found_audio = false; +} + + +//------------------------------------------------- +// write_callback - handle writes to the +// output stream +//------------------------------------------------- + +FLAC__StreamEncoderWriteStatus flac_encoder::write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + return reinterpret_cast(client_data)->write_callback(buffer, bytes, samples, current_frame); +} + +FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame) +{ + // loop over output data + size_t offset = 0; + while (offset < bytes) + { + // if we're ignoring, continue to do so + if (m_ignore_bytes != 0) + { + int ignore = MIN(bytes - offset, m_ignore_bytes); + offset += ignore; + m_ignore_bytes -= ignore; + } + + // if we haven't hit the end of metadata, process a new piece + else if (!m_found_audio) + { + assert(bytes - offset >= 4); + m_found_audio = ((buffer[offset] & 0x80) != 0); + m_ignore_bytes = (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3]; + offset += 4; + } + + // otherwise process as audio data and copy to the output + else + { + int count = bytes - offset; + if (m_file != NULL) + zfile_fwrite(buffer, count, 1, m_file); + else + { + if (m_compressed_offset + count <= m_compressed_length) + memcpy(m_compressed_start + m_compressed_offset, buffer, count); + m_compressed_offset += count; + } + offset += count; + } + } + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + + + +//************************************************************************** +// FLAC DECODER +//************************************************************************** + +//------------------------------------------------- +// flac_decoder - constructor +//------------------------------------------------- + +flac_decoder::flac_decoder() + : m_decoder(FLAC__stream_decoder_new()), + m_file(NULL), + m_compressed_offset(0), + m_compressed_start(NULL), + m_compressed_length(0), + m_compressed2_start(NULL), + m_compressed2_length(0) +{ +} + + +//------------------------------------------------- +// flac_decoder - constructor +//------------------------------------------------- + +flac_decoder::flac_decoder(const void *buffer, UINT32 length, const void *buffer2, UINT32 length2) + : m_decoder(FLAC__stream_decoder_new()), + m_file(NULL), + m_compressed_offset(0), + m_compressed_start(reinterpret_cast(buffer)), + m_compressed_length(length), + m_compressed2_start(reinterpret_cast(buffer2)), + m_compressed2_length(length2) +{ + reset(); +} + + +//------------------------------------------------- +// flac_decoder - constructor +//------------------------------------------------- + +flac_decoder::flac_decoder(struct zfile *file) + : m_decoder(FLAC__stream_decoder_new()), + m_file(file), + m_compressed_offset(0), + m_compressed_start(NULL), + m_compressed_length(0), + m_compressed2_start(NULL), + m_compressed2_length(0) +{ + reset(); +} + + +//------------------------------------------------- +// flac_decoder - destructor +//------------------------------------------------- + +flac_decoder::~flac_decoder() +{ + FLAC__stream_decoder_delete(m_decoder); +} + + +//------------------------------------------------- +// reset - reset state with the original +// parameters +//------------------------------------------------- + +bool flac_decoder::reset() +{ + m_compressed_offset = 0; + if (FLAC__stream_decoder_init_stream(m_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, this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return false; + return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder) != 0; +} + + +//------------------------------------------------- +// reset - reset state with new memory parameters +//------------------------------------------------- + +bool flac_decoder::reset(const void *buffer, UINT32 length, const void *buffer2, UINT32 length2) +{ + m_file = NULL; + m_compressed_start = reinterpret_cast(buffer); + m_compressed_length = length; + m_compressed2_start = reinterpret_cast(buffer2); + m_compressed2_length = length2; + return reset(); +} + + +//------------------------------------------------- +// reset - reset state with new memory parameters +// and a custom-generated header +//------------------------------------------------- + +bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length) +{ + // modify the template header with our parameters + static const UINT8 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(m_custom_header, s_header_template, sizeof(s_header_template)); + m_custom_header[0x08] = m_custom_header[0x0a] = block_size >> 8; + m_custom_header[0x09] = m_custom_header[0x0b] = block_size & 0xff; + m_custom_header[0x12] = sample_rate >> 12; + m_custom_header[0x13] = sample_rate >> 4; + m_custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1); + + // configure the header ahead of the provided buffer + m_file = NULL; + m_compressed_start = reinterpret_cast(m_custom_header); + m_compressed_length = sizeof(m_custom_header); + m_compressed2_start = reinterpret_cast(buffer); + m_compressed2_length = length; + return reset(); +} + + +//------------------------------------------------- +// reset - reset state with new file parameter +//------------------------------------------------- + +bool flac_decoder::reset(struct zfile *file) +{ + m_file = file; + m_compressed_start = NULL; + m_compressed_length = 0; + m_compressed2_start = NULL; + m_compressed2_length = 0; + return reset(); +} + + +//------------------------------------------------- +// decode_interleaved - decode to an interleaved +// sound stream +//------------------------------------------------- + +bool flac_decoder::decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian) +{ + // configure the uncompressed buffer + memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start)); + m_uncompressed_start[0] = samples; + 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; +} + + +//------------------------------------------------- +// decode - decode to an multiple independent +// data streams +//------------------------------------------------- + +bool flac_decoder::decode(INT16 **samples, UINT32 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 flac_decoder::finish() +{ + // get the final decoding position and move forward + FLAC__uint64 position = 0; + FLAC__stream_decoder_get_decode_position(m_decoder, &position); + FLAC__stream_decoder_finish(m_decoder); + + // adjust position if we provided the header + if (position == 0) + return 0; + if (m_compressed_start == reinterpret_cast(m_custom_header)) + position -= m_compressed_length; + return position; +} + + +//------------------------------------------------- +// read_callback - handle reads from the input +// stream +//------------------------------------------------- + +FLAC__StreamDecoderReadStatus flac_decoder::read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + return reinterpret_cast(client_data)->read_callback(buffer, bytes); +} + +FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], size_t *bytes) +{ + UINT32 expected = *bytes; + + // if a file, just read + if (m_file != NULL) + *bytes = zfile_fread(buffer, 1, expected, m_file); + + // otherwise, copy from memory + else + { + // copy from primary buffer first + UINT32 outputpos = 0; + if (outputpos < *bytes && m_compressed_offset < m_compressed_length) + { + UINT32 bytes_to_copy = MIN(*bytes - outputpos, m_compressed_length - m_compressed_offset); + memcpy(&buffer[outputpos], m_compressed_start + m_compressed_offset, bytes_to_copy); + outputpos += bytes_to_copy; + m_compressed_offset += bytes_to_copy; + } + + // once we're out of that, copy from the secondary buffer + if (outputpos < *bytes && m_compressed_offset < m_compressed_length + m_compressed2_length) + { + UINT32 bytes_to_copy = MIN(*bytes - outputpos, m_compressed2_length - (m_compressed_offset - m_compressed_length)); + memcpy(&buffer[outputpos], m_compressed2_start + m_compressed_offset - m_compressed_length, bytes_to_copy); + outputpos += bytes_to_copy; + m_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) +{ + // ignore all but STREAMINFO metadata + if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO) + return; + + // parse out the data we care about + flac_decoder *fldecoder = reinterpret_cast(client_data); + fldecoder->m_sample_rate = metadata->data.stream_info.sample_rate; + fldecoder->m_bits_per_sample = metadata->data.stream_info.bits_per_sample; + fldecoder->m_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 = reinterpret_cast(client_data)->m_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 reinterpret_cast(client_data)->write_callback(frame, buffer); +} + +FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + assert(frame->header.channels == channels()); + + // interleaved case + int shift = m_uncompressed_swap ? 8 : 0; + int blocksize = frame->header.blocksize; + if (m_uncompressed_start[1] == NULL) + { + INT16 *dest = m_uncompressed_start[0] + m_uncompressed_offset * frame->header.channels; + for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++) + for (int chan = 0; chan < frame->header.channels; chan++) + *dest++ = INT16((UINT16(buffer[chan][sampnum]) << shift) | (UINT16(buffer[chan][sampnum]) >> shift)); + } + + // non-interleaved case + else + { + for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++) + for (int chan = 0; chan < frame->header.channels; chan++) + if (m_uncompressed_start[chan] != NULL) + m_uncompressed_start[chan][m_uncompressed_offset] = INT16((UINT16(buffer[chan][sampnum]) << shift) | (UINT16(buffer[chan][sampnum]) >> shift)); + } + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + + +//------------------------------------------------- +// error_callback - handle errors (ignore them) +//------------------------------------------------- + +void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ +} diff --git a/archivers/chd/flac.h b/archivers/chd/flac.h new file mode 100644 index 00000000..3f2cfe3a --- /dev/null +++ b/archivers/chd/flac.h @@ -0,0 +1,178 @@ +/*************************************************************************** + + flac.h + + FLAC compression wrappers + +**************************************************************************** + + 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 __FLAC_H__ +#define __FLAC_H__ + +#include "osdcore.h" +#include "corefile.h" + +#ifdef FLAC__NO_DLL +#include "flac/all.h" +#else +#include +#endif + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> flac_encoder + +class flac_encoder +{ +public: + // construction/destruction + flac_encoder(); + flac_encoder(void *buffer, UINT32 buflength); + flac_encoder(struct zfile *file); + ~flac_encoder(); + + // configuration + void set_sample_rate(UINT32 sample_rate) { m_sample_rate = sample_rate; } + void set_num_channels(UINT8 num_channels) { m_channels = num_channels; } + void set_block_size(UINT32 block_size) { m_block_size = block_size; } + void set_strip_metadata(bool strip) { m_strip_metadata = strip; } + + // getters (valid after reset) + FLAC__StreamEncoderState state() const { return FLAC__stream_encoder_get_state(m_encoder); } + const char *state_string() const { return FLAC__stream_encoder_get_resolved_state_string(m_encoder); } + + // reset + bool reset(); + bool reset(void *buffer, UINT32 buflength); + bool reset(struct zfile *file); + + // encode a buffer + bool encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian = false); + bool encode(INT16 *const *samples, UINT32 samples_per_channel, bool swap_endian = false); + + // finish up + UINT32 finish(); + +private: + // internal helpers + void init_common(); + static FLAC__StreamEncoderWriteStatus write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame); + + // internal state + FLAC__StreamEncoder * m_encoder; // actual encoder + struct zfile * m_file; // output file + UINT32 m_compressed_offset; // current offset with the compressed stream + FLAC__byte * m_compressed_start; // start of compressed data + UINT32 m_compressed_length; // length of the compressed stream + + // parameters + UINT32 m_sample_rate; // sample rate + UINT8 m_channels; // number of channels + UINT32 m_block_size; // block size + + // header stripping + bool m_strip_metadata; // strip the the metadata? + UINT32 m_ignore_bytes; // how many bytes to ignore when writing + bool m_found_audio; // have we hit the audio yet? +}; + + +// ======================> flac_decoder + +class flac_decoder +{ +public: + // construction/destruction + flac_decoder(); + flac_decoder(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0); + flac_decoder(struct zfile *file); + ~flac_decoder(); + + // getters (valid after reset) + UINT32 sample_rate() const { return m_sample_rate; } + UINT8 channels() const { return m_channels; } + UINT8 bits_per_sample() const { return m_bits_per_sample; } + UINT32 total_samples() const { return FLAC__stream_decoder_get_total_samples(m_decoder); } + FLAC__StreamDecoderState state() const { return FLAC__stream_decoder_get_state(m_decoder); } + const char *state_string() const { return FLAC__stream_decoder_get_resolved_state_string(m_decoder); } + + // reset + bool reset(); + bool reset(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0); + bool reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length); + bool reset(struct zfile *file); + + // decode to a buffer; num_samples must be a multiple of the block size + bool decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian = false); + bool decode(INT16 **samples, UINT32 num_samples, bool swap_endian = false); + + // finish up + UINT32 finish(); + +private: + // internal helpers + static FLAC__StreamDecoderReadStatus read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes); + static void metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + static FLAC__StreamDecoderTellStatus tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]); + static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + // output state + FLAC__StreamDecoder * m_decoder; // actual encoder + struct zfile * m_file; // output file + UINT32 m_sample_rate; // decoded sample rate + UINT8 m_channels; // decoded number of channels + UINT8 m_bits_per_sample; // decoded bits per sample + UINT32 m_compressed_offset; // current offset in compressed data + const FLAC__byte * m_compressed_start; // start of compressed data + UINT32 m_compressed_length; // length of compressed data + const FLAC__byte * m_compressed2_start; // start of compressed data + UINT32 m_compressed2_length; // length of compressed data + INT16 * m_uncompressed_start[8];// pointer to start of uncompressed data (up to 8 streams) + UINT32 m_uncompressed_offset; // current position in uncompressed data + UINT32 m_uncompressed_length; // length of uncompressed data + bool m_uncompressed_swap; // swap uncompressed sample data + UINT8 m_custom_header[0x2a]; // custom header +}; + + +#endif // __FLAC_H__ diff --git a/archivers/chd/hashing.cpp b/archivers/chd/hashing.cpp new file mode 100644 index 00000000..0dff0d68 --- /dev/null +++ b/archivers/chd/hashing.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + + hashing.c + + Hashing helper classes. + +**************************************************************************** + + 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. + +***************************************************************************/ + +#include "chdtypes.h" + +#include "hashing.h" +#include "zlib.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +const crc16_t crc16_t::null = { 0 }; +const crc32_t crc32_t::null = { 0 }; +const md5_t md5_t::null = { { 0 } }; +const sha1_t sha1_t::null = { { 0 } }; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// char_to_hex - return the hex value of a +// character +//------------------------------------------------- + +inline int char_to_hex(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + return -1; +} + + + +//************************************************************************** +// SHA-1 HELPERS +//************************************************************************** + +//------------------------------------------------- +// from_string - convert from a string +//------------------------------------------------- + +bool sha1_t::from_string(const char *string, int length) +{ + // must be at least long enough to hold everything + memset(m_raw, 0, sizeof(m_raw)); + if (length == -1) + length = strlen(string); + if (length < 2 * sizeof(m_raw)) + return false; + + // iterate through our raw buffer + for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++) + { + int upper = char_to_hex(*string++); + int lower = char_to_hex(*string++); + if (upper == -1 || lower == -1) + return false; + m_raw[bytenum] = (upper << 4) | lower; + } + return true; +} + + +//------------------------------------------------- +// as_string - convert to a string +//------------------------------------------------- + +const char *sha1_t::as_string(astring &buffer) const +{ + buffer.reset(); + for (int i = 0; i < ARRAY_LENGTH(m_raw); i++) + buffer.catformat("%02x", m_raw[i]); + return buffer; +} + + +//************************************************************************** +// MD-5 HELPERS +//************************************************************************** + +//------------------------------------------------- +// from_string - convert from a string +//------------------------------------------------- + +bool md5_t::from_string(const char *string, int length) +{ + // must be at least long enough to hold everything + memset(m_raw, 0, sizeof(m_raw)); + if (length == -1) + length = strlen(string); + if (length < 2 * sizeof(m_raw)) + return false; + + // iterate through our raw buffer + for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++) + { + int upper = char_to_hex(*string++); + int lower = char_to_hex(*string++); + if (upper == -1 || lower == -1) + return false; + m_raw[bytenum] = (upper << 4) | lower; + } + return true; +} + + +//------------------------------------------------- +// as_string - convert to a string +//------------------------------------------------- + +const char *md5_t::as_string(astring &buffer) const +{ + buffer.reset(); + for (int i = 0; i < ARRAY_LENGTH(m_raw); i++) + buffer.catformat("%02x", m_raw[i]); + return buffer; +} + + + +//************************************************************************** +// CRC-32 HELPERS +//************************************************************************** + +//------------------------------------------------- +// from_string - convert from a string +//------------------------------------------------- + +bool crc32_t::from_string(const char *string, int length) +{ + // must be at least long enough to hold everything + m_raw = 0; + if (length == -1) + length = strlen(string); + if (length < 2 * sizeof(m_raw)) + return false; + + // iterate through our raw buffer + m_raw = 0; + for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++) + { + int nibble = char_to_hex(*string++); + if (nibble == -1) + return false; + m_raw = (m_raw << 4) | nibble; + } + return true; +} + + +//------------------------------------------------- +// as_string - convert to a string +//------------------------------------------------- + +const char *crc32_t::as_string(astring &buffer) const +{ + return buffer.format("%08x", m_raw); +} + + +//------------------------------------------------- +// append - hash a block of data, appending to +// the currently-accumulated value +//------------------------------------------------- + +void crc32_creator::append(const void *data, UINT32 length) +{ + m_accum.m_raw = crc32(m_accum, reinterpret_cast(data), length); +} + + + +//************************************************************************** +// CRC-16 HELPERS +//************************************************************************** + +//------------------------------------------------- +// from_string - convert from a string +//------------------------------------------------- + +bool crc16_t::from_string(const char *string, int length) +{ + // must be at least long enough to hold everything + m_raw = 0; + if (length == -1) + length = strlen(string); + if (length < 2 * sizeof(m_raw)) + return false; + + // iterate through our raw buffer + m_raw = 0; + for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++) + { + int nibble = char_to_hex(*string++); + if (nibble == -1) + return false; + m_raw = (m_raw << 4) | nibble; + } + return true; +} + + +//------------------------------------------------- +// as_string - convert to a string +//------------------------------------------------- + +const char *crc16_t::as_string(astring &buffer) const +{ + return buffer.format("%04x", m_raw); +} + + +//------------------------------------------------- +// append - hash a block of data, appending to +// the currently-accumulated value +//------------------------------------------------- + +void crc16_creator::append(const void *data, UINT32 length) +{ + static const UINT16 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 *src = reinterpret_cast(data); + + // fetch the current value into a local and rip through the source data + UINT16 crc = m_accum.m_raw; + while (length-- != 0) + crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++]; + m_accum.m_raw = crc; +} \ No newline at end of file diff --git a/archivers/chd/hashing.h b/archivers/chd/hashing.h new file mode 100644 index 00000000..d98b617b --- /dev/null +++ b/archivers/chd/hashing.h @@ -0,0 +1,247 @@ +/*************************************************************************** + + hashing.h + + Hashing helper classes. + +**************************************************************************** + + 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 __HASHING_H__ +#define __HASHING_H__ + +#include "astring.h" +#include "md5.h" +#include "sha1.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> SHA-1 + +// final digest +struct sha1_t +{ + bool operator==(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; } + bool operator!=(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; } + operator UINT8 *() { return m_raw; } + bool from_string(const char *string, int length = -1); + const char *as_string(astring &buffer) const; + UINT8 m_raw[20]; + static const sha1_t null; +}; + +// creation helper +class sha1_creator +{ +public: + // construction/destruction + sha1_creator() { reset(); } + + // reset + void reset() { sha1_init(&m_context); } + + // append data + void append(const void *data, UINT32 length) { sha1_update(&m_context, length, reinterpret_cast(data)); } + + // finalize and compute the final digest + sha1_t finish() + { + sha1_t result; + sha1_final(&m_context); + sha1_digest(&m_context, sizeof(result.m_raw), result.m_raw); + return result; + } + + // static wrapper to just get the digest from a block + static sha1_t simple(const void *data, UINT32 length) + { + sha1_creator creator; + creator.append(data, length); + return creator.finish(); + } + +protected: + // internal state + struct sha1_ctx m_context; // internal context +}; + + + +// ======================> MD5 + +// final digest +struct md5_t +{ + bool operator==(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; } + bool operator!=(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; } + operator UINT8 *() { return m_raw; } + bool from_string(const char *string, int length = -1); + const char *as_string(astring &buffer) const; + UINT8 m_raw[16]; + static const md5_t null; +}; + +// creation helper +class md5_creator +{ +public: + // construction/destruction + md5_creator() { reset(); } + + // reset + void reset() { MD5Init(&m_context); } + + // append data + void append(const void *data, UINT32 length) { MD5Update(&m_context, reinterpret_cast(data), length); } + + // finalize and compute the final digest + md5_t finish() + { + md5_t result; + MD5Final(result.m_raw, &m_context); + return result; + } + + // static wrapper to just get the digest from a block + static md5_t simple(const void *data, UINT32 length) + { + md5_creator creator; + creator.append(data, length); + return creator.finish(); + } + +protected: + // internal state + struct MD5Context m_context; // internal context +}; + + + +// ======================> CRC-32 + +// final digest +struct crc32_t +{ + bool operator==(const crc32_t &rhs) const { return m_raw == rhs.m_raw; } + bool operator!=(const crc32_t &rhs) const { return m_raw != rhs.m_raw; } + crc32_t &operator=(const UINT32 crc) { m_raw = crc; return *this; } + operator UINT32() const { return m_raw; } + bool from_string(const char *string, int length = -1); + const char *as_string(astring &buffer) const; + UINT32 m_raw; + static const crc32_t null; +}; + +// creation helper +class crc32_creator +{ +public: + // construction/destruction + crc32_creator() { reset(); } + + // reset + void reset() { m_accum.m_raw = 0; } + + // append data + void append(const void *data, UINT32 length); + + // finalize and compute the final digest + crc32_t finish() { return m_accum; } + + // static wrapper to just get the digest from a block + static crc32_t simple(const void *data, UINT32 length) + { + crc32_creator creator; + creator.append(data, length); + return creator.finish(); + } + +protected: + // internal state + crc32_t m_accum; // internal accumulator +}; + + + +// ======================> CRC-16 + +// final digest +struct crc16_t +{ + bool operator==(const crc16_t &rhs) const { return m_raw == rhs.m_raw; } + bool operator!=(const crc16_t &rhs) const { return m_raw != rhs.m_raw; } + crc16_t &operator=(const UINT16 crc) { m_raw = crc; return *this; } + operator UINT16() const { return m_raw; } + bool from_string(const char *string, int length = -1); + const char *as_string(astring &buffer) const; + UINT16 m_raw; + static const crc16_t null; +}; + +// creation helper +class crc16_creator +{ +public: + // construction/destruction + crc16_creator() { reset(); } + + // reset + void reset() { m_accum.m_raw = 0xffff; } + + // append data + void append(const void *data, UINT32 length); + + // finalize and compute the final digest + crc16_t finish() { return m_accum; } + + // static wrapper to just get the digest from a block + static crc16_t simple(const void *data, UINT32 length) + { + crc16_creator creator; + creator.append(data, length); + return creator.finish(); + } + +protected: + // internal state + crc16_t m_accum; // internal accumulator +}; + + +#endif // __HASHING_H__ diff --git a/archivers/chd/huffman.cpp b/archivers/chd/huffman.cpp new file mode 100644 index 00000000..ff232d11 --- /dev/null +++ b/archivers/chd/huffman.cpp @@ -0,0 +1,782 @@ + +#include "chdtypes.h" + +/*************************************************************************** + + huffman.c + + Static Huffman compression and decompression helpers. + +**************************************************************************** + + 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. + +**************************************************************************** + + 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 + +#include "coretmpl.h" +#include "huffman.h" + + + +//************************************************************************** +// MACROS +//************************************************************************** + +#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f)) + + + +//************************************************************************** +// IMPLEMENTATION +//************************************************************************** + +//------------------------------------------------- +// huffman_context_base - create an encoding/ +// decoding context +//------------------------------------------------- + +huffman_context_base::huffman_context_base(int numcodes, int maxbits, lookup_value *lookup, UINT32 *histo, node_t *nodes) + : m_numcodes(numcodes), + m_maxbits(maxbits), + m_prevdata(0), + m_rleremaining(0), + m_lookup(lookup), + m_datahisto(histo), + m_huffnode(nodes) +{ + // limit to 24 bits + if (maxbits > 24) + throw HUFFERR_TOO_MANY_BITS; +} + + +//------------------------------------------------- +// import_tree_rle - import an RLE-encoded +// huffman tree from a source data stream +//------------------------------------------------- + +huffman_error huffman_context_base::import_tree_rle(bitstream_in &bitbuf) +{ + // bits per entry depends on the maxbits + int numbits; + if (m_maxbits >= 16) + numbits = 5; + else if (m_maxbits >= 8) + numbits = 4; + else + numbits = 3; + + // loop until we read all the nodes + int curnode; + for (curnode = 0; curnode < m_numcodes; ) + { + // a non-one value is just raw + int nodebits = bitbuf.read(numbits); + if (nodebits != 1) + m_huffnode[curnode++].m_numbits = nodebits; + + // a one value is an escape code + else + { + // a double 1 is just a single 1 + nodebits = bitbuf.read(numbits); + if (nodebits == 1) + m_huffnode[curnode++].m_numbits = nodebits; + + // otherwise, we need one for value for the repeat count + else + { + int repcount = bitbuf.read(numbits) + 3; + while (repcount--) + m_huffnode[curnode++].m_numbits = nodebits; + } + } + } + + // make sure we ended up with the right number + if (curnode != m_numcodes) + return HUFFERR_INVALID_DATA; + + // assign canonical codes for all nodes based on their code lengths + huffman_error error = assign_canonical_codes(); + if (error != HUFFERR_NONE) + return error; + + // build the lookup table + build_lookup_table(); + + // determine final input length and report errors + return bitbuf.overflow() ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +//------------------------------------------------- +// export_tree_rle - export a huffman tree to an +// RLE target data stream +//------------------------------------------------- + +huffman_error huffman_context_base::export_tree_rle(bitstream_out &bitbuf) +{ + // bits per entry depends on the maxbits + int numbits; + if (m_maxbits >= 16) + numbits = 5; + else if (m_maxbits >= 8) + numbits = 4; + else + numbits = 3; + + // RLE encode the lengths + int lastval = ~0; + int repcount = 0; + for (int curcode = 0; curcode < m_numcodes; curcode++) + { + // if we match the previous value, just bump the repcount + int newval = m_huffnode[curcode].m_numbits; + if (newval == lastval) + repcount++; + + // otherwise, we need to flush the previous repeats + else + { + if (repcount != 0) + write_rle_tree_bits(bitbuf, lastval, repcount, numbits); + lastval = newval; + repcount = 1; + } + } + + // flush the last value + write_rle_tree_bits(bitbuf, lastval, repcount, numbits); + return bitbuf.overflow() ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +//------------------------------------------------- +// import_tree_huffman - import a huffman-encoded +// huffman tree from a source data stream +//------------------------------------------------- + +huffman_error huffman_context_base::import_tree_huffman(bitstream_in &bitbuf) +{ + // start by parsing the lengths for the small tree + huffman_decoder<24, 6> smallhuff; + smallhuff.m_huffnode[0].m_numbits = bitbuf.read(3); + int start = bitbuf.read(3) + 1; + int count = 0; + for (int index = 1; index < 24; index++) + { + if (index < start || count == 7) + smallhuff.m_huffnode[index].m_numbits = 0; + else + { + count = bitbuf.read(3); + smallhuff.m_huffnode[index].m_numbits = (count == 7) ? 0 : count; + } + } + + // then regenerate the tree + huffman_error error = smallhuff.assign_canonical_codes(); + if (error != HUFFERR_NONE) + return error; + smallhuff.build_lookup_table(); + + // determine the maximum length of an RLE count + UINT32 temp = m_numcodes - 9; + UINT8 rlefullbits = 0; + while (temp != 0) + temp >>= 1, rlefullbits++; + + // now process the rest of the data + int last = 0; + int curcode; + for (curcode = 0; curcode < m_numcodes; ) + { + int value = smallhuff.decode_one(bitbuf); + if (value != 0) + m_huffnode[curcode++].m_numbits = last = value - 1; + else + { + int count = bitbuf.read(3) + 2; + if (count == 7+2) + count += bitbuf.read(rlefullbits); + for ( ; count != 0 && curcode < m_numcodes; count--) + m_huffnode[curcode++].m_numbits = last; + } + } + + // make sure we ended up with the right number + if (curcode != m_numcodes) + return HUFFERR_INVALID_DATA; + + // assign canonical codes for all nodes based on their code lengths + error = assign_canonical_codes(); + if (error != HUFFERR_NONE) + return error; + + // build the lookup table + build_lookup_table(); + + // determine final input length and report errors + return bitbuf.overflow() ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +//------------------------------------------------- +// export_tree_huffman - export a huffman tree to +// a huffman target data stream +//------------------------------------------------- + +huffman_error huffman_context_base::export_tree_huffman(bitstream_out &bitbuf) +{ + // first RLE compress the lengths of all the nodes + dynamic_array rle_data(m_numcodes); + UINT8 *dest = rle_data; + dynamic_array rle_lengths(m_numcodes/3); + UINT16 *lengths = rle_lengths; + int last = ~0; + int repcount = 0; + + // use a small huffman context to create a tree (ignoring RLE lengths) + huffman_encoder<24, 6> smallhuff; + + // RLE-compress the lengths + for (int curcode = 0; curcode < m_numcodes; curcode++) + { + // if this is the end of a repeat, flush any accumulation + int newval = m_huffnode[curcode].m_numbits; + if (newval != last && repcount > 0) + { + if (repcount == 1) + smallhuff.histo_one(*dest++ = last + 1); + else + smallhuff.histo_one(*dest++ = 0), *lengths++ = repcount - 2; + } + + // if same as last, just track repeats + if (newval == last) + repcount++; + + // otherwise, write it and start a new run + else + { + smallhuff.histo_one(*dest++ = newval + 1); + last = newval; + repcount = 0; + } + } + + // flush any final RLE counts + if (repcount > 0) + { + if (repcount == 1) + smallhuff.histo_one(*dest++ = last + 1); + else + smallhuff.histo_one(*dest++ = 0), *lengths++ = repcount - 2; + } + + // compute an optimal tree + smallhuff.compute_tree_from_histo(); + + // determine the first and last non-zero nodes + int first_non_zero = 31, last_non_zero = 0; + for (int index = 1; index < smallhuff.m_numcodes; index++) + if (smallhuff.m_huffnode[index].m_numbits != 0) + { + if (first_non_zero == 31) + first_non_zero = index; + last_non_zero = index; + } + + // clamp first non-zero to be 8 at a maximum + first_non_zero = MIN(first_non_zero, 8); + + // output the lengths of the each small tree node, starting with the RLE + // token (0), followed by the first_non_zero value, followed by the data + // terminated by a 7 + bitbuf.write(smallhuff.m_huffnode[0].m_numbits, 3); + bitbuf.write(first_non_zero - 1, 3); + for (int index = first_non_zero; index <= last_non_zero; index++) + bitbuf.write(smallhuff.m_huffnode[index].m_numbits, 3); + bitbuf.write(7, 3); + + // determine the maximum length of an RLE count + UINT32 temp = m_numcodes - 9; + UINT8 rlefullbits = 0; + while (temp != 0) + temp >>= 1, rlefullbits++; + + // now encode the RLE data + lengths = rle_lengths; + for (UINT8 *src = rle_data; src < dest; src++) + { + // encode the data + UINT8 data = *src; + smallhuff.encode_one(bitbuf, data); + + // if this is an RLE token, encode the length following + if (data == 0) + { + int count = *lengths++; + if (count < 7) + bitbuf.write(count, 3); + else + bitbuf.write(7, 3), bitbuf.write(count - 7, rlefullbits); + } + } + + // flush the final buffer + return bitbuf.overflow() ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +//------------------------------------------------- +// compute_tree_from_histo - common backend for +// computing a tree based on the data histogram +//------------------------------------------------- + +huffman_error huffman_context_base::compute_tree_from_histo() +{ + // compute the number of data items in the histogram + UINT32 sdatacount = 0; + for (int i = 0; i < m_numcodes; i++) + sdatacount += m_datahisto[i]; + + // binary search to achieve the optimum encoding + UINT32 lowerweight = 0; + UINT32 upperweight = sdatacount * 2; + while (1) + { + // build a tree using the current weight + UINT32 curweight = (upperweight + lowerweight) / 2; + int curmaxbits = build_tree(sdatacount, curweight); + + // apply binary search here + if (curmaxbits <= m_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 assign_canonical_codes(); +} + + + +//************************************************************************** +// INTERNAL FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// write_rle_tree_bits - write an RLE encoded +// set of data to a target stream +//------------------------------------------------- + +void huffman_context_base::write_rle_tree_bits(bitstream_out &bitbuf, int value, int repcount, int numbits) +{ + // loop until we have output all of the repeats + while (repcount > 0) + { + // if we have a 1, write it twice as it is an escape code + if (value == 1) + { + bitbuf.write(1, numbits); + bitbuf.write(1, numbits); + repcount--; + } + + // if we have two or fewer in a row, write them raw + else if (repcount <= 2) + { + bitbuf.write(value, numbits); + repcount--; + } + + // otherwise, write a triple using 1 as the escape code + else + { + int cur_reps = MIN(repcount - 3, (1 << numbits) - 1); + bitbuf.write(1, numbits); + bitbuf.write(value, numbits); + bitbuf.write(cur_reps, numbits); + repcount -= cur_reps + 3; + } + } +} + + +//------------------------------------------------- +// tree_node_compare - compare two tree nodes +// by weight +//------------------------------------------------- + +int CLIB_DECL huffman_context_base::tree_node_compare(const void *item1, const void *item2) +{ + const node_t *node1 = *(const node_t **)item1; + const node_t *node2 = *(const node_t **)item2; + if (node2->m_weight != node1->m_weight) + return node2->m_weight - node1->m_weight; + if (node2->m_bits - node1->m_bits == 0) + fprintf(stderr, "identical node sort keys, should not happen!\n"); + return (int)node1->m_bits - (int)node2->m_bits; +} + + +//------------------------------------------------- +// build_tree - build a huffman tree based on the +// data distribution +//------------------------------------------------- + +int huffman_context_base::build_tree(UINT32 totaldata, UINT32 totalweight) +{ + // make a list of all non-zero nodes + dynamic_array list(m_numcodes * 2); + int listitems = 0; + memset(m_huffnode, 0, m_numcodes * sizeof(m_huffnode[0])); + for (int curcode = 0; curcode < m_numcodes; curcode++) + if (m_datahisto[curcode] != 0) + { + list[listitems++] = &m_huffnode[curcode]; + m_huffnode[curcode].m_count = m_datahisto[curcode]; + m_huffnode[curcode].m_bits = curcode; + + // scale the weight by the current effective length, ensuring we don't go to 0 + m_huffnode[curcode].m_weight = UINT64(m_datahisto[curcode]) * UINT64(totalweight) / UINT64(totaldata); + if (m_huffnode[curcode].m_weight == 0) + m_huffnode[curcode].m_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, listitems, sizeof(list[0]), 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 + int nextalloc = m_numcodes; + while (listitems > 1) + { + // remove lowest two items + node_t &node1 = *list[--listitems]; + node_t &node0 = *list[--listitems]; + + // create new node + node_t &newnode = m_huffnode[nextalloc++]; + newnode.m_parent = NULL; + node0.m_parent = node1.m_parent = &newnode; + newnode.m_weight = node0.m_weight + node1.m_weight; + + // insert into list at appropriate location + int curitem; + for (curitem = 0; curitem < listitems; curitem++) + if (newnode.m_weight > list[curitem]->m_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 + int maxbits = 0; + for (int curcode = 0; curcode < m_numcodes; curcode++) + { + node_t &node = m_huffnode[curcode]; + node.m_numbits = 0; + node.m_bits = 0; + + // if we have a non-zero weight, compute the number of bits + if (node.m_weight > 0) + { + // determine the number of bits for this node + for (node_t *curnode = &node; curnode->m_parent != NULL; curnode = curnode->m_parent) + node.m_numbits++; + if (node.m_numbits == 0) + node.m_numbits = 1; + + // keep track of the max + maxbits = MAX(maxbits, node.m_numbits); + } + } + return maxbits; +} + + +//------------------------------------------------- +// assign_canonical_codes - assign canonical codes +// to all the nodes based on the number of bits +// in each +//------------------------------------------------- + +huffman_error huffman_context_base::assign_canonical_codes() +{ + // build up a histogram of bit lengths + UINT32 bithisto[33] = { 0 }; + for (int curcode = 0; curcode < m_numcodes; curcode++) + { + node_t &node = m_huffnode[curcode]; + if (node.m_numbits > m_maxbits) + return HUFFERR_INTERNAL_INCONSISTENCY; + if (node.m_numbits <= 32) + bithisto[node.m_numbits]++; + } + + // for each code length, determine the starting code number + UINT32 curstart = 0; + for (int codelen = 32; codelen > 0; codelen--) + { + UINT32 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 (int curcode = 0; curcode < m_numcodes; curcode++) + { + node_t &node = m_huffnode[curcode]; + if (node.m_numbits > 0) + node.m_bits = bithisto[node.m_numbits]++; + } + return HUFFERR_NONE; +} + + +//------------------------------------------------- +// build_lookup_table - build a lookup table for +// fast decoding +//------------------------------------------------- + +void huffman_context_base::build_lookup_table() +{ + // iterate over all codes + for (int curcode = 0; curcode < m_numcodes; curcode++) + { + // process all nodes which have non-zero bits + node_t &node = m_huffnode[curcode]; + if (node.m_numbits > 0) + { + // set up the entry + lookup_value value = MAKE_LOOKUP(curcode, node.m_numbits); + + // fill all matching entries + int shift = m_maxbits - node.m_numbits; + lookup_value *dest = &m_lookup[node.m_bits << shift]; + lookup_value *destend = &m_lookup[((node.m_bits + 1) << shift) - 1]; + while (dest <= destend) + *dest++ = value; + } + } +} + + + +//************************************************************************** +// 8-BIT ENCODER +//************************************************************************** + +//------------------------------------------------- +// huffman_8bit_encoder - constructor +//------------------------------------------------- + +huffman_8bit_encoder::huffman_8bit_encoder() +{ +} + + +//------------------------------------------------- +// encode - encode a full buffer +//------------------------------------------------- + +huffman_error huffman_8bit_encoder::encode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 &complength) +{ + // first compute the histogram + histo_reset(); + for (UINT32 cur = 0; cur < slength; cur++) + histo_one(source[cur]); + + // then compute the tree + huffman_error err = compute_tree_from_histo(); + if (err != HUFFERR_NONE) + return err; + + // export the tree + bitstream_out bitbuf(dest, dlength); + err = export_tree_huffman(bitbuf); + if (err != HUFFERR_NONE) + return err; + + // then encode the data + for (UINT32 cur = 0; cur < slength; cur++) + encode_one(bitbuf, source[cur]); + complength = bitbuf.flush(); + return bitbuf.overflow() ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + + +//************************************************************************** +// 8-BIT DECODER +//************************************************************************** + +//------------------------------------------------- +// huffman_8bit_decoder - constructor +//------------------------------------------------- + +huffman_8bit_decoder::huffman_8bit_decoder() +{ +} + + +//------------------------------------------------- +// decode - decode a full buffer +//------------------------------------------------- + +huffman_error huffman_8bit_decoder::decode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength) +{ + // first import the tree + bitstream_in bitbuf(source, slength); + huffman_error err = import_tree_huffman(bitbuf); + if (err != HUFFERR_NONE) + return err; + + // then decode the data + for (UINT32 cur = 0; cur < dlength; cur++) + dest[cur] = decode_one(bitbuf); + bitbuf.flush(); + return bitbuf.overflow() ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} diff --git a/archivers/chd/huffman.h b/archivers/chd/huffman.h new file mode 100644 index 00000000..47525d82 --- /dev/null +++ b/archivers/chd/huffman.h @@ -0,0 +1,252 @@ +/*************************************************************************** + + huffman.h + + Static Huffman compression and decompression helpers. + +**************************************************************************** + + 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 __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#include "osdcore.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 +//************************************************************************** + +// ======================> huffman_context_base + +// base class for encoding and decoding +class huffman_context_base +{ +protected: + typedef UINT16 lookup_value; + + // a node in the huffman tree + struct node_t + { + node_t * m_parent; // pointer to parent node + UINT32 m_count; // number of hits on this node + UINT32 m_weight; // assigned weight of this node + UINT32 m_bits; // bits used to encode the node + UINT8 m_numbits; // number of bits needed for this node + }; + + // construction/destruction + huffman_context_base(int numcodes, int maxbits, lookup_value *lookup, UINT32 *histo, node_t *nodes); + + // tree creation + huffman_error compute_tree_from_histo(); + + // static tree import; huffman is notably more efficient + huffman_error import_tree_rle(bitstream_in &bitbuf); + huffman_error import_tree_huffman(bitstream_in &bitbuf); + + // static tree export + huffman_error export_tree_rle(bitstream_out &bitbuf); + huffman_error export_tree_huffman(bitstream_out &bitbuf); + + // internal helpers + void write_rle_tree_bits(bitstream_out &bitbuf, int value, int repcount, int numbits); + static int CLIB_DECL tree_node_compare(const void *item1, const void *item2); + int build_tree(UINT32 totaldata, UINT32 totalweight); + huffman_error assign_canonical_codes(); + void build_lookup_table(); + +protected: + // internal state + UINT32 m_numcodes; // number of total codes being processed + UINT8 m_maxbits; // maximum bits per code + UINT8 m_prevdata; // value of the previous data (for delta-RLE encoding) + int m_rleremaining; // number of RLE bytes remaining (for delta-RLE encoding) + lookup_value * m_lookup; // pointer to the lookup table + UINT32 * m_datahisto; // histogram of data values + node_t * m_huffnode; // array of nodes +}; + + +// ======================> huffman_encoder + +// template class for encoding +template +class huffman_encoder : public huffman_context_base +{ +public: + // pass through to the underlying constructor + huffman_encoder() + : huffman_context_base(_NumCodes, _MaxBits, NULL, m_datahisto_array, m_huffnode_array) { histo_reset(); } + + // single item operations + void histo_reset() { memset(m_datahisto_array, 0, sizeof(m_datahisto_array)); } + void histo_one(UINT32 data); + void encode_one(bitstream_out &bitbuf, UINT32 data); + + // expose tree computation and export + using huffman_context_base::compute_tree_from_histo; + using huffman_context_base::export_tree_rle; + using huffman_context_base::export_tree_huffman; + +private: + // array versions of the info we need + UINT32 m_datahisto_array[_NumCodes]; + node_t m_huffnode_array[_NumCodes * 2]; +}; + + +// ======================> huffman_decoder + +// template class for decoding +template +class huffman_decoder : public huffman_context_base +{ +public: + // pass through to the underlying constructor + huffman_decoder() + : huffman_context_base(_NumCodes, _MaxBits, m_lookup_array, NULL, m_huffnode_array) { } + + // single item operations + UINT32 decode_one(bitstream_in &bitbuf); + + // expose tree import + using huffman_context_base::import_tree_rle; + using huffman_context_base::import_tree_huffman; + +private: + // array versions of the info we need + node_t m_huffnode_array[_NumCodes]; + lookup_value m_lookup_array[1 << _MaxBits]; +}; + + +// ======================> huffman_8bit_encoder + +// generic 8-bit encoder/decoder +class huffman_8bit_encoder : public huffman_encoder<> +{ +public: + // construction/destruction + huffman_8bit_encoder(); + + // operations + huffman_error encode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 destlength, UINT32 &complength); +}; + + +// ======================> huffman_8bit_decoder + +// generic 8-bit encoder/decoder +class huffman_8bit_decoder : public huffman_decoder<> +{ +public: + // construction/destruction + huffman_8bit_decoder(); + + // operations + huffman_error decode(const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 destlength); +}; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// histo_one - update the histogram +//------------------------------------------------- + +template +inline void huffman_encoder<_NumCodes, _MaxBits>::histo_one(UINT32 data) +{ + m_datahisto[data]++; +} + + +//------------------------------------------------- +// encode_one - encode a single code to the +// huffman stream +//------------------------------------------------- + +template +inline void huffman_encoder<_NumCodes, _MaxBits>::encode_one(bitstream_out &bitbuf, UINT32 data) +{ + // write the data + node_t &node = m_huffnode[data]; + bitbuf.write(node.m_bits, node.m_numbits); +} + + +//------------------------------------------------- +// decode_one - decode a single code from the +// huffman stream +//------------------------------------------------- + +template +inline UINT32 huffman_decoder<_NumCodes, _MaxBits>::decode_one(bitstream_in &bitbuf) +{ + // peek ahead to get maxbits worth of data + UINT32 bits = bitbuf.peek(m_maxbits); + + // look it up, then remove the actual number of bits for this code + lookup_value lookup = m_lookup[bits]; + bitbuf.remove(lookup & 0x1f); + + // return the value + return lookup >> 5; +} + + +#endif diff --git a/archivers/chd/md5.cpp b/archivers/chd/md5.cpp new file mode 100644 index 00000000..0c9c91c7 --- /dev/null +++ b/archivers/chd/md5.cpp @@ -0,0 +1,238 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ + +#include /* for memcpy() */ + +#include "md5.h" + +#ifndef LSB_FIRST +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(MD5Context)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/archivers/chd/md5.h b/archivers/chd/md5.h new file mode 100644 index 00000000..5270f1b4 --- /dev/null +++ b/archivers/chd/md5.h @@ -0,0 +1,41 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +typedef unsigned int UWORD32; + +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ diff --git a/archivers/chd/osdcore.h b/archivers/chd/osdcore.h new file mode 100644 index 00000000..e69de29b diff --git a/archivers/chd/sha1.cpp b/archivers/chd/sha1.cpp new file mode 100644 index 00000000..5e2de844 --- /dev/null +++ b/archivers/chd/sha1.cpp @@ -0,0 +1,389 @@ +/* sha1.h + * + * The sha1 hash function. + */ + +/* nettle, low-level cryptographics library + * + * Copyright 2001 Peter Gutmann, Andrew Kuchling, Niels Moeller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "chdtypes.h" + +#include "sha1.h" + +#include +#include +#include + +static unsigned int READ_UINT32(const UINT8* data) +{ + return ((UINT32)data[0] << 24) | + ((UINT32)data[1] << 16) | + ((UINT32)data[2] << 8) | + ((UINT32)data[3]); +} + +static void WRITE_UINT32(unsigned char* data, UINT32 val) +{ + data[0] = (val >> 24) & 0xFF; + data[1] = (val >> 16) & 0xFF; + data[2] = (val >> 8) & 0xFF; + data[3] = (val >> 0) & 0xFF; +} + + +/* A block, treated as a sequence of 32-bit words. */ +#define SHA1_DATA_LENGTH 16 + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/* #define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) Rounds 0-19 */ +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +/* #define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) Rounds 40-59 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHA Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* SHA initial values */ + +#define h0init 0x67452301L +#define h1init 0xEFCDAB89L +#define h2init 0x98BADCFEL +#define h3init 0x10325476L +#define h4init 0xC3D2E1F0L + +/* 32-bit rotate left - kludged with shifts */ +#ifdef _MSC_VER +#define ROTL(n,X) _lrotl(X, n) +#else +#define ROTL(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) +#endif + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHA changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#define expand(W,i) ( W[ i & 15 ] = \ + ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + + +/* The prototype SHA sub-round. The fundamental sub-round is: + + a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + +/* Initialize the SHA values */ + +void +sha1_init(struct sha1_ctx *ctx) +{ + /* Set the h-vars to their initial values */ + ctx->digest[ 0 ] = h0init; + ctx->digest[ 1 ] = h1init; + ctx->digest[ 2 ] = h2init; + ctx->digest[ 3 ] = h3init; + ctx->digest[ 4 ] = h4init; + + /* Initialize bit count */ + ctx->count_low = ctx->count_high = 0; + + /* Initialize buffer */ + ctx->index = 0; +} + +/* Perform the SHA transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds + + Note that this function destroys the data area */ + +static void +sha1_transform(UINT32 *state, UINT32 *data) +{ + UINT32 A, B, C, D, E; /* Local vars */ + + /* Set up first buffer and local data buffer */ + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + E = state[4]; + + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, data[ 0] ); + subRound( E, A, B, C, D, f1, K1, data[ 1] ); + subRound( D, E, A, B, C, f1, K1, data[ 2] ); + subRound( C, D, E, A, B, f1, K1, data[ 3] ); + subRound( B, C, D, E, A, f1, K1, data[ 4] ); + subRound( A, B, C, D, E, f1, K1, data[ 5] ); + subRound( E, A, B, C, D, f1, K1, data[ 6] ); + subRound( D, E, A, B, C, f1, K1, data[ 7] ); + subRound( C, D, E, A, B, f1, K1, data[ 8] ); + subRound( B, C, D, E, A, f1, K1, data[ 9] ); + subRound( A, B, C, D, E, f1, K1, data[10] ); + subRound( E, A, B, C, D, f1, K1, data[11] ); + subRound( D, E, A, B, C, f1, K1, data[12] ); + subRound( C, D, E, A, B, f1, K1, data[13] ); + subRound( B, C, D, E, A, f1, K1, data[14] ); + subRound( A, B, C, D, E, f1, K1, data[15] ); + subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) ); + + subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) ); + + subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) ); + + subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) ); + + /* Build message digest */ + state[0] += A; + state[1] += B; + state[2] += C; + state[3] += D; + state[4] += E; +} + +static void +sha1_block(struct sha1_ctx *ctx, const UINT8 *block) +{ + UINT32 data[SHA1_DATA_LENGTH]; + int i; + + /* Update block count */ + if (!++ctx->count_low) + ++ctx->count_high; + + /* Endian independent conversion */ + for (i = 0; idigest, data); +} + +void +sha1_update(struct sha1_ctx *ctx, + unsigned length, const UINT8 *buffer) +{ + if (ctx->index) + { /* Try to fill partial block */ + unsigned left = SHA1_DATA_SIZE - ctx->index; + if (length < left) + { + memcpy(ctx->block + ctx->index, buffer, length); + ctx->index += length; + return; /* Finished */ + } + else + { + memcpy(ctx->block + ctx->index, buffer, left); + sha1_block(ctx, ctx->block); + buffer += left; + length -= left; + } + } + while (length >= SHA1_DATA_SIZE) + { + sha1_block(ctx, buffer); + buffer += SHA1_DATA_SIZE; + length -= SHA1_DATA_SIZE; + } + ctx->index = length; + if (length) + /* Buffer leftovers */ + memcpy(ctx->block, buffer, length); +} + +/* Final wrapup - pad to SHA1_DATA_SIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +void +sha1_final(struct sha1_ctx *ctx) +{ + UINT32 data[SHA1_DATA_LENGTH]; + int i; + int words; + + i = ctx->index; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + + assert(i < SHA1_DATA_SIZE); + ctx->block[i++] = 0x80; + + /* Fill rest of word */ + for( ; i & 3; i++) + ctx->block[i] = 0; + + /* i is now a multiple of the word size 4 */ + words = i >> 2; + for (i = 0; i < words; i++) + data[i] = READ_UINT32(ctx->block + 4*i); + + if (words > (SHA1_DATA_LENGTH-2)) + { /* No room for length in this block. Process it and + * pad with another one */ + for (i = words ; i < SHA1_DATA_LENGTH; i++) + data[i] = 0; + sha1_transform(ctx->digest, data); + for (i = 0; i < (SHA1_DATA_LENGTH-2); i++) + data[i] = 0; + } + else + for (i = words ; i < SHA1_DATA_LENGTH - 2; i++) + data[i] = 0; + + /* There are 512 = 2^9 bits in one block */ + data[SHA1_DATA_LENGTH-2] = (ctx->count_high << 9) | (ctx->count_low >> 23); + data[SHA1_DATA_LENGTH-1] = (ctx->count_low << 9) | (ctx->index << 3); + sha1_transform(ctx->digest, data); +} + +void +sha1_digest(const struct sha1_ctx *ctx, + unsigned length, + UINT8 *digest) +{ + unsigned i; + unsigned words; + unsigned leftover; + + assert(length <= SHA1_DIGEST_SIZE); + + words = length / 4; + leftover = length % 4; + + for (i = 0; i < words; i++, digest += 4) + WRITE_UINT32(digest, ctx->digest[i]); + + if (leftover) + { + UINT32 word; + unsigned j = leftover; + + assert(i < _SHA1_DIGEST_LENGTH); + + word = ctx->digest[i]; + + switch (leftover) + { + default: + /* this is just here to keep the compiler happy; it can never happen */ + case 3: + digest[--j] = (word >> 8) & 0xff; + /* Fall through */ + case 2: + digest[--j] = (word >> 16) & 0xff; + /* Fall through */ + case 1: + digest[--j] = (word >> 24) & 0xff; + } + } +} diff --git a/archivers/chd/sha1.h b/archivers/chd/sha1.h new file mode 100644 index 00000000..028f3303 --- /dev/null +++ b/archivers/chd/sha1.h @@ -0,0 +1,61 @@ +/* sha1.h + * + * The sha1 hash function. + */ + +/* nettle, low-level cryptographics library + * + * Copyright 2001 Niels Moeller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_SHA1_H_INCLUDED +#define NETTLE_SHA1_H_INCLUDED + +#include "osdcore.h" + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_DATA_SIZE 64 + +/* Digest is kept internally as 4 32-bit words. */ +#define _SHA1_DIGEST_LENGTH 5 + +struct sha1_ctx +{ + UINT32 digest[_SHA1_DIGEST_LENGTH]; /* Message digest */ + UINT32 count_low, count_high; /* 64-bit block count */ + UINT8 block[SHA1_DATA_SIZE]; /* SHA1 data buffer */ + unsigned int index; /* index into buffer */ +}; + +void +sha1_init(struct sha1_ctx *ctx); + +void +sha1_update(struct sha1_ctx *ctx, + unsigned length, + const UINT8 *data); + +void +sha1_final(struct sha1_ctx *ctx); + +void +sha1_digest(const struct sha1_ctx *ctx, + unsigned length, + UINT8 *digest); + +#endif /* NETTLE_SHA1_H_INCLUDED */ -- 2.47.3