]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
chd support added
authorToni Wilen <twilen@winuae.net>
Sat, 12 Jan 2013 15:26:26 +0000 (17:26 +0200)
committerToni Wilen <twilen@winuae.net>
Sat, 12 Jan 2013 15:26:26 +0000 (17:26 +0200)
24 files changed:
archivers/chd/astring.cpp [new file with mode: 0644]
archivers/chd/astring.h [new file with mode: 0644]
archivers/chd/bitstream.h [new file with mode: 0644]
archivers/chd/chd.cpp [new file with mode: 0644]
archivers/chd/chd.h [new file with mode: 0644]
archivers/chd/chdcd.h [new file with mode: 0644]
archivers/chd/chdcdrom.cpp [new file with mode: 0644]
archivers/chd/chdcdrom.h [new file with mode: 0644]
archivers/chd/chdcodec.cpp [new file with mode: 0644]
archivers/chd/chdcodec.h [new file with mode: 0644]
archivers/chd/chdtypes.h [new file with mode: 0644]
archivers/chd/corefile.h [new file with mode: 0644]
archivers/chd/coretmpl.h [new file with mode: 0644]
archivers/chd/flac.cpp [new file with mode: 0644]
archivers/chd/flac.h [new file with mode: 0644]
archivers/chd/hashing.cpp [new file with mode: 0644]
archivers/chd/hashing.h [new file with mode: 0644]
archivers/chd/huffman.cpp [new file with mode: 0644]
archivers/chd/huffman.h [new file with mode: 0644]
archivers/chd/md5.cpp [new file with mode: 0644]
archivers/chd/md5.h [new file with mode: 0644]
archivers/chd/osdcore.h [new file with mode: 0644]
archivers/chd/sha1.cpp [new file with mode: 0644]
archivers/chd/sha1.h [new file with mode: 0644]

