--- /dev/null
+/***************************************************************************
+
+ 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 <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <new>
+
+
+/***************************************************************************
+ 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;
+}
--- /dev/null
+/***************************************************************************
+
+ 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 <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+
+//**************************************************************************
+// 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__ */
--- /dev/null
+/***************************************************************************
+
+ 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<const UINT8 *>(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<UINT8 *>(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
--- /dev/null
+
+#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<UINT8 *>(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<UINT8 *>(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();
+}
--- /dev/null
+/***************************************************************************
+
+ 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
--- /dev/null
+/***************************************************************************
+
+ 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__ */
--- /dev/null
+#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 <stdlib.h>
+#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<UINT32 *>(&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);
+}
--- /dev/null
+/***************************************************************************
+
+ 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__
--- /dev/null
+#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 <zlib.h>
+#include "7z/LzmaEnc.h"
+#include "7z/LzmaDec.h"
+#include <new>
+
+
+//**************************************************************************
+// 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 _BaseCompressor, class _SubcodeCompressor>
+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 _BaseDecompressor, class _SubcodeDecompressor>
+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_zlib_compressor>, &chd_codec_list::construct_decompressor<chd_zlib_decompressor> },
+ { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor<chd_lzma_compressor>, &chd_codec_list::construct_decompressor<chd_lzma_decompressor> },
+ { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor<chd_huffman_compressor>, &chd_codec_list::construct_decompressor<chd_huffman_decompressor> },
+ { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor<chd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_flac_decompressor> },
+
+ // general codecs with CD frontend
+ { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > },
+ { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > },
+ { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor<chd_cd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_cd_flac_decompressor> },
+
+ // A/V codecs
+// { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor<chd_avhuff_compressor>, &chd_codec_list::construct_decompressor<chd_avhuff_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<chd_zlib_allocator *>(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<UINT32 *>(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<chd_zlib_allocator *>(opaque);
+
+ // find the hunk
+ UINT32 *ptr = reinterpret_cast<UINT32 *>(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<Bytef *>(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<Bytef *>(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<chd_lzma_allocator *>(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<UINT32 *>(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<chd_lzma_allocator *>(p);
+
+ // find the hunk
+ UINT32 *ptr = reinterpret_cast<UINT32 *>(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<UINT8 *>(&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<const INT16 *>(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<const INT16 *>(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<const INT16 *>(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<UINT8 *>(&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<INT16 *>(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<UINT8 *>(&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<INT16 *>(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<Bytef *>(&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<UINT8 *>(&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<INT16 *>(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<Bytef *>(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<avhuff_decompress_config *>(config));
+
+ // anything else is invalid
+ else
+ throw CHDERR_INVALID_PARAMETER;
+}
+#endif
\ No newline at end of file
--- /dev/null
+/***************************************************************************
+
+ 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<class _CompressorClass>
+ static chd_compressor *construct_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) { return new _CompressorClass(chd, hunkbytes, lossy); }
+
+ template<class _DecompressorClass>
+ 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__
--- /dev/null
+
+
+#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
--- /dev/null
+/***************************************************************************
+
+ 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 <assert.h>
+#include "osdcore.h"
+
+
+// ======================> dynamic_array
+
+// an array that is dynamically sized and can optionally auto-expand
+template<class _ElementType>
+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<UINT8> dynamic_buffer;
+
+
+
+#endif
--- /dev/null
+#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 <assert.h>
+#include <new>
+
+
+//**************************************************************************
+// 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<FLAC__byte *>(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<flac_encoder *>(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<const FLAC__byte *>(buffer)),
+ m_compressed_length(length),
+ m_compressed2_start(reinterpret_cast<const FLAC__byte *>(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<const FLAC__byte *>(buffer);
+ m_compressed_length = length;
+ m_compressed2_start = reinterpret_cast<const FLAC__byte *>(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<const FLAC__byte *>(m_custom_header);
+ m_compressed_length = sizeof(m_custom_header);
+ m_compressed2_start = reinterpret_cast<const FLAC__byte *>(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<const FLAC__byte *>(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<flac_decoder *>(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<flac_decoder *>(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<flac_decoder *>(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<flac_decoder *>(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)
+{
+}
--- /dev/null
+/***************************************************************************
+
+ 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 <FLAC/all.h>
+#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__
--- /dev/null
+/***************************************************************************
+
+ 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<const Bytef *>(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<const UINT8 *>(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
--- /dev/null
+/***************************************************************************
+
+ 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<const UINT8 *>(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<const unsigned char *>(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__
--- /dev/null
+
+#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 <stdlib.h>
+
+#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<UINT8> rle_data(m_numcodes);
+ UINT8 *dest = rle_data;
+ dynamic_array<UINT16> 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<node_t *> 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;
+}
--- /dev/null
+/***************************************************************************
+
+ 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<int _NumCodes = 256, int _MaxBits = 16>
+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<int _NumCodes = 256, int _MaxBits = 16>
+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<int _NumCodes, int _MaxBits>
+inline void huffman_encoder<_NumCodes, _MaxBits>::histo_one(UINT32 data)
+{
+ m_datahisto[data]++;
+}
+
+
+//-------------------------------------------------
+// encode_one - encode a single code to the
+// huffman stream
+//-------------------------------------------------
+
+template<int _NumCodes, int _MaxBits>
+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<int _NumCodes, int _MaxBits>
+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
--- /dev/null
+/*
+ * 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 <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#include <string.h> /* 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<<s | 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
--- /dev/null
+/*
+ * 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 <ijackson@nyx.cs.du.edu>.
+ * 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 */
--- /dev/null
+/* 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+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; i<SHA1_DATA_LENGTH; i++, block += 4)
+ data[i] = READ_UINT32(block);
+
+ sha1_transform(ctx->digest, 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;
+ }
+ }
+}
--- /dev/null
+/* 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 */