diff --git a/archivers/chd/astring.cpp b/archivers/chd/astring.cpp
new file mode 100644 (file)
index 0000000..e74d812
--- /dev/null
@@ -0,0 +1,513 @@
+/***************************************************************************
+
+    astring.c
+
+    Allocated string manipulation functions.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************/
+
+#include "chdtypes.h"
+
+#include "astring.h"
+
+#include <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;
+}
diff --git a/archivers/chd/astring.h b/archivers/chd/astring.h
new file mode 100644 (file)
index 0000000..ca528d6
--- /dev/null
@@ -0,0 +1,189 @@
+/***************************************************************************
+
+    astring.h
+
+    Allocated string manipulation functions.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __ASTRING_H__
+#define __ASTRING_H__
+
+#include <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__ */
diff --git a/archivers/chd/bitstream.h b/archivers/chd/bitstream.h
new file mode 100644 (file)
index 0000000..1c18591
--- /dev/null
@@ -0,0 +1,265 @@
+/***************************************************************************
+
+    bitstream.h
+
+    Helper classes for reading/writing at the bit level.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __BITSTREAM_H__
+#define __BITSTREAM_H__
+
+#include "osdcore.h"
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// helper class for reading from a bit buffer
+class bitstream_in
+{
+public:
+       // construction/destruction
+       bitstream_in(const void *src, UINT32 srclength);
+
+       // getters
+       bool overflow() const { return ((m_doffset - m_bits / 8) > m_dlength); }
+       UINT32 read_offset() const;
+
+       // operations
+       UINT32 read(int numbits);
+       UINT32 peek(int numbits);
+       void remove(int numbits);
+       UINT32 flush();
+
+private:
+       // internal state
+       UINT32                  m_buffer;               // current bit accumulator
+       int                             m_bits;                 // number of bits in the accumulator
+       const UINT8 *   m_read;                 // read pointer
+       UINT32                  m_doffset;              // byte offset within the data
+       UINT32                  m_dlength;              // length of the data
+};
+
+
+// helper class for writing to a bit buffer
+class bitstream_out
+{
+public:
+       // construction/destruction
+       bitstream_out(void *dest, UINT32 destlength);
+
+       // getters
+       bool overflow() const { return (m_doffset > m_dlength); }
+
+       // operations
+       void write(UINT32 newbits, int numbits);
+       UINT32 flush();
+
+private:
+       // internal state
+       UINT32                  m_buffer;                       // current bit accumulator
+       int                             m_bits;                         // number of bits in the accumulator
+       UINT8 *                 m_write;                        // write pointer
+       UINT32                  m_doffset;                      // byte offset within the data
+       UINT32                  m_dlength;                      // length of the data
+};
+
+
+
+//**************************************************************************
+//  INLINE FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+//  bitstream_in - constructor
+//-------------------------------------------------
+
+inline bitstream_in::bitstream_in(const void *src, UINT32 srclength)
+       : m_buffer(0),
+         m_bits(0),
+         m_read(reinterpret_cast<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
diff --git a/archivers/chd/chd.cpp b/archivers/chd/chd.cpp
new file mode 100644 (file)
index 0000000..14d7d5e
--- /dev/null
@@ -0,0 +1,1116 @@
+
+#include "chdtypes.h"
+
+#include "chd.h"
+#include "hashing.h"
+#include "chdcdrom.h"
+#include "coretmpl.h"
+#include "bitstream.h"
+#include "huffman.h"
+
+// standard metadata formats
+const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d";
+const char *CDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d";
+const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
+const char *GDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
+const char *AV_METADATA_FORMAT = "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d";
+
+static const UINT32 METADATA_HEADER_SIZE = 16;                 // metadata header size
+
+static const UINT8 V34_MAP_ENTRY_FLAG_TYPE_MASK        = 0x0f;         // what type of hunk
+static const UINT8 V34_MAP_ENTRY_FLAG_NO_CRC = 0x10;           // no CRC is present
+
+
+
+// V3-V4 entry types
+enum
+{
+       V34_MAP_ENTRY_TYPE_INVALID = 0,                         // invalid type
+       V34_MAP_ENTRY_TYPE_COMPRESSED = 1,                      // standard compression
+       V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2,            // uncompressed data
+       V34_MAP_ENTRY_TYPE_MINI = 3,                            // mini: use offset as raw data
+       V34_MAP_ENTRY_TYPE_SELF_HUNK = 4,                       // same as another hunk in this file
+       V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5,                     // same as a hunk in the parent file
+       V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6           // compressed with secondary algorithm (usually FLAC CDDA)
+};
+
+// V5 compression types
+enum
+{
+       // these types are live when running
+       COMPRESSION_TYPE_0 = 0,                                         // codec #0
+       COMPRESSION_TYPE_1 = 1,                                         // codec #1
+       COMPRESSION_TYPE_2 = 2,                                         // codec #2
+       COMPRESSION_TYPE_3 = 3,                                         // codec #3
+       COMPRESSION_NONE = 4,                                           // no compression; implicit length = hunkbytes
+       COMPRESSION_SELF = 5,                                           // same as another block in this chd
+       COMPRESSION_PARENT = 6,                                         // same as a hunk's worth of units in the parent chd
+
+       // these additional pseudo-types are used for compressed encodings:
+       COMPRESSION_RLE_SMALL,                                          // start of small RLE run (4-bit length)
+       COMPRESSION_RLE_LARGE,                                          // start of large RLE run (8-bit length)
+       COMPRESSION_SELF_0,                                                     // same as the last COMPRESSION_SELF block
+       COMPRESSION_SELF_1,                                                     // same as the last COMPRESSION_SELF block + 1
+       COMPRESSION_PARENT_SELF,                                        // same block in the parent
+       COMPRESSION_PARENT_0,                                           // same as the last COMPRESSION_PARENT block
+       COMPRESSION_PARENT_1                                            // same as the last COMPRESSION_PARENT block + 1
+};
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> metadata_entry
+
+// description of where a metadata entry lives within the file
+struct chd_file::metadata_entry
+{
+       UINT64                                  offset;                 // offset within the file of the header
+       UINT64                                  next;                   // offset within the file of the next header
+       UINT64                                  prev;                   // offset within the file of the previous header
+       UINT32                                  length;                 // length of the metadata
+       UINT32                                  metatag;                // metadata tag
+       UINT8                                   flags;                  // flag bits
+};
+
+
+// ======================> metadata_hash
+
+struct chd_file::metadata_hash
+{
+       UINT8                                   tag[4];                 // tag of the metadata in big-endian
+       sha1_t                                  sha1;                   // hash data
+};
+
+
+//-------------------------------------------------
+//  be_read - extract a big-endian number from
+//  a byte buffer
+//-------------------------------------------------
+
+inline UINT64 chd_file::be_read(const UINT8 *base, int numbytes)
+{
+       UINT64 result = 0;
+       while (numbytes--)
+               result = (result << 8) | *base++;
+       return result;
+}
+
+//-------------------------------------------------
+//  be_write - write a big-endian number to a byte
+//  buffer
+//-------------------------------------------------
+
+inline void chd_file::be_write(UINT8 *base, UINT64 value, int numbytes)
+{
+       base += numbytes;
+       while (numbytes--)
+       {
+               *--base = value;
+               value >>= 8;
+       }
+}
+
+//-------------------------------------------------
+//  be_read_sha1 - fetch a sha1_t from a data
+//  stream in bigendian order
+//-------------------------------------------------
+
+inline sha1_t chd_file::be_read_sha1(const UINT8 *base)
+{
+       sha1_t result;
+       memcpy(&result.m_raw[0], base, sizeof(result.m_raw));
+       return result;
+}
+
+//-------------------------------------------------
+//  file_read - read from the file at the given
+//  offset; on failure throw an error
+//-------------------------------------------------
+
+inline void chd_file::file_read(UINT64 offset, void *dest, UINT32 length)
+{
+       // no file = failure
+       if (m_file == NULL)
+               throw CHDERR_NOT_OPEN;
+
+       // seek and read
+       zfile_fseek(m_file, offset, SEEK_SET);
+       UINT32 count = zfile_fread(dest, 1, length, m_file);
+       if (count != length)
+               throw CHDERR_READ_ERROR;
+}
+
+//-------------------------------------------------
+//  hunk_info - return information about this
+//  hunk
+//-------------------------------------------------
+
+chd_error chd_file::hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes)
+{
+       // error if invalid
+       if (hunknum >= m_hunkcount)
+               return CHDERR_HUNK_OUT_OF_RANGE;
+
+       // get the map pointer
+       UINT8 *rawmap;
+       switch (m_version)
+       {
+               // v3/v4 map entries
+               case 3:
+               case 4:
+                       rawmap = m_rawmap + 16 * hunknum;
+                       switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
+                       {
+                               case V34_MAP_ENTRY_TYPE_COMPRESSED:
+                                       compressor = CHD_CODEC_ZLIB;
+                                       compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16);
+                                       break;
+
+                               case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
+                                       compressor = CHD_CODEC_NONE;
+                                       compbytes = m_hunkbytes;
+                                       break;
+
+                               case V34_MAP_ENTRY_TYPE_MINI:
+                                       compressor = CHD_CODEC_MINI;
+                                       compbytes = 0;
+                                       break;
+
+                               case V34_MAP_ENTRY_TYPE_SELF_HUNK:
+                                       compressor = CHD_CODEC_SELF;
+                                       compbytes = 0;
+                                       break;
+
+                               case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
+                                       compressor = CHD_CODEC_PARENT;
+                                       compbytes = 0;
+                                       break;
+                       }
+                       break;
+
+               // v5 map entries
+               case 5:
+                       rawmap = m_rawmap + m_mapentrybytes * hunknum;
+
+                       // uncompressed case
+                       if (!compressed())
+                       {
+                               if (be_read(&rawmap[0], 4) == 0)
+                               {
+                                       compressor = CHD_CODEC_PARENT;
+                                       compbytes = 0;
+                               }
+                               else
+                               {
+                                       compressor = CHD_CODEC_NONE;
+                                       compbytes = m_hunkbytes;
+                               }
+                               break;
+                       }
+
+                       // compressed case
+                       switch (rawmap[0])
+                       {
+                               case COMPRESSION_TYPE_0:
+                               case COMPRESSION_TYPE_1:
+                               case COMPRESSION_TYPE_2:
+                               case COMPRESSION_TYPE_3:
+                                       compressor = m_compression[rawmap[0]];
+                                       compbytes = be_read(&rawmap[1], 3);
+                                       break;
+
+                               case COMPRESSION_NONE:
+                                       compressor = CHD_CODEC_NONE;
+                                       compbytes = m_hunkbytes;
+                                       break;
+
+                               case COMPRESSION_SELF:
+                                       compressor = CHD_CODEC_SELF;
+                                       compbytes = 0;
+                                       break;
+
+                               case COMPRESSION_PARENT:
+                                       compressor = CHD_CODEC_PARENT;
+                                       compbytes = 0;
+                                       break;
+                       }
+                       break;
+       }
+       return CHDERR_NONE;
+}
+
+//-------------------------------------------------
+//  open - open an existing file for read or
+//  read/write
+//-------------------------------------------------
+
+chd_error chd_file::open(struct zfile *file, bool writeable, chd_file *parent)
+{
+       // make sure we don't already have a file open
+       if (m_file != NULL)
+               return CHDERR_ALREADY_OPEN;
+
+       // open the file
+       m_file = file;
+       m_owns_file = false;
+       m_parent = parent;
+       return open_common(writeable);
+}
+
+//-------------------------------------------------
+//  close - close a CHD file for access
+//-------------------------------------------------
+
+void chd_file::close()
+{
+       // reset file characteristics
+       if (m_owns_file && m_file != NULL)
+               zfile_fclose(m_file);
+       m_file = NULL;
+       m_owns_file = false;
+       m_allow_reads = false;
+       m_allow_writes = false;
+
+       // reset core parameters from the header
+       m_version = HEADER_VERSION;
+       m_logicalbytes = 0;
+       m_mapoffset = 0;
+       m_metaoffset = 0;
+       m_hunkbytes = 0;
+       m_hunkcount = 0;
+       m_unitbytes = 0;
+       m_unitcount = 0;
+       memset(m_compression, 0, sizeof(m_compression));
+       m_parent = NULL;
+       m_parent_missing = false;
+
+       // reset key offsets within the header
+       m_mapoffset_offset = 0;
+       m_metaoffset_offset = 0;
+       m_sha1_offset = 0;
+       m_rawsha1_offset = 0;
+       m_parentsha1_offset = 0;
+
+       // reset map information
+       m_mapentrybytes = 0;
+       m_rawmap.reset();
+
+       // reset compression management
+       for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++)
+       {
+               delete m_decompressor[decompnum];
+               m_decompressor[decompnum] = NULL;
+       }
+       m_compressed.reset();
+
+       // reset caching
+       m_cache.reset();
+       m_cachehunk = ~0;
+}
+
+
+//-------------------------------------------------
+//  read - read a single hunk from the CHD file
+//-------------------------------------------------
+
+chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer)
+{
+       // wrap this for clean reporting
+       try
+       {
+               // punt if no file
+               if (m_file == NULL)
+                       throw CHDERR_NOT_OPEN;
+
+               // return an error if out of range
+               if (hunknum >= m_hunkcount)
+                       throw CHDERR_HUNK_OUT_OF_RANGE;
+
+               // get a pointer to the map entry
+               UINT64 blockoffs;
+               UINT32 blocklen;
+               UINT32 blockcrc;
+               UINT8 *rawmap;
+               UINT8 *dest = reinterpret_cast<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();
+}
diff --git a/archivers/chd/chd.h b/archivers/chd/chd.h
new file mode 100644 (file)
index 0000000..c548a34
--- /dev/null
@@ -0,0 +1,447 @@
+/***************************************************************************
+
+    chd.h
+
+    MAME Compressed Hunks of Data file format
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CHD_H__
+#define __CHD_H__
+
+#include "osdcore.h"
+#include "coretmpl.h"
+#include "astring.h"
+#include "corefile.h"
+#include "hashing.h"
+#include "chdcodec.h"
+
+
+/***************************************************************************
+
+    Compressed Hunks of Data header format. All numbers are stored in
+    Motorola (big-endian) byte ordering.
+
+    =========================================================================
+
+    V1 header:
+
+    [  0] char   tag[8];        // 'MComprHD'
+    [  8] UINT32 length;        // length of header (including tag and length fields)
+    [ 12] UINT32 version;       // drive format version
+    [ 16] UINT32 flags;         // flags (see below)
+    [ 20] UINT32 compression;   // compression type
+    [ 24] UINT32 hunksize;      // 512-byte sectors per hunk
+    [ 28] UINT32 totalhunks;    // total # of hunks represented
+    [ 32] UINT32 cylinders;     // number of cylinders on hard disk
+    [ 36] UINT32 heads;         // number of heads on hard disk
+    [ 40] UINT32 sectors;       // number of sectors on hard disk
+    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
+    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
+    [ 76] (V1 header length)
+
+    Flags:
+        0x00000001 - set if this drive has a parent
+        0x00000002 - set if this drive allows writes
+
+    Compression types:
+        CHDCOMPRESSION_NONE = 0
+        CHDCOMPRESSION_ZLIB = 1
+
+    V1 map format:
+
+    [  0] UINT64 offset : 44;   // starting offset within the file
+    [  0] UINT64 length : 20;   // length of data; if == hunksize, data is uncompressed
+
+    =========================================================================
+
+    V2 header:
+
+    [  0] char   tag[8];        // 'MComprHD'
+    [  8] UINT32 length;        // length of header (including tag and length fields)
+    [ 12] UINT32 version;       // drive format version
+    [ 16] UINT32 flags;         // flags (see below)
+    [ 20] UINT32 compression;   // compression type
+    [ 24] UINT32 hunksize;      // seclen-byte sectors per hunk
+    [ 28] UINT32 totalhunks;    // total # of hunks represented
+    [ 32] UINT32 cylinders;     // number of cylinders on hard disk
+    [ 36] UINT32 heads;         // number of heads on hard disk
+    [ 40] UINT32 sectors;       // number of sectors on hard disk
+    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
+    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
+    [ 76] UINT32 seclen;        // number of bytes per sector
+    [ 80] (V2 header length)
+
+    Flags and map format are same as V1
+
+    =========================================================================
+
+    V3 header:
+
+    [  0] char   tag[8];        // 'MComprHD'
+    [  8] UINT32 length;        // length of header (including tag and length fields)
+    [ 12] UINT32 version;       // drive format version
+    [ 16] UINT32 flags;         // flags (see below)
+    [ 20] UINT32 compression;   // compression type
+    [ 24] UINT32 totalhunks;    // total # of hunks represented
+    [ 28] UINT64 logicalbytes;  // logical size of the data (in bytes)
+    [ 36] UINT64 metaoffset;    // offset to the first blob of metadata
+    [ 44] UINT8  md5[16];       // MD5 checksum of raw data
+    [ 60] UINT8  parentmd5[16]; // MD5 checksum of parent file
+    [ 76] UINT32 hunkbytes;     // number of bytes per hunk
+    [ 80] UINT8  sha1[20];      // SHA1 checksum of raw data
+    [100] UINT8  parentsha1[20];// SHA1 checksum of parent file
+    [120] (V3 header length)
+
+    Flags are the same as V1
+
+    Compression types:
+        CHDCOMPRESSION_NONE = 0
+        CHDCOMPRESSION_ZLIB = 1
+        CHDCOMPRESSION_ZLIB_PLUS = 2
+
+    V3 map format:
+
+    [  0] UINT64 offset;        // starting offset within the file
+    [  8] UINT32 crc32;         // 32-bit CRC of the uncompressed data
+    [ 12] UINT16 length_lo;     // lower 16 bits of length
+    [ 14] UINT8 length_hi;      // upper 8 bits of length
+    [ 15] UINT8 flags;          // flags, indicating compression info
+
+    =========================================================================
+
+    V4 header:
+
+    [  0] char   tag[8];        // 'MComprHD'
+    [  8] UINT32 length;        // length of header (including tag and length fields)
+    [ 12] UINT32 version;       // drive format version
+    [ 16] UINT32 flags;         // flags (see below)
+    [ 20] UINT32 compression;   // compression type
+    [ 24] UINT32 totalhunks;    // total # of hunks represented
+    [ 28] UINT64 logicalbytes;  // logical size of the data (in bytes)
+    [ 36] UINT64 metaoffset;    // offset to the first blob of metadata
+    [ 44] UINT32 hunkbytes;     // number of bytes per hunk
+    [ 48] UINT8  sha1[20];      // combined raw+meta SHA1
+    [ 68] UINT8  parentsha1[20];// combined raw+meta SHA1 of parent
+    [ 88] UINT8  rawsha1[20];   // raw data SHA1
+    [108] (V4 header length)
+
+    Flags are the same as V1
+
+    Compression types:
+        CHDCOMPRESSION_NONE = 0
+        CHDCOMPRESSION_ZLIB = 1
+        CHDCOMPRESSION_ZLIB_PLUS = 2
+        CHDCOMPRESSION_AV = 3
+
+    Map format is the same as V3
+
+    =========================================================================
+
+    V5 header:
+
+    [  0] char   tag[8];        // 'MComprHD'
+    [  8] UINT32 length;        // length of header (including tag and length fields)
+    [ 12] UINT32 version;       // drive format version
+    [ 16] UINT32 compressors[4];// which custom compressors are used?
+    [ 32] UINT64 logicalbytes;  // logical size of the data (in bytes)
+    [ 40] UINT64 mapoffset;     // offset to the map
+    [ 48] UINT64 metaoffset;    // offset to the first blob of metadata
+    [ 56] UINT32 hunkbytes;     // number of bytes per hunk (512k maximum)
+    [ 60] UINT32 unitbytes;     // number of bytes per unit within each hunk
+    [ 64] UINT8  rawsha1[20];   // raw data SHA1
+    [ 84] UINT8  sha1[20];      // combined raw+meta SHA1
+    [104] UINT8  parentsha1[20];// combined raw+meta SHA1 of parent
+    [124] (V5 header length)
+
+    If parentsha1 != 0, we have a parent (no need for flags)
+    If compressors[0] == 0, we are uncompressed (including maps)
+
+    V5 uncompressed map format:
+
+    [  0] UINT32 offset;        // starting offset / hunk size
+
+    V5 compressed map format header:
+
+    [  0] UINT32 length;        // length of compressed map
+    [  4] UINT48 datastart;     // offset of first block
+    [ 10] UINT16 crc;           // crc-16 of the map
+    [ 12] UINT8 lengthbits;     // bits used to encode complength
+    [ 13] UINT8 hunkbits;       // bits used to encode self-refs
+    [ 14] UINT8 parentunitbits; // bits used to encode parent unit refs
+    [ 15] UINT8 reserved;       // future use
+    [ 16] (compressed header length)
+
+    Each compressed map entry, once expanded, looks like:
+
+    [  0] UINT8 compression;    // compression type
+    [  1] UINT24 complength;    // compressed length
+    [  4] UINT48 offset;        // offset
+    [ 10] UINT16 crc;           // crc-16 of the data
+
+***************************************************************************/
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
+
+// pseudo-codecs returned by hunk_info
+const chd_codec_type CHD_CODEC_SELF            = 1;    // copy of another hunk
+const chd_codec_type CHD_CODEC_PARENT          = 2;    // copy of a parent's hunk
+const chd_codec_type CHD_CODEC_MINI            = 3;    // legacy "mini" 8-byte repeat
+
+// core types
+typedef UINT32 chd_metadata_tag;
+
+// metadata parameters
+const chd_metadata_tag CHDMETATAG_WILDCARD = 0;
+const UINT32 CHDMETAINDEX_APPEND = ~0;
+
+// metadata flags
+const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01;               // indicates data is checksummed
+
+// standard hard disk metadata
+const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D');
+extern const char *HARD_DISK_METADATA_FORMAT;
+
+// hard disk identify information
+const chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T');
+
+// hard disk key information
+const chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' ');
+
+// pcmcia CIS information
+const chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' ');
+
+// standard CD-ROM metadata
+const chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D');
+const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R');
+extern const char *CDROM_TRACK_METADATA_FORMAT;
+const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2');
+extern const char *CDROM_TRACK_METADATA2_FORMAT;
+const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T');
+extern const char *GDROM_TRACK_METADATA_FORMAT;
+
+// standard A/V metadata
+const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V');
+extern const char *AV_METADATA_FORMAT;
+
+// A/V laserdisc frame metadata
+const chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D');
+
+// error types
+enum chd_error
+{
+       CHDERR_NONE,
+       CHDERR_NO_INTERFACE,
+       CHDERR_OUT_OF_MEMORY,
+       CHDERR_NOT_OPEN,
+       CHDERR_ALREADY_OPEN,
+       CHDERR_INVALID_FILE,
+       CHDERR_INVALID_PARAMETER,
+       CHDERR_INVALID_DATA,
+       CHDERR_FILE_NOT_FOUND,
+       CHDERR_REQUIRES_PARENT,
+       CHDERR_FILE_NOT_WRITEABLE,
+       CHDERR_READ_ERROR,
+       CHDERR_WRITE_ERROR,
+       CHDERR_CODEC_ERROR,
+       CHDERR_INVALID_PARENT,
+       CHDERR_HUNK_OUT_OF_RANGE,
+       CHDERR_DECOMPRESSION_ERROR,
+       CHDERR_COMPRESSION_ERROR,
+       CHDERR_CANT_CREATE_FILE,
+       CHDERR_CANT_VERIFY,
+       CHDERR_NOT_SUPPORTED,
+       CHDERR_METADATA_NOT_FOUND,
+       CHDERR_INVALID_METADATA_SIZE,
+       CHDERR_UNSUPPORTED_VERSION,
+       CHDERR_VERIFY_INCOMPLETE,
+       CHDERR_INVALID_METADATA,
+       CHDERR_INVALID_STATE,
+       CHDERR_OPERATION_PENDING,
+       CHDERR_UNSUPPORTED_FORMAT,
+       CHDERR_UNKNOWN_COMPRESSION,
+       CHDERR_WALKING_PARENT,
+       CHDERR_COMPRESSING
+};
+
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+class chd_codec;
+
+
+// ======================> chd_file
+
+// core file class
+class chd_file
+{
+       friend class chd_file_compressor;
+       friend class chd_verifier;
+
+       // constants
+       static const UINT32 HEADER_VERSION = 5;
+       static const UINT32 V3_HEADER_SIZE = 120;
+       static const UINT32 V4_HEADER_SIZE = 108;
+       static const UINT32 V5_HEADER_SIZE = 124;
+       static const UINT32 MAX_HEADER_SIZE = V5_HEADER_SIZE;
+
+public:
+       // construction/destruction
+       chd_file();
+       virtual ~chd_file();
+
+       // getters
+       bool opened() const { return (m_file != NULL); }
+       UINT32 version() const { return m_version; }
+       UINT64 logical_bytes() const { return m_logicalbytes; }
+       UINT32 hunk_bytes() const { return m_hunkbytes; }
+       UINT32 hunk_count() const { return m_hunkcount; }
+       UINT32 unit_bytes() const { return m_unitbytes; }
+       UINT64 unit_count() const { return m_unitcount; }
+       bool compressed() const { return (m_compression[0] != CHD_CODEC_NONE); }
+       chd_codec_type compression(int index) const { return m_compression[index]; }
+       chd_file *parent() const { return m_parent; }
+       sha1_t sha1();
+       sha1_t raw_sha1();
+       sha1_t parent_sha1();
+       chd_error hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes);
+
+       // file open
+       chd_error open(const char *filename, bool writeable = false, chd_file *parent = NULL);
+       chd_error open(struct zfile *file, bool writeable = false, chd_file *parent = NULL);
+
+       // file close
+       void close();
+
+       // read/write
+       chd_error read_hunk(UINT32 hunknum, void *buffer);
+       chd_error read_units(UINT64 unitnum, void *buffer, UINT32 count = 1);
+       chd_error read_bytes(UINT64 offset, void *buffer, UINT32 bytes);
+
+       // metadata management
+       chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output);
+       chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output);
+       chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen);
+       chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags);
+
+       // hashing helper
+       sha1_t compute_overall_sha1(sha1_t rawsha1);
+
+       // codec interfaces
+       chd_error codec_configure(chd_codec_type codec, int param, void *config);
+
+       // static helpers
+       static const char *error_string(chd_error err);
+
+private:
+       struct metadata_entry;
+       struct metadata_hash;
+
+       // inline helpers
+       UINT64 be_read(const UINT8 *base, int numbytes);
+       void be_write(UINT8 *base, UINT64 value, int numbytes);
+       sha1_t be_read_sha1(const UINT8 *base);
+       void be_write_sha1(UINT8 *base, sha1_t value);
+       void file_read(UINT64 offset, void *dest, UINT32 length);
+       void file_write(UINT64 offset, const void *source, UINT32 length);
+       UINT64 file_append(const void *source, UINT32 length, UINT32 alignment = 0);
+       UINT8 bits_for_value(UINT64 value);
+
+       // internal helpers
+       UINT32 guess_unitbytes();
+       void parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1);
+       void parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1);
+       void parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1);
+       chd_error compress_v5_map();
+       void decompress_v5_map();
+       chd_error create_common();
+       chd_error open_common(bool writeable);
+       void create_open_common();
+       void verify_proper_compression_append(UINT32 hunknum);
+       void hunk_write_compressed(UINT32 hunknum, INT8 compression, const UINT8 *compressed, UINT32 complength, crc16_t crc16);
+       void hunk_copy_from_self(UINT32 hunknum, UINT32 otherhunk);
+       void hunk_copy_from_parent(UINT32 hunknum, UINT64 parentunit);
+       bool metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume = false);
+       void metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset);
+       void metadata_update_hash();
+//     static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2);
+
+       // file characteristics
+       struct zfile *                          m_file;                         // handle to the open core file
+       bool                                    m_owns_file;            // flag indicating if this file should be closed on chd_close()
+       bool                                    m_allow_reads;          // permit reads from this CHD?
+       bool                                    m_allow_writes;         // permit writes to this CHD?
+
+       // core parameters from the header
+       UINT32                                  m_version;                      // version of the header
+       UINT64                                  m_logicalbytes;         // logical size of the raw CHD data in bytes
+       UINT64                                  m_mapoffset;            // offset of map
+       UINT64                                  m_metaoffset;           // offset to first metadata bit
+       UINT32                                  m_hunkbytes;            // size of each raw hunk in bytes
+       UINT32                                  m_hunkcount;            // number of hunks represented
+       UINT32                                  m_unitbytes;            // size of each unit in bytes
+       UINT64                                  m_unitcount;            // number of units represented
+       chd_codec_type                  m_compression[4];       // array of compression types used
+       chd_file *                              m_parent;                       // pointer to parent file, or NULL if none
+       bool                                    m_parent_missing;       // are we missing our parent?
+
+       // key offsets within the header
+       UINT64                                  m_mapoffset_offset;     // offset of map offset field
+       UINT64                                  m_metaoffset_offset;// offset of metaoffset field
+       UINT64                                  m_sha1_offset;          // offset of SHA1 field
+       UINT64                                  m_rawsha1_offset;       // offset of raw SHA1 field
+       UINT64                                  m_parentsha1_offset;// offset of paren SHA1 field
+
+       // map information
+       UINT32                                  m_mapentrybytes;        // length of each entry in a map
+       dynamic_buffer                  m_rawmap;                       // raw map data
+
+       // compression management
+       chd_decompressor *              m_decompressor[4];      // array of decompression codecs
+       dynamic_buffer                  m_compressed;           // temporary buffer for compressed data
+
+       // caching
+       dynamic_buffer                  m_cache;                        // single-hunk cache for partial reads/writes
+       UINT32                                  m_cachehunk;            // which hunk is in the cache?
+};
+
+#endif // __CHD_H__
\ No newline at end of file
diff --git a/archivers/chd/chdcd.h b/archivers/chd/chdcd.h
new file mode 100644 (file)
index 0000000..ac8b183
--- /dev/null
@@ -0,0 +1,39 @@
+/***************************************************************************
+
+    CDRDAO TOC parser for CHD compression frontend
+
+    Copyright Nicola Salmoria and the MAME Team.
+    Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CHDCD_H__
+#define __CHDCD_H__
+
+#include "chdcdrom.h"
+
+struct chdcd_track_input_entry
+{
+       chdcd_track_input_entry() { reset(); }
+       void reset() { fname.reset(); offset = idx0offs = idx1offs = 0; swap = false; }
+
+       astring fname;          // filename for each track
+       UINT32 offset;          // offset in the data file for each track
+       bool swap;                      // data needs to be byte swapped
+       UINT32 idx0offs;
+       UINT32 idx1offs;
+};
+
+struct chdcd_track_input_info
+{
+       void reset() { for (int i = 0; i < CD_MAX_TRACKS; i++) track[i].reset(); }
+
+       chdcd_track_input_entry track[CD_MAX_TRACKS];
+};
+
+
+chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo);
+
+#endif /* __CHDCD_H__ */
diff --git a/archivers/chd/chdcdrom.cpp b/archivers/chd/chdcdrom.cpp
new file mode 100644 (file)
index 0000000..a6549ea
--- /dev/null
@@ -0,0 +1,1200 @@
+#include "chdtypes.h"
+
+/***************************************************************************
+
+    cdrom.c
+
+    Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+    IMPORTANT:
+    "physical" block addresses are the actual addresses on the emulated CD.
+    "chd" block addresses are the block addresses in the CHD file.
+    Because we pad each track to a 4-frame boundry, these addressing
+    schemes will differ after track 1!
+
+***************************************************************************/
+
+#include "chdcdrom.h"
+
+#include <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(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
+       memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
+}
diff --git a/archivers/chd/chdcdrom.h b/archivers/chd/chdcdrom.h
new file mode 100644 (file)
index 0000000..dab3917
--- /dev/null
@@ -0,0 +1,211 @@
+/***************************************************************************
+
+    cdrom.h
+
+    Generic MAME cd-rom implementation
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CDROM_H__
+#define __CDROM_H__
+
+#include "osdcore.h"
+#include "chd.h"
+
+
+
+/***************************************************************************
+    CONSTANTS
+***************************************************************************/
+
+// tracks are padded to a multiple of this many frames
+const UINT32 CD_TRACK_PADDING = 4;
+
+#define CD_MAX_TRACKS                  (99)    /* AFAIK the theoretical limit */
+#define CD_MAX_SECTOR_DATA             (2352)
+#define CD_MAX_SUBCODE_DATA            (96)
+
+#define CD_FRAME_SIZE                  (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
+#define CD_FRAMES_PER_HUNK             (8)
+
+#define CD_METADATA_WORDS              (1+(CD_MAX_TRACKS * 6))
+
+enum
+{
+       CD_TRACK_MODE1 = 0,             /* mode 1 2048 bytes/sector */
+       CD_TRACK_MODE1_RAW,             /* mode 1 2352 bytes/sector */
+       CD_TRACK_MODE2,                 /* mode 2 2336 bytes/sector */
+       CD_TRACK_MODE2_FORM1,           /* mode 2 2048 bytes/sector */
+       CD_TRACK_MODE2_FORM2,           /* mode 2 2324 bytes/sector */
+       CD_TRACK_MODE2_FORM_MIX,        /* mode 2 2336 bytes/sector */
+       CD_TRACK_MODE2_RAW,             /* mode 2 2352 bytes / sector */
+       CD_TRACK_AUDIO,                 /* redbook audio track 2352 bytes/sector (588 samples) */
+
+       CD_TRACK_RAW_DONTCARE           /* special flag for cdrom_read_data: just return me whatever is there */
+};
+
+enum
+{
+       CD_SUB_NORMAL = 0,                      /* "cooked" 96 bytes per sector */
+       CD_SUB_RAW,                                     /* raw uninterleaved 96 bytes per sector */
+       CD_SUB_NONE                                     /* no subcode data stored */
+};
+
+#define        CD_FLAG_GDROM   0x00000001      // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+
+/***************************************************************************
+    TYPE DEFINITIONS
+***************************************************************************/
+
+struct cdrom_file;
+
+struct cdrom_track_info
+{
+       /* fields used by CHDMAN and in MAME */
+       UINT32 trktype;         /* track type */
+       UINT32 subtype;         /* subcode data type */
+       UINT32 datasize;        /* size of data in each sector of this track */
+       UINT32 subsize;         /* size of subchannel data in each sector of this track */
+       UINT32 frames;          /* number of frames in this track */
+       UINT32 extraframes;     /* number of "spillage" frames in this track */
+       UINT32 pregap;          /* number of pregap frames */
+       UINT32 postgap;         /* number of postgap frames */
+       UINT32 pgtype;          /* type of sectors in pregap */
+       UINT32 pgsub;           /* type of subchannel data in pregap */
+       UINT32 pgdatasize;      /* size of data in each sector of the pregap */
+       UINT32 pgsubsize;       /* size of subchannel data in each sector of the pregap */
+
+       /* fields used in CHDMAN only */
+       UINT32 padframes;       /* number of frames of padding to add to the end of the track; needed for GDI */
+
+       /* fields used in MAME only */
+       UINT32 physframeofs;    /* frame number on the real CD this track starts at */
+       UINT32 chdframeofs;     /* frame number this track starts at on the CHD */
+};
+
+
+struct cdrom_toc
+{
+       UINT32 numtrks;         /* number of tracks */
+       UINT32 flags;           /* see FLAG_ above */
+       cdrom_track_info tracks[CD_MAX_TRACKS];
+};
+
+
+
+/***************************************************************************
+    FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* base functionality */
+cdrom_file *cdrom_open(chd_file *chd);
+void cdrom_close(cdrom_file *file);
+
+cdrom_file *cdrom_open(const char *inputfile);
+
+/* core read access */
+UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype);
+UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer);
+chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length);
+
+/* handy utilities */
+UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame);
+UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track);
+
+/* TOC utilities */
+int cdrom_get_last_track(cdrom_file *file);
+int cdrom_get_adr_control(cdrom_file *file, int track);
+int cdrom_get_track_type(cdrom_file *file, int track);
+const cdrom_toc *cdrom_get_toc(cdrom_file *file);
+
+/* extra utilities */
+void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info);
+void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info);
+void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info);
+void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info);
+const char *cdrom_get_type_string(UINT32 trktype);
+const char *cdrom_get_subtype_string(UINT32 subtype);
+chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc);
+chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc);
+
+// ECC utilities
+bool ecc_verify(const UINT8 *sector);
+void ecc_generate(UINT8 *sector);
+void ecc_clear(UINT8 *sector);
+
+
+
+/***************************************************************************
+    INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE UINT32 msf_to_lba(UINT32 msf)
+{
+       return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
+}
+
+INLINE UINT32 lba_to_msf(UINT32 lba)
+{
+       UINT8 m, s, f;
+
+       m = lba / (60 * 75);
+       lba -= m * (60 * 75);
+       s = lba / 75;
+       f = lba % 75;
+
+       return ((m / 10) << 20) | ((m % 10) << 16) |
+              ((s / 10) << 12) | ((s % 10) <<  8) |
+              ((f / 10) <<  4) | ((f % 10) <<  0);
+}
+
+// segacd needs it like this.. investigate
+// Angelo also says PCE tracks often start playing at the
+// wrong address.. related?
+INLINE UINT32 lba_to_msf_alt(int lba)
+{
+       UINT32 ret = 0;
+
+       ret |= ((lba / (60 * 75))&0xff)<<16;
+       ret |= (((lba / 75) % 60)&0xff)<<8;
+       ret |= ((lba % 75)&0xff)<<0;
+
+       return ret;
+}
+
+
+
+
+#endif // __CDROM_H__
diff --git a/archivers/chd/chdcodec.cpp b/archivers/chd/chdcodec.cpp
new file mode 100644 (file)
index 0000000..f123446
--- /dev/null
@@ -0,0 +1,1752 @@
+#include "chdtypes.h"
+
+/***************************************************************************
+
+    chdcodec.c
+
+    Codecs used by the CHD format
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "chd.h"
+#include "hashing.h"
+#include "flac.h"
+#include "chdcdrom.h"
+#include "huffman.h"
+#include <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
diff --git a/archivers/chd/chdcodec.h b/archivers/chd/chdcodec.h
new file mode 100644 (file)
index 0000000..a8d6f53
--- /dev/null
@@ -0,0 +1,222 @@
+/***************************************************************************
+
+    chdcodec.h
+
+    Codecs used by the CHD format
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CHDCODEC_H__
+#define __CHDCODEC_H__
+
+#include "osdcore.h"
+
+
+#define CHDCODEC_VERIFY_COMPRESSION 0
+
+
+//**************************************************************************
+//  MACROS
+//**************************************************************************
+
+#define CHD_MAKE_TAG(a,b,c,d)          (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// forward references
+class chd_file;
+
+// base types
+typedef UINT32 chd_codec_type;
+
+
+// ======================> chd_codec
+
+// common base class for all compressors and decompressors
+class chd_codec
+{
+protected:
+       // can't create these directly
+       chd_codec(chd_file &file, UINT32 hunkbytes, bool lossy);
+
+public:
+       // allow public deletion
+       virtual ~chd_codec();
+
+       // accessors
+       chd_file &chd() const { return m_chd; }
+       UINT32 hunkbytes() const { return m_hunkbytes; }
+       bool lossy() const { return m_lossy; }
+
+       // implementation
+       virtual void configure(int param, void *config);
+
+private:
+       // internal state
+       chd_file &                      m_chd;
+       UINT32                          m_hunkbytes;
+       bool                            m_lossy;
+};
+
+
+// ======================> chd_compressor
+
+// base class for all compressors
+class chd_compressor : public chd_codec
+{
+protected:
+       // can't create these directly
+       chd_compressor(chd_file &file, UINT32 hunkbytes, bool lossy);
+
+public:
+       // implementation
+       virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) = 0;
+};
+
+
+// ======================> chd_decompressor
+
+// base class for all decompressors
+class chd_decompressor : public chd_codec
+{
+protected:
+       // can't create these directly
+       chd_decompressor(chd_file &file, UINT32 hunkbytes, bool lossy);
+
+public:
+       // implementation
+       virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) = 0;
+};
+
+
+// ======================> chd_codec_list
+
+// wrapper to get at the list of codecs
+class chd_codec_list
+{
+public:
+       // create compressors or decompressors
+       static chd_compressor *new_compressor(chd_codec_type type, chd_file &file);
+       static chd_decompressor *new_decompressor(chd_codec_type type, chd_file &file);
+
+       // utilities
+       static bool codec_exists(chd_codec_type type) { return (find_in_list(type) != NULL); }
+       static const char *codec_name(chd_codec_type type);
+
+private:
+       // an entry in the list
+       struct codec_entry
+       {
+               chd_codec_type          m_type;
+               bool                            m_lossy;
+               const char *            m_name;
+               chd_compressor *        (*m_construct_compressor)(chd_file &, UINT32, bool);
+               chd_decompressor *      (*m_construct_decompressor)(chd_file &, UINT32, bool);
+       };
+
+       // internal helper functions
+       static const codec_entry *find_in_list(chd_codec_type type);
+
+       template<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__
diff --git a/archivers/chd/chdtypes.h b/archivers/chd/chdtypes.h
new file mode 100644 (file)
index 0000000..cad86c9
--- /dev/null
@@ -0,0 +1,48 @@
+
+
+#ifndef UAE_CHD
+#define UAE_CHD
+
+#ifndef USE_ZFILE
+#include "sysconfig.h"
+#include "sysdeps.h"
+#include "zfile.h"
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+#define DECL_NORETURN                  __declspec(noreturn)
+#else
+#define DECL_NORETURN
+#endif
+
+#define INLINE __inline
+
+#ifndef MIN
+#define MIN(x,y)                       ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x,y)                       ((x) > (y) ? (x) : (y))
+#endif
+
+#define ARRAY_LENGTH(x)                (sizeof(x) / sizeof(x[0]))
+
+#define CLIB_DECL __cdecl
+
+#define FLAC__NO_DLL
+
+/* Macros for normalizing data into big or little endian formats */
+#define FLIPENDIAN_INT16(x)    (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff)
+#define FLIPENDIAN_INT32(x)    ((((UINT32) (x)) << 24) | (((UINT32) (x)) >> 24) | \
+       (( ((UINT32) (x)) & 0x0000ff00) << 8) | (( ((UINT32) (x)) & 0x00ff0000) >> 8))
+#define FLIPENDIAN_INT64(x)    \
+       (                                                                                               \
+               (((((UINT64) (x)) >> 56) & ((UINT64) 0xFF)) <<  0)      |       \
+               (((((UINT64) (x)) >> 48) & ((UINT64) 0xFF)) <<  8)      |       \
+               (((((UINT64) (x)) >> 40) & ((UINT64) 0xFF)) << 16)      |       \
+               (((((UINT64) (x)) >> 32) & ((UINT64) 0xFF)) << 24)      |       \
+               (((((UINT64) (x)) >> 24) & ((UINT64) 0xFF)) << 32)      |       \
+               (((((UINT64) (x)) >> 16) & ((UINT64) 0xFF)) << 40)      |       \
+               (((((UINT64) (x)) >>  8) & ((UINT64) 0xFF)) << 48)      |       \
+               (((((UINT64) (x)) >>  0) & ((UINT64) 0xFF)) << 56)              \
+       )
+#endif
diff --git a/archivers/chd/corefile.h b/archivers/chd/corefile.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/archivers/chd/coretmpl.h b/archivers/chd/coretmpl.h
new file mode 100644 (file)
index 0000000..30d181e
--- /dev/null
@@ -0,0 +1,109 @@
+/***************************************************************************
+
+    coretmpl.h
+
+    Core templates for basic non-string types.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __CORETMPL_H__
+#define __CORETMPL_H__
+
+#include <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
diff --git a/archivers/chd/flac.cpp b/archivers/chd/flac.cpp
new file mode 100644 (file)
index 0000000..955a284
--- /dev/null
@@ -0,0 +1,648 @@
+#include "chdtypes.h"
+
+/***************************************************************************
+
+    flac.c
+
+    FLAC compression wrappers
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "flac.h"
+#include <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)
+{
+}
diff --git a/archivers/chd/flac.h b/archivers/chd/flac.h
new file mode 100644 (file)
index 0000000..3f2cfe3
--- /dev/null
@@ -0,0 +1,178 @@
+/***************************************************************************
+
+    flac.h
+
+    FLAC compression wrappers
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __FLAC_H__
+#define __FLAC_H__
+
+#include "osdcore.h"
+#include "corefile.h"
+
+#ifdef FLAC__NO_DLL
+#include "flac/all.h"
+#else
+#include <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__
diff --git a/archivers/chd/hashing.cpp b/archivers/chd/hashing.cpp
new file mode 100644 (file)
index 0000000..0dff0d6
--- /dev/null
@@ -0,0 +1,308 @@
+/***************************************************************************
+
+    hashing.c
+
+    Hashing helper classes.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "chdtypes.h"
+
+#include "hashing.h"
+#include "zlib.h"
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
+
+const crc16_t crc16_t::null = { 0 };
+const crc32_t crc32_t::null = { 0 };
+const md5_t md5_t::null = { { 0 } };
+const sha1_t sha1_t::null = { { 0 } };
+
+
+
+//**************************************************************************
+//  INLINE FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+//  char_to_hex - return the hex value of a
+//  character
+//-------------------------------------------------
+
+inline int char_to_hex(char c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return 10 + c - 'a';
+       if (c >= 'A' && c <= 'F')
+               return 10 + c - 'A';
+       return -1;
+}
+
+
+
+//**************************************************************************
+//  SHA-1 HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+//  from_string - convert from a string
+//-------------------------------------------------
+
+bool sha1_t::from_string(const char *string, int length)
+{
+       // must be at least long enough to hold everything
+       memset(m_raw, 0, sizeof(m_raw));
+       if (length == -1)
+               length = strlen(string);
+       if (length < 2 * sizeof(m_raw))
+               return false;
+
+       // iterate through our raw buffer
+       for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++)
+       {
+               int upper = char_to_hex(*string++);
+               int lower = char_to_hex(*string++);
+               if (upper == -1 || lower == -1)
+                       return false;
+               m_raw[bytenum] = (upper << 4) | lower;
+       }
+       return true;
+}
+
+
+//-------------------------------------------------
+//  as_string - convert to a string
+//-------------------------------------------------
+
+const char *sha1_t::as_string(astring &buffer) const
+{
+       buffer.reset();
+       for (int i = 0; i < ARRAY_LENGTH(m_raw); i++)
+               buffer.catformat("%02x", m_raw[i]);
+       return buffer;
+}
+
+
+//**************************************************************************
+//  MD-5 HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+//  from_string - convert from a string
+//-------------------------------------------------
+
+bool md5_t::from_string(const char *string, int length)
+{
+       // must be at least long enough to hold everything
+       memset(m_raw, 0, sizeof(m_raw));
+       if (length == -1)
+               length = strlen(string);
+       if (length < 2 * sizeof(m_raw))
+               return false;
+
+       // iterate through our raw buffer
+       for (int bytenum = 0; bytenum < sizeof(m_raw); bytenum++)
+       {
+               int upper = char_to_hex(*string++);
+               int lower = char_to_hex(*string++);
+               if (upper == -1 || lower == -1)
+                       return false;
+               m_raw[bytenum] = (upper << 4) | lower;
+       }
+       return true;
+}
+
+
+//-------------------------------------------------
+//  as_string - convert to a string
+//-------------------------------------------------
+
+const char *md5_t::as_string(astring &buffer) const
+{
+       buffer.reset();
+       for (int i = 0; i < ARRAY_LENGTH(m_raw); i++)
+               buffer.catformat("%02x", m_raw[i]);
+       return buffer;
+}
+
+
+
+//**************************************************************************
+//  CRC-32 HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+//  from_string - convert from a string
+//-------------------------------------------------
+
+bool crc32_t::from_string(const char *string, int length)
+{
+       // must be at least long enough to hold everything
+       m_raw = 0;
+       if (length == -1)
+               length = strlen(string);
+       if (length < 2 * sizeof(m_raw))
+               return false;
+
+       // iterate through our raw buffer
+       m_raw = 0;
+       for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
+       {
+               int nibble = char_to_hex(*string++);
+               if (nibble == -1)
+                       return false;
+               m_raw = (m_raw << 4) | nibble;
+       }
+       return true;
+}
+
+
+//-------------------------------------------------
+//  as_string - convert to a string
+//-------------------------------------------------
+
+const char *crc32_t::as_string(astring &buffer) const
+{
+       return buffer.format("%08x", m_raw);
+}
+
+
+//-------------------------------------------------
+//  append - hash a block of data, appending to
+//  the currently-accumulated value
+//-------------------------------------------------
+
+void crc32_creator::append(const void *data, UINT32 length)
+{
+       m_accum.m_raw = crc32(m_accum, reinterpret_cast<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
diff --git a/archivers/chd/hashing.h b/archivers/chd/hashing.h
new file mode 100644 (file)
index 0000000..d98b617
--- /dev/null
@@ -0,0 +1,247 @@
+/***************************************************************************
+
+    hashing.h
+
+    Hashing helper classes.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __HASHING_H__
+#define __HASHING_H__
+
+#include "astring.h"
+#include "md5.h"
+#include "sha1.h"
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> SHA-1
+
+// final digest
+struct sha1_t
+{
+       bool operator==(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
+       bool operator!=(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
+       operator UINT8 *() { return m_raw; }
+       bool from_string(const char *string, int length = -1);
+       const char *as_string(astring &buffer) const;
+       UINT8 m_raw[20];
+       static const sha1_t null;
+};
+
+// creation helper
+class sha1_creator
+{
+public:
+       // construction/destruction
+       sha1_creator() { reset(); }
+
+       // reset
+       void reset() { sha1_init(&m_context); }
+
+       // append data
+       void append(const void *data, UINT32 length) { sha1_update(&m_context, length, reinterpret_cast<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__
diff --git a/archivers/chd/huffman.cpp b/archivers/chd/huffman.cpp
new file mode 100644 (file)
index 0000000..ff232d1
--- /dev/null
@@ -0,0 +1,782 @@
+
+#include "chdtypes.h"
+
+/***************************************************************************
+
+    huffman.c
+
+    Static Huffman compression and decompression helpers.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+    Maximum codelength is officially (alphabetsize - 1). This would be 255 bits
+    (since we use 1 byte values). However, it is also dependent upon the number
+    of samples used, as follows:
+
+         2 bits -> 3..4 samples
+         3 bits -> 5..7 samples
+         4 bits -> 8..12 samples
+         5 bits -> 13..20 samples
+         6 bits -> 21..33 samples
+         7 bits -> 34..54 samples
+         8 bits -> 55..88 samples
+         9 bits -> 89..143 samples
+        10 bits -> 144..232 samples
+        11 bits -> 233..376 samples
+        12 bits -> 377..609 samples
+        13 bits -> 610..986 samples
+        14 bits -> 987..1596 samples
+        15 bits -> 1597..2583 samples
+        16 bits -> 2584..4180 samples   -> note that a 4k data size guarantees codelength <= 16 bits
+        17 bits -> 4181..6764 samples
+        18 bits -> 6765..10945 samples
+        19 bits -> 10946..17710 samples
+        20 bits -> 17711..28656 samples
+        21 bits -> 28657..46367 samples
+        22 bits -> 46368..75024 samples
+        23 bits -> 75025..121392 samples
+        24 bits -> 121393..196417 samples
+        25 bits -> 196418..317810 samples
+        26 bits -> 317811..514228 samples
+        27 bits -> 514229..832039 samples
+        28 bits -> 832040..1346268 samples
+        29 bits -> 1346269..2178308 samples
+        30 bits -> 2178309..3524577 samples
+        31 bits -> 3524578..5702886 samples
+        32 bits -> 5702887..9227464 samples
+
+    Looking at it differently, here is where powers of 2 fall into these buckets:
+
+          256 samples -> 11 bits max
+          512 samples -> 12 bits max
+           1k samples -> 14 bits max
+           2k samples -> 15 bits max
+           4k samples -> 16 bits max
+           8k samples -> 18 bits max
+          16k samples -> 19 bits max
+          32k samples -> 21 bits max
+          64k samples -> 22 bits max
+         128k samples -> 24 bits max
+         256k samples -> 25 bits max
+         512k samples -> 27 bits max
+           1M samples -> 28 bits max
+           2M samples -> 29 bits max
+           4M samples -> 31 bits max
+           8M samples -> 32 bits max
+
+****************************************************************************
+
+    Delta-RLE encoding works as follows:
+
+    Starting value is assumed to be 0. All data is encoded as a delta
+    from the previous value, such that final[i] = final[i - 1] + delta.
+    Long runs of 0s are RLE-encoded as follows:
+
+        0x100 = repeat count of 8
+        0x101 = repeat count of 9
+        0x102 = repeat count of 10
+        0x103 = repeat count of 11
+        0x104 = repeat count of 12
+        0x105 = repeat count of 13
+        0x106 = repeat count of 14
+        0x107 = repeat count of 15
+        0x108 = repeat count of 16
+        0x109 = repeat count of 32
+        0x10a = repeat count of 64
+        0x10b = repeat count of 128
+        0x10c = repeat count of 256
+        0x10d = repeat count of 512
+        0x10e = repeat count of 1024
+        0x10f = repeat count of 2048
+
+    Note that repeat counts are reset at the end of a row, so if a 0 run
+    extends to the end of a row, a large repeat count may be used.
+
+    The reason for starting the run counts at 8 is that 0 is expected to
+    be the most common symbol, and is typically encoded in 1 or 2 bits.
+
+***************************************************************************/
+
+#include <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;
+}
diff --git a/archivers/chd/huffman.h b/archivers/chd/huffman.h
new file mode 100644 (file)
index 0000000..47525d8
--- /dev/null
@@ -0,0 +1,252 @@
+/***************************************************************************
+
+    huffman.h
+
+    Static Huffman compression and decompression helpers.
+
+****************************************************************************
+
+    Copyright Aaron Giles
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above copyright
+          notice, this list of conditions and the following disclaimer in
+          the documentation and/or other materials provided with the
+          distribution.
+        * Neither the name 'MAME' nor the names of its contributors may be
+          used to endorse or promote products derived from this software
+          without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __HUFFMAN_H__
+#define __HUFFMAN_H__
+
+#include "osdcore.h"
+#include "bitstream.h"
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
+
+enum huffman_error
+{
+       HUFFERR_NONE = 0,
+       HUFFERR_TOO_MANY_BITS,
+       HUFFERR_INVALID_DATA,
+       HUFFERR_INPUT_BUFFER_TOO_SMALL,
+       HUFFERR_OUTPUT_BUFFER_TOO_SMALL,
+       HUFFERR_INTERNAL_INCONSISTENCY,
+       HUFFERR_TOO_MANY_CONTEXTS
+};
+
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> huffman_context_base
+
+// base class for encoding and decoding
+class huffman_context_base
+{
+protected:
+       typedef UINT16 lookup_value;
+
+       // a node in the huffman tree
+       struct node_t
+       {
+               node_t *                        m_parent;                               // pointer to parent node
+               UINT32                          m_count;                                // number of hits on this node
+               UINT32                          m_weight;                               // assigned weight of this node
+               UINT32                          m_bits;                                 // bits used to encode the node
+               UINT8                           m_numbits;                              // number of bits needed for this node
+       };
+
+       // construction/destruction
+       huffman_context_base(int numcodes, int maxbits, lookup_value *lookup, UINT32 *histo, node_t *nodes);
+
+       // tree creation
+       huffman_error compute_tree_from_histo();
+
+       // static tree import; huffman is notably more efficient
+       huffman_error import_tree_rle(bitstream_in &bitbuf);
+       huffman_error import_tree_huffman(bitstream_in &bitbuf);
+
+       // static tree export
+       huffman_error export_tree_rle(bitstream_out &bitbuf);
+       huffman_error export_tree_huffman(bitstream_out &bitbuf);
+
+       // internal helpers
+       void write_rle_tree_bits(bitstream_out &bitbuf, int value, int repcount, int numbits);
+       static int CLIB_DECL tree_node_compare(const void *item1, const void *item2);
+       int build_tree(UINT32 totaldata, UINT32 totalweight);
+       huffman_error assign_canonical_codes();
+       void build_lookup_table();
+
+protected:
+       // internal state
+       UINT32                                  m_numcodes;                             // number of total codes being processed
+       UINT8                                   m_maxbits;                              // maximum bits per code
+       UINT8                                   m_prevdata;                             // value of the previous data (for delta-RLE encoding)
+       int                                             m_rleremaining;                 // number of RLE bytes remaining (for delta-RLE encoding)
+       lookup_value *                  m_lookup;                               // pointer to the lookup table
+       UINT32 *                                m_datahisto;                    // histogram of data values
+       node_t *                                m_huffnode;                             // array of nodes
+};
+
+
+// ======================> huffman_encoder
+
+// template class for encoding
+template<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
diff --git a/archivers/chd/md5.cpp b/archivers/chd/md5.cpp
new file mode 100644 (file)
index 0000000..0c9c91c
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ *  - Ian Jackson <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
diff --git a/archivers/chd/md5.h b/archivers/chd/md5.h
new file mode 100644 (file)
index 0000000..5270f1b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ *  - Ian Jackson <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 */
diff --git a/archivers/chd/osdcore.h b/archivers/chd/osdcore.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/archivers/chd/sha1.cpp b/archivers/chd/sha1.cpp
new file mode 100644 (file)
index 0000000..5e2de84
--- /dev/null
@@ -0,0 +1,389 @@
+/* sha1.h
+ *
+ * The sha1 hash function.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright 2001 Peter Gutmann, Andrew Kuchling, Niels Moeller
+ *
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "chdtypes.h"
+
+#include "sha1.h"
+
+#include <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;
+       }
+    }
+}
diff --git a/archivers/chd/sha1.h b/archivers/chd/sha1.h
new file mode 100644 (file)
index 0000000..028f330
--- /dev/null
@@ -0,0 +1,61 @@
+/* sha1.h
+ *
+ * The sha1 hash function.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright 2001 Niels Moeller
+ *
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef NETTLE_SHA1_H_INCLUDED
+#define NETTLE_SHA1_H_INCLUDED
+
+#include "osdcore.h"
+
+#define SHA1_DIGEST_SIZE 20
+#define SHA1_DATA_SIZE 64
+
+/* Digest is kept internally as 4 32-bit words. */
+#define _SHA1_DIGEST_LENGTH 5
+
+struct sha1_ctx
+{
+  UINT32 digest[_SHA1_DIGEST_LENGTH];   /* Message digest */
+  UINT32 count_low, count_high;         /* 64-bit block count */
+  UINT8 block[SHA1_DATA_SIZE];          /* SHA1 data buffer */
+  unsigned int index;                     /* index into buffer */
+};
+
+void
+sha1_init(struct sha1_ctx *ctx);
+
+void
+sha1_update(struct sha1_ctx *ctx,
+           unsigned length,
+           const UINT8 *data);
+
+void
+sha1_final(struct sha1_ctx *ctx);
+
+void
+sha1_digest(const struct sha1_ctx *ctx,
+           unsigned length,
+           UINT8 *digest);
+
+#endif /* NETTLE_SHA1_H_INCLUDED */